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  }