Merge pull request #1271 from nspcc-dev/core/call_from_native

core: contractCall from native contracts
This commit is contained in:
Roman Khimov 2020-08-07 21:24:50 +03:00 committed by GitHub
commit f5131491b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 558 additions and 394 deletions

View file

@ -23,6 +23,7 @@ func TestSHA256(t *testing.T) {
` `
v := vmAndCompile(t, src) v := vmAndCompile(t, src)
ic := &interop.Context{Trigger: trigger.Verification} ic := &interop.Context{Trigger: trigger.Verification}
ic.VM = v
crypto.Register(ic) crypto.Register(ic)
v.SyscallHandler = ic.SyscallHandler v.SyscallHandler = ic.SyscallHandler
require.NoError(t, v.Run()) require.NoError(t, v.Run())

View file

@ -18,19 +18,19 @@ type Callback interface {
} }
// Invoke invokes provided callback. // Invoke invokes provided callback.
func Invoke(ic *interop.Context, v *vm.VM) error { func Invoke(ic *interop.Context) error {
cb := v.Estack().Pop().Interop().Value().(Callback) cb := ic.VM.Estack().Pop().Interop().Value().(Callback)
args := v.Estack().Pop().Array() args := ic.VM.Estack().Pop().Array()
if cb.ArgCount() != len(args) { if cb.ArgCount() != len(args) {
return errors.New("invalid argument count") return errors.New("invalid argument count")
} }
cb.LoadContext(v, args) cb.LoadContext(ic.VM, args)
switch t := cb.(type) { switch t := cb.(type) {
case *MethodCallback: case *MethodCallback:
id := emit.InteropNameToID([]byte("System.Contract.Call")) id := emit.InteropNameToID([]byte("System.Contract.Call"))
return ic.SyscallHandler(v, id) return ic.SyscallHandler(ic.VM, id)
case *SyscallCallback: case *SyscallCallback:
return ic.SyscallHandler(v, t.desc.ID) return ic.SyscallHandler(ic.VM, t.desc.ID)
default: default:
return nil return nil
} }

View file

@ -33,8 +33,8 @@ func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
} }
// CreateFromMethod creates callback for a contract method. // CreateFromMethod creates callback for a contract method.
func CreateFromMethod(ic *interop.Context, v *vm.VM) error { func CreateFromMethod(ic *interop.Context) error {
rawHash := v.Estack().Pop().Bytes() rawHash := ic.VM.Estack().Pop().Bytes()
h, err := util.Uint160DecodeBytesBE(rawHash) h, err := util.Uint160DecodeBytesBE(rawHash)
if err != nil { if err != nil {
return err return err
@ -43,16 +43,16 @@ func CreateFromMethod(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return errors.New("contract not found") return errors.New("contract not found")
} }
method := string(v.Estack().Pop().Bytes()) method := string(ic.VM.Estack().Pop().Bytes())
if strings.HasPrefix(method, "_") { if strings.HasPrefix(method, "_") {
return errors.New("invalid method name") return errors.New("invalid method name")
} }
currCs, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) currCs, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
if err == nil && !currCs.Manifest.CanCall(&cs.Manifest, method) { if err == nil && !currCs.Manifest.CanCall(&cs.Manifest, method) {
return errors.New("method call is not allowed") return errors.New("method call is not allowed")
} }
md := cs.Manifest.ABI.GetMethod(method) md := cs.Manifest.ABI.GetMethod(method)
v.Estack().PushVal(stackitem.NewInterop(&MethodCallback{ ic.VM.Estack().PushVal(stackitem.NewInterop(&MethodCallback{
contract: cs, contract: cs,
method: md, method: md,
})) }))

View file

@ -29,11 +29,11 @@ func (p *PointerCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
} }
// Create creates callback using pointer and parameters count. // Create creates callback using pointer and parameters count.
func Create(_ *interop.Context, v *vm.VM) error { func Create(ic *interop.Context) error {
ctx := v.Estack().Pop().Item().(*vm.Context) ctx := ic.VM.Estack().Pop().Item().(*vm.Context)
offset := v.Estack().Pop().Item().(*stackitem.Pointer).Position() offset := ic.VM.Estack().Pop().Item().(*stackitem.Pointer).Position()
count := v.Estack().Pop().BigInt().Int64() count := ic.VM.Estack().Pop().BigInt().Int64()
v.Estack().PushVal(stackitem.NewInterop(&PointerCallback{ ic.VM.Estack().PushVal(stackitem.NewInterop(&PointerCallback{
paramCount: int(count), paramCount: int(count),
offset: offset, offset: offset,
context: ctx, context: ctx,

View file

@ -28,8 +28,8 @@ func (p *SyscallCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
} }
// CreateFromSyscall creates callback from syscall. // CreateFromSyscall creates callback from syscall.
func CreateFromSyscall(ic *interop.Context, v *vm.VM) error { func CreateFromSyscall(ic *interop.Context) error {
id := uint32(v.Estack().Pop().BigInt().Int64()) id := uint32(ic.VM.Estack().Pop().BigInt().Int64())
f := ic.GetFunction(id) f := ic.GetFunction(id)
if f == nil { if f == nil {
return errors.New("syscall not found") return errors.New("syscall not found")
@ -37,6 +37,6 @@ func CreateFromSyscall(ic *interop.Context, v *vm.VM) error {
if f.DisallowCallback { if f.DisallowCallback {
return errors.New("syscall is not allowed to be used in a callback") return errors.New("syscall is not allowed to be used in a callback")
} }
v.Estack().PushVal(stackitem.NewInterop(&SyscallCallback{f})) ic.VM.Estack().PushVal(stackitem.NewInterop(&SyscallCallback{f}))
return nil return nil
} }

View file

@ -35,7 +35,7 @@ type Context struct {
Notifications []state.NotificationEvent Notifications []state.NotificationEvent
Log *zap.Logger Log *zap.Logger
Invocations map[util.Uint160]int Invocations map[util.Uint160]int
ScriptGetter vm.ScriptHashGetter VM *vm.VM
Functions [][]Function Functions [][]Function
} }
@ -64,7 +64,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n
type Function struct { type Function struct {
ID uint32 ID uint32
Name string Name string
Func func(*Context, *vm.VM) error Func func(*Context) error
// DisallowCallback is true iff syscall can't be used in a callback. // DisallowCallback is true iff syscall can't be used in a callback.
DisallowCallback bool DisallowCallback bool
// ParamCount is a number of function parameters. // ParamCount is a number of function parameters.
@ -155,26 +155,26 @@ func (ic *Context) GetFunction(id uint32) *Function {
} }
// SyscallHandler handles syscall with id. // SyscallHandler handles syscall with id.
func (ic *Context) SyscallHandler(v *vm.VM, id uint32) error { func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
f := ic.GetFunction(id) f := ic.GetFunction(id)
if f == nil { if f == nil {
return errors.New("syscall not found") return errors.New("syscall not found")
} }
cf := v.Context().GetCallFlags() cf := ic.VM.Context().GetCallFlags()
if !cf.Has(f.RequiredFlags) { if !cf.Has(f.RequiredFlags) {
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags) return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
} }
if !v.AddGas(f.Price) { if !ic.VM.AddGas(f.Price) {
return errors.New("insufficient amount of gas") return errors.New("insufficient amount of gas")
} }
return f.Func(ic, v) return f.Func(ic)
} }
// SpawnVM spawns new VM with the specified gas limit. // SpawnVM spawns new VM with the specified gas limit and set context.VM field.
func (ic *Context) SpawnVM() *vm.VM { func (ic *Context) SpawnVM() *vm.VM {
v := vm.NewWithTrigger(ic.Trigger) v := vm.NewWithTrigger(ic.Trigger)
v.GasLimit = -1 v.GasLimit = -1
v.SyscallHandler = ic.SyscallHandler v.SyscallHandler = ic.SyscallHandler
ic.ScriptGetter = v ic.VM = v
return v return v
} }

View file

@ -18,56 +18,56 @@ import (
const ECDSAVerifyPrice = 1000000 const ECDSAVerifyPrice = 1000000
// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve. // ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve.
func ECDSASecp256r1Verify(ic *interop.Context, v *vm.VM) error { func ECDSASecp256r1Verify(ic *interop.Context) error {
return ecdsaVerify(ic, v, elliptic.P256()) return ecdsaVerify(ic, elliptic.P256())
} }
// ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve // ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve
func ECDSASecp256k1Verify(ic *interop.Context, v *vm.VM) error { func ECDSASecp256k1Verify(ic *interop.Context) error {
return ecdsaVerify(ic, v, btcec.S256()) return ecdsaVerify(ic, btcec.S256())
} }
// ecdsaVerify is internal representation of ECDSASecp256k1Verify and // ecdsaVerify is internal representation of ECDSASecp256k1Verify and
// ECDSASecp256r1Verify. // ECDSASecp256r1Verify.
func ecdsaVerify(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error { func ecdsaVerify(ic *interop.Context, curve elliptic.Curve) error {
msg := getMessage(ic, v.Estack().Pop().Item()) msg := getMessage(ic, ic.VM.Estack().Pop().Item())
hashToCheck := hash.Sha256(msg).BytesBE() hashToCheck := hash.Sha256(msg).BytesBE()
keyb := v.Estack().Pop().Bytes() keyb := ic.VM.Estack().Pop().Bytes()
signature := v.Estack().Pop().Bytes() signature := ic.VM.Estack().Pop().Bytes()
pkey, err := keys.NewPublicKeyFromBytes(keyb, curve) pkey, err := keys.NewPublicKeyFromBytes(keyb, curve)
if err != nil { if err != nil {
return err return err
} }
res := pkey.Verify(signature, hashToCheck) res := pkey.Verify(signature, hashToCheck)
v.Estack().PushVal(res) ic.VM.Estack().PushVal(res)
return nil return nil
} }
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using // ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
// Secp256r1 elliptic curve. // Secp256r1 elliptic curve.
func ECDSASecp256r1CheckMultisig(ic *interop.Context, v *vm.VM) error { func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
return ecdsaCheckMultisig(ic, v, elliptic.P256()) return ecdsaCheckMultisig(ic, elliptic.P256())
} }
// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using // ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using
// Secp256k1 elliptic curve. // Secp256k1 elliptic curve.
func ECDSASecp256k1CheckMultisig(ic *interop.Context, v *vm.VM) error { func ECDSASecp256k1CheckMultisig(ic *interop.Context) error {
return ecdsaCheckMultisig(ic, v, btcec.S256()) return ecdsaCheckMultisig(ic, btcec.S256())
} }
// ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and // ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and
// ECDSASecp256k1CheckMultisig // ECDSASecp256k1CheckMultisig
func ecdsaCheckMultisig(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error { func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error {
msg := getMessage(ic, v.Estack().Pop().Item()) msg := getMessage(ic, ic.VM.Estack().Pop().Item())
hashToCheck := hash.Sha256(msg).BytesBE() hashToCheck := hash.Sha256(msg).BytesBE()
pkeys, err := v.Estack().PopSigElements() pkeys, err := ic.VM.Estack().PopSigElements()
if err != nil { if err != nil {
return fmt.Errorf("wrong parameters: %w", err) return fmt.Errorf("wrong parameters: %w", err)
} }
if !v.AddGas(ECDSAVerifyPrice * int64(len(pkeys))) { if !ic.VM.AddGas(ECDSAVerifyPrice * int64(len(pkeys))) {
return errors.New("gas limit exceeded") return errors.New("gas limit exceeded")
} }
sigs, err := v.Estack().PopSigElements() sigs, err := ic.VM.Estack().PopSigElements()
if err != nil { if err != nil {
return fmt.Errorf("wrong parameters: %w", err) return fmt.Errorf("wrong parameters: %w", err)
} }
@ -76,8 +76,8 @@ func ecdsaCheckMultisig(ic *interop.Context, v *vm.VM, curve elliptic.Curve) err
if len(pkeys) < len(sigs) { if len(pkeys) < len(sigs) {
return errors.New("more signatures than there are keys") return errors.New("more signatures than there are keys")
} }
sigok := vm.CheckMultisigPar(v, curve, hashToCheck, pkeys, sigs) sigok := vm.CheckMultisigPar(ic.VM, curve, hashToCheck, pkeys, sigs)
v.Estack().PushVal(sigok) ic.VM.Estack().PushVal(sigok)
return nil return nil
} }

View file

@ -3,21 +3,20 @@ package crypto
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/vm"
) )
// Sha256 returns sha256 hash of the data. // Sha256 returns sha256 hash of the data.
func Sha256(ic *interop.Context, v *vm.VM) error { func Sha256(ic *interop.Context) error {
msg := getMessage(ic, v.Estack().Pop().Item()) msg := getMessage(ic, ic.VM.Estack().Pop().Item())
h := hash.Sha256(msg).BytesBE() h := hash.Sha256(msg).BytesBE()
v.Estack().PushVal(h) ic.VM.Estack().PushVal(h)
return nil return nil
} }
// RipeMD160 returns RipeMD160 hash of the data. // RipeMD160 returns RipeMD160 hash of the data.
func RipeMD160(ic *interop.Context, v *vm.VM) error { func RipeMD160(ic *interop.Context) error {
msg := getMessage(ic, v.Estack().Pop().Item()) msg := getMessage(ic, ic.VM.Estack().Pop().Item())
h := hash.RipeMD160(msg).BytesBE() h := hash.RipeMD160(msg).BytesBE()
v.Estack().PushVal(h) ic.VM.Estack().PushVal(h)
return nil return nil
} }

View file

@ -6,22 +6,22 @@ import (
) )
// Concat concatenates 2 enumerators into a single one. // Concat concatenates 2 enumerators into a single one.
func Concat(_ *interop.Context, v *vm.VM) error { func Concat(ic *interop.Context) error {
return vm.EnumeratorConcat(v) return vm.EnumeratorConcat(ic.VM)
} }
// Create creates an enumerator from an array-like or bytearray-like stack item. // Create creates an enumerator from an array-like or bytearray-like stack item.
func Create(_ *interop.Context, v *vm.VM) error { func Create(ic *interop.Context) error {
return vm.EnumeratorCreate(v) return vm.EnumeratorCreate(ic.VM)
} }
// Next advances the enumerator, pushes true if is it was successful // Next advances the enumerator, pushes true if is it was successful
// and false otherwise. // and false otherwise.
func Next(_ *interop.Context, v *vm.VM) error { func Next(ic *interop.Context) error {
return vm.EnumeratorNext(v) return vm.EnumeratorNext(ic.VM)
} }
// Value returns the current value of the enumerator. // Value returns the current value of the enumerator.
func Value(_ *interop.Context, v *vm.VM) error { func Value(ic *interop.Context) error {
return vm.EnumeratorValue(v) return vm.EnumeratorValue(ic.VM)
} }

View file

@ -6,26 +6,26 @@ import (
) )
// Concat concatenates 2 iterators into a single one. // Concat concatenates 2 iterators into a single one.
func Concat(_ *interop.Context, v *vm.VM) error { func Concat(ic *interop.Context) error {
return vm.IteratorConcat(v) return vm.IteratorConcat(ic.VM)
} }
// Create creates an iterator from array-like or map stack item. // Create creates an iterator from array-like or map stack item.
func Create(_ *interop.Context, v *vm.VM) error { func Create(ic *interop.Context) error {
return vm.IteratorCreate(v) return vm.IteratorCreate(ic.VM)
} }
// Key returns current iterator key. // Key returns current iterator key.
func Key(_ *interop.Context, v *vm.VM) error { func Key(ic *interop.Context) error {
return vm.IteratorKey(v) return vm.IteratorKey(ic.VM)
} }
// Keys returns keys of the iterator. // Keys returns keys of the iterator.
func Keys(_ *interop.Context, v *vm.VM) error { func Keys(ic *interop.Context) error {
return vm.IteratorKeys(v) return vm.IteratorKeys(ic.VM)
} }
// Values returns values of the iterator. // Values returns values of the iterator.
func Values(_ *interop.Context, v *vm.VM) error { func Values(ic *interop.Context) error {
return vm.IteratorValues(v) return vm.IteratorValues(ic.VM)
} }

View file

@ -2,28 +2,27 @@ package json
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Serialize handles System.JSON.Serialize syscall. // Serialize handles System.JSON.Serialize syscall.
func Serialize(_ *interop.Context, v *vm.VM) error { func Serialize(ic *interop.Context) error {
item := v.Estack().Pop().Item() item := ic.VM.Estack().Pop().Item()
data, err := stackitem.ToJSON(item) data, err := stackitem.ToJSON(item)
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(data) ic.VM.Estack().PushVal(data)
return nil return nil
} }
// Deserialize handles System.JSON.Deserialize syscall. // Deserialize handles System.JSON.Deserialize syscall.
func Deserialize(_ *interop.Context, v *vm.VM) error { func Deserialize(ic *interop.Context) error {
data := v.Estack().Pop().Bytes() data := ic.VM.Estack().Pop().Bytes()
item, err := stackitem.FromJSON(data) item, err := stackitem.FromJSON(data)
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(item) ic.VM.Estack().PushVal(item)
return nil return nil
} }

View file

@ -11,18 +11,18 @@ import (
) )
// GasLeft returns remaining amount of GAS. // GasLeft returns remaining amount of GAS.
func GasLeft(_ *interop.Context, v *vm.VM) error { func GasLeft(ic *interop.Context) error {
if v.GasLimit == -1 { if ic.VM.GasLimit == -1 {
v.Estack().PushVal(v.GasLimit) ic.VM.Estack().PushVal(ic.VM.GasLimit)
} else { } else {
v.Estack().PushVal(v.GasLimit - v.GasConsumed()) ic.VM.Estack().PushVal(ic.VM.GasLimit - ic.VM.GasConsumed())
} }
return nil return nil
} }
// GetNotifications returns notifications emitted by current contract execution. // GetNotifications returns notifications emitted by current contract execution.
func GetNotifications(ic *interop.Context, v *vm.VM) error { func GetNotifications(ic *interop.Context) error {
item := v.Estack().Pop().Item() item := ic.VM.Estack().Pop().Item()
notifications := ic.Notifications notifications := ic.Notifications
if _, ok := item.(stackitem.Null); !ok { if _, ok := item.(stackitem.Null); !ok {
b, err := item.TryBytes() b, err := item.TryBytes()
@ -52,16 +52,16 @@ func GetNotifications(ic *interop.Context, v *vm.VM) error {
}) })
arr.Append(ev) arr.Append(ev)
} }
v.Estack().PushVal(arr) ic.VM.Estack().PushVal(arr)
return nil return nil
} }
// GetInvocationCounter returns how many times current contract was invoked during current tx execution. // GetInvocationCounter returns how many times current contract was invoked during current tx execution.
func GetInvocationCounter(ic *interop.Context, v *vm.VM) error { func GetInvocationCounter(ic *interop.Context) error {
count, ok := ic.Invocations[v.GetCurrentScriptHash()] count, ok := ic.Invocations[ic.VM.GetCurrentScriptHash()]
if !ok { if !ok {
return errors.New("current contract wasn't invoked from others") return errors.New("current contract wasn't invoked from others")
} }
v.Estack().PushVal(count) ic.VM.Estack().PushVal(count)
return nil return nil
} }

View file

@ -17,13 +17,13 @@ import (
// for verifying in the interop context. // for verifying in the interop context.
func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) { func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
if tx, ok := ic.Container.(*transaction.Transaction); ok { if tx, ok := ic.Container.(*transaction.Transaction); ok {
return checkScope(ic.DAO, tx, ic.ScriptGetter, hash) return checkScope(ic.DAO, tx, ic.VM, hash)
} }
return false, errors.New("script container is not a transaction") return false, errors.New("script container is not a transaction")
} }
func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) { func checkScope(d dao.DAO, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
for _, c := range tx.Signers { for _, c := range tx.Signers {
if c.Account == hash { if c.Account == hash {
if c.Scopes == transaction.Global { if c.Scopes == transaction.Global {
@ -75,11 +75,11 @@ func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) {
} }
// CheckWitness checks witnesses. // CheckWitness checks witnesses.
func CheckWitness(ic *interop.Context, v *vm.VM) error { func CheckWitness(ic *interop.Context) error {
var res bool var res bool
var err error var err error
hashOrKey := v.Estack().Pop().Bytes() hashOrKey := ic.VM.Estack().Pop().Bytes()
hash, err := util.Uint160DecodeBytesBE(hashOrKey) hash, err := util.Uint160DecodeBytesBE(hashOrKey)
if err != nil { if err != nil {
var key *keys.PublicKey var key *keys.PublicKey
@ -94,6 +94,6 @@ func CheckWitness(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to check witness: %w", err) return fmt.Errorf("failed to check witness: %w", err)
} }
v.Estack().PushVal(res) ic.VM.Estack().PushVal(res)
return nil return nil
} }

View file

@ -29,13 +29,13 @@ const (
var errGasLimitExceeded = errors.New("gas limit exceeded") var errGasLimitExceeded = errors.New("gas limit exceeded")
// storageFind finds stored key-value pair. // storageFind finds stored key-value pair.
func storageFind(ic *interop.Context, v *vm.VM) error { func storageFind(ic *interop.Context) error {
stcInterface := v.Estack().Pop().Value() stcInterface := ic.VM.Estack().Pop().Value()
stc, ok := stcInterface.(*StorageContext) stc, ok := stcInterface.(*StorageContext)
if !ok { if !ok {
return fmt.Errorf("%T is not a StorageContext", stcInterface) return fmt.Errorf("%T is not a StorageContext", stcInterface)
} }
prefix := v.Estack().Pop().Bytes() prefix := ic.VM.Estack().Pop().Bytes()
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix) siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
if err != nil { if err != nil {
return err return err
@ -51,7 +51,7 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
}) })
item := vm.NewMapIterator(filteredMap) item := vm.NewMapIterator(filteredMap)
v.Estack().PushVal(item) ic.VM.Estack().PushVal(item)
return nil return nil
} }
@ -59,16 +59,16 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
// createContractStateFromVM pops all contract state elements from the VM // createContractStateFromVM pops all contract state elements from the VM
// evaluation stack, does a lot of checks and returns Contract if it // evaluation stack, does a lot of checks and returns Contract if it
// succeeds. // succeeds.
func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, error) { func createContractStateFromVM(ic *interop.Context) (*state.Contract, error) {
script := v.Estack().Pop().Bytes() script := ic.VM.Estack().Pop().Bytes()
if len(script) > MaxContractScriptSize { if len(script) > MaxContractScriptSize {
return nil, errors.New("the script is too big") return nil, errors.New("the script is too big")
} }
manifestBytes := v.Estack().Pop().Bytes() manifestBytes := ic.VM.Estack().Pop().Bytes()
if len(manifestBytes) > manifest.MaxManifestSize { if len(manifestBytes) > manifest.MaxManifestSize {
return nil, errors.New("manifest is too big") return nil, errors.New("manifest is too big")
} }
if !v.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) { if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
return nil, errGasLimitExceeded return nil, errGasLimitExceeded
} }
var m manifest.Manifest var m manifest.Manifest
@ -83,8 +83,8 @@ func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract,
} }
// contractCreate creates a contract. // contractCreate creates a contract.
func contractCreate(ic *interop.Context, v *vm.VM) error { func contractCreate(ic *interop.Context) error {
newcontract, err := createContractStateFromVM(ic, v) newcontract, err := createContractStateFromVM(ic)
if err != nil { if err != nil {
return err return err
} }
@ -107,26 +107,26 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return fmt.Errorf("cannot convert contract to stack item: %w", err) return fmt.Errorf("cannot convert contract to stack item: %w", err)
} }
v.Estack().PushVal(cs) ic.VM.Estack().PushVal(cs)
return nil return nil
} }
// contractUpdate migrates a contract. This method assumes that Manifest and Script // contractUpdate migrates a contract. This method assumes that Manifest and Script
// of the contract can be updated independently. // of the contract can be updated independently.
func contractUpdate(ic *interop.Context, v *vm.VM) error { func contractUpdate(ic *interop.Context) error {
contract, _ := ic.DAO.GetContractState(v.GetCurrentScriptHash()) contract, _ := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
if contract == nil { if contract == nil {
return errors.New("contract doesn't exist") return errors.New("contract doesn't exist")
} }
script := v.Estack().Pop().Bytes() script := ic.VM.Estack().Pop().Bytes()
if len(script) > MaxContractScriptSize { if len(script) > MaxContractScriptSize {
return errors.New("the script is too big") return errors.New("the script is too big")
} }
manifestBytes := v.Estack().Pop().Bytes() manifestBytes := ic.VM.Estack().Pop().Bytes()
if len(manifestBytes) > manifest.MaxManifestSize { if len(manifestBytes) > manifest.MaxManifestSize {
return errors.New("manifest is too big") return errors.New("manifest is too big")
} }
if !v.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) { if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
return errGasLimitExceeded return errGasLimitExceeded
} }
// if script was provided, update the old contract script and Manifest.ABI hash // if script was provided, update the old contract script and Manifest.ABI hash
@ -186,30 +186,30 @@ func contractUpdate(ic *interop.Context, v *vm.VM) error {
} }
// runtimeSerialize serializes top stack item into a ByteArray. // runtimeSerialize serializes top stack item into a ByteArray.
func runtimeSerialize(_ *interop.Context, v *vm.VM) error { func runtimeSerialize(ic *interop.Context) error {
return vm.RuntimeSerialize(v) return vm.RuntimeSerialize(ic.VM)
} }
// runtimeDeserialize deserializes ByteArray from a stack into an item. // runtimeDeserialize deserializes ByteArray from a stack into an item.
func runtimeDeserialize(_ *interop.Context, v *vm.VM) error { func runtimeDeserialize(ic *interop.Context) error {
return vm.RuntimeDeserialize(v) return vm.RuntimeDeserialize(ic.VM)
} }
// runtimeEncode encodes top stack item into a base64 string. // runtimeEncode encodes top stack item into a base64 string.
func runtimeEncode(_ *interop.Context, v *vm.VM) error { func runtimeEncode(ic *interop.Context) error {
src := v.Estack().Pop().Bytes() src := ic.VM.Estack().Pop().Bytes()
result := base64.StdEncoding.EncodeToString(src) result := base64.StdEncoding.EncodeToString(src)
v.Estack().PushVal([]byte(result)) ic.VM.Estack().PushVal([]byte(result))
return nil return nil
} }
// runtimeDecode decodes top stack item from base64 string to byte array. // runtimeDecode decodes top stack item from base64 string to byte array.
func runtimeDecode(_ *interop.Context, v *vm.VM) error { func runtimeDecode(ic *interop.Context) error {
src := v.Estack().Pop().String() src := ic.VM.Estack().Pop().String()
result, err := base64.StdEncoding.DecodeString(src) result, err := base64.StdEncoding.DecodeString(src)
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(result) ic.VM.Estack().PushVal(result)
return nil return nil
} }

