e4b5b59663
core: serialise manifest as stackitem
321 lines
8.6 KiB
Go
321 lines
8.6 KiB
Go
package manifest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"math"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
)
|
|
|
|
const (
|
|
// MaxManifestSize is a max length for a valid contract manifest.
|
|
MaxManifestSize = math.MaxUint16
|
|
|
|
// MethodInit is a name for default initialization method.
|
|
MethodInit = "_initialize"
|
|
|
|
// MethodDeploy is a name for default method called during contract deployment.
|
|
MethodDeploy = "_deploy"
|
|
|
|
// MethodVerify is a name for default verification method.
|
|
MethodVerify = "verify"
|
|
|
|
// MethodOnNEP17Payment is name of the method which is called when contract receives NEP-17 tokens.
|
|
MethodOnNEP17Payment = "onNEP17Payment"
|
|
|
|
// MethodOnNEP11Payment is the name of the method which is called when contract receives NEP-11 tokens.
|
|
MethodOnNEP11Payment = "onNEP11Payment"
|
|
|
|
// NEP10StandardName represents the name of NEP10 smartcontract standard.
|
|
NEP10StandardName = "NEP-10"
|
|
// NEP17StandardName represents the name of NEP17 smartcontract standard.
|
|
NEP17StandardName = "NEP-17"
|
|
)
|
|
|
|
// ABI represents a contract application binary interface.
|
|
type ABI struct {
|
|
Methods []Method `json:"methods"`
|
|
Events []Event `json:"events"`
|
|
}
|
|
|
|
// Manifest represens contract metadata.
|
|
type Manifest struct {
|
|
// Name is a contract's name.
|
|
Name string `json:"name"`
|
|
// ABI is a contract's ABI.
|
|
ABI ABI `json:"abi"`
|
|
// Groups is a set of groups to which a contract belongs.
|
|
Groups []Group `json:"groups"`
|
|
Permissions []Permission `json:"permissions"`
|
|
// SupportedStandards is a list of standards supported by the contract.
|
|
SupportedStandards []string `json:"supportedstandards"`
|
|
// Trusts is a set of hashes to a which contract trusts.
|
|
Trusts WildUint160s `json:"trusts"`
|
|
// Extra is an implementation-defined user data.
|
|
Extra interface{} `json:"extra"`
|
|
}
|
|
|
|
// NewManifest returns new manifest with necessary fields initialized.
|
|
func NewManifest(name string) *Manifest {
|
|
m := &Manifest{
|
|
Name: name,
|
|
ABI: ABI{
|
|
Methods: []Method{},
|
|
Events: []Event{},
|
|
},
|
|
Groups: []Group{},
|
|
Permissions: []Permission{},
|
|
SupportedStandards: []string{},
|
|
}
|
|
m.Trusts.Restrict()
|
|
return m
|
|
}
|
|
|
|
// DefaultManifest returns default contract manifest.
|
|
func DefaultManifest(name string) *Manifest {
|
|
m := NewManifest(name)
|
|
m.Permissions = []Permission{*NewPermission(PermissionWildcard)}
|
|
return m
|
|
}
|
|
|
|
// GetMethod returns methods with the specified name.
|
|
func (a *ABI) GetMethod(name string, paramCount int) *Method {
|
|
for i := range a.Methods {
|
|
if a.Methods[i].Name == name && (paramCount == -1 || len(a.Methods[i].Parameters) == paramCount) {
|
|
return &a.Methods[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetEvent returns event with the specified name.
|
|
func (a *ABI) GetEvent(name string) *Event {
|
|
for i := range a.Events {
|
|
if a.Events[i].Name == name {
|
|
return &a.Events[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CanCall returns true is current contract is allowed to call
|
|
// method of another contract with specified hash.
|
|
func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) bool {
|
|
for i := range m.Permissions {
|
|
if m.Permissions[i].IsAllowed(hash, toCall, method) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsValid checks whether the hash given is correct wrt manifest's groups.
|
|
func (m *Manifest) IsValid(hash util.Uint160) bool {
|
|
for _, g := range m.Groups {
|
|
if !g.IsValid(hash) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// EncodeBinary implements io.Serializable.
|
|
func (m *Manifest) EncodeBinary(w *io.BinWriter) {
|
|
data, err := json.Marshal(m)
|
|
if err != nil {
|
|
w.Err = err
|
|
return
|
|
}
|
|
w.WriteVarBytes(data)
|
|
}
|
|
|
|
// DecodeBinary implements io.Serializable.
|
|
func (m *Manifest) DecodeBinary(r *io.BinReader) {
|
|
data := r.ReadVarBytes(MaxManifestSize)
|
|
if r.Err != nil {
|
|
return
|
|
} else if err := json.Unmarshal(data, m); err != nil {
|
|
r.Err = err
|
|
}
|
|
}
|
|
|
|
// ToStackItem converts Manifest to stackitem.Item.
|
|
func (m *Manifest) ToStackItem() (stackitem.Item, error) {
|
|
groups := make([]stackitem.Item, len(m.Groups))
|
|
for i := range m.Groups {
|
|
groups[i] = m.Groups[i].ToStackItem()
|
|
}
|
|
supportedStandards := make([]stackitem.Item, len(m.SupportedStandards))
|
|
for i := range m.SupportedStandards {
|
|
supportedStandards[i] = stackitem.Make(m.SupportedStandards[i])
|
|
}
|
|
abi := m.ABI.ToStackItem()
|
|
permissions := make([]stackitem.Item, len(m.Permissions))
|
|
for i := range m.Permissions {
|
|
permissions[i] = m.Permissions[i].ToStackItem()
|
|
}
|
|
trusts := stackitem.Item(stackitem.Null{})
|
|
if !m.Trusts.IsWildcard() {
|
|
tItems := make([]stackitem.Item, len(m.Trusts.Value))
|
|
for i := range m.Trusts.Value {
|
|
tItems[i] = stackitem.NewByteArray(m.Trusts.Value[i].BytesBE())
|
|
}
|
|
trusts = stackitem.Make(tItems)
|
|
}
|
|
extra := stackitem.Make("null")
|
|
if m.Extra != nil {
|
|
e, err := json.Marshal(m.Extra)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extra = stackitem.NewByteArray(e)
|
|
}
|
|
return stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(m.Name),
|
|
stackitem.Make(groups),
|
|
stackitem.Make(supportedStandards),
|
|
abi,
|
|
stackitem.Make(permissions),
|
|
trusts,
|
|
extra,
|
|
}), nil
|
|
}
|
|
|
|
// FromStackItem converts stackitem.Item to Manifest.
|
|
func (m *Manifest) FromStackItem(item stackitem.Item) error {
|
|
var err error
|
|
if item.Type() != stackitem.StructT {
|
|
return errors.New("invalid Manifest stackitem type")
|
|
}
|
|
str := item.Value().([]stackitem.Item)
|
|
if len(str) != 7 {
|
|
return errors.New("invalid stackitem length")
|
|
}
|
|
m.Name, err = stackitem.ToString(str[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if str[1].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Groups stackitem type")
|
|
}
|
|
groups := str[1].Value().([]stackitem.Item)
|
|
m.Groups = make([]Group, len(groups))
|
|
for i := range groups {
|
|
group := new(Group)
|
|
err := group.FromStackItem(groups[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Groups[i] = *group
|
|
}
|
|
if str[2].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid SupportedStandards stackitem type")
|
|
}
|
|
supportedStandards := str[2].Value().([]stackitem.Item)
|
|
m.SupportedStandards = make([]string, len(supportedStandards))
|
|
for i := range supportedStandards {
|
|
m.SupportedStandards[i], err = stackitem.ToString(supportedStandards[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
abi := new(ABI)
|
|
if err := abi.FromStackItem(str[3]); err != nil {
|
|
return err
|
|
}
|
|
m.ABI = *abi
|
|
if str[4].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Permissions stackitem type")
|
|
}
|
|
permissions := str[4].Value().([]stackitem.Item)
|
|
m.Permissions = make([]Permission, len(permissions))
|
|
for i := range permissions {
|
|
p := new(Permission)
|
|
if err := p.FromStackItem(permissions[i]); err != nil {
|
|
return err
|
|
}
|
|
m.Permissions[i] = *p
|
|
}
|
|
if _, ok := str[5].(stackitem.Null); ok {
|
|
m.Trusts.Restrict()
|
|
} else {
|
|
if str[5].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Trusts stackitem type")
|
|
}
|
|
trusts := str[5].Value().([]stackitem.Item)
|
|
m.Trusts = WildUint160s{Value: make([]util.Uint160, len(trusts))}
|
|
for i := range trusts {
|
|
bytes, err := trusts[i].TryBytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Trusts.Value[i], err = util.Uint160DecodeBytesBE(bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
extra, err := str[6].TryBytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(extra) == "null" {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(extra, &m.Extra)
|
|
}
|
|
|
|
// ToStackItem converts ABI to stackitem.Item.
|
|
func (a *ABI) ToStackItem() stackitem.Item {
|
|
methods := make([]stackitem.Item, len(a.Methods))
|
|
for i := range a.Methods {
|
|
methods[i] = a.Methods[i].ToStackItem()
|
|
}
|
|
events := make([]stackitem.Item, len(a.Events))
|
|
for i := range a.Events {
|
|
events[i] = a.Events[i].ToStackItem()
|
|
}
|
|
return stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(methods),
|
|
stackitem.Make(events),
|
|
})
|
|
}
|
|
|
|
// FromStackItem converts stackitem.Item to ABI.
|
|
func (a *ABI) FromStackItem(item stackitem.Item) error {
|
|
if item.Type() != stackitem.StructT {
|
|
return errors.New("invalid ABI stackitem type")
|
|
}
|
|
str := item.Value().([]stackitem.Item)
|
|
if len(str) != 2 {
|
|
return errors.New("invalid ABI stackitem length")
|
|
}
|
|
if str[0].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Methods stackitem type")
|
|
}
|
|
methods := str[0].Value().([]stackitem.Item)
|
|
a.Methods = make([]Method, len(methods))
|
|
for i := range methods {
|
|
m := new(Method)
|
|
if err := m.FromStackItem(methods[i]); err != nil {
|
|
return err
|
|
}
|
|
a.Methods[i] = *m
|
|
}
|
|
if str[1].Type() != stackitem.ArrayT {
|
|
return errors.New("invalid Events stackitem type")
|
|
}
|
|
events := str[1].Value().([]stackitem.Item)
|
|
a.Events = make([]Event, len(events))
|
|
for i := range events {
|
|
e := new(Event)
|
|
if err := e.FromStackItem(events[i]); err != nil {
|
|
return err
|
|
}
|
|
a.Events[i] = *e
|
|
}
|
|
return nil
|
|
}
|