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 }