View file

@ -40,9 +40,9 @@ import (
*/ */
func TestGetTrigger(t *testing.T) { func TestGetTrigger(t *testing.T) {
v, _, context, chain := createVMAndPushBlock(t) _, _, context, chain := createVMAndPushBlock(t)
defer chain.Close() defer chain.Close()
require.NoError(t, runtimeGetTrigger(context, v)) require.NoError(t, runtimeGetTrigger(context))
} }
func TestStorageFind(t *testing.T) { func TestStorageFind(t *testing.T) {
@ -75,37 +75,37 @@ func TestStorageFind(t *testing.T) {
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
err := storageFind(context, v) err := storageFind(context)
require.NoError(t, err) require.NoError(t, err)
var iter *stackitem.Interop var iter *stackitem.Interop
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() }) require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context))
require.True(t, v.Estack().Pop().Bool()) require.True(t, v.Estack().Pop().Bool())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, iterator.Key(context, v)) require.NoError(t, iterator.Key(context))
require.Equal(t, []byte{0x01, 0x01}, v.Estack().Pop().Bytes()) require.Equal(t, []byte{0x01, 0x01}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, enumerator.Value(context, v)) require.NoError(t, enumerator.Value(context))
require.Equal(t, []byte{0x03, 0x04, 0x05, 0x06}, v.Estack().Pop().Bytes()) require.Equal(t, []byte{0x03, 0x04, 0x05, 0x06}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context))
require.True(t, v.Estack().Pop().Bool()) require.True(t, v.Estack().Pop().Bool())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, iterator.Key(context, v)) require.NoError(t, iterator.Key(context))
require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes()) require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, enumerator.Value(context, v)) require.NoError(t, enumerator.Value(context))
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes()) require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes())
v.Estack().PushVal(iter) v.Estack().PushVal(iter)
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context))
require.False(t, v.Estack().Pop().Bool()) require.False(t, v.Estack().Pop().Bool())
}) })
@ -113,10 +113,10 @@ func TestStorageFind(t *testing.T) {
v.Estack().PushVal([]byte{0x03}) v.Estack().PushVal([]byte{0x03})
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
err := storageFind(context, v) err := storageFind(context)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context))
require.False(t, v.Estack().Pop().Bool()) require.False(t, v.Estack().Pop().Bool())
}) })
@ -124,7 +124,7 @@ func TestStorageFind(t *testing.T) {
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(stackitem.NewInterop(nil)) v.Estack().PushVal(stackitem.NewInterop(nil))
require.Error(t, storageFind(context, v)) require.Error(t, storageFind(context))
}) })
t.Run("invalid id", func(t *testing.T) { t.Run("invalid id", func(t *testing.T) {
@ -133,8 +133,8 @@ func TestStorageFind(t *testing.T) {
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID}))
require.NoError(t, storageFind(context, v)) require.NoError(t, storageFind(context))
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context))
require.False(t, v.Estack().Pop().Bool()) require.False(t, v.Estack().Pop().Bool())
}) })
} }
@ -149,6 +149,7 @@ func TestECDSAVerify(t *testing.T) {
ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
v := vm.New() v := vm.New()
ic.VM = v
for i := range args { for i := range args {
v.Estack().PushVal(args[i]) v.Estack().PushVal(args[i])
} }
@ -160,7 +161,7 @@ func TestECDSAVerify(t *testing.T) {
err = fmt.Errorf("panic: %v", r) err = fmt.Errorf("panic: %v", r)
} }
}() }()
err = crypto.ECDSASecp256r1Verify(ic, v) err = crypto.ECDSASecp256r1Verify(ic)
}() }()
if isErr { if isErr {
@ -227,7 +228,7 @@ func TestRuntimeEncode(t *testing.T) {
defer bc.Close() defer bc.Close()
v.Estack().PushVal(str) v.Estack().PushVal(str)
require.NoError(t, runtimeEncode(ic, v)) require.NoError(t, runtimeEncode(ic))
expected := []byte(base64.StdEncoding.EncodeToString(str)) expected := []byte(base64.StdEncoding.EncodeToString(str))
actual := v.Estack().Pop().Bytes() actual := v.Estack().Pop().Bytes()
@ -242,7 +243,7 @@ func TestRuntimeDecode(t *testing.T) {
t.Run("positive", func(t *testing.T) { t.Run("positive", func(t *testing.T) {
v.Estack().PushVal(str) v.Estack().PushVal(str)
require.NoError(t, runtimeDecode(ic, v)) require.NoError(t, runtimeDecode(ic))
actual := v.Estack().Pop().Bytes() actual := v.Estack().Pop().Bytes()
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
@ -250,17 +251,17 @@ func TestRuntimeDecode(t *testing.T) {
t.Run("error", func(t *testing.T) { t.Run("error", func(t *testing.T) {
v.Estack().PushVal(str + "%") v.Estack().PushVal(str + "%")
require.Error(t, runtimeDecode(ic, v)) require.Error(t, runtimeDecode(ic))
}) })
} }
// Helper functions to create VM, InteropContext, TX, Account, Contract. // Helper functions to create VM, InteropContext, TX, Account, Contract.
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) { func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
v := vm.New()
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, context := chain.newInteropContext(trigger.Application,
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
v := context.SpawnVM()
return v, context, chain return v, context, chain
} }
@ -271,10 +272,10 @@ func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context,
} }
func createVMAndBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) { func createVMAndBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) {
v := vm.New()
block := newDumbBlock() block := newDumbBlock()
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), block, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), block, nil)
v := context.SpawnVM()
return v, block, context, chain return v, block, context, chain
} }
@ -285,7 +286,6 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop
} }
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) { func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
v := vm.New()
script := []byte("testscript") script := []byte("testscript")
m := manifest.NewManifest(hash.Hash160(script)) m := manifest.NewManifest(hash.Hash160(script))
m.Features = smartcontract.HasStorage m.Features = smartcontract.HasStorage
@ -297,11 +297,11 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.C
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
v := context.SpawnVM()
return v, contractState, context, chain return v, contractState, context, chain
} }
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context, *Blockchain) { func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context, *Blockchain) {
v := vm.New()
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302" rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
hash, err := util.Uint160DecodeStringBE(rawHash) hash, err := util.Uint160DecodeStringBE(rawHash)
accountState := state.NewAccount(hash) accountState := state.NewAccount(hash)
@ -309,11 +309,11 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
require.NoError(t, err) require.NoError(t, err)
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
v := context.SpawnVM()
return v, accountState, context, chain return v, accountState, context, chain
} }
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) { func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
v := vm.New()
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)} script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
tx := transaction.New(netmode.UnitTestNet, script, 0) tx := transaction.New(netmode.UnitTestNet, script, 0)
@ -327,5 +327,6 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}} tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}}
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx)
v := context.SpawnVM()
return v, tx, context, chain return v, tx, context, chain
} }

