github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/state/contract.go (about) 1 package state 2 3 import ( 4 "errors" 5 "math" 6 "math/big" 7 8 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 9 "github.com/nspcc-dev/neo-go/pkg/io" 10 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 11 "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" 12 "github.com/nspcc-dev/neo-go/pkg/util" 13 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 14 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 15 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 16 ) 17 18 // Contract holds information about a smart contract in the Neo blockchain. 19 type Contract struct { 20 ContractBase 21 UpdateCounter uint16 `json:"updatecounter"` 22 } 23 24 // ContractBase represents a part shared by native and user-deployed contracts. 25 type ContractBase struct { 26 ID int32 `json:"id"` 27 Hash util.Uint160 `json:"hash"` 28 NEF nef.File `json:"nef"` 29 Manifest manifest.Manifest `json:"manifest"` 30 } 31 32 // ToStackItem converts state.Contract to stackitem.Item. 33 func (c *Contract) ToStackItem() (stackitem.Item, error) { 34 // Do not skip the NEF size check, it won't affect native Management related 35 // states as the same checked is performed during contract deploy/update. 36 rawNef, err := c.NEF.Bytes() 37 if err != nil { 38 return nil, err 39 } 40 m, err := c.Manifest.ToStackItem() 41 if err != nil { 42 return nil, err 43 } 44 return stackitem.NewArray([]stackitem.Item{ 45 stackitem.Make(c.ID), 46 stackitem.Make(c.UpdateCounter), 47 stackitem.NewByteArray(c.Hash.BytesBE()), 48 stackitem.NewByteArray(rawNef), 49 m, 50 }), nil 51 } 52 53 // FromStackItem fills Contract's data from the given stack itemized contract 54 // representation. 55 func (c *Contract) FromStackItem(item stackitem.Item) error { 56 arr, ok := item.Value().([]stackitem.Item) 57 if !ok { 58 return errors.New("not an array") 59 } 60 if len(arr) != 5 { 61 return errors.New("invalid structure") 62 } 63 bi, ok := arr[0].Value().(*big.Int) 64 if !ok { 65 return errors.New("ID is not an integer") 66 } 67 if !bi.IsInt64() || bi.Int64() > math.MaxInt32 || bi.Int64() < math.MinInt32 { 68 return errors.New("ID not in int32 range") 69 } 70 c.ID = int32(bi.Int64()) 71 bi, ok = arr[1].Value().(*big.Int) 72 if !ok { 73 return errors.New("UpdateCounter is not an integer") 74 } 75 if !bi.IsUint64() || bi.Uint64() > math.MaxUint16 { 76 return errors.New("UpdateCounter not in uint16 range") 77 } 78 c.UpdateCounter = uint16(bi.Uint64()) 79 bytes, err := arr[2].TryBytes() 80 if err != nil { 81 return err 82 } 83 c.Hash, err = util.Uint160DecodeBytesBE(bytes) 84 if err != nil { 85 return err 86 } 87 bytes, err = arr[3].TryBytes() 88 if err != nil { 89 return err 90 } 91 c.NEF, err = nef.FileFromBytes(bytes) 92 if err != nil { 93 return err 94 } 95 m := new(manifest.Manifest) 96 err = m.FromStackItem(arr[4]) 97 if err != nil { 98 return err 99 } 100 c.Manifest = *m 101 return nil 102 } 103 104 // CreateContractHash creates a deployed contract hash from the transaction sender 105 // and the contract script. 106 func CreateContractHash(sender util.Uint160, checksum uint32, name string) util.Uint160 { 107 w := io.NewBufBinWriter() 108 emit.Opcodes(w.BinWriter, opcode.ABORT) 109 emit.Bytes(w.BinWriter, sender.BytesBE()) 110 emit.Int(w.BinWriter, int64(checksum)) 111 emit.String(w.BinWriter, name) 112 if w.Err != nil { 113 panic(w.Err) 114 } 115 return hash.Hash160(w.Bytes()) 116 } 117 118 // CreateNativeContractHash calculates the hash for the native contract with the 119 // given name. 120 func CreateNativeContractHash(name string) util.Uint160 { 121 return CreateContractHash(util.Uint160{}, 0, name) 122 }