github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/manifest.go (about)

     1  package manifest
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  
    10  	ojson "github.com/nspcc-dev/go-ordered-json"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    13  )
    14  
    15  const (
    16  	// MaxManifestSize is the max length for a valid contract manifest.
    17  	MaxManifestSize = math.MaxUint16
    18  
    19  	// NEP11StandardName represents the name of NEP-11 smartcontract standard.
    20  	NEP11StandardName = "NEP-11"
    21  	// NEP17StandardName represents the name of NEP-17 smartcontract standard.
    22  	NEP17StandardName = "NEP-17"
    23  	// NEP11Payable represents the name of contract interface which can receive NEP-11 tokens.
    24  	NEP11Payable = "NEP-11-Payable"
    25  	// NEP17Payable represents the name of contract interface which can receive NEP-17 tokens.
    26  	NEP17Payable = "NEP-17-Payable"
    27  )
    28  
    29  // Manifest represens contract metadata.
    30  type Manifest struct {
    31  	// Name is a contract's name.
    32  	Name string `json:"name"`
    33  	// ABI is a contract's ABI.
    34  	ABI ABI `json:"abi"`
    35  	// Features is a set of contract features. Currently unused.
    36  	Features json.RawMessage `json:"features"`
    37  	// Groups is a set of groups to which a contract belongs.
    38  	Groups      []Group      `json:"groups"`
    39  	Permissions []Permission `json:"permissions"`
    40  	// SupportedStandards is a list of standards supported by the contract.
    41  	SupportedStandards []string `json:"supportedstandards"`
    42  	// Trusts is a set of hashes to a which contract trusts.
    43  	Trusts WildPermissionDescs `json:"trusts"`
    44  	// Extra is an implementation-defined user data.
    45  	Extra json.RawMessage `json:"extra"`
    46  }
    47  
    48  // NewManifest returns a new manifest with necessary fields initialized.
    49  func NewManifest(name string) *Manifest {
    50  	m := &Manifest{
    51  		Name: name,
    52  		ABI: ABI{
    53  			Methods: []Method{},
    54  			Events:  []Event{},
    55  		},
    56  		Features:           json.RawMessage("{}"),
    57  		Groups:             []Group{},
    58  		Permissions:        []Permission{},
    59  		SupportedStandards: []string{},
    60  		Extra:              json.RawMessage("null"),
    61  	}
    62  	m.Trusts.Restrict()
    63  	return m
    64  }
    65  
    66  // DefaultManifest returns the default contract manifest.
    67  func DefaultManifest(name string) *Manifest {
    68  	m := NewManifest(name)
    69  	m.Permissions = []Permission{*NewPermission(PermissionWildcard)}
    70  	return m
    71  }
    72  
    73  // CanCall returns true if the current contract is allowed to call
    74  // the method of another contract with the specified hash.
    75  func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) bool {
    76  	for i := range m.Permissions {
    77  		if m.Permissions[i].IsAllowed(hash, toCall, method) {
    78  			return true
    79  		}
    80  	}
    81  	return false
    82  }
    83  
    84  // IsValid checks manifest internal consistency and correctness, one of the
    85  // checks is for group signature correctness, contract hash is passed for it.
    86  // If hash is empty, then hash-related checks are omitted.
    87  func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error {
    88  	var err error
    89  
    90  	if m.Name == "" {
    91  		return errors.New("no name")
    92  	}
    93  
    94  	for i := range m.SupportedStandards {
    95  		if m.SupportedStandards[i] == "" {
    96  			return errors.New("invalid nameless supported standard")
    97  		}
    98  	}
    99  	if len(m.SupportedStandards) > 1 {
   100  		names := make([]string, len(m.SupportedStandards))
   101  		copy(names, m.SupportedStandards)
   102  		if stringsHaveDups(names) {
   103  			return errors.New("duplicate supported standards")
   104  		}
   105  	}
   106  	err = m.ABI.IsValid()
   107  	if err != nil {
   108  		return fmt.Errorf("ABI: %w", err)
   109  	}
   110  	err = Groups(m.Groups).AreValid(hash)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if len(m.Trusts.Value) > 1 {
   115  		hashes := make([]PermissionDesc, len(m.Trusts.Value))
   116  		copy(hashes, m.Trusts.Value)
   117  		if permissionDescsHaveDups(hashes) {
   118  			return errors.New("duplicate trusted contracts")
   119  		}
   120  	}
   121  	err = Permissions(m.Permissions).AreValid()
   122  	if err != nil {
   123  		return err
   124  	}
   125  	if !checkSize {
   126  		return nil
   127  	}
   128  	si, err := m.ToStackItem()
   129  	if err != nil {
   130  		return fmt.Errorf("failed to check manifest serialisation: %w", err)
   131  	}
   132  	_, err = stackitem.Serialize(si)
   133  	if err != nil {
   134  		return fmt.Errorf("manifest is not serializable: %w", err)
   135  	}
   136  	return nil
   137  }
   138  
   139  // IsStandardSupported denotes whether the specified standard is supported by the contract.
   140  func (m *Manifest) IsStandardSupported(standard string) bool {
   141  	for _, st := range m.SupportedStandards {
   142  		if st == standard {
   143  			return true
   144  		}
   145  	}
   146  	return false
   147  }
   148  
   149  // ToStackItem converts Manifest to stackitem.Item.
   150  func (m *Manifest) ToStackItem() (stackitem.Item, error) {
   151  	groups := make([]stackitem.Item, len(m.Groups))
   152  	for i := range m.Groups {
   153  		groups[i] = m.Groups[i].ToStackItem()
   154  	}
   155  	supportedStandards := make([]stackitem.Item, len(m.SupportedStandards))
   156  	for i := range m.SupportedStandards {
   157  		supportedStandards[i] = stackitem.Make(m.SupportedStandards[i])
   158  	}
   159  	abi := m.ABI.ToStackItem()
   160  	permissions := make([]stackitem.Item, len(m.Permissions))
   161  	for i := range m.Permissions {
   162  		permissions[i] = m.Permissions[i].ToStackItem()
   163  	}
   164  	trusts := stackitem.Item(stackitem.Null{})
   165  	if !m.Trusts.IsWildcard() {
   166  		tItems := make([]stackitem.Item, len(m.Trusts.Value))
   167  		for i, v := range m.Trusts.Value {
   168  			tItems[i] = v.ToStackItem()
   169  		}
   170  		trusts = stackitem.Make(tItems)
   171  	}
   172  	extra := extraToStackItem(m.Extra)
   173  	return stackitem.NewStruct([]stackitem.Item{
   174  		stackitem.Make(m.Name),
   175  		stackitem.Make(groups),
   176  		stackitem.NewMap(),
   177  		stackitem.Make(supportedStandards),
   178  		abi,
   179  		stackitem.Make(permissions),
   180  		trusts,
   181  		extra,
   182  	}), nil
   183  }
   184  
   185  // extraToStackItem removes indentation from `Extra` field in JSON and
   186  // converts it to a byte-array stack item.
   187  func extraToStackItem(rawExtra []byte) stackitem.Item {
   188  	extra := stackitem.Make("null")
   189  	if rawExtra == nil || string(rawExtra) == "null" {
   190  		return extra
   191  	}
   192  
   193  	d := ojson.NewDecoder(bytes.NewReader(rawExtra))
   194  	// The result is put directly in the database and affects state-root calculation,
   195  	// thus use ordered map to stay compatible with C# implementation.
   196  	d.UseOrderedObject()
   197  	// Prevent accidental precision loss.
   198  	d.UseNumber()
   199  
   200  	var obj any
   201  
   202  	// The error can't really occur because `json.RawMessage` is already a valid json.
   203  	_ = d.Decode(&obj)
   204  	res, _ := ojson.Marshal(obj)
   205  	return stackitem.NewByteArray(res)
   206  }
   207  
   208  // FromStackItem converts stackitem.Item to Manifest.
   209  func (m *Manifest) FromStackItem(item stackitem.Item) error {
   210  	var err error
   211  	if item.Type() != stackitem.StructT {
   212  		return errors.New("invalid Manifest stackitem type")
   213  	}
   214  	str := item.Value().([]stackitem.Item)
   215  	if len(str) != 8 {
   216  		return errors.New("invalid stackitem length")
   217  	}
   218  	m.Name, err = stackitem.ToString(str[0])
   219  	if err != nil {
   220  		return err
   221  	}
   222  	if str[1].Type() != stackitem.ArrayT {
   223  		return errors.New("invalid Groups stackitem type")
   224  	}
   225  	groups := str[1].Value().([]stackitem.Item)
   226  	m.Groups = make([]Group, len(groups))
   227  	for i := range groups {
   228  		group := new(Group)
   229  		err := group.FromStackItem(groups[i])
   230  		if err != nil {
   231  			return err
   232  		}
   233  		m.Groups[i] = *group
   234  	}
   235  	if str[2].Type() != stackitem.MapT || str[2].(*stackitem.Map).Len() != 0 {
   236  		return errors.New("invalid Features stackitem")
   237  	}
   238  	m.Features = json.RawMessage("{}")
   239  	if str[3].Type() != stackitem.ArrayT {
   240  		return errors.New("invalid SupportedStandards stackitem type")
   241  	}
   242  	supportedStandards := str[3].Value().([]stackitem.Item)
   243  	m.SupportedStandards = make([]string, len(supportedStandards))
   244  	for i := range supportedStandards {
   245  		m.SupportedStandards[i], err = stackitem.ToString(supportedStandards[i])
   246  		if err != nil {
   247  			return err
   248  		}
   249  	}
   250  	abi := new(ABI)
   251  	if err := abi.FromStackItem(str[4]); err != nil {
   252  		return err
   253  	}
   254  	m.ABI = *abi
   255  	if str[5].Type() != stackitem.ArrayT {
   256  		return errors.New("invalid Permissions stackitem type")
   257  	}
   258  	permissions := str[5].Value().([]stackitem.Item)
   259  	m.Permissions = make([]Permission, len(permissions))
   260  	for i := range permissions {
   261  		p := new(Permission)
   262  		if err := p.FromStackItem(permissions[i]); err != nil {
   263  			return err
   264  		}
   265  		m.Permissions[i] = *p
   266  	}
   267  	if _, ok := str[6].(stackitem.Null); ok {
   268  		m.Trusts = WildPermissionDescs{Value: nil} // wildcard by default
   269  	} else {
   270  		if str[6].Type() != stackitem.ArrayT {
   271  			return errors.New("invalid Trusts stackitem type")
   272  		}
   273  		trusts := str[6].Value().([]stackitem.Item)
   274  		m.Trusts = WildPermissionDescs{Value: make([]PermissionDesc, len(trusts))}
   275  		for i := range trusts {
   276  			v := new(PermissionDesc)
   277  			err = v.FromStackItem(trusts[i])
   278  			if err != nil {
   279  				return err
   280  			}
   281  			m.Trusts.Value[i] = *v
   282  		}
   283  	}
   284  	extra, err := str[7].TryBytes()
   285  	if err != nil {
   286  		return err
   287  	}
   288  	m.Extra = extra
   289  	return nil
   290  }