View file

@ -89,16 +89,16 @@ func blockToStackItem(b *block.Block) stackitem.Item {
} }
// bcGetBlock returns current block. // bcGetBlock returns current block.
func bcGetBlock(ic *interop.Context, v *vm.VM) error { func bcGetBlock(ic *interop.Context) error {
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop()) hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
if err != nil { if err != nil {
return err return err
} }
block, err := ic.Chain.GetBlock(hash) block, err := ic.Chain.GetBlock(hash)
if err != nil || !isTraceableBlock(ic, block.Index) { if err != nil || !isTraceableBlock(ic, block.Index) {
v.Estack().PushVal(stackitem.Null{}) ic.VM.Estack().PushVal(stackitem.Null{})
} else { } else {
v.Estack().PushVal(blockToStackItem(block)) ic.VM.Estack().PushVal(blockToStackItem(block))
} }
return nil return nil
} }
@ -118,28 +118,28 @@ func contractToStackItem(cs *state.Contract) (stackitem.Item, error) {
} }
// bcGetContract returns contract. // bcGetContract returns contract.
func bcGetContract(ic *interop.Context, v *vm.VM) error { func bcGetContract(ic *interop.Context) error {
hashbytes := v.Estack().Pop().Bytes() hashbytes := ic.VM.Estack().Pop().Bytes()
hash, err := util.Uint160DecodeBytesBE(hashbytes) hash, err := util.Uint160DecodeBytesBE(hashbytes)
if err != nil { if err != nil {
return err return err
} }
cs, err := ic.DAO.GetContractState(hash) cs, err := ic.DAO.GetContractState(hash)
if err != nil { if err != nil {
v.Estack().PushVal(stackitem.Null{}) ic.VM.Estack().PushVal(stackitem.Null{})
} else { } else {
item, err := contractToStackItem(cs) item, err := contractToStackItem(cs)
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(item) ic.VM.Estack().PushVal(item)
} }
return nil return nil
} }
// bcGetHeight returns blockchain height. // bcGetHeight returns blockchain height.
func bcGetHeight(ic *interop.Context, v *vm.VM) error { func bcGetHeight(ic *interop.Context) error {
v.Estack().PushVal(ic.Chain.BlockHeight()) ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
return nil return nil
} }
@ -176,51 +176,51 @@ func transactionToStackItem(t *transaction.Transaction) stackitem.Item {
} }
// bcGetTransaction returns transaction. // bcGetTransaction returns transaction.
func bcGetTransaction(ic *interop.Context, v *vm.VM) error { func bcGetTransaction(ic *interop.Context) error {
tx, h, err := getTransactionAndHeight(ic.DAO, v) tx, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
if err != nil || !isTraceableBlock(ic, h) { if err != nil || !isTraceableBlock(ic, h) {
v.Estack().PushVal(stackitem.Null{}) ic.VM.Estack().PushVal(stackitem.Null{})
return nil return nil
} }
v.Estack().PushVal(transactionToStackItem(tx)) ic.VM.Estack().PushVal(transactionToStackItem(tx))
return nil return nil
} }
// bcGetTransactionFromBlock returns transaction with the given index from the // bcGetTransactionFromBlock returns transaction with the given index from the
// block with height or hash specified. // block with height or hash specified.
func bcGetTransactionFromBlock(ic *interop.Context, v *vm.VM) error { func bcGetTransactionFromBlock(ic *interop.Context) error {
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop()) hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
if err != nil { if err != nil {
return err return err
} }
index := v.Estack().Pop().BigInt().Int64() index := ic.VM.Estack().Pop().BigInt().Int64()
block, err := ic.DAO.GetBlock(hash) block, err := ic.DAO.GetBlock(hash)
if err != nil || !isTraceableBlock(ic, block.Index) { if err != nil || !isTraceableBlock(ic, block.Index) {
v.Estack().PushVal(stackitem.Null{}) ic.VM.Estack().PushVal(stackitem.Null{})
return nil return nil
} }
if index < 0 || index >= int64(len(block.Transactions)) { if index < 0 || index >= int64(len(block.Transactions)) {
return errors.New("wrong transaction index") return errors.New("wrong transaction index")
} }
tx := block.Transactions[index] tx := block.Transactions[index]
v.Estack().PushVal(tx.Hash().BytesBE()) ic.VM.Estack().PushVal(tx.Hash().BytesBE())
return nil return nil
} }
// bcGetTransactionHeight returns transaction height. // bcGetTransactionHeight returns transaction height.
func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error { func bcGetTransactionHeight(ic *interop.Context) error {
_, h, err := getTransactionAndHeight(ic.DAO, v) _, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
if err != nil || !isTraceableBlock(ic, h) { if err != nil || !isTraceableBlock(ic, h) {
v.Estack().PushVal(-1) ic.VM.Estack().PushVal(-1)
return nil return nil
} }
v.Estack().PushVal(h) ic.VM.Estack().PushVal(h)
return nil return nil
} }
// engineGetScriptContainer returns transaction or block that contains the script // engineGetScriptContainer returns transaction or block that contains the script
// being run. // being run.
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { func engineGetScriptContainer(ic *interop.Context) error {
var item stackitem.Item var item stackitem.Item
switch t := ic.Container.(type) { switch t := ic.Container.(type) {
case *transaction.Transaction: case *transaction.Transaction:
@ -230,45 +230,45 @@ func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
default: default:
return errors.New("unknown script container") return errors.New("unknown script container")
} }
v.Estack().PushVal(item) ic.VM.Estack().PushVal(item)
return nil return nil
} }
// engineGetExecutingScriptHash returns executing script hash. // engineGetExecutingScriptHash returns executing script hash.
func engineGetExecutingScriptHash(ic *interop.Context, v *vm.VM) error { func engineGetExecutingScriptHash(ic *interop.Context) error {
return v.PushContextScriptHash(0) return ic.VM.PushContextScriptHash(0)
} }
// engineGetCallingScriptHash returns calling script hash. // engineGetCallingScriptHash returns calling script hash.
func engineGetCallingScriptHash(ic *interop.Context, v *vm.VM) error { func engineGetCallingScriptHash(ic *interop.Context) error {
return v.PushContextScriptHash(1) return ic.VM.PushContextScriptHash(1)
} }
// engineGetEntryScriptHash returns entry script hash. // engineGetEntryScriptHash returns entry script hash.
func engineGetEntryScriptHash(ic *interop.Context, v *vm.VM) error { func engineGetEntryScriptHash(ic *interop.Context) error {
return v.PushContextScriptHash(v.Istack().Len() - 1) return ic.VM.PushContextScriptHash(ic.VM.Istack().Len() - 1)
} }
// runtimePlatform returns the name of the platform. // runtimePlatform returns the name of the platform.
func runtimePlatform(ic *interop.Context, v *vm.VM) error { func runtimePlatform(ic *interop.Context) error {
v.Estack().PushVal([]byte("NEO")) ic.VM.Estack().PushVal([]byte("NEO"))
return nil return nil
} }
// runtimeGetTrigger returns the script trigger. // runtimeGetTrigger returns the script trigger.
func runtimeGetTrigger(ic *interop.Context, v *vm.VM) error { func runtimeGetTrigger(ic *interop.Context) error {
v.Estack().PushVal(byte(ic.Trigger)) ic.VM.Estack().PushVal(byte(ic.Trigger))
return nil return nil
} }
// runtimeNotify should pass stack item to the notify plugin to handle it, but // runtimeNotify should pass stack item to the notify plugin to handle it, but
// in neo-go the only meaningful thing to do here is to log. // in neo-go the only meaningful thing to do here is to log.
func runtimeNotify(ic *interop.Context, v *vm.VM) error { func runtimeNotify(ic *interop.Context) error {
name := v.Estack().Pop().String() name := ic.VM.Estack().Pop().String()
if len(name) > MaxEventNameLen { if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", MaxEventNameLen) return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
} }
elem := v.Estack().Pop() elem := ic.VM.Estack().Pop()
args := elem.Array() args := elem.Array()
// But it has to be serializable, otherwise we either have some broken // But it has to be serializable, otherwise we either have some broken
// (recursive) structure inside or an interop item that can't be used // (recursive) structure inside or an interop item that can't be used
@ -280,7 +280,7 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
args = []stackitem.Item{stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))} args = []stackitem.Item{stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))}
} }
ne := state.NotificationEvent{ ne := state.NotificationEvent{
ScriptHash: v.GetCurrentScriptHash(), ScriptHash: ic.VM.GetCurrentScriptHash(),
Name: name, Name: name,
Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array), Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array),
} }
@ -289,21 +289,21 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
} }
// runtimeLog logs the message passed. // runtimeLog logs the message passed.
func runtimeLog(ic *interop.Context, v *vm.VM) error { func runtimeLog(ic *interop.Context) error {
state := v.Estack().Pop().String() state := ic.VM.Estack().Pop().String()
if len(state) > MaxNotificationSize { if len(state) > MaxNotificationSize {
return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize) return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize)
} }
msg := fmt.Sprintf("%q", state) msg := fmt.Sprintf("%q", state)
ic.Log.Info("runtime log", ic.Log.Info("runtime log",
zap.Stringer("script", v.GetCurrentScriptHash()), zap.Stringer("script", ic.VM.GetCurrentScriptHash()),
zap.String("logs", msg)) zap.String("logs", msg))
return nil return nil
} }
// runtimeGetTime returns timestamp of the block being verified, or the latest // runtimeGetTime returns timestamp of the block being verified, or the latest
// one in the blockchain if no block is given to Context. // one in the blockchain if no block is given to Context.
func runtimeGetTime(ic *interop.Context, v *vm.VM) error { func runtimeGetTime(ic *interop.Context) error {
var header *block.Header var header *block.Header
if ic.Block == nil { if ic.Block == nil {
var err error var err error
@ -314,13 +314,13 @@ func runtimeGetTime(ic *interop.Context, v *vm.VM) error {
} else { } else {
header = ic.Block.Header() header = ic.Block.Header()
} }
v.Estack().PushVal(header.Timestamp) ic.VM.Estack().PushVal(header.Timestamp)
return nil return nil
} }
// storageDelete deletes stored key-value pair. // storageDelete deletes stored key-value pair.
func storageDelete(ic *interop.Context, v *vm.VM) error { func storageDelete(ic *interop.Context) error {
stcInterface := v.Estack().Pop().Value() stcInterface := ic.VM.Estack().Pop().Value()
stc, ok := stcInterface.(*StorageContext) stc, ok := stcInterface.(*StorageContext)
if !ok { if !ok {
return fmt.Errorf("%T is not a StorageContext", stcInterface) return fmt.Errorf("%T is not a StorageContext", stcInterface)
@ -328,7 +328,7 @@ func storageDelete(ic *interop.Context, v *vm.VM) error {
if stc.ReadOnly { if stc.ReadOnly {
return errors.New("StorageContext is read only") return errors.New("StorageContext is read only")
} }
key := v.Estack().Pop().Bytes() key := ic.VM.Estack().Pop().Bytes()
si := ic.DAO.GetStorageItem(stc.ID, key) si := ic.DAO.GetStorageItem(stc.ID, key)
if si != nil && si.IsConst { if si != nil && si.IsConst {
return errors.New("storage item is constant") return errors.New("storage item is constant")
@ -337,36 +337,36 @@ func storageDelete(ic *interop.Context, v *vm.VM) error {
} }
// storageGet returns stored key-value pair. // storageGet returns stored key-value pair.
func storageGet(ic *interop.Context, v *vm.VM) error { func storageGet(ic *interop.Context) error {
stcInterface := v.Estack().Pop().Value() stcInterface := ic.VM.Estack().Pop().Value()
stc, ok := stcInterface.(*StorageContext) stc, ok := stcInterface.(*StorageContext)
if !ok { if !ok {
return fmt.Errorf("%T is not a StorageContext", stcInterface) return fmt.Errorf("%T is not a StorageContext", stcInterface)
} }
key := v.Estack().Pop().Bytes() key := ic.VM.Estack().Pop().Bytes()
si := ic.DAO.GetStorageItem(stc.ID, key) si := ic.DAO.GetStorageItem(stc.ID, key)
if si != nil && si.Value != nil { if si != nil && si.Value != nil {
v.Estack().PushVal(si.Value) ic.VM.Estack().PushVal(si.Value)
} else { } else {
v.Estack().PushVal(stackitem.Null{}) ic.VM.Estack().PushVal(stackitem.Null{})
} }
return nil return nil
} }
// storageGetContext returns storage context (scripthash). // storageGetContext returns storage context (scripthash).
func storageGetContext(ic *interop.Context, v *vm.VM) error { func storageGetContext(ic *interop.Context) error {
return storageGetContextInternal(ic, v, false) return storageGetContextInternal(ic, false)
} }
// storageGetReadOnlyContext returns read-only context (scripthash). // storageGetReadOnlyContext returns read-only context (scripthash).
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error { func storageGetReadOnlyContext(ic *interop.Context) error {
return storageGetContextInternal(ic, v, true) return storageGetContextInternal(ic, true)
} }
// storageGetContextInternal is internal version of storageGetContext and // storageGetContextInternal is internal version of storageGetContext and
// storageGetReadOnlyContext which allows to specify ReadOnly context flag. // storageGetReadOnlyContext which allows to specify ReadOnly context flag.
func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) error { func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) contract, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
if err != nil { if err != nil {
return err return err
} }
@ -377,11 +377,11 @@ func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) e
ID: contract.ID, ID: contract.ID,
ReadOnly: isReadOnly, ReadOnly: isReadOnly,
} }
v.Estack().PushVal(stackitem.NewInterop(sc)) ic.VM.Estack().PushVal(stackitem.NewInterop(sc))
return nil return nil
} }
func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext, key []byte, value []byte, isConst bool) error { func putWithContextAndFlags(ic *interop.Context, stc *StorageContext, key []byte, value []byte, isConst bool) error {
if len(key) > MaxStorageKeyLen { if len(key) > MaxStorageKeyLen {
return errors.New("key is too big") return errors.New("key is too big")
} }
@ -402,7 +402,7 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext,
if len(value) > len(si.Value) { if len(value) > len(si.Value) {
sizeInc = len(value) - len(si.Value) sizeInc = len(value) - len(si.Value)
} }
if !v.AddGas(int64(sizeInc) * StoragePrice) { if !ic.VM.AddGas(int64(sizeInc) * StoragePrice) {
return errGasLimitExceeded return errGasLimitExceeded
} }
si.Value = value si.Value = value
@ -411,34 +411,34 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext,
} }
// storagePutInternal is a unified implementation of storagePut and storagePutEx. // storagePutInternal is a unified implementation of storagePut and storagePutEx.
func storagePutInternal(ic *interop.Context, v *vm.VM, getFlag bool) error { func storagePutInternal(ic *interop.Context, getFlag bool) error {
stcInterface := v.Estack().Pop().Value() stcInterface := ic.VM.Estack().Pop().Value()
stc, ok := stcInterface.(*StorageContext) stc, ok := stcInterface.(*StorageContext)
if !ok { if !ok {
return fmt.Errorf("%T is not a StorageContext", stcInterface) return fmt.Errorf("%T is not a StorageContext", stcInterface)
} }
key := v.Estack().Pop().Bytes() key := ic.VM.Estack().Pop().Bytes()
value := v.Estack().Pop().Bytes() value := ic.VM.Estack().Pop().Bytes()
var flag int var flag int
if getFlag { if getFlag {
flag = int(v.Estack().Pop().BigInt().Int64()) flag = int(ic.VM.Estack().Pop().BigInt().Int64())
} }
return putWithContextAndFlags(ic, v, stc, key, value, int(Constant)&flag != 0) return putWithContextAndFlags(ic, stc, key, value, int(Constant)&flag != 0)
} }
// storagePut puts key-value pair into the storage. // storagePut puts key-value pair into the storage.
func storagePut(ic *interop.Context, v *vm.VM) error { func storagePut(ic *interop.Context) error {
return storagePutInternal(ic, v, false) return storagePutInternal(ic, false)
} }
// storagePutEx puts key-value pair with given flags into the storage. // storagePutEx puts key-value pair with given flags into the storage.
func storagePutEx(ic *interop.Context, v *vm.VM) error { func storagePutEx(ic *interop.Context) error {
return storagePutInternal(ic, v, true) return storagePutInternal(ic, true)
} }
// storageContextAsReadOnly sets given context to read-only mode. // storageContextAsReadOnly sets given context to read-only mode.
func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error { func storageContextAsReadOnly(ic *interop.Context) error {
stcInterface := v.Estack().Pop().Value() stcInterface := ic.VM.Estack().Pop().Value()
stc, ok := stcInterface.(*StorageContext) stc, ok := stcInterface.(*StorageContext)
if !ok { if !ok {
return fmt.Errorf("%T is not a StorageContext", stcInterface) return fmt.Errorf("%T is not a StorageContext", stcInterface)
@ -450,31 +450,31 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
} }
stc = stx stc = stx
} }
v.Estack().PushVal(stackitem.NewInterop(stc)) ic.VM.Estack().PushVal(stackitem.NewInterop(stc))
return nil return nil
} }
// contractCall calls a contract. // contractCall calls a contract.
func contractCall(ic *interop.Context, v *vm.VM) error { func contractCall(ic *interop.Context) error {
h := v.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()
method := v.Estack().Pop().String() method := ic.VM.Estack().Pop().String()
args := v.Estack().Pop().Array() args := ic.VM.Estack().Pop().Array()
return contractCallExInternal(ic, v, h, method, args, smartcontract.All) return contractCallExInternal(ic, h, method, args, smartcontract.All)
} }
// contractCallEx calls a contract with flags. // contractCallEx calls a contract with flags.
func contractCallEx(ic *interop.Context, v *vm.VM) error { func contractCallEx(ic *interop.Context) error {
h := v.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()
method := v.Estack().Pop().String() method := ic.VM.Estack().Pop().String()
args := v.Estack().Pop().Array() args := ic.VM.Estack().Pop().Array()
flags := smartcontract.CallFlag(int32(v.Estack().Pop().BigInt().Int64())) flags := smartcontract.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64()))
if flags&^smartcontract.All != 0 { if flags&^smartcontract.All != 0 {
return errors.New("call flags out of range") return errors.New("call flags out of range")
} }
return contractCallExInternal(ic, v, h, method, args, flags) return contractCallExInternal(ic, h, method, args, flags)
} }
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string, args []stackitem.Item, f smartcontract.CallFlag) error { func contractCallExInternal(ic *interop.Context, h []byte, name string, args []stackitem.Item, f smartcontract.CallFlag) error {
u, err := util.Uint160DecodeBytesBE(h) u, err := util.Uint160DecodeBytesBE(h)
if err != nil { if err != nil {
return errors.New("invalid contract hash") return errors.New("invalid contract hash")
@ -490,7 +490,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
if md == nil { if md == nil {
return fmt.Errorf("method '%s' not found", name) return fmt.Errorf("method '%s' not found", name)
} }
curr, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
if err == nil { if err == nil {
if !curr.Manifest.CanCall(&cs.Manifest, name) { if !curr.Manifest.CanCall(&cs.Manifest, name) {
return errors.New("disallowed method call") return errors.New("disallowed method call")
@ -502,7 +502,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
} }
ic.Invocations[u]++ ic.Invocations[u]++
v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f) ic.VM.LoadScriptWithHash(cs.Script, u, ic.VM.Context().GetCallFlags()&f)
var isNative bool var isNative bool
for i := range ic.Natives { for i := range ic.Natives {
if ic.Natives[i].Metadata().Hash.Equals(u) { if ic.Natives[i].Metadata().Hash.Equals(u) {
@ -511,27 +511,27 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
} }
} }
if isNative { if isNative {
v.Estack().PushVal(args) ic.VM.Estack().PushVal(args)
v.Estack().PushVal(name) ic.VM.Estack().PushVal(name)
} else { } else {
for i := len(args) - 1; i >= 0; i-- { for i := len(args) - 1; i >= 0; i-- {
v.Estack().PushVal(args[i]) ic.VM.Estack().PushVal(args[i])
} }
// use Jump not Call here because context was loaded in LoadScript above. // use Jump not Call here because context was loaded in LoadScript above.
v.Jump(v.Context(), md.Offset) ic.VM.Jump(ic.VM.Context(), md.Offset)
} }
md = cs.Manifest.ABI.GetMethod(manifest.MethodInit) md = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
if md != nil { if md != nil {
v.Call(v.Context(), md.Offset) ic.VM.Call(ic.VM.Context(), md.Offset)
} }
return nil return nil
} }
// contractDestroy destroys a contract. // contractDestroy destroys a contract.
func contractDestroy(ic *interop.Context, v *vm.VM) error { func contractDestroy(ic *interop.Context) error {
hash := v.GetCurrentScriptHash() hash := ic.VM.GetCurrentScriptHash()
cs, err := ic.DAO.GetContractState(hash) cs, err := ic.DAO.GetContractState(hash)
if err != nil { if err != nil {
return nil return nil
@ -553,8 +553,8 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error {
} }
// contractIsStandard checks if contract is standard (sig or multisig) contract. // contractIsStandard checks if contract is standard (sig or multisig) contract.
func contractIsStandard(ic *interop.Context, v *vm.VM) error { func contractIsStandard(ic *interop.Context) error {
h := v.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(h) u, err := util.Uint160DecodeBytesBE(h)
if err != nil { if err != nil {
return err return err
@ -573,23 +573,23 @@ func contractIsStandard(ic *interop.Context, v *vm.VM) error {
} }
} }
} }
v.Estack().PushVal(result) ic.VM.Estack().PushVal(result)
return nil return nil
} }
// contractCreateStandardAccount calculates contract scripthash for a given public key. // contractCreateStandardAccount calculates contract scripthash for a given public key.
func contractCreateStandardAccount(ic *interop.Context, v *vm.VM) error { func contractCreateStandardAccount(ic *interop.Context) error {
h := v.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256()) p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(p.GetScriptHash().BytesBE()) ic.VM.Estack().PushVal(p.GetScriptHash().BytesBE())
return nil return nil
} }
// contractGetCallFlags returns current context calling flags. // contractGetCallFlags returns current context calling flags.
func contractGetCallFlags(_ *interop.Context, v *vm.VM) error { func contractGetCallFlags(ic *interop.Context) error {
v.Estack().PushVal(v.Context().GetCallFlags()) ic.VM.Estack().PushVal(ic.VM.Context().GetCallFlags())
return nil return nil
} }

View file

@ -29,7 +29,7 @@ func TestBCGetTransaction(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
require.NoError(t, context.DAO.StoreAsTransaction(tx, 0)) require.NoError(t, context.DAO.StoreAsTransaction(tx, 0))
v.Estack().PushVal(tx.Hash().BytesBE()) v.Estack().PushVal(tx.Hash().BytesBE())
err := bcGetTransaction(context, v) err := bcGetTransaction(context)
require.NoError(t, err) require.NoError(t, err)
value := v.Estack().Pop().Value() value := v.Estack().Pop().Value()
@ -49,7 +49,7 @@ func TestBCGetTransaction(t *testing.T) {
t.Run("isn't traceable", func(t *testing.T) { t.Run("isn't traceable", func(t *testing.T) {
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1)) require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
v.Estack().PushVal(tx.Hash().BytesBE()) v.Estack().PushVal(tx.Hash().BytesBE())
err := bcGetTransaction(context, v) err := bcGetTransaction(context)
require.NoError(t, err) require.NoError(t, err)
_, ok := v.Estack().Pop().Item().(stackitem.Null) _, ok := v.Estack().Pop().Item().(stackitem.Null)
@ -59,7 +59,7 @@ func TestBCGetTransaction(t *testing.T) {
t.Run("bad hash", func(t *testing.T) { t.Run("bad hash", func(t *testing.T) {
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1)) require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
v.Estack().PushVal(tx.Hash().BytesLE()) v.Estack().PushVal(tx.Hash().BytesLE())
err := bcGetTransaction(context, v) err := bcGetTransaction(context)
require.NoError(t, err) require.NoError(t, err)
_, ok := v.Estack().Pop().Item().(stackitem.Null) _, ok := v.Estack().Pop().Item().(stackitem.Null)
@ -76,7 +76,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
v.Estack().PushVal(0) v.Estack().PushVal(0)
v.Estack().PushVal(block.Hash().BytesBE()) v.Estack().PushVal(block.Hash().BytesBE())
err := bcGetTransactionFromBlock(context, v) err := bcGetTransactionFromBlock(context)
require.NoError(t, err) require.NoError(t, err)
value := v.Estack().Pop().Value() value := v.Estack().Pop().Value()
@ -88,7 +88,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
t.Run("invalid block hash", func(t *testing.T) { t.Run("invalid block hash", func(t *testing.T) {
v.Estack().PushVal(0) v.Estack().PushVal(0)
v.Estack().PushVal(block.Hash().BytesBE()[:10]) v.Estack().PushVal(block.Hash().BytesBE()[:10])
err := bcGetTransactionFromBlock(context, v) err := bcGetTransactionFromBlock(context)
require.Error(t, err) require.Error(t, err)
}) })
@ -97,7 +97,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
require.NoError(t, context.DAO.StoreAsBlock(block)) require.NoError(t, context.DAO.StoreAsBlock(block))
v.Estack().PushVal(0) v.Estack().PushVal(0)
v.Estack().PushVal(block.Hash().BytesBE()) v.Estack().PushVal(block.Hash().BytesBE())
err := bcGetTransactionFromBlock(context, v) err := bcGetTransactionFromBlock(context)
require.NoError(t, err) require.NoError(t, err)
_, ok := v.Estack().Pop().Item().(stackitem.Null) _, ok := v.Estack().Pop().Item().(stackitem.Null)
@ -109,7 +109,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
require.NoError(t, context.DAO.StoreAsBlock(block)) require.NoError(t, context.DAO.StoreAsBlock(block))
v.Estack().PushVal(0) v.Estack().PushVal(0)
v.Estack().PushVal(block.Hash().BytesLE()) v.Estack().PushVal(block.Hash().BytesLE())
err := bcGetTransactionFromBlock(context, v) err := bcGetTransactionFromBlock(context)
require.NoError(t, err) require.NoError(t, err)
_, ok := v.Estack().Pop().Item().(stackitem.Null) _, ok := v.Estack().Pop().Item().(stackitem.Null)
@ -120,7 +120,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
require.NoError(t, context.DAO.StoreAsBlock(block)) require.NoError(t, context.DAO.StoreAsBlock(block))
v.Estack().PushVal(1) v.Estack().PushVal(1)
v.Estack().PushVal(block.Hash().BytesBE()) v.Estack().PushVal(block.Hash().BytesBE())
err := bcGetTransactionFromBlock(context, v) err := bcGetTransactionFromBlock(context)
require.Error(t, err) require.Error(t, err)
}) })
} }
@ -133,7 +133,7 @@ func TestBCGetBlock(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
v.Estack().PushVal(block.Hash().BytesBE()) v.Estack().PushVal(block.Hash().BytesBE())
err := bcGetBlock(context, v) err := bcGetBlock(context)
require.NoError(t, err) require.NoError(t, err)
value := v.Estack().Pop().Value() value := v.Estack().Pop().Value()
@ -152,7 +152,7 @@ func TestBCGetBlock(t *testing.T) {
t.Run("bad hash", func(t *testing.T) { t.Run("bad hash", func(t *testing.T) {
v.Estack().PushVal(block.Hash().BytesLE()) v.Estack().PushVal(block.Hash().BytesLE())
err := bcGetTransaction(context, v) err := bcGetTransaction(context)
require.NoError(t, err) require.NoError(t, err)
_, ok := v.Estack().Pop().Item().(stackitem.Null) _, ok := v.Estack().Pop().Item().(stackitem.Null)
@ -180,14 +180,14 @@ func TestContractIsStandard(t *testing.T) {
t.Run("true", func(t *testing.T) { t.Run("true", func(t *testing.T) {
v.Estack().PushVal(pub.GetScriptHash().BytesBE()) v.Estack().PushVal(pub.GetScriptHash().BytesBE())
require.NoError(t, contractIsStandard(ic, v)) require.NoError(t, contractIsStandard(ic))
require.True(t, v.Estack().Pop().Bool()) require.True(t, v.Estack().Pop().Bool())
}) })
t.Run("false", func(t *testing.T) { t.Run("false", func(t *testing.T) {
tx.Scripts[0].VerificationScript = []byte{9, 8, 7} tx.Scripts[0].VerificationScript = []byte{9, 8, 7}
v.Estack().PushVal(pub.GetScriptHash().BytesBE()) v.Estack().PushVal(pub.GetScriptHash().BytesBE())
require.NoError(t, contractIsStandard(ic, v)) require.NoError(t, contractIsStandard(ic))
require.False(t, v.Estack().Pop().Bool()) require.False(t, v.Estack().Pop().Bool())
}) })
}) })
@ -201,7 +201,7 @@ func TestContractIsStandard(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
v.Estack().PushVal(pub.GetScriptHash().BytesBE()) v.Estack().PushVal(pub.GetScriptHash().BytesBE())
require.NoError(t, contractIsStandard(ic, v)) require.NoError(t, contractIsStandard(ic))
require.True(t, v.Estack().Pop().Bool()) require.True(t, v.Estack().Pop().Bool())
}) })
t.Run("contract stored, false", func(t *testing.T) { t.Run("contract stored, false", func(t *testing.T) {
@ -209,7 +209,7 @@ func TestContractIsStandard(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script})) require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script}))
v.Estack().PushVal(crypto.Hash160(script).BytesBE()) v.Estack().PushVal(crypto.Hash160(script).BytesBE())
require.NoError(t, contractIsStandard(ic, v)) require.NoError(t, contractIsStandard(ic))
require.False(t, v.Estack().Pop().Bool()) require.False(t, v.Estack().Pop().Bool())
}) })
} }
@ -222,7 +222,7 @@ func TestContractCreateAccount(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
pub := priv.PublicKey() pub := priv.PublicKey()
v.Estack().PushVal(pub.Bytes()) v.Estack().PushVal(pub.Bytes())
require.NoError(t, contractCreateStandardAccount(ic, v)) require.NoError(t, contractCreateStandardAccount(ic))
value := v.Estack().Pop().Bytes() value := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(value) u, err := util.Uint160DecodeBytesBE(value)
@ -231,7 +231,7 @@ func TestContractCreateAccount(t *testing.T) {
}) })
t.Run("InvalidKey", func(t *testing.T) { t.Run("InvalidKey", func(t *testing.T) {
v.Estack().PushVal([]byte{1, 2, 3}) v.Estack().PushVal([]byte{1, 2, 3})
require.Error(t, contractCreateStandardAccount(ic, v)) require.Error(t, contractCreateStandardAccount(ic))
}) })
} }
@ -241,7 +241,7 @@ func TestRuntimeGasLeft(t *testing.T) {
v.GasLimit = 100 v.GasLimit = 100
v.AddGas(58) v.AddGas(58)
require.NoError(t, runtime.GasLeft(ic, v)) require.NoError(t, runtime.GasLeft(ic))
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64()) require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
} }
@ -257,7 +257,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
t.Run("NoFilter", func(t *testing.T) { t.Run("NoFilter", func(t *testing.T) {
v.Estack().PushVal(stackitem.Null{}) v.Estack().PushVal(stackitem.Null{})
require.NoError(t, runtime.GetNotifications(ic, v)) require.NoError(t, runtime.GetNotifications(ic))
arr := v.Estack().Pop().Array() arr := v.Estack().Pop().Array()
require.Equal(t, len(ic.Notifications), len(arr)) require.Equal(t, len(ic.Notifications), len(arr))
@ -274,7 +274,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
t.Run("WithFilter", func(t *testing.T) { t.Run("WithFilter", func(t *testing.T) {
h := util.Uint160{2}.BytesBE() h := util.Uint160{2}.BytesBE()
v.Estack().PushVal(h) v.Estack().PushVal(h)
require.NoError(t, runtime.GetNotifications(ic, v)) require.NoError(t, runtime.GetNotifications(ic))
arr := v.Estack().Pop().Array() arr := v.Estack().Pop().Array()
require.Equal(t, 1, len(arr)) require.Equal(t, 1, len(arr))
@ -295,11 +295,11 @@ func TestRuntimeGetInvocationCounter(t *testing.T) {
t.Run("Zero", func(t *testing.T) { t.Run("Zero", func(t *testing.T) {
v.LoadScript([]byte{1}) v.LoadScript([]byte{1})
require.Error(t, runtime.GetInvocationCounter(ic, v)) require.Error(t, runtime.GetInvocationCounter(ic))
}) })
t.Run("NonZero", func(t *testing.T) { t.Run("NonZero", func(t *testing.T) {
v.LoadScript([]byte{2}) v.LoadScript([]byte{2})
require.NoError(t, runtime.GetInvocationCounter(ic, v)) require.NoError(t, runtime.GetInvocationCounter(ic))
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64()) require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
}) })
} }
@ -311,7 +311,7 @@ func TestBlockchainGetContractState(t *testing.T) {
t.Run("positive", func(t *testing.T) { t.Run("positive", func(t *testing.T) {
v.Estack().PushVal(cs.ScriptHash().BytesBE()) v.Estack().PushVal(cs.ScriptHash().BytesBE())
require.NoError(t, bcGetContract(ic, v)) require.NoError(t, bcGetContract(ic))
actual := v.Estack().Pop().Item() actual := v.Estack().Pop().Item()
compareContractStates(t, cs, actual) compareContractStates(t, cs, actual)
@ -319,7 +319,7 @@ func TestBlockchainGetContractState(t *testing.T) {
t.Run("uncknown contract state", func(t *testing.T) { t.Run("uncknown contract state", func(t *testing.T) {
v.Estack().PushVal(util.Uint160{1, 2, 3}.BytesBE()) v.Estack().PushVal(util.Uint160{1, 2, 3}.BytesBE())
require.NoError(t, bcGetContract(ic, v)) require.NoError(t, bcGetContract(ic))
actual := v.Estack().Pop().Item() actual := v.Estack().Pop().Item()
require.Equal(t, stackitem.Null{}, actual) require.Equal(t, stackitem.Null{}, actual)
@ -394,14 +394,14 @@ func getTestContractState() (*state.Contract, *state.Contract) {
} }
} }
func loadScript(script []byte, args ...interface{}) *vm.VM { func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
v := vm.New() v := vm.New()
v.LoadScriptWithFlags(script, smartcontract.AllowCall) v.LoadScriptWithFlags(script, smartcontract.AllowCall)
for i := range args { for i := range args {
v.Estack().PushVal(args[i]) v.Estack().PushVal(args[i])
} }
v.GasLimit = -1 v.GasLimit = -1
return v ic.VM = v
} }
func TestContractCall(t *testing.T) { func TestContractCall(t *testing.T) {
@ -417,36 +417,36 @@ func TestContractCall(t *testing.T) {
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)}) addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
t.Run("Good", func(t *testing.T) { t.Run("Good", func(t *testing.T) {
v := loadScript(currScript, 42) loadScript(ic, currScript, 42)
v.Estack().PushVal(addArgs) ic.VM.Estack().PushVal(addArgs)
v.Estack().PushVal("add") ic.VM.Estack().PushVal("add")
v.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contractCall(ic, v)) require.NoError(t, contractCall(ic))
require.NoError(t, v.Run()) require.NoError(t, ic.VM.Run())
require.Equal(t, 2, v.Estack().Len()) require.Equal(t, 2, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(3), v.Estack().Pop().Value()) require.Equal(t, big.NewInt(3), ic.VM.Estack().Pop().Value())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value()) require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
}) })
t.Run("CallExInvalidFlag", func(t *testing.T) { t.Run("CallExInvalidFlag", func(t *testing.T) {
v := loadScript(currScript, 42) loadScript(ic, currScript, 42)
v.Estack().PushVal(byte(0xFF)) ic.VM.Estack().PushVal(byte(0xFF))
v.Estack().PushVal(addArgs) ic.VM.Estack().PushVal(addArgs)
v.Estack().PushVal("add") ic.VM.Estack().PushVal("add")
v.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.Error(t, contractCallEx(ic, v)) require.Error(t, contractCallEx(ic))
}) })
runInvalid := func(args ...interface{}) func(t *testing.T) { runInvalid := func(args ...interface{}) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
v := loadScript(currScript, 42) loadScript(ic, currScript, 42)
for i := range args { for i := range args {
v.Estack().PushVal(args[i]) ic.VM.Estack().PushVal(args[i])
} }
// interops can both return error and panic, // interops can both return error and panic,
// we don't care which kind of error has occured // we don't care which kind of error has occured
require.Panics(t, func() { require.Panics(t, func() {
err := contractCall(ic, v) err := contractCall(ic)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -466,26 +466,26 @@ func TestContractCall(t *testing.T) {
}) })
t.Run("IsolatedStack", func(t *testing.T) { t.Run("IsolatedStack", func(t *testing.T) {
v := loadScript(currScript, 42) loadScript(ic, currScript, 42)
v.Estack().PushVal(stackitem.NewArray(nil)) ic.VM.Estack().PushVal(stackitem.NewArray(nil))
v.Estack().PushVal("drop") ic.VM.Estack().PushVal("drop")
v.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contractCall(ic, v)) require.NoError(t, contractCall(ic))
require.Error(t, v.Run()) require.Error(t, ic.VM.Run())
}) })
t.Run("CallInitialize", func(t *testing.T) { t.Run("CallInitialize", func(t *testing.T) {
t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE())) t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE()))
v := loadScript(currScript, 42) loadScript(ic, currScript, 42)
v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)})) ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)}))
v.Estack().PushVal("add3") ic.VM.Estack().PushVal("add3")
v.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contractCall(ic, v)) require.NoError(t, contractCall(ic))
require.NoError(t, v.Run()) require.NoError(t, ic.VM.Run())
require.Equal(t, 2, v.Estack().Len()) require.Equal(t, 2, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(8), v.Estack().Pop().Value()) require.Equal(t, big.NewInt(8), ic.VM.Estack().Pop().Value())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value()) require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
}) })
} }
@ -504,7 +504,7 @@ func TestContractCreate(t *testing.T) {
t.Run("positive", func(t *testing.T) { t.Run("positive", func(t *testing.T) {
putArgsOnStack() putArgsOnStack()
require.NoError(t, contractCreate(ic, v)) require.NoError(t, contractCreate(ic))
actual := v.Estack().Pop().Item() actual := v.Estack().Pop().Item()
compareContractStates(t, cs, actual) compareContractStates(t, cs, actual)
}) })
@ -513,7 +513,7 @@ func TestContractCreate(t *testing.T) {
cs.Script = append(cs.Script, 0x01) cs.Script = append(cs.Script, 0x01)
putArgsOnStack() putArgsOnStack()
require.Error(t, contractCreate(ic, v)) require.Error(t, contractCreate(ic))
}) })
t.Run("contract already exists", func(t *testing.T) { t.Run("contract already exists", func(t *testing.T) {
@ -521,7 +521,7 @@ func TestContractCreate(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(cs)) require.NoError(t, ic.DAO.PutContractState(cs))
putArgsOnStack() putArgsOnStack()
require.Error(t, contractCreate(ic, v)) require.Error(t, contractCreate(ic))
}) })
} }
@ -553,27 +553,27 @@ func TestContractUpdate(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(cs)) require.NoError(t, ic.DAO.PutContractState(cs))
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(nil, nil) putArgsOnStack(nil, nil)
require.NoError(t, contractUpdate(ic, v)) require.NoError(t, contractUpdate(ic))
}) })
t.Run("no contract", func(t *testing.T) { t.Run("no contract", func(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(cs)) require.NoError(t, ic.DAO.PutContractState(cs))
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("too large script", func(t *testing.T) { t.Run("too large script", func(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(cs)) require.NoError(t, ic.DAO.PutContractState(cs))
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(make([]byte, MaxContractScriptSize+1), nil) putArgsOnStack(make([]byte, MaxContractScriptSize+1), nil)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("too large manifest", func(t *testing.T) { t.Run("too large manifest", func(t *testing.T) {
require.NoError(t, ic.DAO.PutContractState(cs)) require.NoError(t, ic.DAO.PutContractState(cs))
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(nil, make([]byte, manifest.MaxManifestSize+1)) putArgsOnStack(nil, make([]byte, manifest.MaxManifestSize+1))
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("gas limit exceeded", func(t *testing.T) { t.Run("gas limit exceeded", func(t *testing.T) {
@ -581,7 +581,7 @@ func TestContractUpdate(t *testing.T) {
v.GasLimit = 0 v.GasLimit = 0
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack([]byte{1}, []byte{2}) putArgsOnStack([]byte{1}, []byte{2})
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update script, the same script", func(t *testing.T) { t.Run("update script, the same script", func(t *testing.T) {
@ -590,7 +590,7 @@ func TestContractUpdate(t *testing.T) {
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(cs.Script, nil) putArgsOnStack(cs.Script, nil)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update script, already exists", func(t *testing.T) { t.Run("update script, already exists", func(t *testing.T) {
@ -608,7 +608,7 @@ func TestContractUpdate(t *testing.T) {
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(duplicateScript, nil) putArgsOnStack(duplicateScript, nil)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update script, positive", func(t *testing.T) { t.Run("update script, positive", func(t *testing.T) {
@ -617,7 +617,7 @@ func TestContractUpdate(t *testing.T) {
newScript := []byte{9, 8, 7, 6, 5} newScript := []byte{9, 8, 7, 6, 5}
putArgsOnStack(newScript, nil) putArgsOnStack(newScript, nil)
require.NoError(t, contractUpdate(ic, v)) require.NoError(t, contractUpdate(ic))
// updated contract should have new scripthash // updated contract should have new scripthash
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript)) actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
@ -641,7 +641,7 @@ func TestContractUpdate(t *testing.T) {
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
putArgsOnStack(nil, []byte{1, 2, 3}) putArgsOnStack(nil, []byte{1, 2, 3})
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update manifest, bad contract hash", func(t *testing.T) { t.Run("update manifest, bad contract hash", func(t *testing.T) {
@ -656,7 +656,7 @@ func TestContractUpdate(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
putArgsOnStack(nil, manifestBytes) putArgsOnStack(nil, manifestBytes)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update manifest, old contract shouldn't have storage", func(t *testing.T) { t.Run("update manifest, old contract shouldn't have storage", func(t *testing.T) {
@ -676,7 +676,7 @@ func TestContractUpdate(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
putArgsOnStack(nil, manifestBytes) putArgsOnStack(nil, manifestBytes)
require.Error(t, contractUpdate(ic, v)) require.Error(t, contractUpdate(ic))
}) })
t.Run("update manifest, positive", func(t *testing.T) { t.Run("update manifest, positive", func(t *testing.T) {
@ -693,7 +693,7 @@ func TestContractUpdate(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
putArgsOnStack(nil, manifestBytes) putArgsOnStack(nil, manifestBytes)
require.NoError(t, contractUpdate(ic, v)) require.NoError(t, contractUpdate(ic))
// updated contract should have new scripthash // updated contract should have new scripthash
actual, err := ic.DAO.GetContractState(cs.ScriptHash()) actual, err := ic.DAO.GetContractState(cs.ScriptHash())
@ -721,7 +721,7 @@ func TestContractUpdate(t *testing.T) {
putArgsOnStack(newScript, newManifestBytes) putArgsOnStack(newScript, newManifestBytes)
require.NoError(t, contractUpdate(ic, v)) require.NoError(t, contractUpdate(ic))
// updated contract should have new script and manifest // updated contract should have new script and manifest
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript)) actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
@ -746,7 +746,7 @@ func TestContractGetCallFlags(t *testing.T) {
defer bc.Close() defer bc.Close()
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, smartcontract.All) v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, smartcontract.All)
require.NoError(t, contractGetCallFlags(ic, v)) require.NoError(t, contractGetCallFlags(ic))
require.Equal(t, int64(smartcontract.All), v.Estack().Pop().Value().(*big.Int).Int64()) require.Equal(t, int64(smartcontract.All), v.Estack().Pop().Value().(*big.Int).Int64())
} }
@ -759,27 +759,27 @@ func TestPointerCallback(t *testing.T) {
byte(opcode.DIV), byte(opcode.RET), byte(opcode.DIV), byte(opcode.RET),
} }
t.Run("Good", func(t *testing.T) { t.Run("Good", func(t *testing.T) {
v := loadScript(script, 2, stackitem.NewPointer(3, script)) loadScript(ic, script, 2, stackitem.NewPointer(3, script))
v.Estack().PushVal(v.Context()) ic.VM.Estack().PushVal(ic.VM.Context())
require.NoError(t, callback.Create(ic, v)) require.NoError(t, callback.Create(ic))
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)})
v.Estack().InsertAt(vm.NewElement(args), 1) ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
require.NoError(t, callback.Invoke(ic, v)) require.NoError(t, callback.Invoke(ic))
require.NoError(t, v.Run()) require.NoError(t, ic.VM.Run())
require.Equal(t, 1, v.Estack().Len()) require.Equal(t, 1, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(5), v.Estack().Pop().Item().Value()) require.Equal(t, big.NewInt(5), ic.VM.Estack().Pop().Item().Value())
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
t.Run("NotEnoughParameters", func(t *testing.T) { t.Run("NotEnoughParameters", func(t *testing.T) {
v := loadScript(script, 2, stackitem.NewPointer(3, script)) loadScript(ic, script, 2, stackitem.NewPointer(3, script))
v.Estack().PushVal(v.Context()) ic.VM.Estack().PushVal(ic.VM.Context())
require.NoError(t, callback.Create(ic, v)) require.NoError(t, callback.Create(ic))
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)})
v.Estack().InsertAt(vm.NewElement(args), 1) ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
require.Error(t, callback.Invoke(ic, v)) require.Error(t, callback.Invoke(ic))
}) })
}) })
@ -799,11 +799,11 @@ func TestMethodCallback(t *testing.T) {
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
runInvalid := func(args ...interface{}) func(t *testing.T) { runInvalid := func(args ...interface{}) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
v := loadScript(currCs.Script, 42) loadScript(ic, currCs.Script, 42)
for i := range args { for i := range args {
v.Estack().PushVal(args[i]) ic.VM.Estack().PushVal(args[i])
} }
require.Error(t, callback.CreateFromMethod(ic, v)) require.Error(t, callback.CreateFromMethod(ic))
} }
} }
t.Run("Hash", runInvalid("add", rawHash[1:])) t.Run("Hash", runInvalid("add", rawHash[1:]))
@ -812,37 +812,38 @@ func TestMethodCallback(t *testing.T) {
t.Run("DisallowedMethod", runInvalid("ret7", rawHash)) t.Run("DisallowedMethod", runInvalid("ret7", rawHash))
t.Run("Initialize", runInvalid("_initialize", rawHash)) t.Run("Initialize", runInvalid("_initialize", rawHash))
t.Run("NotEnoughArguments", func(t *testing.T) { t.Run("NotEnoughArguments", func(t *testing.T) {
v := loadScript(currCs.Script, 42, "add", rawHash) loadScript(ic, currCs.Script, 42, "add", rawHash)
require.NoError(t, callback.CreateFromMethod(ic, v)) require.NoError(t, callback.CreateFromMethod(ic))
v.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1) ic.VM.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1)
require.Error(t, callback.Invoke(ic, v)) require.Error(t, callback.Invoke(ic))
}) })
t.Run("CallIsNotAllowed", func(t *testing.T) { t.Run("CallIsNotAllowed", func(t *testing.T) {
v := vm.New() v := vm.New()
ic.VM = v
v.Load(currCs.Script) v.Load(currCs.Script)
v.Estack().PushVal("add") v.Estack().PushVal("add")
v.Estack().PushVal(rawHash) v.Estack().PushVal(rawHash)
require.NoError(t, callback.CreateFromMethod(ic, v)) require.NoError(t, callback.CreateFromMethod(ic))
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
v.Estack().InsertAt(vm.NewElement(args), 1) v.Estack().InsertAt(vm.NewElement(args), 1)
require.Error(t, callback.Invoke(ic, v)) require.Error(t, callback.Invoke(ic))
}) })
}) })
t.Run("Good", func(t *testing.T) { t.Run("Good", func(t *testing.T) {
v := loadScript(currCs.Script, 42, "add", rawHash) loadScript(ic, currCs.Script, 42, "add", rawHash)
require.NoError(t, callback.CreateFromMethod(ic, v)) require.NoError(t, callback.CreateFromMethod(ic))
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
v.Estack().InsertAt(vm.NewElement(args), 1) ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
require.NoError(t, callback.Invoke(ic, v)) require.NoError(t, callback.Invoke(ic))
require.NoError(t, v.Run()) require.NoError(t, ic.VM.Run())
require.Equal(t, 2, v.Estack().Len()) require.Equal(t, 2, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(6), v.Estack().Pop().Item().Value()) require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Item().Value())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value()) require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
}) })
} }
func TestSyscallCallback(t *testing.T) { func TestSyscallCallback(t *testing.T) {
@ -852,44 +853,44 @@ func TestSyscallCallback(t *testing.T) {
ic.Functions = append(ic.Functions, []interop.Function{ ic.Functions = append(ic.Functions, []interop.Function{
{ {
ID: 0x42, ID: 0x42,
Func: func(_ *interop.Context, v *vm.VM) error { Func: func(ic *interop.Context) error {
a := v.Estack().Pop().BigInt() a := ic.VM.Estack().Pop().BigInt()
b := v.Estack().Pop().BigInt() b := ic.VM.Estack().Pop().BigInt()
v.Estack().PushVal(new(big.Int).Add(a, b)) ic.VM.Estack().PushVal(new(big.Int).Add(a, b))
return nil return nil
}, },
ParamCount: 2, ParamCount: 2,
}, },
{ {
ID: 0x53, ID: 0x53,
Func: func(_ *interop.Context, _ *vm.VM) error { return nil }, Func: func(_ *interop.Context) error { return nil },
DisallowCallback: true, DisallowCallback: true,
}, },
}) })
t.Run("Good", func(t *testing.T) { t.Run("Good", func(t *testing.T) {
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)})
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42) loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
require.NoError(t, callback.CreateFromSyscall(ic, v)) require.NoError(t, callback.CreateFromSyscall(ic))
require.NoError(t, callback.Invoke(ic, v)) require.NoError(t, callback.Invoke(ic))
require.Equal(t, 1, v.Estack().Len()) require.Equal(t, 1, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value()) require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
t.Run("InvalidParameterCount", func(t *testing.T) { t.Run("InvalidParameterCount", func(t *testing.T) {
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)}) args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)})
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42) loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
require.NoError(t, callback.CreateFromSyscall(ic, v)) require.NoError(t, callback.CreateFromSyscall(ic))
require.Error(t, callback.Invoke(ic, v)) require.Error(t, callback.Invoke(ic))
}) })
t.Run("MissingSyscall", func(t *testing.T) { t.Run("MissingSyscall", func(t *testing.T) {
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43) loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43)
require.Error(t, callback.CreateFromSyscall(ic, v)) require.Error(t, callback.CreateFromSyscall(ic))
}) })
t.Run("Disallowed", func(t *testing.T) { t.Run("Disallowed", func(t *testing.T) {
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53) loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53)
require.Error(t, callback.CreateFromSyscall(ic, v)) require.Error(t, callback.CreateFromSyscall(ic))
}) })
}) })
} }

