github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/interop/runtime/util.go (about) 1 package runtime 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "math/big" 7 8 "github.com/nspcc-dev/neo-go/pkg/config" 9 "github.com/nspcc-dev/neo-go/pkg/core/interop" 10 "github.com/nspcc-dev/neo-go/pkg/core/state" 11 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 12 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 13 "github.com/nspcc-dev/neo-go/pkg/util" 14 "github.com/nspcc-dev/neo-go/pkg/vm" 15 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 16 "github.com/twmb/murmur3" 17 ) 18 19 // GasLeft returns the remaining amount of GAS. 20 func GasLeft(ic *interop.Context) error { 21 if ic.VM.GasLimit == -1 { 22 ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit))) 23 } else { 24 ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit - ic.VM.GasConsumed()))) 25 } 26 return nil 27 } 28 29 // GetNotifications returns notifications emitted in the current execution context. 30 func GetNotifications(ic *interop.Context) error { 31 item := ic.VM.Estack().Pop().Item() 32 notifications := ic.Notifications 33 if _, ok := item.(stackitem.Null); !ok { 34 b, err := item.TryBytes() 35 if err != nil { 36 return err 37 } 38 u, err := util.Uint160DecodeBytesBE(b) 39 if err != nil { 40 return err 41 } 42 notifications = []state.NotificationEvent{} 43 for i := range ic.Notifications { 44 if ic.Notifications[i].ScriptHash.Equals(u) { 45 notifications = append(notifications, ic.Notifications[i]) 46 } 47 } 48 } 49 if len(notifications) > vm.MaxStackSize { 50 return errors.New("too many notifications") 51 } 52 arr := stackitem.NewArray(make([]stackitem.Item, 0, len(notifications))) 53 for i := range notifications { 54 ev := stackitem.NewArray([]stackitem.Item{ 55 stackitem.NewByteArray(notifications[i].ScriptHash.BytesBE()), 56 stackitem.Make(notifications[i].Name), 57 notifications[i].Item, 58 }) 59 arr.Append(ev) 60 } 61 ic.VM.Estack().PushItem(arr) 62 return nil 63 } 64 65 // GetInvocationCounter returns how many times the current contract has been invoked during the current tx execution. 66 func GetInvocationCounter(ic *interop.Context) error { 67 currentScriptHash := ic.VM.GetCurrentScriptHash() 68 count, ok := ic.Invocations[currentScriptHash] 69 if !ok { 70 count = 1 71 ic.Invocations[currentScriptHash] = count 72 } 73 ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(count)))) 74 return nil 75 } 76 77 // GetAddressVersion returns the address version of the current protocol. 78 func GetAddressVersion(ic *interop.Context) error { 79 ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(address.NEO3Prefix)))) 80 return nil 81 } 82 83 // GetNetwork returns chain network number. 84 func GetNetwork(ic *interop.Context) error { 85 m := ic.Chain.GetConfig().Magic 86 ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(m)))) 87 return nil 88 } 89 90 // GetRandom returns pseudo-random number which depends on block nonce and transaction hash. 91 func GetRandom(ic *interop.Context) error { 92 var ( 93 price int64 94 seed = ic.Network 95 ) 96 isHF := ic.IsHardforkEnabled(config.HFAspidochelone) 97 if isHF { 98 price = 1 << 13 99 seed += ic.GetRandomCounter 100 ic.GetRandomCounter++ 101 } else { 102 price = 1 << 4 103 } 104 res := murmur128(ic.NonceData[:], seed) 105 if !isHF { 106 copy(ic.NonceData[:], res) 107 } 108 if !ic.VM.AddGas(ic.BaseExecFee() * price) { 109 return errors.New("gas limit exceeded") 110 } 111 ic.VM.Estack().PushItem(stackitem.NewBigInteger(bigint.FromBytesUnsigned(res))) 112 return nil 113 } 114 115 func murmur128(data []byte, seed uint32) []byte { 116 h1, h2 := murmur3.SeedSum128(uint64(seed), uint64(seed), data) 117 result := make([]byte, 16) 118 binary.LittleEndian.PutUint64(result, h1) 119 binary.LittleEndian.PutUint64(result[8:], h2) 120 return result 121 }