neo-go/pkg/core/interop/runtime/util.go

124 lines
3.6 KiB
Go
Raw Normal View History

package runtime
import (
"encoding/binary"
"errors"
"math/big"
"slices"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/twmb/murmur3"
)
// GasLeft returns the remaining amount of GAS.
2020-08-07 11:37:49 +00:00
func GasLeft(ic *interop.Context) error {
if ic.VM.GasLimit == -1 {
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit)))
} else {
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit - ic.VM.GasConsumed())))
}
return nil
}
// GetNotifications returns notifications emitted in the current execution context.
2020-08-07 11:37:49 +00:00
func GetNotifications(ic *interop.Context) error {
item := ic.VM.Estack().Pop().Item()
notifications := ic.Notifications
if _, ok := item.(stackitem.Null); !ok {
b, err := item.TryBytes()
if err != nil {
return err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return err
}
notifications = []state.NotificationEvent{}
for i := range ic.Notifications {
if ic.Notifications[i].ScriptHash.Equals(u) {
notifications = append(notifications, ic.Notifications[i])
}
}
}
if len(notifications) > vm.MaxStackSize {
return errors.New("too many notifications")
}
arr := stackitem.NewArray(make([]stackitem.Item, 0, len(notifications)))
for i := range notifications {
ev := stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(notifications[i].ScriptHash.BytesBE()),
stackitem.Make(notifications[i].Name),
notifications[i].Item,
})
arr.Append(ev)
}
ic.VM.Estack().PushItem(arr)
return nil
}
// GetInvocationCounter returns how many times the current contract has been invoked during the current tx execution.
2020-08-07 11:37:49 +00:00
func GetInvocationCounter(ic *interop.Context) error {
currentScriptHash := ic.VM.GetCurrentScriptHash()
count, ok := ic.Invocations[currentScriptHash]
if !ok {
count = 1
ic.Invocations[currentScriptHash] = count
}
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(count))))
return nil
}
// GetAddressVersion returns the address version of the current protocol.
func GetAddressVersion(ic *interop.Context) error {
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(address.NEO3Prefix))))
return nil
}
// GetNetwork returns chain network number.
func GetNetwork(ic *interop.Context) error {
m := ic.Chain.GetConfig().Magic
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(m))))
return nil
}
// GetRandom returns pseudo-random number which depends on block nonce and transaction hash.
func GetRandom(ic *interop.Context) error {
var (
price int64
seed = ic.Network
)
isHF := ic.IsHardforkEnabled(config.HFAspidochelone)
if isHF {
price = 1 << 13
seed += ic.GetRandomCounter
ic.GetRandomCounter++
} else {
price = 1 << 4
}
res := murmur128(ic.NonceData[:], seed)
if !isHF {
ic.NonceData = [interop.ContextNonceDataLen]byte(res)
}
if !ic.VM.AddGas(ic.BaseExecFee() * price) {
return errors.New("gas limit exceeded")
}
// Resulting data is interpreted as an unsigned LE integer.
slices.Reverse(res)
ic.VM.Estack().PushItem(stackitem.NewBigInteger(new(big.Int).SetBytes(res)))
return nil
}
func murmur128(data []byte, seed uint32) []byte {
h1, h2 := murmur3.SeedSum128(uint64(seed), uint64(seed), data)
result := make([]byte, 16)
binary.LittleEndian.PutUint64(result, h1)
binary.LittleEndian.PutUint64(result[8:], h2)
return result
}