View file

@ -14,13 +14,14 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func testNonInterop(t *testing.T, value interface{}, f func(*interop.Context, *vm.VM) error) { func testNonInterop(t *testing.T, value interface{}, f func(*interop.Context) error) {
v := vm.New() v := vm.New()
v.Estack().PushVal(value) v.Estack().PushVal(value)
chain := newTestChain(t) chain := newTestChain(t)
defer chain.Close() defer chain.Close()
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
require.Error(t, f(context, v)) context.VM = v
require.Error(t, f(context))
} }
func TestUnexpectedNonInterops(t *testing.T) { func TestUnexpectedNonInterops(t *testing.T) {
@ -32,7 +33,7 @@ func TestUnexpectedNonInterops(t *testing.T) {
} }
// All of these functions expect an interop item on the stack. // All of these functions expect an interop item on the stack.
funcs := []func(*interop.Context, *vm.VM) error{ funcs := []func(*interop.Context) error{
storageContextAsReadOnly, storageContextAsReadOnly,
storageDelete, storageDelete,
storageFind, storageFind,

View file

@ -6,11 +6,10 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "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/core/state"
"github.com/nspcc-dev/neo-go/pkg/vm"
) )
// Deploy deploys native contract. // Deploy deploys native contract.
func Deploy(ic *interop.Context, _ *vm.VM) error { func Deploy(ic *interop.Context) error {
if ic.Block == nil || ic.Block.Index != 0 { if ic.Block == nil || ic.Block.Index != 0 {
return errors.New("native contracts can be deployed only at 0 block") return errors.New("native contracts can be deployed only at 0 block")
} }
@ -34,8 +33,8 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
} }
// Call calls specified native contract method. // Call calls specified native contract method.
func Call(ic *interop.Context, v *vm.VM) error { func Call(ic *interop.Context) error {
name := v.Estack().Pop().String() name := ic.VM.Estack().Pop().String()
var c interop.Contract var c interop.Contract
for _, ctr := range ic.Natives { for _, ctr := range ic.Natives {
if ctr.Metadata().Name == name { if ctr.Metadata().Name == name {
@ -46,23 +45,23 @@ func Call(ic *interop.Context, v *vm.VM) error {
if c == nil { if c == nil {
return fmt.Errorf("native contract %s not found", name) return fmt.Errorf("native contract %s not found", name)
} }
h := v.GetCurrentScriptHash() h := ic.VM.GetCurrentScriptHash()
if !h.Equals(c.Metadata().Hash) { if !h.Equals(c.Metadata().Hash) {
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used") return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
} }
operation := v.Estack().Pop().String() operation := ic.VM.Estack().Pop().String()
args := v.Estack().Pop().Array() args := ic.VM.Estack().Pop().Array()
m, ok := c.Metadata().Methods[operation] m, ok := c.Metadata().Methods[operation]
if !ok { if !ok {
return fmt.Errorf("method %s not found", operation) return fmt.Errorf("method %s not found", operation)
} }
if !v.Context().GetCallFlags().Has(m.RequiredFlags) { if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) {
return errors.New("missing call flags") return errors.New("missing call flags")
} }
if !v.AddGas(m.Price) { if !ic.VM.AddGas(m.Price) {
return errors.New("gas limit exceeded") return errors.New("gas limit exceeded")
} }
result := m.Func(ic, args) result := m.Func(ic, args)
v.Estack().PushVal(result) ic.VM.Estack().PushVal(result)
return nil return nil
} }

View file

@ -177,7 +177,7 @@ func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Ui
return errors.New("negative amount") return errors.New("negative amount")
} }
caller := ic.ScriptGetter.GetCallingScriptHash() caller := ic.VM.GetCallingScriptHash()
if caller.Equals(util.Uint160{}) || !from.Equals(caller) { if caller.Equals(util.Uint160{}) || !from.Equals(caller) {
ok, err := runtime.CheckHashedWitness(ic, from) ok, err := runtime.CheckHashedWitness(ic, from)
if err != nil { if err != nil {

View file

@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
@ -18,6 +19,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -76,6 +78,35 @@ func newTestNative() *testNative {
} }
tn.meta.AddMethod(md, desc, true) tn.meta.AddMethod(md, desc, true)
desc = &manifest.Method{
Name: "callOtherContractWithoutArgs",
Parameters: []manifest.Parameter{
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
manifest.NewParameter("method", smartcontract.StringType),
},
ReturnType: smartcontract.AnyType,
}
md = &interop.MethodAndPrice{
Func: tn.callOtherContractWithoutArgs,
Price: testSumPrice,
RequiredFlags: smartcontract.NoneFlag}
tn.meta.AddMethod(md, desc, true)
desc = &manifest.Method{
Name: "callOtherContractWithArg",
Parameters: []manifest.Parameter{
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
manifest.NewParameter("method", smartcontract.StringType),
manifest.NewParameter("arg", smartcontract.ArrayType),
},
ReturnType: smartcontract.AnyType,
}
md = &interop.MethodAndPrice{
Func: tn.callOtherContractWithArg,
Price: testSumPrice,
RequiredFlags: smartcontract.NoneFlag}
tn.meta.AddMethod(md, desc, true)
desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType} desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType}
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates} md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates}
tn.meta.AddMethod(md, desc, false) tn.meta.AddMethod(md, desc, false)
@ -95,6 +126,38 @@ func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.I
return stackitem.NewBigInteger(s1.Add(s1, s2)) return stackitem.NewBigInteger(s1.Add(s1, s2))
} }
func (tn *testNative) callOtherContractWithoutArgs(ic *interop.Context, args []stackitem.Item) stackitem.Item {
vm := ic.VM
vm.Estack().PushVal(stackitem.NewArray([]stackitem.Item{})) // no args
vm.Estack().PushVal(args[1]) // method
vm.Estack().PushVal(args[0]) // contract hash
err := contractCall(ic)
if err != nil {
return stackitem.NewBigInteger(big.NewInt(-1))
}
_ = vm.Run()
if vm.HasFailed() {
return stackitem.NewBigInteger(big.NewInt(-2))
}
return vm.Estack().Pop().Item()
}
func (tn *testNative) callOtherContractWithArg(ic *interop.Context, args []stackitem.Item) stackitem.Item {
vm := ic.VM
vm.Estack().PushVal(stackitem.NewArray([]stackitem.Item{args[2]})) // arg
vm.Estack().PushVal(args[1]) // method
vm.Estack().PushVal(args[0]) // contract hash
err := contractCall(ic)
if err != nil {
return stackitem.NewBigInteger(big.NewInt(-1))
}
_ = vm.Run()
if vm.HasFailed() {
return stackitem.NewBigInteger(big.NewInt(-2))
}
return vm.Estack().Pop().Item()
}
func TestNativeContract_Invoke(t *testing.T) { func TestNativeContract_Invoke(t *testing.T) {
chain := newTestChain(t) chain := newTestChain(t)
defer chain.Close() defer chain.Close()
@ -159,10 +222,9 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
v := vm.New()
v.GasLimit = -1
ic := chain.newInteropContext(trigger.Application, ic := chain.newInteropContext(trigger.Application,
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil) dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
v := ic.SpawnVM()
t.Run("fail, bad current script hash", func(t *testing.T) { t.Run("fail, bad current script hash", func(t *testing.T) {
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, smartcontract.All) v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, smartcontract.All)
@ -171,7 +233,7 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
v.Estack().PushVal(tn.Metadata().Name) v.Estack().PushVal(tn.Metadata().Name)
// it's prohibited to call natives directly // it's prohibited to call natives directly
require.Error(t, native.Call(ic, v)) require.Error(t, native.Call(ic))
}) })
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
@ -180,9 +242,117 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
v.Estack().PushVal("sum") v.Estack().PushVal("sum")
v.Estack().PushVal(tn.Metadata().Name) v.Estack().PushVal(tn.Metadata().Name)
require.NoError(t, native.Call(ic, v)) require.NoError(t, native.Call(ic))
value := v.Estack().Pop().BigInt() value := v.Estack().Pop().BigInt()
require.Equal(t, int64(42), value.Int64()) require.Equal(t, int64(42), value.Int64())
}) })
} }
func TestNativeContract_InvokeOtherContract(t *testing.T) {
chain := newTestChain(t)
defer chain.Close()
tn := newTestNative()
chain.registerNative(tn)
err := chain.dao.PutContractState(&state.Contract{
Script: tn.meta.Script,
Manifest: tn.meta.Manifest,
})
require.NoError(t, err)
t.Run("native Policy, getFeePerByte", func(t *testing.T) {
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithoutArgs", chain.contracts.Policy.Hash, "getFeePerByte")
require.NoError(t, w.Err)
script := w.Bytes()
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*4+10000)
validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil
addSigners(tx)
require.NoError(t, signTx(chain, tx))
b := chain.newBlock(tx)
require.NoError(t, chain.AddBlock(b))
res, err := chain.GetAppExecResult(tx.Hash())
require.NoError(t, err)
// we expect it to be FeePerByte from Policy contract
require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, big.NewInt(1000), res.Stack[0].Value())
})
t.Run("native Policy, setFeePerByte", func(t *testing.T) {
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithArg", chain.contracts.Policy.Hash, "setFeePerByte", int64(500))
require.NoError(t, w.Err)
script := w.Bytes()
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*5+10000)
validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil
addSigners(tx)
// to pass policy.checkValidators
tx.Signers[0].Scopes = transaction.Global
require.NoError(t, signTx(chain, tx))
b := chain.newBlock(tx)
require.NoError(t, chain.AddBlock(b))
require.NoError(t, chain.persist())
res, err := chain.GetAppExecResult(tx.Hash())
require.NoError(t, err)
// we expect it to be `true` which means that native policy value was successfully updated
require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, true, res.Stack[0].Value())
require.NoError(t, chain.persist())
// check that feePerByte was updated
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
require.Equal(t, 500, int(n))
})
t.Run("non-native contract", func(t *testing.T) {
// put some other contract into chain (this contract just pushes `5` on stack)
avm := []byte{byte(opcode.PUSH5), byte(opcode.RET)}
contractHash := hash.Hash160(avm)
m := manifest.NewManifest(contractHash)
m.ABI.Methods = []manifest.Method{
{
Name: "five",
Offset: 0,
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
},
}
err = chain.dao.PutContractState(&state.Contract{
Script: avm,
Manifest: *m,
})
require.NoError(t, err)
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithoutArgs", contractHash, "five")
require.NoError(t, w.Err)
script := w.Bytes()
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*4+10000)
validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil
addSigners(tx)
require.NoError(t, signTx(chain, tx))
b := chain.newBlock(tx)
require.NoError(t, chain.AddBlock(b))
res, err := chain.GetAppExecResult(tx.Hash())
require.NoError(t, err)
require.Equal(t, vm.HaltState, res.VMState)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, int64(5), res.Stack[0].Value().(*big.Int).Int64())
})
}

View file

@ -40,13 +40,6 @@ func newError(ip int, op opcode.Opcode, err interface{}) *errorAtInstruct {
// StateMessage is a vm state message which could be used as additional info for example by cli. // StateMessage is a vm state message which could be used as additional info for example by cli.
type StateMessage string type StateMessage string
// ScriptHashGetter defines an interface for getting calling, entry and current script hashes.
type ScriptHashGetter interface {
GetCallingScriptHash() util.Uint160
GetEntryScriptHash() util.Uint160
GetCurrentScriptHash() util.Uint160
}
const ( const (
// MaxInvocationStackSize is the maximum size of an invocation stack. // MaxInvocationStackSize is the maximum size of an invocation stack.
MaxInvocationStackSize = 1024 MaxInvocationStackSize = 1024