forked from TrueCloudLab/neoneo-go
Merge pull request #1271 from nspcc-dev/core/call_from_native
core: contractCall from native contracts
This commit is contained in:
commit
f5131491b7
22 changed files with 558 additions and 394 deletions
|
@ -23,6 +23,7 @@ func TestSHA256(t *testing.T) {
|
|||
`
|
||||
v := vmAndCompile(t, src)
|
||||
ic := &interop.Context{Trigger: trigger.Verification}
|
||||
ic.VM = v
|
||||
crypto.Register(ic)
|
||||
v.SyscallHandler = ic.SyscallHandler
|
||||
require.NoError(t, v.Run())
|
||||
|
|
|
@ -18,19 +18,19 @@ type Callback interface {
|
|||
}
|
||||
|
||||
// Invoke invokes provided callback.
|
||||
func Invoke(ic *interop.Context, v *vm.VM) error {
|
||||
cb := v.Estack().Pop().Interop().Value().(Callback)
|
||||
args := v.Estack().Pop().Array()
|
||||
func Invoke(ic *interop.Context) error {
|
||||
cb := ic.VM.Estack().Pop().Interop().Value().(Callback)
|
||||
args := ic.VM.Estack().Pop().Array()
|
||||
if cb.ArgCount() != len(args) {
|
||||
return errors.New("invalid argument count")
|
||||
}
|
||||
cb.LoadContext(v, args)
|
||||
cb.LoadContext(ic.VM, args)
|
||||
switch t := cb.(type) {
|
||||
case *MethodCallback:
|
||||
id := emit.InteropNameToID([]byte("System.Contract.Call"))
|
||||
return ic.SyscallHandler(v, id)
|
||||
return ic.SyscallHandler(ic.VM, id)
|
||||
case *SyscallCallback:
|
||||
return ic.SyscallHandler(v, t.desc.ID)
|
||||
return ic.SyscallHandler(ic.VM, t.desc.ID)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
|||
}
|
||||
|
||||
// CreateFromMethod creates callback for a contract method.
|
||||
func CreateFromMethod(ic *interop.Context, v *vm.VM) error {
|
||||
rawHash := v.Estack().Pop().Bytes()
|
||||
func CreateFromMethod(ic *interop.Context) error {
|
||||
rawHash := ic.VM.Estack().Pop().Bytes()
|
||||
h, err := util.Uint160DecodeBytesBE(rawHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -43,16 +43,16 @@ func CreateFromMethod(ic *interop.Context, v *vm.VM) error {
|
|||
if err != nil {
|
||||
return errors.New("contract not found")
|
||||
}
|
||||
method := string(v.Estack().Pop().Bytes())
|
||||
method := string(ic.VM.Estack().Pop().Bytes())
|
||||
if strings.HasPrefix(method, "_") {
|
||||
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) {
|
||||
return errors.New("method call is not allowed")
|
||||
}
|
||||
md := cs.Manifest.ABI.GetMethod(method)
|
||||
v.Estack().PushVal(stackitem.NewInterop(&MethodCallback{
|
||||
ic.VM.Estack().PushVal(stackitem.NewInterop(&MethodCallback{
|
||||
contract: cs,
|
||||
method: md,
|
||||
}))
|
||||
|
|
|
@ -29,11 +29,11 @@ func (p *PointerCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
|||
}
|
||||
|
||||
// Create creates callback using pointer and parameters count.
|
||||
func Create(_ *interop.Context, v *vm.VM) error {
|
||||
ctx := v.Estack().Pop().Item().(*vm.Context)
|
||||
offset := v.Estack().Pop().Item().(*stackitem.Pointer).Position()
|
||||
count := v.Estack().Pop().BigInt().Int64()
|
||||
v.Estack().PushVal(stackitem.NewInterop(&PointerCallback{
|
||||
func Create(ic *interop.Context) error {
|
||||
ctx := ic.VM.Estack().Pop().Item().(*vm.Context)
|
||||
offset := ic.VM.Estack().Pop().Item().(*stackitem.Pointer).Position()
|
||||
count := ic.VM.Estack().Pop().BigInt().Int64()
|
||||
ic.VM.Estack().PushVal(stackitem.NewInterop(&PointerCallback{
|
||||
paramCount: int(count),
|
||||
offset: offset,
|
||||
context: ctx,
|
||||
|
|
|
@ -28,8 +28,8 @@ func (p *SyscallCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
|||
}
|
||||
|
||||
// CreateFromSyscall creates callback from syscall.
|
||||
func CreateFromSyscall(ic *interop.Context, v *vm.VM) error {
|
||||
id := uint32(v.Estack().Pop().BigInt().Int64())
|
||||
func CreateFromSyscall(ic *interop.Context) error {
|
||||
id := uint32(ic.VM.Estack().Pop().BigInt().Int64())
|
||||
f := ic.GetFunction(id)
|
||||
if f == nil {
|
||||
return errors.New("syscall not found")
|
||||
|
@ -37,6 +37,6 @@ func CreateFromSyscall(ic *interop.Context, v *vm.VM) error {
|
|||
if f.DisallowCallback {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ type Context struct {
|
|||
Notifications []state.NotificationEvent
|
||||
Log *zap.Logger
|
||||
Invocations map[util.Uint160]int
|
||||
ScriptGetter vm.ScriptHashGetter
|
||||
VM *vm.VM
|
||||
Functions [][]Function
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n
|
|||
type Function struct {
|
||||
ID uint32
|
||||
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 bool
|
||||
// ParamCount is a number of function parameters.
|
||||
|
@ -155,26 +155,26 @@ func (ic *Context) GetFunction(id uint32) *Function {
|
|||
}
|
||||
|
||||
// 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)
|
||||
if f == nil {
|
||||
return errors.New("syscall not found")
|
||||
}
|
||||
cf := v.Context().GetCallFlags()
|
||||
cf := ic.VM.Context().GetCallFlags()
|
||||
if !cf.Has(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 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 {
|
||||
v := vm.NewWithTrigger(ic.Trigger)
|
||||
v.GasLimit = -1
|
||||
v.SyscallHandler = ic.SyscallHandler
|
||||
ic.ScriptGetter = v
|
||||
ic.VM = v
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -18,56 +18,56 @@ import (
|
|||
const ECDSAVerifyPrice = 1000000
|
||||
|
||||
// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve.
|
||||
func ECDSASecp256r1Verify(ic *interop.Context, v *vm.VM) error {
|
||||
return ecdsaVerify(ic, v, elliptic.P256())
|
||||
func ECDSASecp256r1Verify(ic *interop.Context) error {
|
||||
return ecdsaVerify(ic, elliptic.P256())
|
||||
}
|
||||
|
||||
// ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve
|
||||
func ECDSASecp256k1Verify(ic *interop.Context, v *vm.VM) error {
|
||||
return ecdsaVerify(ic, v, btcec.S256())
|
||||
func ECDSASecp256k1Verify(ic *interop.Context) error {
|
||||
return ecdsaVerify(ic, btcec.S256())
|
||||
}
|
||||
|
||||
// ecdsaVerify is internal representation of ECDSASecp256k1Verify and
|
||||
// ECDSASecp256r1Verify.
|
||||
func ecdsaVerify(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error {
|
||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
||||
func ecdsaVerify(ic *interop.Context, curve elliptic.Curve) error {
|
||||
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||
hashToCheck := hash.Sha256(msg).BytesBE()
|
||||
keyb := v.Estack().Pop().Bytes()
|
||||
signature := v.Estack().Pop().Bytes()
|
||||
keyb := ic.VM.Estack().Pop().Bytes()
|
||||
signature := ic.VM.Estack().Pop().Bytes()
|
||||
pkey, err := keys.NewPublicKeyFromBytes(keyb, curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := pkey.Verify(signature, hashToCheck)
|
||||
v.Estack().PushVal(res)
|
||||
ic.VM.Estack().PushVal(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
|
||||
// Secp256r1 elliptic curve.
|
||||
func ECDSASecp256r1CheckMultisig(ic *interop.Context, v *vm.VM) error {
|
||||
return ecdsaCheckMultisig(ic, v, elliptic.P256())
|
||||
func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
|
||||
return ecdsaCheckMultisig(ic, elliptic.P256())
|
||||
}
|
||||
|
||||
// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using
|
||||
// Secp256k1 elliptic curve.
|
||||
func ECDSASecp256k1CheckMultisig(ic *interop.Context, v *vm.VM) error {
|
||||
return ecdsaCheckMultisig(ic, v, btcec.S256())
|
||||
func ECDSASecp256k1CheckMultisig(ic *interop.Context) error {
|
||||
return ecdsaCheckMultisig(ic, btcec.S256())
|
||||
}
|
||||
|
||||
// ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and
|
||||
// ECDSASecp256k1CheckMultisig
|
||||
func ecdsaCheckMultisig(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error {
|
||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
||||
func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error {
|
||||
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||
hashToCheck := hash.Sha256(msg).BytesBE()
|
||||
pkeys, err := v.Estack().PopSigElements()
|
||||
pkeys, err := ic.VM.Estack().PopSigElements()
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
sigs, err := v.Estack().PopSigElements()
|
||||
sigs, err := ic.VM.Estack().PopSigElements()
|
||||
if err != nil {
|
||||
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) {
|
||||
return errors.New("more signatures than there are keys")
|
||||
}
|
||||
sigok := vm.CheckMultisigPar(v, curve, hashToCheck, pkeys, sigs)
|
||||
v.Estack().PushVal(sigok)
|
||||
sigok := vm.CheckMultisigPar(ic.VM, curve, hashToCheck, pkeys, sigs)
|
||||
ic.VM.Estack().PushVal(sigok)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,21 +3,20 @@ package crypto
|
|||
import (
|
||||
"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/vm"
|
||||
)
|
||||
|
||||
// Sha256 returns sha256 hash of the data.
|
||||
func Sha256(ic *interop.Context, v *vm.VM) error {
|
||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
||||
func Sha256(ic *interop.Context) error {
|
||||
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||
h := hash.Sha256(msg).BytesBE()
|
||||
v.Estack().PushVal(h)
|
||||
ic.VM.Estack().PushVal(h)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RipeMD160 returns RipeMD160 hash of the data.
|
||||
func RipeMD160(ic *interop.Context, v *vm.VM) error {
|
||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
||||
func RipeMD160(ic *interop.Context) error {
|
||||
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||
h := hash.RipeMD160(msg).BytesBE()
|
||||
v.Estack().PushVal(h)
|
||||
ic.VM.Estack().PushVal(h)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,22 +6,22 @@ import (
|
|||
)
|
||||
|
||||
// Concat concatenates 2 enumerators into a single one.
|
||||
func Concat(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.EnumeratorConcat(v)
|
||||
func Concat(ic *interop.Context) error {
|
||||
return vm.EnumeratorConcat(ic.VM)
|
||||
}
|
||||
|
||||
// Create creates an enumerator from an array-like or bytearray-like stack item.
|
||||
func Create(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.EnumeratorCreate(v)
|
||||
func Create(ic *interop.Context) error {
|
||||
return vm.EnumeratorCreate(ic.VM)
|
||||
}
|
||||
|
||||
// Next advances the enumerator, pushes true if is it was successful
|
||||
// and false otherwise.
|
||||
func Next(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.EnumeratorNext(v)
|
||||
func Next(ic *interop.Context) error {
|
||||
return vm.EnumeratorNext(ic.VM)
|
||||
}
|
||||
|
||||
// Value returns the current value of the enumerator.
|
||||
func Value(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.EnumeratorValue(v)
|
||||
func Value(ic *interop.Context) error {
|
||||
return vm.EnumeratorValue(ic.VM)
|
||||
}
|
||||
|
|
|
@ -6,26 +6,26 @@ import (
|
|||
)
|
||||
|
||||
// Concat concatenates 2 iterators into a single one.
|
||||
func Concat(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.IteratorConcat(v)
|
||||
func Concat(ic *interop.Context) error {
|
||||
return vm.IteratorConcat(ic.VM)
|
||||
}
|
||||
|
||||
// Create creates an iterator from array-like or map stack item.
|
||||
func Create(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.IteratorCreate(v)
|
||||
func Create(ic *interop.Context) error {
|
||||
return vm.IteratorCreate(ic.VM)
|
||||
}
|
||||
|
||||
// Key returns current iterator key.
|
||||
func Key(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.IteratorKey(v)
|
||||
func Key(ic *interop.Context) error {
|
||||
return vm.IteratorKey(ic.VM)
|
||||
}
|
||||
|
||||
// Keys returns keys of the iterator.
|
||||
func Keys(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.IteratorKeys(v)
|
||||
func Keys(ic *interop.Context) error {
|
||||
return vm.IteratorKeys(ic.VM)
|
||||
}
|
||||
|
||||
// Values returns values of the iterator.
|
||||
func Values(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.IteratorValues(v)
|
||||
func Values(ic *interop.Context) error {
|
||||
return vm.IteratorValues(ic.VM)
|
||||
}
|
||||
|
|
|
@ -2,28 +2,27 @@ package json
|
|||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// Serialize handles System.JSON.Serialize syscall.
|
||||
func Serialize(_ *interop.Context, v *vm.VM) error {
|
||||
item := v.Estack().Pop().Item()
|
||||
func Serialize(ic *interop.Context) error {
|
||||
item := ic.VM.Estack().Pop().Item()
|
||||
data, err := stackitem.ToJSON(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(data)
|
||||
ic.VM.Estack().PushVal(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deserialize handles System.JSON.Deserialize syscall.
|
||||
func Deserialize(_ *interop.Context, v *vm.VM) error {
|
||||
data := v.Estack().Pop().Bytes()
|
||||
func Deserialize(ic *interop.Context) error {
|
||||
data := ic.VM.Estack().Pop().Bytes()
|
||||
item, err := stackitem.FromJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(item)
|
||||
ic.VM.Estack().PushVal(item)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -11,18 +11,18 @@ import (
|
|||
)
|
||||
|
||||
// GasLeft returns remaining amount of GAS.
|
||||
func GasLeft(_ *interop.Context, v *vm.VM) error {
|
||||
if v.GasLimit == -1 {
|
||||
v.Estack().PushVal(v.GasLimit)
|
||||
func GasLeft(ic *interop.Context) error {
|
||||
if ic.VM.GasLimit == -1 {
|
||||
ic.VM.Estack().PushVal(ic.VM.GasLimit)
|
||||
} else {
|
||||
v.Estack().PushVal(v.GasLimit - v.GasConsumed())
|
||||
ic.VM.Estack().PushVal(ic.VM.GasLimit - ic.VM.GasConsumed())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNotifications returns notifications emitted by current contract execution.
|
||||
func GetNotifications(ic *interop.Context, v *vm.VM) error {
|
||||
item := v.Estack().Pop().Item()
|
||||
func GetNotifications(ic *interop.Context) error {
|
||||
item := ic.VM.Estack().Pop().Item()
|
||||
notifications := ic.Notifications
|
||||
if _, ok := item.(stackitem.Null); !ok {
|
||||
b, err := item.TryBytes()
|
||||
|
@ -52,16 +52,16 @@ func GetNotifications(ic *interop.Context, v *vm.VM) error {
|
|||
})
|
||||
arr.Append(ev)
|
||||
}
|
||||
v.Estack().PushVal(arr)
|
||||
ic.VM.Estack().PushVal(arr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInvocationCounter returns how many times current contract was invoked during current tx execution.
|
||||
func GetInvocationCounter(ic *interop.Context, v *vm.VM) error {
|
||||
count, ok := ic.Invocations[v.GetCurrentScriptHash()]
|
||||
func GetInvocationCounter(ic *interop.Context) error {
|
||||
count, ok := ic.Invocations[ic.VM.GetCurrentScriptHash()]
|
||||
if !ok {
|
||||
return errors.New("current contract wasn't invoked from others")
|
||||
}
|
||||
v.Estack().PushVal(count)
|
||||
ic.VM.Estack().PushVal(count)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ import (
|
|||
// for verifying in the interop context.
|
||||
func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
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 {
|
||||
if c.Account == hash {
|
||||
if c.Scopes == transaction.Global {
|
||||
|
@ -75,11 +75,11 @@ func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) {
|
|||
}
|
||||
|
||||
// CheckWitness checks witnesses.
|
||||
func CheckWitness(ic *interop.Context, v *vm.VM) error {
|
||||
func CheckWitness(ic *interop.Context) error {
|
||||
var res bool
|
||||
var err error
|
||||
|
||||
hashOrKey := v.Estack().Pop().Bytes()
|
||||
hashOrKey := ic.VM.Estack().Pop().Bytes()
|
||||
hash, err := util.Uint160DecodeBytesBE(hashOrKey)
|
||||
if err != nil {
|
||||
var key *keys.PublicKey
|
||||
|
@ -94,6 +94,6 @@ func CheckWitness(ic *interop.Context, v *vm.VM) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to check witness: %w", err)
|
||||
}
|
||||
v.Estack().PushVal(res)
|
||||
ic.VM.Estack().PushVal(res)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ const (
|
|||
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
||||
|
||||
// storageFind finds stored key-value pair.
|
||||
func storageFind(ic *interop.Context, v *vm.VM) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
func storageFind(ic *interop.Context) error {
|
||||
stcInterface := ic.VM.Estack().Pop().Value()
|
||||
stc, ok := stcInterface.(*StorageContext)
|
||||
if !ok {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -51,7 +51,7 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
|
|||
})
|
||||
|
||||
item := vm.NewMapIterator(filteredMap)
|
||||
v.Estack().PushVal(item)
|
||||
ic.VM.Estack().PushVal(item)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -59,16 +59,16 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
|
|||
// createContractStateFromVM pops all contract state elements from the VM
|
||||
// evaluation stack, does a lot of checks and returns Contract if it
|
||||
// succeeds.
|
||||
func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, error) {
|
||||
script := v.Estack().Pop().Bytes()
|
||||
func createContractStateFromVM(ic *interop.Context) (*state.Contract, error) {
|
||||
script := ic.VM.Estack().Pop().Bytes()
|
||||
if len(script) > MaxContractScriptSize {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
var m manifest.Manifest
|
||||
|
@ -83,8 +83,8 @@ func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract,
|
|||
}
|
||||
|
||||
// contractCreate creates a contract.
|
||||
func contractCreate(ic *interop.Context, v *vm.VM) error {
|
||||
newcontract, err := createContractStateFromVM(ic, v)
|
||||
func contractCreate(ic *interop.Context) error {
|
||||
newcontract, err := createContractStateFromVM(ic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -107,26 +107,26 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("cannot convert contract to stack item: %w", err)
|
||||
}
|
||||
v.Estack().PushVal(cs)
|
||||
ic.VM.Estack().PushVal(cs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
||||
// of the contract can be updated independently.
|
||||
func contractUpdate(ic *interop.Context, v *vm.VM) error {
|
||||
contract, _ := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
||||
func contractUpdate(ic *interop.Context) error {
|
||||
contract, _ := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||
if contract == nil {
|
||||
return errors.New("contract doesn't exist")
|
||||
}
|
||||
script := v.Estack().Pop().Bytes()
|
||||
script := ic.VM.Estack().Pop().Bytes()
|
||||
if len(script) > MaxContractScriptSize {
|
||||
return errors.New("the script is too big")
|
||||
}
|
||||
manifestBytes := v.Estack().Pop().Bytes()
|
||||
manifestBytes := ic.VM.Estack().Pop().Bytes()
|
||||
if len(manifestBytes) > manifest.MaxManifestSize {
|
||||
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
|
||||
}
|
||||
// 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.
|
||||
func runtimeSerialize(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.RuntimeSerialize(v)
|
||||
func runtimeSerialize(ic *interop.Context) error {
|
||||
return vm.RuntimeSerialize(ic.VM)
|
||||
}
|
||||
|
||||
// runtimeDeserialize deserializes ByteArray from a stack into an item.
|
||||
func runtimeDeserialize(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.RuntimeDeserialize(v)
|
||||
func runtimeDeserialize(ic *interop.Context) error {
|
||||
return vm.RuntimeDeserialize(ic.VM)
|
||||
}
|
||||
|
||||
// runtimeEncode encodes top stack item into a base64 string.
|
||||
func runtimeEncode(_ *interop.Context, v *vm.VM) error {
|
||||
src := v.Estack().Pop().Bytes()
|
||||
func runtimeEncode(ic *interop.Context) error {
|
||||
src := ic.VM.Estack().Pop().Bytes()
|
||||
result := base64.StdEncoding.EncodeToString(src)
|
||||
v.Estack().PushVal([]byte(result))
|
||||
ic.VM.Estack().PushVal([]byte(result))
|
||||
return nil
|
||||
}
|
||||
|
||||
// runtimeDecode decodes top stack item from base64 string to byte array.
|
||||
func runtimeDecode(_ *interop.Context, v *vm.VM) error {
|
||||
src := v.Estack().Pop().String()
|
||||
func runtimeDecode(ic *interop.Context) error {
|
||||
src := ic.VM.Estack().Pop().String()
|
||||
result, err := base64.StdEncoding.DecodeString(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(result)
|
||||
ic.VM.Estack().PushVal(result)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -40,9 +40,9 @@ import (
|
|||
*/
|
||||
|
||||
func TestGetTrigger(t *testing.T) {
|
||||
v, _, context, chain := createVMAndPushBlock(t)
|
||||
_, _, context, chain := createVMAndPushBlock(t)
|
||||
defer chain.Close()
|
||||
require.NoError(t, runtimeGetTrigger(context, v))
|
||||
require.NoError(t, runtimeGetTrigger(context))
|
||||
}
|
||||
|
||||
func TestStorageFind(t *testing.T) {
|
||||
|
@ -75,37 +75,37 @@ func TestStorageFind(t *testing.T) {
|
|||
v.Estack().PushVal([]byte{0x01})
|
||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
||||
|
||||
err := storageFind(context, v)
|
||||
err := storageFind(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
var iter *stackitem.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())
|
||||
|
||||
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())
|
||||
|
||||
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())
|
||||
|
||||
v.Estack().PushVal(iter)
|
||||
require.NoError(t, enumerator.Next(context, v))
|
||||
require.NoError(t, enumerator.Next(context))
|
||||
require.True(t, v.Estack().Pop().Bool())
|
||||
|
||||
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())
|
||||
|
||||
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())
|
||||
|
||||
v.Estack().PushVal(iter)
|
||||
require.NoError(t, enumerator.Next(context, v))
|
||||
require.NoError(t, enumerator.Next(context))
|
||||
require.False(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
|
||||
|
@ -113,10 +113,10 @@ func TestStorageFind(t *testing.T) {
|
|||
v.Estack().PushVal([]byte{0x03})
|
||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
||||
|
||||
err := storageFind(context, v)
|
||||
err := storageFind(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, enumerator.Next(context, v))
|
||||
require.NoError(t, enumerator.Next(context))
|
||||
require.False(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
|
||||
|
@ -124,7 +124,7 @@ func TestStorageFind(t *testing.T) {
|
|||
v.Estack().PushVal([]byte{0x01})
|
||||
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) {
|
||||
|
@ -133,8 +133,8 @@ func TestStorageFind(t *testing.T) {
|
|||
v.Estack().PushVal([]byte{0x01})
|
||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID}))
|
||||
|
||||
require.NoError(t, storageFind(context, v))
|
||||
require.NoError(t, enumerator.Next(context, v))
|
||||
require.NoError(t, storageFind(context))
|
||||
require.NoError(t, enumerator.Next(context))
|
||||
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)
|
||||
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
|
||||
v := vm.New()
|
||||
ic.VM = v
|
||||
for i := range args {
|
||||
v.Estack().PushVal(args[i])
|
||||
}
|
||||
|
@ -160,7 +161,7 @@ func TestECDSAVerify(t *testing.T) {
|
|||
err = fmt.Errorf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
err = crypto.ECDSASecp256r1Verify(ic, v)
|
||||
err = crypto.ECDSASecp256r1Verify(ic)
|
||||
}()
|
||||
|
||||
if isErr {
|
||||
|
@ -227,7 +228,7 @@ func TestRuntimeEncode(t *testing.T) {
|
|||
defer bc.Close()
|
||||
|
||||
v.Estack().PushVal(str)
|
||||
require.NoError(t, runtimeEncode(ic, v))
|
||||
require.NoError(t, runtimeEncode(ic))
|
||||
|
||||
expected := []byte(base64.StdEncoding.EncodeToString(str))
|
||||
actual := v.Estack().Pop().Bytes()
|
||||
|
@ -242,7 +243,7 @@ func TestRuntimeDecode(t *testing.T) {
|
|||
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
v.Estack().PushVal(str)
|
||||
require.NoError(t, runtimeDecode(ic, v))
|
||||
require.NoError(t, runtimeDecode(ic))
|
||||
|
||||
actual := v.Estack().Pop().Bytes()
|
||||
require.Equal(t, expected, actual)
|
||||
|
@ -250,17 +251,17 @@ func TestRuntimeDecode(t *testing.T) {
|
|||
|
||||
t.Run("error", func(t *testing.T) {
|
||||
v.Estack().PushVal(str + "%")
|
||||
require.Error(t, runtimeDecode(ic, v))
|
||||
require.Error(t, runtimeDecode(ic))
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
||||
|
||||
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application,
|
||||
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||
v := context.SpawnVM()
|
||||
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) {
|
||||
v := vm.New()
|
||||
block := newDumbBlock()
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), block, nil)
|
||||
v := context.SpawnVM()
|
||||
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) {
|
||||
v := vm.New()
|
||||
script := []byte("testscript")
|
||||
m := manifest.NewManifest(hash.Hash160(script))
|
||||
m.Features = smartcontract.HasStorage
|
||||
|
@ -297,11 +297,11 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.C
|
|||
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||
v := context.SpawnVM()
|
||||
return v, contractState, context, chain
|
||||
}
|
||||
|
||||
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
|
||||
hash, err := util.Uint160DecodeStringBE(rawHash)
|
||||
accountState := state.NewAccount(hash)
|
||||
|
@ -309,11 +309,11 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
|
|||
require.NoError(t, err)
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||
v := context.SpawnVM()
|
||||
return v, accountState, context, chain
|
||||
}
|
||||
|
||||
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
||||
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}}}
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx)
|
||||
v := context.SpawnVM()
|
||||
return v, tx, context, chain
|
||||
}
|
||||
|
|
|
@ -89,16 +89,16 @@ func blockToStackItem(b *block.Block) stackitem.Item {
|
|||
}
|
||||
|
||||
// bcGetBlock returns current block.
|
||||
func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
||||
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
||||
func bcGetBlock(ic *interop.Context) error {
|
||||
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block, err := ic.Chain.GetBlock(hash)
|
||||
if err != nil || !isTraceableBlock(ic, block.Index) {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||
} else {
|
||||
v.Estack().PushVal(blockToStackItem(block))
|
||||
ic.VM.Estack().PushVal(blockToStackItem(block))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -118,28 +118,28 @@ func contractToStackItem(cs *state.Contract) (stackitem.Item, error) {
|
|||
}
|
||||
|
||||
// bcGetContract returns contract.
|
||||
func bcGetContract(ic *interop.Context, v *vm.VM) error {
|
||||
hashbytes := v.Estack().Pop().Bytes()
|
||||
func bcGetContract(ic *interop.Context) error {
|
||||
hashbytes := ic.VM.Estack().Pop().Bytes()
|
||||
hash, err := util.Uint160DecodeBytesBE(hashbytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs, err := ic.DAO.GetContractState(hash)
|
||||
if err != nil {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||
} else {
|
||||
item, err := contractToStackItem(cs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(item)
|
||||
ic.VM.Estack().PushVal(item)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bcGetHeight returns blockchain height.
|
||||
func bcGetHeight(ic *interop.Context, v *vm.VM) error {
|
||||
v.Estack().PushVal(ic.Chain.BlockHeight())
|
||||
func bcGetHeight(ic *interop.Context) error {
|
||||
ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -176,51 +176,51 @@ func transactionToStackItem(t *transaction.Transaction) stackitem.Item {
|
|||
}
|
||||
|
||||
// bcGetTransaction returns transaction.
|
||||
func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
|
||||
tx, h, err := getTransactionAndHeight(ic.DAO, v)
|
||||
func bcGetTransaction(ic *interop.Context) error {
|
||||
tx, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
|
||||
if err != nil || !isTraceableBlock(ic, h) {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||
return nil
|
||||
}
|
||||
v.Estack().PushVal(transactionToStackItem(tx))
|
||||
ic.VM.Estack().PushVal(transactionToStackItem(tx))
|
||||
return nil
|
||||
}
|
||||
|
||||
// bcGetTransactionFromBlock returns transaction with the given index from the
|
||||
// block with height or hash specified.
|
||||
func bcGetTransactionFromBlock(ic *interop.Context, v *vm.VM) error {
|
||||
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
||||
func bcGetTransactionFromBlock(ic *interop.Context) error {
|
||||
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
index := v.Estack().Pop().BigInt().Int64()
|
||||
index := ic.VM.Estack().Pop().BigInt().Int64()
|
||||
block, err := ic.DAO.GetBlock(hash)
|
||||
if err != nil || !isTraceableBlock(ic, block.Index) {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||
return nil
|
||||
}
|
||||
if index < 0 || index >= int64(len(block.Transactions)) {
|
||||
return errors.New("wrong transaction index")
|
||||
}
|
||||
tx := block.Transactions[index]
|
||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
||||
ic.VM.Estack().PushVal(tx.Hash().BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// bcGetTransactionHeight returns transaction height.
|
||||
func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error {
|
||||
_, h, err := getTransactionAndHeight(ic.DAO, v)
|
||||
func bcGetTransactionHeight(ic *interop.Context) error {
|
||||
_, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
|
||||
if err != nil || !isTraceableBlock(ic, h) {
|
||||
v.Estack().PushVal(-1)
|
||||
ic.VM.Estack().PushVal(-1)
|
||||
return nil
|
||||
}
|
||||
v.Estack().PushVal(h)
|
||||
ic.VM.Estack().PushVal(h)
|
||||
return nil
|
||||
}
|
||||
|
||||
// engineGetScriptContainer returns transaction or block that contains the script
|
||||
// being run.
|
||||
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
||||
func engineGetScriptContainer(ic *interop.Context) error {
|
||||
var item stackitem.Item
|
||||
switch t := ic.Container.(type) {
|
||||
case *transaction.Transaction:
|
||||
|
@ -230,45 +230,45 @@ func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
|||
default:
|
||||
return errors.New("unknown script container")
|
||||
}
|
||||
v.Estack().PushVal(item)
|
||||
ic.VM.Estack().PushVal(item)
|
||||
return nil
|
||||
}
|
||||
|
||||
// engineGetExecutingScriptHash returns executing script hash.
|
||||
func engineGetExecutingScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||
return v.PushContextScriptHash(0)
|
||||
func engineGetExecutingScriptHash(ic *interop.Context) error {
|
||||
return ic.VM.PushContextScriptHash(0)
|
||||
}
|
||||
|
||||
// engineGetCallingScriptHash returns calling script hash.
|
||||
func engineGetCallingScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||
return v.PushContextScriptHash(1)
|
||||
func engineGetCallingScriptHash(ic *interop.Context) error {
|
||||
return ic.VM.PushContextScriptHash(1)
|
||||
}
|
||||
|
||||
// engineGetEntryScriptHash returns entry script hash.
|
||||
func engineGetEntryScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||
return v.PushContextScriptHash(v.Istack().Len() - 1)
|
||||
func engineGetEntryScriptHash(ic *interop.Context) error {
|
||||
return ic.VM.PushContextScriptHash(ic.VM.Istack().Len() - 1)
|
||||
}
|
||||
|
||||
// runtimePlatform returns the name of the platform.
|
||||
func runtimePlatform(ic *interop.Context, v *vm.VM) error {
|
||||
v.Estack().PushVal([]byte("NEO"))
|
||||
func runtimePlatform(ic *interop.Context) error {
|
||||
ic.VM.Estack().PushVal([]byte("NEO"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// runtimeGetTrigger returns the script trigger.
|
||||
func runtimeGetTrigger(ic *interop.Context, v *vm.VM) error {
|
||||
v.Estack().PushVal(byte(ic.Trigger))
|
||||
func runtimeGetTrigger(ic *interop.Context) error {
|
||||
ic.VM.Estack().PushVal(byte(ic.Trigger))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func runtimeNotify(ic *interop.Context, v *vm.VM) error {
|
||||
name := v.Estack().Pop().String()
|
||||
func runtimeNotify(ic *interop.Context) error {
|
||||
name := ic.VM.Estack().Pop().String()
|
||||
if len(name) > MaxEventNameLen {
|
||||
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
|
||||
}
|
||||
elem := v.Estack().Pop()
|
||||
elem := ic.VM.Estack().Pop()
|
||||
args := elem.Array()
|
||||
// But it has to be serializable, otherwise we either have some broken
|
||||
// (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)))}
|
||||
}
|
||||
ne := state.NotificationEvent{
|
||||
ScriptHash: v.GetCurrentScriptHash(),
|
||||
ScriptHash: ic.VM.GetCurrentScriptHash(),
|
||||
Name: name,
|
||||
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.
|
||||
func runtimeLog(ic *interop.Context, v *vm.VM) error {
|
||||
state := v.Estack().Pop().String()
|
||||
func runtimeLog(ic *interop.Context) error {
|
||||
state := ic.VM.Estack().Pop().String()
|
||||
if len(state) > MaxNotificationSize {
|
||||
return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize)
|
||||
}
|
||||
msg := fmt.Sprintf("%q", state)
|
||||
ic.Log.Info("runtime log",
|
||||
zap.Stringer("script", v.GetCurrentScriptHash()),
|
||||
zap.Stringer("script", ic.VM.GetCurrentScriptHash()),
|
||||
zap.String("logs", msg))
|
||||
return nil
|
||||
}
|
||||
|
||||
// runtimeGetTime returns timestamp of the block being verified, or the latest
|
||||
// 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
|
||||
if ic.Block == nil {
|
||||
var err error
|
||||
|
@ -314,13 +314,13 @@ func runtimeGetTime(ic *interop.Context, v *vm.VM) error {
|
|||
} else {
|
||||
header = ic.Block.Header()
|
||||
}
|
||||
v.Estack().PushVal(header.Timestamp)
|
||||
ic.VM.Estack().PushVal(header.Timestamp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// storageDelete deletes stored key-value pair.
|
||||
func storageDelete(ic *interop.Context, v *vm.VM) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
func storageDelete(ic *interop.Context) error {
|
||||
stcInterface := ic.VM.Estack().Pop().Value()
|
||||
stc, ok := stcInterface.(*StorageContext)
|
||||
if !ok {
|
||||
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 {
|
||||
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)
|
||||
if si != nil && si.IsConst {
|
||||
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.
|
||||
func storageGet(ic *interop.Context, v *vm.VM) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
func storageGet(ic *interop.Context) error {
|
||||
stcInterface := ic.VM.Estack().Pop().Value()
|
||||
stc, ok := stcInterface.(*StorageContext)
|
||||
if !ok {
|
||||
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)
|
||||
if si != nil && si.Value != nil {
|
||||
v.Estack().PushVal(si.Value)
|
||||
ic.VM.Estack().PushVal(si.Value)
|
||||
} else {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// storageGetContext returns storage context (scripthash).
|
||||
func storageGetContext(ic *interop.Context, v *vm.VM) error {
|
||||
return storageGetContextInternal(ic, v, false)
|
||||
func storageGetContext(ic *interop.Context) error {
|
||||
return storageGetContextInternal(ic, false)
|
||||
}
|
||||
|
||||
// storageGetReadOnlyContext returns read-only context (scripthash).
|
||||
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
|
||||
return storageGetContextInternal(ic, v, true)
|
||||
func storageGetReadOnlyContext(ic *interop.Context) error {
|
||||
return storageGetContextInternal(ic, true)
|
||||
}
|
||||
|
||||
// storageGetContextInternal is internal version of storageGetContext and
|
||||
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
|
||||
func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) error {
|
||||
contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
||||
func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
|
||||
contract, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -377,11 +377,11 @@ func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) e
|
|||
ID: contract.ID,
|
||||
ReadOnly: isReadOnly,
|
||||
}
|
||||
v.Estack().PushVal(stackitem.NewInterop(sc))
|
||||
ic.VM.Estack().PushVal(stackitem.NewInterop(sc))
|
||||
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 {
|
||||
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) {
|
||||
sizeInc = len(value) - len(si.Value)
|
||||
}
|
||||
if !v.AddGas(int64(sizeInc) * StoragePrice) {
|
||||
if !ic.VM.AddGas(int64(sizeInc) * StoragePrice) {
|
||||
return errGasLimitExceeded
|
||||
}
|
||||
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.
|
||||
func storagePutInternal(ic *interop.Context, v *vm.VM, getFlag bool) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
func storagePutInternal(ic *interop.Context, getFlag bool) error {
|
||||
stcInterface := ic.VM.Estack().Pop().Value()
|
||||
stc, ok := stcInterface.(*StorageContext)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||
}
|
||||
key := v.Estack().Pop().Bytes()
|
||||
value := v.Estack().Pop().Bytes()
|
||||
key := ic.VM.Estack().Pop().Bytes()
|
||||
value := ic.VM.Estack().Pop().Bytes()
|
||||
var flag int
|
||||
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.
|
||||
func storagePut(ic *interop.Context, v *vm.VM) error {
|
||||
return storagePutInternal(ic, v, false)
|
||||
func storagePut(ic *interop.Context) error {
|
||||
return storagePutInternal(ic, false)
|
||||
}
|
||||
|
||||
// storagePutEx puts key-value pair with given flags into the storage.
|
||||
func storagePutEx(ic *interop.Context, v *vm.VM) error {
|
||||
return storagePutInternal(ic, v, true)
|
||||
func storagePutEx(ic *interop.Context) error {
|
||||
return storagePutInternal(ic, true)
|
||||
}
|
||||
|
||||
// storageContextAsReadOnly sets given context to read-only mode.
|
||||
func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
func storageContextAsReadOnly(ic *interop.Context) error {
|
||||
stcInterface := ic.VM.Estack().Pop().Value()
|
||||
stc, ok := stcInterface.(*StorageContext)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||
|
@ -450,31 +450,31 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
|
|||
}
|
||||
stc = stx
|
||||
}
|
||||
v.Estack().PushVal(stackitem.NewInterop(stc))
|
||||
ic.VM.Estack().PushVal(stackitem.NewInterop(stc))
|
||||
return nil
|
||||
}
|
||||
|
||||
// contractCall calls a contract.
|
||||
func contractCall(ic *interop.Context, v *vm.VM) error {
|
||||
h := v.Estack().Pop().Bytes()
|
||||
method := v.Estack().Pop().String()
|
||||
args := v.Estack().Pop().Array()
|
||||
return contractCallExInternal(ic, v, h, method, args, smartcontract.All)
|
||||
func contractCall(ic *interop.Context) error {
|
||||
h := ic.VM.Estack().Pop().Bytes()
|
||||
method := ic.VM.Estack().Pop().String()
|
||||
args := ic.VM.Estack().Pop().Array()
|
||||
return contractCallExInternal(ic, h, method, args, smartcontract.All)
|
||||
}
|
||||
|
||||
// contractCallEx calls a contract with flags.
|
||||
func contractCallEx(ic *interop.Context, v *vm.VM) error {
|
||||
h := v.Estack().Pop().Bytes()
|
||||
method := v.Estack().Pop().String()
|
||||
args := v.Estack().Pop().Array()
|
||||
flags := smartcontract.CallFlag(int32(v.Estack().Pop().BigInt().Int64()))
|
||||
func contractCallEx(ic *interop.Context) error {
|
||||
h := ic.VM.Estack().Pop().Bytes()
|
||||
method := ic.VM.Estack().Pop().String()
|
||||
args := ic.VM.Estack().Pop().Array()
|
||||
flags := smartcontract.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64()))
|
||||
if flags&^smartcontract.All != 0 {
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 !curr.Manifest.CanCall(&cs.Manifest, name) {
|
||||
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]++
|
||||
v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f)
|
||||
ic.VM.LoadScriptWithHash(cs.Script, u, ic.VM.Context().GetCallFlags()&f)
|
||||
var isNative bool
|
||||
for i := range ic.Natives {
|
||||
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 {
|
||||
v.Estack().PushVal(args)
|
||||
v.Estack().PushVal(name)
|
||||
ic.VM.Estack().PushVal(args)
|
||||
ic.VM.Estack().PushVal(name)
|
||||
} else {
|
||||
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.
|
||||
v.Jump(v.Context(), md.Offset)
|
||||
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
||||
}
|
||||
|
||||
md = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
||||
if md != nil {
|
||||
v.Call(v.Context(), md.Offset)
|
||||
ic.VM.Call(ic.VM.Context(), md.Offset)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// contractDestroy destroys a contract.
|
||||
func contractDestroy(ic *interop.Context, v *vm.VM) error {
|
||||
hash := v.GetCurrentScriptHash()
|
||||
func contractDestroy(ic *interop.Context) error {
|
||||
hash := ic.VM.GetCurrentScriptHash()
|
||||
cs, err := ic.DAO.GetContractState(hash)
|
||||
if err != 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.
|
||||
func contractIsStandard(ic *interop.Context, v *vm.VM) error {
|
||||
h := v.Estack().Pop().Bytes()
|
||||
func contractIsStandard(ic *interop.Context) error {
|
||||
h := ic.VM.Estack().Pop().Bytes()
|
||||
u, err := util.Uint160DecodeBytesBE(h)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// contractCreateStandardAccount calculates contract scripthash for a given public key.
|
||||
func contractCreateStandardAccount(ic *interop.Context, v *vm.VM) error {
|
||||
h := v.Estack().Pop().Bytes()
|
||||
func contractCreateStandardAccount(ic *interop.Context) error {
|
||||
h := ic.VM.Estack().Pop().Bytes()
|
||||
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(p.GetScriptHash().BytesBE())
|
||||
ic.VM.Estack().PushVal(p.GetScriptHash().BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// contractGetCallFlags returns current context calling flags.
|
||||
func contractGetCallFlags(_ *interop.Context, v *vm.VM) error {
|
||||
v.Estack().PushVal(v.Context().GetCallFlags())
|
||||
func contractGetCallFlags(ic *interop.Context) error {
|
||||
ic.VM.Estack().PushVal(ic.VM.Context().GetCallFlags())
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestBCGetTransaction(t *testing.T) {
|
|||
t.Run("success", func(t *testing.T) {
|
||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 0))
|
||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
||||
err := bcGetTransaction(context, v)
|
||||
err := bcGetTransaction(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
value := v.Estack().Pop().Value()
|
||||
|
@ -49,7 +49,7 @@ func TestBCGetTransaction(t *testing.T) {
|
|||
t.Run("isn't traceable", func(t *testing.T) {
|
||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
||||
err := bcGetTransaction(context, v)
|
||||
err := bcGetTransaction(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||
|
@ -59,7 +59,7 @@ func TestBCGetTransaction(t *testing.T) {
|
|||
t.Run("bad hash", func(t *testing.T) {
|
||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
||||
v.Estack().PushVal(tx.Hash().BytesLE())
|
||||
err := bcGetTransaction(context, v)
|
||||
err := bcGetTransaction(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||
|
@ -76,7 +76,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
|||
t.Run("success", func(t *testing.T) {
|
||||
v.Estack().PushVal(0)
|
||||
v.Estack().PushVal(block.Hash().BytesBE())
|
||||
err := bcGetTransactionFromBlock(context, v)
|
||||
err := bcGetTransactionFromBlock(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
value := v.Estack().Pop().Value()
|
||||
|
@ -88,7 +88,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
|||
t.Run("invalid block hash", func(t *testing.T) {
|
||||
v.Estack().PushVal(0)
|
||||
v.Estack().PushVal(block.Hash().BytesBE()[:10])
|
||||
err := bcGetTransactionFromBlock(context, v)
|
||||
err := bcGetTransactionFromBlock(context)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
|
@ -97,7 +97,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
|||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||
v.Estack().PushVal(0)
|
||||
v.Estack().PushVal(block.Hash().BytesBE())
|
||||
err := bcGetTransactionFromBlock(context, v)
|
||||
err := bcGetTransactionFromBlock(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||
|
@ -109,7 +109,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
|||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||
v.Estack().PushVal(0)
|
||||
v.Estack().PushVal(block.Hash().BytesLE())
|
||||
err := bcGetTransactionFromBlock(context, v)
|
||||
err := bcGetTransactionFromBlock(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||
|
@ -120,7 +120,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
|||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||
v.Estack().PushVal(1)
|
||||
v.Estack().PushVal(block.Hash().BytesBE())
|
||||
err := bcGetTransactionFromBlock(context, v)
|
||||
err := bcGetTransactionFromBlock(context)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ func TestBCGetBlock(t *testing.T) {
|
|||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
v.Estack().PushVal(block.Hash().BytesBE())
|
||||
err := bcGetBlock(context, v)
|
||||
err := bcGetBlock(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
value := v.Estack().Pop().Value()
|
||||
|
@ -152,7 +152,7 @@ func TestBCGetBlock(t *testing.T) {
|
|||
|
||||
t.Run("bad hash", func(t *testing.T) {
|
||||
v.Estack().PushVal(block.Hash().BytesLE())
|
||||
err := bcGetTransaction(context, v)
|
||||
err := bcGetTransaction(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||
|
@ -180,14 +180,14 @@ func TestContractIsStandard(t *testing.T) {
|
|||
|
||||
t.Run("true", func(t *testing.T) {
|
||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||
require.NoError(t, contractIsStandard(ic, v))
|
||||
require.NoError(t, contractIsStandard(ic))
|
||||
require.True(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
|
||||
t.Run("false", func(t *testing.T) {
|
||||
tx.Scripts[0].VerificationScript = []byte{9, 8, 7}
|
||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||
require.NoError(t, contractIsStandard(ic, v))
|
||||
require.NoError(t, contractIsStandard(ic))
|
||||
require.False(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
})
|
||||
|
@ -201,7 +201,7 @@ func TestContractIsStandard(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||
require.NoError(t, contractIsStandard(ic, v))
|
||||
require.NoError(t, contractIsStandard(ic))
|
||||
require.True(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
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}))
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ func TestContractCreateAccount(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
pub := priv.PublicKey()
|
||||
v.Estack().PushVal(pub.Bytes())
|
||||
require.NoError(t, contractCreateStandardAccount(ic, v))
|
||||
require.NoError(t, contractCreateStandardAccount(ic))
|
||||
|
||||
value := v.Estack().Pop().Bytes()
|
||||
u, err := util.Uint160DecodeBytesBE(value)
|
||||
|
@ -231,7 +231,7 @@ func TestContractCreateAccount(t *testing.T) {
|
|||
})
|
||||
t.Run("InvalidKey", func(t *testing.T) {
|
||||
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.AddGas(58)
|
||||
require.NoError(t, runtime.GasLeft(ic, v))
|
||||
require.NoError(t, runtime.GasLeft(ic))
|
||||
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) {
|
||||
v.Estack().PushVal(stackitem.Null{})
|
||||
require.NoError(t, runtime.GetNotifications(ic, v))
|
||||
require.NoError(t, runtime.GetNotifications(ic))
|
||||
|
||||
arr := v.Estack().Pop().Array()
|
||||
require.Equal(t, len(ic.Notifications), len(arr))
|
||||
|
@ -274,7 +274,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
|||
t.Run("WithFilter", func(t *testing.T) {
|
||||
h := util.Uint160{2}.BytesBE()
|
||||
v.Estack().PushVal(h)
|
||||
require.NoError(t, runtime.GetNotifications(ic, v))
|
||||
require.NoError(t, runtime.GetNotifications(ic))
|
||||
|
||||
arr := v.Estack().Pop().Array()
|
||||
require.Equal(t, 1, len(arr))
|
||||
|
@ -295,11 +295,11 @@ func TestRuntimeGetInvocationCounter(t *testing.T) {
|
|||
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
v.LoadScript([]byte{1})
|
||||
require.Error(t, runtime.GetInvocationCounter(ic, v))
|
||||
require.Error(t, runtime.GetInvocationCounter(ic))
|
||||
})
|
||||
t.Run("NonZero", func(t *testing.T) {
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ func TestBlockchainGetContractState(t *testing.T) {
|
|||
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
v.Estack().PushVal(cs.ScriptHash().BytesBE())
|
||||
require.NoError(t, bcGetContract(ic, v))
|
||||
require.NoError(t, bcGetContract(ic))
|
||||
|
||||
actual := v.Estack().Pop().Item()
|
||||
compareContractStates(t, cs, actual)
|
||||
|
@ -319,7 +319,7 @@ func TestBlockchainGetContractState(t *testing.T) {
|
|||
|
||||
t.Run("uncknown contract state", func(t *testing.T) {
|
||||
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()
|
||||
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.LoadScriptWithFlags(script, smartcontract.AllowCall)
|
||||
for i := range args {
|
||||
v.Estack().PushVal(args[i])
|
||||
}
|
||||
v.GasLimit = -1
|
||||
return v
|
||||
ic.VM = v
|
||||
}
|
||||
|
||||
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)})
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
v := loadScript(currScript, 42)
|
||||
v.Estack().PushVal(addArgs)
|
||||
v.Estack().PushVal("add")
|
||||
v.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic, v))
|
||||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 2, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(3), v.Estack().Pop().Value())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
||||
loadScript(ic, currScript, 42)
|
||||
ic.VM.Estack().PushVal(addArgs)
|
||||
ic.VM.Estack().PushVal("add")
|
||||
ic.VM.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic))
|
||||
require.NoError(t, ic.VM.Run())
|
||||
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(3), ic.VM.Estack().Pop().Value())
|
||||
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
|
||||
})
|
||||
|
||||
t.Run("CallExInvalidFlag", func(t *testing.T) {
|
||||
v := loadScript(currScript, 42)
|
||||
v.Estack().PushVal(byte(0xFF))
|
||||
v.Estack().PushVal(addArgs)
|
||||
v.Estack().PushVal("add")
|
||||
v.Estack().PushVal(h.BytesBE())
|
||||
require.Error(t, contractCallEx(ic, v))
|
||||
loadScript(ic, currScript, 42)
|
||||
ic.VM.Estack().PushVal(byte(0xFF))
|
||||
ic.VM.Estack().PushVal(addArgs)
|
||||
ic.VM.Estack().PushVal("add")
|
||||
ic.VM.Estack().PushVal(h.BytesBE())
|
||||
require.Error(t, contractCallEx(ic))
|
||||
})
|
||||
|
||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
v := loadScript(currScript, 42)
|
||||
loadScript(ic, currScript, 42)
|
||||
for i := range args {
|
||||
v.Estack().PushVal(args[i])
|
||||
ic.VM.Estack().PushVal(args[i])
|
||||
}
|
||||
// interops can both return error and panic,
|
||||
// we don't care which kind of error has occured
|
||||
require.Panics(t, func() {
|
||||
err := contractCall(ic, v)
|
||||
err := contractCall(ic)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -466,26 +466,26 @@ func TestContractCall(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("IsolatedStack", func(t *testing.T) {
|
||||
v := loadScript(currScript, 42)
|
||||
v.Estack().PushVal(stackitem.NewArray(nil))
|
||||
v.Estack().PushVal("drop")
|
||||
v.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic, v))
|
||||
require.Error(t, v.Run())
|
||||
loadScript(ic, currScript, 42)
|
||||
ic.VM.Estack().PushVal(stackitem.NewArray(nil))
|
||||
ic.VM.Estack().PushVal("drop")
|
||||
ic.VM.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic))
|
||||
require.Error(t, ic.VM.Run())
|
||||
})
|
||||
|
||||
t.Run("CallInitialize", func(t *testing.T) {
|
||||
t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE()))
|
||||
|
||||
v := loadScript(currScript, 42)
|
||||
v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)}))
|
||||
v.Estack().PushVal("add3")
|
||||
v.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic, v))
|
||||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 2, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(8), v.Estack().Pop().Value())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
||||
loadScript(ic, currScript, 42)
|
||||
ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)}))
|
||||
ic.VM.Estack().PushVal("add3")
|
||||
ic.VM.Estack().PushVal(h.BytesBE())
|
||||
require.NoError(t, contractCall(ic))
|
||||
require.NoError(t, ic.VM.Run())
|
||||
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(8), ic.VM.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) {
|
||||
putArgsOnStack()
|
||||
|
||||
require.NoError(t, contractCreate(ic, v))
|
||||
require.NoError(t, contractCreate(ic))
|
||||
actual := v.Estack().Pop().Item()
|
||||
compareContractStates(t, cs, actual)
|
||||
})
|
||||
|
@ -513,7 +513,7 @@ func TestContractCreate(t *testing.T) {
|
|||
cs.Script = append(cs.Script, 0x01)
|
||||
putArgsOnStack()
|
||||
|
||||
require.Error(t, contractCreate(ic, v))
|
||||
require.Error(t, contractCreate(ic))
|
||||
})
|
||||
|
||||
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))
|
||||
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))
|
||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
putArgsOnStack(nil, nil)
|
||||
require.NoError(t, contractUpdate(ic, v))
|
||||
require.NoError(t, contractUpdate(ic))
|
||||
})
|
||||
|
||||
t.Run("no contract", func(t *testing.T) {
|
||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||
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) {
|
||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
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) {
|
||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
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) {
|
||||
|
@ -581,7 +581,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
v.GasLimit = 0
|
||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
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) {
|
||||
|
@ -590,7 +590,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
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) {
|
||||
|
@ -608,7 +608,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||
putArgsOnStack(duplicateScript, nil)
|
||||
|
||||
require.Error(t, contractUpdate(ic, v))
|
||||
require.Error(t, contractUpdate(ic))
|
||||
})
|
||||
|
||||
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}
|
||||
putArgsOnStack(newScript, nil)
|
||||
|
||||
require.NoError(t, contractUpdate(ic, v))
|
||||
require.NoError(t, contractUpdate(ic))
|
||||
|
||||
// updated contract should have new scripthash
|
||||
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)
|
||||
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) {
|
||||
|
@ -656,7 +656,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
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) {
|
||||
|
@ -676,7 +676,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
putArgsOnStack(nil, manifestBytes)
|
||||
|
||||
require.Error(t, contractUpdate(ic, v))
|
||||
require.Error(t, contractUpdate(ic))
|
||||
})
|
||||
|
||||
t.Run("update manifest, positive", func(t *testing.T) {
|
||||
|
@ -693,7 +693,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
putArgsOnStack(nil, manifestBytes)
|
||||
|
||||
require.NoError(t, contractUpdate(ic, v))
|
||||
require.NoError(t, contractUpdate(ic))
|
||||
|
||||
// updated contract should have new scripthash
|
||||
actual, err := ic.DAO.GetContractState(cs.ScriptHash())
|
||||
|
@ -721,7 +721,7 @@ func TestContractUpdate(t *testing.T) {
|
|||
|
||||
putArgsOnStack(newScript, newManifestBytes)
|
||||
|
||||
require.NoError(t, contractUpdate(ic, v))
|
||||
require.NoError(t, contractUpdate(ic))
|
||||
|
||||
// updated contract should have new script and manifest
|
||||
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
||||
|
@ -746,7 +746,7 @@ func TestContractGetCallFlags(t *testing.T) {
|
|||
defer bc.Close()
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -759,27 +759,27 @@ func TestPointerCallback(t *testing.T) {
|
|||
byte(opcode.DIV), byte(opcode.RET),
|
||||
}
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
v := loadScript(script, 2, stackitem.NewPointer(3, script))
|
||||
v.Estack().PushVal(v.Context())
|
||||
require.NoError(t, callback.Create(ic, v))
|
||||
loadScript(ic, script, 2, stackitem.NewPointer(3, script))
|
||||
ic.VM.Estack().PushVal(ic.VM.Context())
|
||||
require.NoError(t, callback.Create(ic))
|
||||
|
||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)})
|
||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
||||
require.NoError(t, callback.Invoke(ic, v))
|
||||
ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
|
||||
require.NoError(t, callback.Invoke(ic))
|
||||
|
||||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 1, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(5), v.Estack().Pop().Item().Value())
|
||||
require.NoError(t, ic.VM.Run())
|
||||
require.Equal(t, 1, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(5), ic.VM.Estack().Pop().Item().Value())
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("NotEnoughParameters", func(t *testing.T) {
|
||||
v := loadScript(script, 2, stackitem.NewPointer(3, script))
|
||||
v.Estack().PushVal(v.Context())
|
||||
require.NoError(t, callback.Create(ic, v))
|
||||
loadScript(ic, script, 2, stackitem.NewPointer(3, script))
|
||||
ic.VM.Estack().PushVal(ic.VM.Context())
|
||||
require.NoError(t, callback.Create(ic))
|
||||
|
||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)})
|
||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
||||
require.Error(t, callback.Invoke(ic, v))
|
||||
ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
|
||||
require.Error(t, callback.Invoke(ic))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -799,11 +799,11 @@ func TestMethodCallback(t *testing.T) {
|
|||
t.Run("Invalid", func(t *testing.T) {
|
||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
v := loadScript(currCs.Script, 42)
|
||||
loadScript(ic, currCs.Script, 42)
|
||||
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:]))
|
||||
|
@ -812,37 +812,38 @@ func TestMethodCallback(t *testing.T) {
|
|||
t.Run("DisallowedMethod", runInvalid("ret7", rawHash))
|
||||
t.Run("Initialize", runInvalid("_initialize", rawHash))
|
||||
t.Run("NotEnoughArguments", func(t *testing.T) {
|
||||
v := loadScript(currCs.Script, 42, "add", rawHash)
|
||||
require.NoError(t, callback.CreateFromMethod(ic, v))
|
||||
loadScript(ic, currCs.Script, 42, "add", rawHash)
|
||||
require.NoError(t, callback.CreateFromMethod(ic))
|
||||
|
||||
v.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1)
|
||||
require.Error(t, callback.Invoke(ic, v))
|
||||
ic.VM.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1)
|
||||
require.Error(t, callback.Invoke(ic))
|
||||
})
|
||||
t.Run("CallIsNotAllowed", func(t *testing.T) {
|
||||
v := vm.New()
|
||||
ic.VM = v
|
||||
v.Load(currCs.Script)
|
||||
v.Estack().PushVal("add")
|
||||
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)})
|
||||
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) {
|
||||
v := loadScript(currCs.Script, 42, "add", rawHash)
|
||||
require.NoError(t, callback.CreateFromMethod(ic, v))
|
||||
loadScript(ic, currCs.Script, 42, "add", rawHash)
|
||||
require.NoError(t, callback.CreateFromMethod(ic))
|
||||
|
||||
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, v.Run())
|
||||
require.Equal(t, 2, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(6), v.Estack().Pop().Item().Value())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value())
|
||||
require.NoError(t, callback.Invoke(ic))
|
||||
require.NoError(t, ic.VM.Run())
|
||||
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Item().Value())
|
||||
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
|
||||
})
|
||||
}
|
||||
func TestSyscallCallback(t *testing.T) {
|
||||
|
@ -852,44 +853,44 @@ func TestSyscallCallback(t *testing.T) {
|
|||
ic.Functions = append(ic.Functions, []interop.Function{
|
||||
{
|
||||
ID: 0x42,
|
||||
Func: func(_ *interop.Context, v *vm.VM) error {
|
||||
a := v.Estack().Pop().BigInt()
|
||||
b := v.Estack().Pop().BigInt()
|
||||
v.Estack().PushVal(new(big.Int).Add(a, b))
|
||||
Func: func(ic *interop.Context) error {
|
||||
a := ic.VM.Estack().Pop().BigInt()
|
||||
b := ic.VM.Estack().Pop().BigInt()
|
||||
ic.VM.Estack().PushVal(new(big.Int).Add(a, b))
|
||||
return nil
|
||||
},
|
||||
ParamCount: 2,
|
||||
},
|
||||
{
|
||||
ID: 0x53,
|
||||
Func: func(_ *interop.Context, _ *vm.VM) error { return nil },
|
||||
Func: func(_ *interop.Context) error { return nil },
|
||||
DisallowCallback: true,
|
||||
},
|
||||
})
|
||||
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)})
|
||||
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42)
|
||||
require.NoError(t, callback.CreateFromSyscall(ic, v))
|
||||
require.NoError(t, callback.Invoke(ic, v))
|
||||
require.Equal(t, 1, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value())
|
||||
loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
|
||||
require.NoError(t, callback.CreateFromSyscall(ic))
|
||||
require.NoError(t, callback.Invoke(ic))
|
||||
require.Equal(t, 1, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("InvalidParameterCount", func(t *testing.T) {
|
||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)})
|
||||
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42)
|
||||
require.NoError(t, callback.CreateFromSyscall(ic, v))
|
||||
require.Error(t, callback.Invoke(ic, v))
|
||||
loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
|
||||
require.NoError(t, callback.CreateFromSyscall(ic))
|
||||
require.Error(t, callback.Invoke(ic))
|
||||
})
|
||||
t.Run("MissingSyscall", func(t *testing.T) {
|
||||
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43)
|
||||
require.Error(t, callback.CreateFromSyscall(ic, v))
|
||||
loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43)
|
||||
require.Error(t, callback.CreateFromSyscall(ic))
|
||||
})
|
||||
t.Run("Disallowed", func(t *testing.T) {
|
||||
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53)
|
||||
require.Error(t, callback.CreateFromSyscall(ic, v))
|
||||
loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53)
|
||||
require.Error(t, callback.CreateFromSyscall(ic))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,13 +14,14 @@ import (
|
|||
"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.Estack().PushVal(value)
|
||||
chain := newTestChain(t)
|
||||
defer chain.Close()
|
||||
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) {
|
||||
|
@ -32,7 +33,7 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
}
|
||||
|
||||
// All of these functions expect an interop item on the stack.
|
||||
funcs := []func(*interop.Context, *vm.VM) error{
|
||||
funcs := []func(*interop.Context) error{
|
||||
storageContextAsReadOnly,
|
||||
storageDelete,
|
||||
storageFind,
|
||||
|
|
|
@ -6,11 +6,10 @@ import (
|
|||
|
||||
"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/vm"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
func Call(ic *interop.Context, v *vm.VM) error {
|
||||
name := v.Estack().Pop().String()
|
||||
func Call(ic *interop.Context) error {
|
||||
name := ic.VM.Estack().Pop().String()
|
||||
var c interop.Contract
|
||||
for _, ctr := range ic.Natives {
|
||||
if ctr.Metadata().Name == name {
|
||||
|
@ -46,23 +45,23 @@ func Call(ic *interop.Context, v *vm.VM) error {
|
|||
if c == nil {
|
||||
return fmt.Errorf("native contract %s not found", name)
|
||||
}
|
||||
h := v.GetCurrentScriptHash()
|
||||
h := ic.VM.GetCurrentScriptHash()
|
||||
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")
|
||||
}
|
||||
operation := v.Estack().Pop().String()
|
||||
args := v.Estack().Pop().Array()
|
||||
operation := ic.VM.Estack().Pop().String()
|
||||
args := ic.VM.Estack().Pop().Array()
|
||||
m, ok := c.Metadata().Methods[operation]
|
||||
if !ok {
|
||||
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")
|
||||
}
|
||||
if !v.AddGas(m.Price) {
|
||||
if !ic.VM.AddGas(m.Price) {
|
||||
return errors.New("gas limit exceeded")
|
||||
}
|
||||
result := m.Func(ic, args)
|
||||
v.Estack().PushVal(result)
|
||||
ic.VM.Estack().PushVal(result)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Ui
|
|||
return errors.New("negative amount")
|
||||
}
|
||||
|
||||
caller := ic.ScriptGetter.GetCallingScriptHash()
|
||||
caller := ic.VM.GetCallingScriptHash()
|
||||
if caller.Equals(util.Uint160{}) || !from.Equals(caller) {
|
||||
ok, err := runtime.CheckHashedWitness(ic, from)
|
||||
if err != nil {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"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/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/smartcontract"
|
||||
"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/vm"
|
||||
"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/stretchr/testify/require"
|
||||
)
|
||||
|
@ -76,6 +78,35 @@ func newTestNative() *testNative {
|
|||
}
|
||||
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}
|
||||
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates}
|
||||
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))
|
||||
}
|
||||
|
||||
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) {
|
||||
chain := newTestChain(t)
|
||||
defer chain.Close()
|
||||
|
@ -159,10 +222,9 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
v := vm.New()
|
||||
v.GasLimit = -1
|
||||
ic := chain.newInteropContext(trigger.Application,
|
||||
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||
v := ic.SpawnVM()
|
||||
|
||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
||||
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)
|
||||
|
||||
// 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) {
|
||||
|
@ -180,9 +242,117 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
|||
v.Estack().PushVal("sum")
|
||||
v.Estack().PushVal(tn.Metadata().Name)
|
||||
|
||||
require.NoError(t, native.Call(ic, v))
|
||||
require.NoError(t, native.Call(ic))
|
||||
|
||||
value := v.Estack().Pop().BigInt()
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
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 (
|
||||
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
||||
MaxInvocationStackSize = 1024
|
||||
|
|
Loading…
Reference in a new issue