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)
|
v := vmAndCompile(t, src)
|
||||||
ic := &interop.Context{Trigger: trigger.Verification}
|
ic := &interop.Context{Trigger: trigger.Verification}
|
||||||
|
ic.VM = v
|
||||||
crypto.Register(ic)
|
crypto.Register(ic)
|
||||||
v.SyscallHandler = ic.SyscallHandler
|
v.SyscallHandler = ic.SyscallHandler
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
|
|
|
@ -18,19 +18,19 @@ type Callback interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke invokes provided callback.
|
// Invoke invokes provided callback.
|
||||||
func Invoke(ic *interop.Context, v *vm.VM) error {
|
func Invoke(ic *interop.Context) error {
|
||||||
cb := v.Estack().Pop().Interop().Value().(Callback)
|
cb := ic.VM.Estack().Pop().Interop().Value().(Callback)
|
||||||
args := v.Estack().Pop().Array()
|
args := ic.VM.Estack().Pop().Array()
|
||||||
if cb.ArgCount() != len(args) {
|
if cb.ArgCount() != len(args) {
|
||||||
return errors.New("invalid argument count")
|
return errors.New("invalid argument count")
|
||||||
}
|
}
|
||||||
cb.LoadContext(v, args)
|
cb.LoadContext(ic.VM, args)
|
||||||
switch t := cb.(type) {
|
switch t := cb.(type) {
|
||||||
case *MethodCallback:
|
case *MethodCallback:
|
||||||
id := emit.InteropNameToID([]byte("System.Contract.Call"))
|
id := emit.InteropNameToID([]byte("System.Contract.Call"))
|
||||||
return ic.SyscallHandler(v, id)
|
return ic.SyscallHandler(ic.VM, id)
|
||||||
case *SyscallCallback:
|
case *SyscallCallback:
|
||||||
return ic.SyscallHandler(v, t.desc.ID)
|
return ic.SyscallHandler(ic.VM, t.desc.ID)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFromMethod creates callback for a contract method.
|
// CreateFromMethod creates callback for a contract method.
|
||||||
func CreateFromMethod(ic *interop.Context, v *vm.VM) error {
|
func CreateFromMethod(ic *interop.Context) error {
|
||||||
rawHash := v.Estack().Pop().Bytes()
|
rawHash := ic.VM.Estack().Pop().Bytes()
|
||||||
h, err := util.Uint160DecodeBytesBE(rawHash)
|
h, err := util.Uint160DecodeBytesBE(rawHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -43,16 +43,16 @@ func CreateFromMethod(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("contract not found")
|
return errors.New("contract not found")
|
||||||
}
|
}
|
||||||
method := string(v.Estack().Pop().Bytes())
|
method := string(ic.VM.Estack().Pop().Bytes())
|
||||||
if strings.HasPrefix(method, "_") {
|
if strings.HasPrefix(method, "_") {
|
||||||
return errors.New("invalid method name")
|
return errors.New("invalid method name")
|
||||||
}
|
}
|
||||||
currCs, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
currCs, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if err == nil && !currCs.Manifest.CanCall(&cs.Manifest, method) {
|
if err == nil && !currCs.Manifest.CanCall(&cs.Manifest, method) {
|
||||||
return errors.New("method call is not allowed")
|
return errors.New("method call is not allowed")
|
||||||
}
|
}
|
||||||
md := cs.Manifest.ABI.GetMethod(method)
|
md := cs.Manifest.ABI.GetMethod(method)
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&MethodCallback{
|
ic.VM.Estack().PushVal(stackitem.NewInterop(&MethodCallback{
|
||||||
contract: cs,
|
contract: cs,
|
||||||
method: md,
|
method: md,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -29,11 +29,11 @@ func (p *PointerCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates callback using pointer and parameters count.
|
// Create creates callback using pointer and parameters count.
|
||||||
func Create(_ *interop.Context, v *vm.VM) error {
|
func Create(ic *interop.Context) error {
|
||||||
ctx := v.Estack().Pop().Item().(*vm.Context)
|
ctx := ic.VM.Estack().Pop().Item().(*vm.Context)
|
||||||
offset := v.Estack().Pop().Item().(*stackitem.Pointer).Position()
|
offset := ic.VM.Estack().Pop().Item().(*stackitem.Pointer).Position()
|
||||||
count := v.Estack().Pop().BigInt().Int64()
|
count := ic.VM.Estack().Pop().BigInt().Int64()
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&PointerCallback{
|
ic.VM.Estack().PushVal(stackitem.NewInterop(&PointerCallback{
|
||||||
paramCount: int(count),
|
paramCount: int(count),
|
||||||
offset: offset,
|
offset: offset,
|
||||||
context: ctx,
|
context: ctx,
|
||||||
|
|
|
@ -28,8 +28,8 @@ func (p *SyscallCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFromSyscall creates callback from syscall.
|
// CreateFromSyscall creates callback from syscall.
|
||||||
func CreateFromSyscall(ic *interop.Context, v *vm.VM) error {
|
func CreateFromSyscall(ic *interop.Context) error {
|
||||||
id := uint32(v.Estack().Pop().BigInt().Int64())
|
id := uint32(ic.VM.Estack().Pop().BigInt().Int64())
|
||||||
f := ic.GetFunction(id)
|
f := ic.GetFunction(id)
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return errors.New("syscall not found")
|
return errors.New("syscall not found")
|
||||||
|
@ -37,6 +37,6 @@ func CreateFromSyscall(ic *interop.Context, v *vm.VM) error {
|
||||||
if f.DisallowCallback {
|
if f.DisallowCallback {
|
||||||
return errors.New("syscall is not allowed to be used in a callback")
|
return errors.New("syscall is not allowed to be used in a callback")
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&SyscallCallback{f}))
|
ic.VM.Estack().PushVal(stackitem.NewInterop(&SyscallCallback{f}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ type Context struct {
|
||||||
Notifications []state.NotificationEvent
|
Notifications []state.NotificationEvent
|
||||||
Log *zap.Logger
|
Log *zap.Logger
|
||||||
Invocations map[util.Uint160]int
|
Invocations map[util.Uint160]int
|
||||||
ScriptGetter vm.ScriptHashGetter
|
VM *vm.VM
|
||||||
Functions [][]Function
|
Functions [][]Function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n
|
||||||
type Function struct {
|
type Function struct {
|
||||||
ID uint32
|
ID uint32
|
||||||
Name string
|
Name string
|
||||||
Func func(*Context, *vm.VM) error
|
Func func(*Context) error
|
||||||
// DisallowCallback is true iff syscall can't be used in a callback.
|
// DisallowCallback is true iff syscall can't be used in a callback.
|
||||||
DisallowCallback bool
|
DisallowCallback bool
|
||||||
// ParamCount is a number of function parameters.
|
// ParamCount is a number of function parameters.
|
||||||
|
@ -155,26 +155,26 @@ func (ic *Context) GetFunction(id uint32) *Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyscallHandler handles syscall with id.
|
// SyscallHandler handles syscall with id.
|
||||||
func (ic *Context) SyscallHandler(v *vm.VM, id uint32) error {
|
func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
|
||||||
f := ic.GetFunction(id)
|
f := ic.GetFunction(id)
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return errors.New("syscall not found")
|
return errors.New("syscall not found")
|
||||||
}
|
}
|
||||||
cf := v.Context().GetCallFlags()
|
cf := ic.VM.Context().GetCallFlags()
|
||||||
if !cf.Has(f.RequiredFlags) {
|
if !cf.Has(f.RequiredFlags) {
|
||||||
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
|
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
|
||||||
}
|
}
|
||||||
if !v.AddGas(f.Price) {
|
if !ic.VM.AddGas(f.Price) {
|
||||||
return errors.New("insufficient amount of gas")
|
return errors.New("insufficient amount of gas")
|
||||||
}
|
}
|
||||||
return f.Func(ic, v)
|
return f.Func(ic)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnVM spawns new VM with the specified gas limit.
|
// SpawnVM spawns new VM with the specified gas limit and set context.VM field.
|
||||||
func (ic *Context) SpawnVM() *vm.VM {
|
func (ic *Context) SpawnVM() *vm.VM {
|
||||||
v := vm.NewWithTrigger(ic.Trigger)
|
v := vm.NewWithTrigger(ic.Trigger)
|
||||||
v.GasLimit = -1
|
v.GasLimit = -1
|
||||||
v.SyscallHandler = ic.SyscallHandler
|
v.SyscallHandler = ic.SyscallHandler
|
||||||
ic.ScriptGetter = v
|
ic.VM = v
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,56 +18,56 @@ import (
|
||||||
const ECDSAVerifyPrice = 1000000
|
const ECDSAVerifyPrice = 1000000
|
||||||
|
|
||||||
// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve.
|
// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve.
|
||||||
func ECDSASecp256r1Verify(ic *interop.Context, v *vm.VM) error {
|
func ECDSASecp256r1Verify(ic *interop.Context) error {
|
||||||
return ecdsaVerify(ic, v, elliptic.P256())
|
return ecdsaVerify(ic, elliptic.P256())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve
|
// ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve
|
||||||
func ECDSASecp256k1Verify(ic *interop.Context, v *vm.VM) error {
|
func ECDSASecp256k1Verify(ic *interop.Context) error {
|
||||||
return ecdsaVerify(ic, v, btcec.S256())
|
return ecdsaVerify(ic, btcec.S256())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ecdsaVerify is internal representation of ECDSASecp256k1Verify and
|
// ecdsaVerify is internal representation of ECDSASecp256k1Verify and
|
||||||
// ECDSASecp256r1Verify.
|
// ECDSASecp256r1Verify.
|
||||||
func ecdsaVerify(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error {
|
func ecdsaVerify(ic *interop.Context, curve elliptic.Curve) error {
|
||||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||||
hashToCheck := hash.Sha256(msg).BytesBE()
|
hashToCheck := hash.Sha256(msg).BytesBE()
|
||||||
keyb := v.Estack().Pop().Bytes()
|
keyb := ic.VM.Estack().Pop().Bytes()
|
||||||
signature := v.Estack().Pop().Bytes()
|
signature := ic.VM.Estack().Pop().Bytes()
|
||||||
pkey, err := keys.NewPublicKeyFromBytes(keyb, curve)
|
pkey, err := keys.NewPublicKeyFromBytes(keyb, curve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := pkey.Verify(signature, hashToCheck)
|
res := pkey.Verify(signature, hashToCheck)
|
||||||
v.Estack().PushVal(res)
|
ic.VM.Estack().PushVal(res)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
|
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
|
||||||
// Secp256r1 elliptic curve.
|
// Secp256r1 elliptic curve.
|
||||||
func ECDSASecp256r1CheckMultisig(ic *interop.Context, v *vm.VM) error {
|
func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
|
||||||
return ecdsaCheckMultisig(ic, v, elliptic.P256())
|
return ecdsaCheckMultisig(ic, elliptic.P256())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using
|
// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using
|
||||||
// Secp256k1 elliptic curve.
|
// Secp256k1 elliptic curve.
|
||||||
func ECDSASecp256k1CheckMultisig(ic *interop.Context, v *vm.VM) error {
|
func ECDSASecp256k1CheckMultisig(ic *interop.Context) error {
|
||||||
return ecdsaCheckMultisig(ic, v, btcec.S256())
|
return ecdsaCheckMultisig(ic, btcec.S256())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and
|
// ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and
|
||||||
// ECDSASecp256k1CheckMultisig
|
// ECDSASecp256k1CheckMultisig
|
||||||
func ecdsaCheckMultisig(ic *interop.Context, v *vm.VM, curve elliptic.Curve) error {
|
func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error {
|
||||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||||
hashToCheck := hash.Sha256(msg).BytesBE()
|
hashToCheck := hash.Sha256(msg).BytesBE()
|
||||||
pkeys, err := v.Estack().PopSigElements()
|
pkeys, err := ic.VM.Estack().PopSigElements()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("wrong parameters: %w", err)
|
return fmt.Errorf("wrong parameters: %w", err)
|
||||||
}
|
}
|
||||||
if !v.AddGas(ECDSAVerifyPrice * int64(len(pkeys))) {
|
if !ic.VM.AddGas(ECDSAVerifyPrice * int64(len(pkeys))) {
|
||||||
return errors.New("gas limit exceeded")
|
return errors.New("gas limit exceeded")
|
||||||
}
|
}
|
||||||
sigs, err := v.Estack().PopSigElements()
|
sigs, err := ic.VM.Estack().PopSigElements()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("wrong parameters: %w", err)
|
return fmt.Errorf("wrong parameters: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -76,8 +76,8 @@ func ecdsaCheckMultisig(ic *interop.Context, v *vm.VM, curve elliptic.Curve) err
|
||||||
if len(pkeys) < len(sigs) {
|
if len(pkeys) < len(sigs) {
|
||||||
return errors.New("more signatures than there are keys")
|
return errors.New("more signatures than there are keys")
|
||||||
}
|
}
|
||||||
sigok := vm.CheckMultisigPar(v, curve, hashToCheck, pkeys, sigs)
|
sigok := vm.CheckMultisigPar(ic.VM, curve, hashToCheck, pkeys, sigs)
|
||||||
v.Estack().PushVal(sigok)
|
ic.VM.Estack().PushVal(sigok)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,20 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sha256 returns sha256 hash of the data.
|
// Sha256 returns sha256 hash of the data.
|
||||||
func Sha256(ic *interop.Context, v *vm.VM) error {
|
func Sha256(ic *interop.Context) error {
|
||||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||||
h := hash.Sha256(msg).BytesBE()
|
h := hash.Sha256(msg).BytesBE()
|
||||||
v.Estack().PushVal(h)
|
ic.VM.Estack().PushVal(h)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RipeMD160 returns RipeMD160 hash of the data.
|
// RipeMD160 returns RipeMD160 hash of the data.
|
||||||
func RipeMD160(ic *interop.Context, v *vm.VM) error {
|
func RipeMD160(ic *interop.Context) error {
|
||||||
msg := getMessage(ic, v.Estack().Pop().Item())
|
msg := getMessage(ic, ic.VM.Estack().Pop().Item())
|
||||||
h := hash.RipeMD160(msg).BytesBE()
|
h := hash.RipeMD160(msg).BytesBE()
|
||||||
v.Estack().PushVal(h)
|
ic.VM.Estack().PushVal(h)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Concat concatenates 2 enumerators into a single one.
|
// Concat concatenates 2 enumerators into a single one.
|
||||||
func Concat(_ *interop.Context, v *vm.VM) error {
|
func Concat(ic *interop.Context) error {
|
||||||
return vm.EnumeratorConcat(v)
|
return vm.EnumeratorConcat(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates an enumerator from an array-like or bytearray-like stack item.
|
// Create creates an enumerator from an array-like or bytearray-like stack item.
|
||||||
func Create(_ *interop.Context, v *vm.VM) error {
|
func Create(ic *interop.Context) error {
|
||||||
return vm.EnumeratorCreate(v)
|
return vm.EnumeratorCreate(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next advances the enumerator, pushes true if is it was successful
|
// Next advances the enumerator, pushes true if is it was successful
|
||||||
// and false otherwise.
|
// and false otherwise.
|
||||||
func Next(_ *interop.Context, v *vm.VM) error {
|
func Next(ic *interop.Context) error {
|
||||||
return vm.EnumeratorNext(v)
|
return vm.EnumeratorNext(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the current value of the enumerator.
|
// Value returns the current value of the enumerator.
|
||||||
func Value(_ *interop.Context, v *vm.VM) error {
|
func Value(ic *interop.Context) error {
|
||||||
return vm.EnumeratorValue(v)
|
return vm.EnumeratorValue(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Concat concatenates 2 iterators into a single one.
|
// Concat concatenates 2 iterators into a single one.
|
||||||
func Concat(_ *interop.Context, v *vm.VM) error {
|
func Concat(ic *interop.Context) error {
|
||||||
return vm.IteratorConcat(v)
|
return vm.IteratorConcat(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates an iterator from array-like or map stack item.
|
// Create creates an iterator from array-like or map stack item.
|
||||||
func Create(_ *interop.Context, v *vm.VM) error {
|
func Create(ic *interop.Context) error {
|
||||||
return vm.IteratorCreate(v)
|
return vm.IteratorCreate(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key returns current iterator key.
|
// Key returns current iterator key.
|
||||||
func Key(_ *interop.Context, v *vm.VM) error {
|
func Key(ic *interop.Context) error {
|
||||||
return vm.IteratorKey(v)
|
return vm.IteratorKey(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys returns keys of the iterator.
|
// Keys returns keys of the iterator.
|
||||||
func Keys(_ *interop.Context, v *vm.VM) error {
|
func Keys(ic *interop.Context) error {
|
||||||
return vm.IteratorKeys(v)
|
return vm.IteratorKeys(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values returns values of the iterator.
|
// Values returns values of the iterator.
|
||||||
func Values(_ *interop.Context, v *vm.VM) error {
|
func Values(ic *interop.Context) error {
|
||||||
return vm.IteratorValues(v)
|
return vm.IteratorValues(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,27 @@ package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Serialize handles System.JSON.Serialize syscall.
|
// Serialize handles System.JSON.Serialize syscall.
|
||||||
func Serialize(_ *interop.Context, v *vm.VM) error {
|
func Serialize(ic *interop.Context) error {
|
||||||
item := v.Estack().Pop().Item()
|
item := ic.VM.Estack().Pop().Item()
|
||||||
data, err := stackitem.ToJSON(item)
|
data, err := stackitem.ToJSON(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(data)
|
ic.VM.Estack().PushVal(data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize handles System.JSON.Deserialize syscall.
|
// Deserialize handles System.JSON.Deserialize syscall.
|
||||||
func Deserialize(_ *interop.Context, v *vm.VM) error {
|
func Deserialize(ic *interop.Context) error {
|
||||||
data := v.Estack().Pop().Bytes()
|
data := ic.VM.Estack().Pop().Bytes()
|
||||||
item, err := stackitem.FromJSON(data)
|
item, err := stackitem.FromJSON(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(item)
|
ic.VM.Estack().PushVal(item)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,18 +11,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GasLeft returns remaining amount of GAS.
|
// GasLeft returns remaining amount of GAS.
|
||||||
func GasLeft(_ *interop.Context, v *vm.VM) error {
|
func GasLeft(ic *interop.Context) error {
|
||||||
if v.GasLimit == -1 {
|
if ic.VM.GasLimit == -1 {
|
||||||
v.Estack().PushVal(v.GasLimit)
|
ic.VM.Estack().PushVal(ic.VM.GasLimit)
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(v.GasLimit - v.GasConsumed())
|
ic.VM.Estack().PushVal(ic.VM.GasLimit - ic.VM.GasConsumed())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNotifications returns notifications emitted by current contract execution.
|
// GetNotifications returns notifications emitted by current contract execution.
|
||||||
func GetNotifications(ic *interop.Context, v *vm.VM) error {
|
func GetNotifications(ic *interop.Context) error {
|
||||||
item := v.Estack().Pop().Item()
|
item := ic.VM.Estack().Pop().Item()
|
||||||
notifications := ic.Notifications
|
notifications := ic.Notifications
|
||||||
if _, ok := item.(stackitem.Null); !ok {
|
if _, ok := item.(stackitem.Null); !ok {
|
||||||
b, err := item.TryBytes()
|
b, err := item.TryBytes()
|
||||||
|
@ -52,16 +52,16 @@ func GetNotifications(ic *interop.Context, v *vm.VM) error {
|
||||||
})
|
})
|
||||||
arr.Append(ev)
|
arr.Append(ev)
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(arr)
|
ic.VM.Estack().PushVal(arr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInvocationCounter returns how many times current contract was invoked during current tx execution.
|
// GetInvocationCounter returns how many times current contract was invoked during current tx execution.
|
||||||
func GetInvocationCounter(ic *interop.Context, v *vm.VM) error {
|
func GetInvocationCounter(ic *interop.Context) error {
|
||||||
count, ok := ic.Invocations[v.GetCurrentScriptHash()]
|
count, ok := ic.Invocations[ic.VM.GetCurrentScriptHash()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("current contract wasn't invoked from others")
|
return errors.New("current contract wasn't invoked from others")
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(count)
|
ic.VM.Estack().PushVal(count)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ import (
|
||||||
// for verifying in the interop context.
|
// for verifying in the interop context.
|
||||||
func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
|
func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
|
||||||
if tx, ok := ic.Container.(*transaction.Transaction); ok {
|
if tx, ok := ic.Container.(*transaction.Transaction); ok {
|
||||||
return checkScope(ic.DAO, tx, ic.ScriptGetter, hash)
|
return checkScope(ic.DAO, tx, ic.VM, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, errors.New("script container is not a transaction")
|
return false, errors.New("script container is not a transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) {
|
func checkScope(d dao.DAO, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
|
||||||
for _, c := range tx.Signers {
|
for _, c := range tx.Signers {
|
||||||
if c.Account == hash {
|
if c.Account == hash {
|
||||||
if c.Scopes == transaction.Global {
|
if c.Scopes == transaction.Global {
|
||||||
|
@ -75,11 +75,11 @@ func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckWitness checks witnesses.
|
// CheckWitness checks witnesses.
|
||||||
func CheckWitness(ic *interop.Context, v *vm.VM) error {
|
func CheckWitness(ic *interop.Context) error {
|
||||||
var res bool
|
var res bool
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
hashOrKey := v.Estack().Pop().Bytes()
|
hashOrKey := ic.VM.Estack().Pop().Bytes()
|
||||||
hash, err := util.Uint160DecodeBytesBE(hashOrKey)
|
hash, err := util.Uint160DecodeBytesBE(hashOrKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var key *keys.PublicKey
|
var key *keys.PublicKey
|
||||||
|
@ -94,6 +94,6 @@ func CheckWitness(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check witness: %w", err)
|
return fmt.Errorf("failed to check witness: %w", err)
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(res)
|
ic.VM.Estack().PushVal(res)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,13 @@ const (
|
||||||
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
||||||
|
|
||||||
// storageFind finds stored key-value pair.
|
// storageFind finds stored key-value pair.
|
||||||
func storageFind(ic *interop.Context, v *vm.VM) error {
|
func storageFind(ic *interop.Context) error {
|
||||||
stcInterface := v.Estack().Pop().Value()
|
stcInterface := ic.VM.Estack().Pop().Value()
|
||||||
stc, ok := stcInterface.(*StorageContext)
|
stc, ok := stcInterface.(*StorageContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||||
}
|
}
|
||||||
prefix := v.Estack().Pop().Bytes()
|
prefix := ic.VM.Estack().Pop().Bytes()
|
||||||
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -51,7 +51,7 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
item := vm.NewMapIterator(filteredMap)
|
item := vm.NewMapIterator(filteredMap)
|
||||||
v.Estack().PushVal(item)
|
ic.VM.Estack().PushVal(item)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -59,16 +59,16 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
|
||||||
// createContractStateFromVM pops all contract state elements from the VM
|
// createContractStateFromVM pops all contract state elements from the VM
|
||||||
// evaluation stack, does a lot of checks and returns Contract if it
|
// evaluation stack, does a lot of checks and returns Contract if it
|
||||||
// succeeds.
|
// succeeds.
|
||||||
func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, error) {
|
func createContractStateFromVM(ic *interop.Context) (*state.Contract, error) {
|
||||||
script := v.Estack().Pop().Bytes()
|
script := ic.VM.Estack().Pop().Bytes()
|
||||||
if len(script) > MaxContractScriptSize {
|
if len(script) > MaxContractScriptSize {
|
||||||
return nil, errors.New("the script is too big")
|
return nil, errors.New("the script is too big")
|
||||||
}
|
}
|
||||||
manifestBytes := v.Estack().Pop().Bytes()
|
manifestBytes := ic.VM.Estack().Pop().Bytes()
|
||||||
if len(manifestBytes) > manifest.MaxManifestSize {
|
if len(manifestBytes) > manifest.MaxManifestSize {
|
||||||
return nil, errors.New("manifest is too big")
|
return nil, errors.New("manifest is too big")
|
||||||
}
|
}
|
||||||
if !v.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
||||||
return nil, errGasLimitExceeded
|
return nil, errGasLimitExceeded
|
||||||
}
|
}
|
||||||
var m manifest.Manifest
|
var m manifest.Manifest
|
||||||
|
@ -83,8 +83,8 @@ func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract,
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractCreate creates a contract.
|
// contractCreate creates a contract.
|
||||||
func contractCreate(ic *interop.Context, v *vm.VM) error {
|
func contractCreate(ic *interop.Context) error {
|
||||||
newcontract, err := createContractStateFromVM(ic, v)
|
newcontract, err := createContractStateFromVM(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,26 +107,26 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot convert contract to stack item: %w", err)
|
return fmt.Errorf("cannot convert contract to stack item: %w", err)
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(cs)
|
ic.VM.Estack().PushVal(cs)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
||||||
// of the contract can be updated independently.
|
// of the contract can be updated independently.
|
||||||
func contractUpdate(ic *interop.Context, v *vm.VM) error {
|
func contractUpdate(ic *interop.Context) error {
|
||||||
contract, _ := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
contract, _ := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if contract == nil {
|
if contract == nil {
|
||||||
return errors.New("contract doesn't exist")
|
return errors.New("contract doesn't exist")
|
||||||
}
|
}
|
||||||
script := v.Estack().Pop().Bytes()
|
script := ic.VM.Estack().Pop().Bytes()
|
||||||
if len(script) > MaxContractScriptSize {
|
if len(script) > MaxContractScriptSize {
|
||||||
return errors.New("the script is too big")
|
return errors.New("the script is too big")
|
||||||
}
|
}
|
||||||
manifestBytes := v.Estack().Pop().Bytes()
|
manifestBytes := ic.VM.Estack().Pop().Bytes()
|
||||||
if len(manifestBytes) > manifest.MaxManifestSize {
|
if len(manifestBytes) > manifest.MaxManifestSize {
|
||||||
return errors.New("manifest is too big")
|
return errors.New("manifest is too big")
|
||||||
}
|
}
|
||||||
if !v.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
||||||
return errGasLimitExceeded
|
return errGasLimitExceeded
|
||||||
}
|
}
|
||||||
// if script was provided, update the old contract script and Manifest.ABI hash
|
// if script was provided, update the old contract script and Manifest.ABI hash
|
||||||
|
@ -186,30 +186,30 @@ func contractUpdate(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeSerialize serializes top stack item into a ByteArray.
|
// runtimeSerialize serializes top stack item into a ByteArray.
|
||||||
func runtimeSerialize(_ *interop.Context, v *vm.VM) error {
|
func runtimeSerialize(ic *interop.Context) error {
|
||||||
return vm.RuntimeSerialize(v)
|
return vm.RuntimeSerialize(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeDeserialize deserializes ByteArray from a stack into an item.
|
// runtimeDeserialize deserializes ByteArray from a stack into an item.
|
||||||
func runtimeDeserialize(_ *interop.Context, v *vm.VM) error {
|
func runtimeDeserialize(ic *interop.Context) error {
|
||||||
return vm.RuntimeDeserialize(v)
|
return vm.RuntimeDeserialize(ic.VM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeEncode encodes top stack item into a base64 string.
|
// runtimeEncode encodes top stack item into a base64 string.
|
||||||
func runtimeEncode(_ *interop.Context, v *vm.VM) error {
|
func runtimeEncode(ic *interop.Context) error {
|
||||||
src := v.Estack().Pop().Bytes()
|
src := ic.VM.Estack().Pop().Bytes()
|
||||||
result := base64.StdEncoding.EncodeToString(src)
|
result := base64.StdEncoding.EncodeToString(src)
|
||||||
v.Estack().PushVal([]byte(result))
|
ic.VM.Estack().PushVal([]byte(result))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeDecode decodes top stack item from base64 string to byte array.
|
// runtimeDecode decodes top stack item from base64 string to byte array.
|
||||||
func runtimeDecode(_ *interop.Context, v *vm.VM) error {
|
func runtimeDecode(ic *interop.Context) error {
|
||||||
src := v.Estack().Pop().String()
|
src := ic.VM.Estack().Pop().String()
|
||||||
result, err := base64.StdEncoding.DecodeString(src)
|
result, err := base64.StdEncoding.DecodeString(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(result)
|
ic.VM.Estack().PushVal(result)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,9 @@ import (
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func TestGetTrigger(t *testing.T) {
|
func TestGetTrigger(t *testing.T) {
|
||||||
v, _, context, chain := createVMAndPushBlock(t)
|
_, _, context, chain := createVMAndPushBlock(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
require.NoError(t, runtimeGetTrigger(context, v))
|
require.NoError(t, runtimeGetTrigger(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStorageFind(t *testing.T) {
|
func TestStorageFind(t *testing.T) {
|
||||||
|
@ -75,37 +75,37 @@ func TestStorageFind(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
||||||
|
|
||||||
err := storageFind(context, v)
|
err := storageFind(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var iter *stackitem.Interop
|
var iter *stackitem.Interop
|
||||||
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
|
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
|
||||||
|
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context))
|
||||||
require.True(t, v.Estack().Pop().Bool())
|
require.True(t, v.Estack().Pop().Bool())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, iterator.Key(context, v))
|
require.NoError(t, iterator.Key(context))
|
||||||
require.Equal(t, []byte{0x01, 0x01}, v.Estack().Pop().Bytes())
|
require.Equal(t, []byte{0x01, 0x01}, v.Estack().Pop().Bytes())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, enumerator.Value(context, v))
|
require.NoError(t, enumerator.Value(context))
|
||||||
require.Equal(t, []byte{0x03, 0x04, 0x05, 0x06}, v.Estack().Pop().Bytes())
|
require.Equal(t, []byte{0x03, 0x04, 0x05, 0x06}, v.Estack().Pop().Bytes())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context))
|
||||||
require.True(t, v.Estack().Pop().Bool())
|
require.True(t, v.Estack().Pop().Bool())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, iterator.Key(context, v))
|
require.NoError(t, iterator.Key(context))
|
||||||
require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes())
|
require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, enumerator.Value(context, v))
|
require.NoError(t, enumerator.Value(context))
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes())
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context))
|
||||||
require.False(t, v.Estack().Pop().Bool())
|
require.False(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -113,10 +113,10 @@ func TestStorageFind(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x03})
|
v.Estack().PushVal([]byte{0x03})
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
|
||||||
|
|
||||||
err := storageFind(context, v)
|
err := storageFind(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context))
|
||||||
require.False(t, v.Estack().Pop().Bool())
|
require.False(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func TestStorageFind(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(stackitem.NewInterop(nil))
|
v.Estack().PushVal(stackitem.NewInterop(nil))
|
||||||
|
|
||||||
require.Error(t, storageFind(context, v))
|
require.Error(t, storageFind(context))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid id", func(t *testing.T) {
|
t.Run("invalid id", func(t *testing.T) {
|
||||||
|
@ -133,8 +133,8 @@ func TestStorageFind(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID}))
|
||||||
|
|
||||||
require.NoError(t, storageFind(context, v))
|
require.NoError(t, storageFind(context))
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context))
|
||||||
require.False(t, v.Estack().Pop().Bool())
|
require.False(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,7 @@ func TestECDSAVerify(t *testing.T) {
|
||||||
ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
|
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
|
ic.VM = v
|
||||||
for i := range args {
|
for i := range args {
|
||||||
v.Estack().PushVal(args[i])
|
v.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
|
@ -160,7 +161,7 @@ func TestECDSAVerify(t *testing.T) {
|
||||||
err = fmt.Errorf("panic: %v", r)
|
err = fmt.Errorf("panic: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
err = crypto.ECDSASecp256r1Verify(ic, v)
|
err = crypto.ECDSASecp256r1Verify(ic)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if isErr {
|
if isErr {
|
||||||
|
@ -227,7 +228,7 @@ func TestRuntimeEncode(t *testing.T) {
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
v.Estack().PushVal(str)
|
v.Estack().PushVal(str)
|
||||||
require.NoError(t, runtimeEncode(ic, v))
|
require.NoError(t, runtimeEncode(ic))
|
||||||
|
|
||||||
expected := []byte(base64.StdEncoding.EncodeToString(str))
|
expected := []byte(base64.StdEncoding.EncodeToString(str))
|
||||||
actual := v.Estack().Pop().Bytes()
|
actual := v.Estack().Pop().Bytes()
|
||||||
|
@ -242,7 +243,7 @@ func TestRuntimeDecode(t *testing.T) {
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
v.Estack().PushVal(str)
|
v.Estack().PushVal(str)
|
||||||
require.NoError(t, runtimeDecode(ic, v))
|
require.NoError(t, runtimeDecode(ic))
|
||||||
|
|
||||||
actual := v.Estack().Pop().Bytes()
|
actual := v.Estack().Pop().Bytes()
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
|
@ -250,17 +251,17 @@ func TestRuntimeDecode(t *testing.T) {
|
||||||
|
|
||||||
t.Run("error", func(t *testing.T) {
|
t.Run("error", func(t *testing.T) {
|
||||||
v.Estack().PushVal(str + "%")
|
v.Estack().PushVal(str + "%")
|
||||||
require.Error(t, runtimeDecode(ic, v))
|
require.Error(t, runtimeDecode(ic))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
||||||
|
|
||||||
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
|
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
|
||||||
v := vm.New()
|
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application,
|
context := chain.newInteropContext(trigger.Application,
|
||||||
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
|
v := context.SpawnVM()
|
||||||
return v, context, chain
|
return v, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,10 +272,10 @@ func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) {
|
func createVMAndBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) {
|
||||||
v := vm.New()
|
|
||||||
block := newDumbBlock()
|
block := newDumbBlock()
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), block, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), block, nil)
|
||||||
|
v := context.SpawnVM()
|
||||||
return v, block, context, chain
|
return v, block, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +286,6 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
||||||
v := vm.New()
|
|
||||||
script := []byte("testscript")
|
script := []byte("testscript")
|
||||||
m := manifest.NewManifest(hash.Hash160(script))
|
m := manifest.NewManifest(hash.Hash160(script))
|
||||||
m.Features = smartcontract.HasStorage
|
m.Features = smartcontract.HasStorage
|
||||||
|
@ -297,11 +297,11 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.C
|
||||||
|
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
|
v := context.SpawnVM()
|
||||||
return v, contractState, context, chain
|
return v, contractState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context, *Blockchain) {
|
func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context, *Blockchain) {
|
||||||
v := vm.New()
|
|
||||||
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
|
rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
|
||||||
hash, err := util.Uint160DecodeStringBE(rawHash)
|
hash, err := util.Uint160DecodeStringBE(rawHash)
|
||||||
accountState := state.NewAccount(hash)
|
accountState := state.NewAccount(hash)
|
||||||
|
@ -309,11 +309,11 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
|
v := context.SpawnVM()
|
||||||
return v, accountState, context, chain
|
return v, accountState, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
||||||
v := vm.New()
|
|
||||||
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
||||||
tx := transaction.New(netmode.UnitTestNet, script, 0)
|
tx := transaction.New(netmode.UnitTestNet, script, 0)
|
||||||
|
|
||||||
|
@ -327,5 +327,6 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con
|
||||||
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}}
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}}
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx)
|
||||||
|
v := context.SpawnVM()
|
||||||
return v, tx, context, chain
|
return v, tx, context, chain
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,16 +89,16 @@ func blockToStackItem(b *block.Block) stackitem.Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetBlock returns current block.
|
// bcGetBlock returns current block.
|
||||||
func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
func bcGetBlock(ic *interop.Context) error {
|
||||||
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
block, err := ic.Chain.GetBlock(hash)
|
block, err := ic.Chain.GetBlock(hash)
|
||||||
if err != nil || !isTraceableBlock(ic, block.Index) {
|
if err != nil || !isTraceableBlock(ic, block.Index) {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(blockToStackItem(block))
|
ic.VM.Estack().PushVal(blockToStackItem(block))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -118,28 +118,28 @@ func contractToStackItem(cs *state.Contract) (stackitem.Item, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetContract returns contract.
|
// bcGetContract returns contract.
|
||||||
func bcGetContract(ic *interop.Context, v *vm.VM) error {
|
func bcGetContract(ic *interop.Context) error {
|
||||||
hashbytes := v.Estack().Pop().Bytes()
|
hashbytes := ic.VM.Estack().Pop().Bytes()
|
||||||
hash, err := util.Uint160DecodeBytesBE(hashbytes)
|
hash, err := util.Uint160DecodeBytesBE(hashbytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
cs, err := ic.DAO.GetContractState(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||||
} else {
|
} else {
|
||||||
item, err := contractToStackItem(cs)
|
item, err := contractToStackItem(cs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(item)
|
ic.VM.Estack().PushVal(item)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetHeight returns blockchain height.
|
// bcGetHeight returns blockchain height.
|
||||||
func bcGetHeight(ic *interop.Context, v *vm.VM) error {
|
func bcGetHeight(ic *interop.Context) error {
|
||||||
v.Estack().PushVal(ic.Chain.BlockHeight())
|
ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,51 +176,51 @@ func transactionToStackItem(t *transaction.Transaction) stackitem.Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetTransaction returns transaction.
|
// bcGetTransaction returns transaction.
|
||||||
func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
|
func bcGetTransaction(ic *interop.Context) error {
|
||||||
tx, h, err := getTransactionAndHeight(ic.DAO, v)
|
tx, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
|
||||||
if err != nil || !isTraceableBlock(ic, h) {
|
if err != nil || !isTraceableBlock(ic, h) {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(transactionToStackItem(tx))
|
ic.VM.Estack().PushVal(transactionToStackItem(tx))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetTransactionFromBlock returns transaction with the given index from the
|
// bcGetTransactionFromBlock returns transaction with the given index from the
|
||||||
// block with height or hash specified.
|
// block with height or hash specified.
|
||||||
func bcGetTransactionFromBlock(ic *interop.Context, v *vm.VM) error {
|
func bcGetTransactionFromBlock(ic *interop.Context) error {
|
||||||
hash, err := getBlockHashFromElement(ic.Chain, v.Estack().Pop())
|
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
index := v.Estack().Pop().BigInt().Int64()
|
index := ic.VM.Estack().Pop().BigInt().Int64()
|
||||||
block, err := ic.DAO.GetBlock(hash)
|
block, err := ic.DAO.GetBlock(hash)
|
||||||
if err != nil || !isTraceableBlock(ic, block.Index) {
|
if err != nil || !isTraceableBlock(ic, block.Index) {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if index < 0 || index >= int64(len(block.Transactions)) {
|
if index < 0 || index >= int64(len(block.Transactions)) {
|
||||||
return errors.New("wrong transaction index")
|
return errors.New("wrong transaction index")
|
||||||
}
|
}
|
||||||
tx := block.Transactions[index]
|
tx := block.Transactions[index]
|
||||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
ic.VM.Estack().PushVal(tx.Hash().BytesBE())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetTransactionHeight returns transaction height.
|
// bcGetTransactionHeight returns transaction height.
|
||||||
func bcGetTransactionHeight(ic *interop.Context, v *vm.VM) error {
|
func bcGetTransactionHeight(ic *interop.Context) error {
|
||||||
_, h, err := getTransactionAndHeight(ic.DAO, v)
|
_, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
|
||||||
if err != nil || !isTraceableBlock(ic, h) {
|
if err != nil || !isTraceableBlock(ic, h) {
|
||||||
v.Estack().PushVal(-1)
|
ic.VM.Estack().PushVal(-1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(h)
|
ic.VM.Estack().PushVal(h)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// engineGetScriptContainer returns transaction or block that contains the script
|
// engineGetScriptContainer returns transaction or block that contains the script
|
||||||
// being run.
|
// being run.
|
||||||
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
func engineGetScriptContainer(ic *interop.Context) error {
|
||||||
var item stackitem.Item
|
var item stackitem.Item
|
||||||
switch t := ic.Container.(type) {
|
switch t := ic.Container.(type) {
|
||||||
case *transaction.Transaction:
|
case *transaction.Transaction:
|
||||||
|
@ -230,45 +230,45 @@ func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown script container")
|
return errors.New("unknown script container")
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(item)
|
ic.VM.Estack().PushVal(item)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// engineGetExecutingScriptHash returns executing script hash.
|
// engineGetExecutingScriptHash returns executing script hash.
|
||||||
func engineGetExecutingScriptHash(ic *interop.Context, v *vm.VM) error {
|
func engineGetExecutingScriptHash(ic *interop.Context) error {
|
||||||
return v.PushContextScriptHash(0)
|
return ic.VM.PushContextScriptHash(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// engineGetCallingScriptHash returns calling script hash.
|
// engineGetCallingScriptHash returns calling script hash.
|
||||||
func engineGetCallingScriptHash(ic *interop.Context, v *vm.VM) error {
|
func engineGetCallingScriptHash(ic *interop.Context) error {
|
||||||
return v.PushContextScriptHash(1)
|
return ic.VM.PushContextScriptHash(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// engineGetEntryScriptHash returns entry script hash.
|
// engineGetEntryScriptHash returns entry script hash.
|
||||||
func engineGetEntryScriptHash(ic *interop.Context, v *vm.VM) error {
|
func engineGetEntryScriptHash(ic *interop.Context) error {
|
||||||
return v.PushContextScriptHash(v.Istack().Len() - 1)
|
return ic.VM.PushContextScriptHash(ic.VM.Istack().Len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimePlatform returns the name of the platform.
|
// runtimePlatform returns the name of the platform.
|
||||||
func runtimePlatform(ic *interop.Context, v *vm.VM) error {
|
func runtimePlatform(ic *interop.Context) error {
|
||||||
v.Estack().PushVal([]byte("NEO"))
|
ic.VM.Estack().PushVal([]byte("NEO"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeGetTrigger returns the script trigger.
|
// runtimeGetTrigger returns the script trigger.
|
||||||
func runtimeGetTrigger(ic *interop.Context, v *vm.VM) error {
|
func runtimeGetTrigger(ic *interop.Context) error {
|
||||||
v.Estack().PushVal(byte(ic.Trigger))
|
ic.VM.Estack().PushVal(byte(ic.Trigger))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeNotify should pass stack item to the notify plugin to handle it, but
|
// runtimeNotify should pass stack item to the notify plugin to handle it, but
|
||||||
// in neo-go the only meaningful thing to do here is to log.
|
// in neo-go the only meaningful thing to do here is to log.
|
||||||
func runtimeNotify(ic *interop.Context, v *vm.VM) error {
|
func runtimeNotify(ic *interop.Context) error {
|
||||||
name := v.Estack().Pop().String()
|
name := ic.VM.Estack().Pop().String()
|
||||||
if len(name) > MaxEventNameLen {
|
if len(name) > MaxEventNameLen {
|
||||||
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
|
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
|
||||||
}
|
}
|
||||||
elem := v.Estack().Pop()
|
elem := ic.VM.Estack().Pop()
|
||||||
args := elem.Array()
|
args := elem.Array()
|
||||||
// But it has to be serializable, otherwise we either have some broken
|
// But it has to be serializable, otherwise we either have some broken
|
||||||
// (recursive) structure inside or an interop item that can't be used
|
// (recursive) structure inside or an interop item that can't be used
|
||||||
|
@ -280,7 +280,7 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
|
||||||
args = []stackitem.Item{stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))}
|
args = []stackitem.Item{stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))}
|
||||||
}
|
}
|
||||||
ne := state.NotificationEvent{
|
ne := state.NotificationEvent{
|
||||||
ScriptHash: v.GetCurrentScriptHash(),
|
ScriptHash: ic.VM.GetCurrentScriptHash(),
|
||||||
Name: name,
|
Name: name,
|
||||||
Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array),
|
Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array),
|
||||||
}
|
}
|
||||||
|
@ -289,21 +289,21 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeLog logs the message passed.
|
// runtimeLog logs the message passed.
|
||||||
func runtimeLog(ic *interop.Context, v *vm.VM) error {
|
func runtimeLog(ic *interop.Context) error {
|
||||||
state := v.Estack().Pop().String()
|
state := ic.VM.Estack().Pop().String()
|
||||||
if len(state) > MaxNotificationSize {
|
if len(state) > MaxNotificationSize {
|
||||||
return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize)
|
return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize)
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("%q", state)
|
msg := fmt.Sprintf("%q", state)
|
||||||
ic.Log.Info("runtime log",
|
ic.Log.Info("runtime log",
|
||||||
zap.Stringer("script", v.GetCurrentScriptHash()),
|
zap.Stringer("script", ic.VM.GetCurrentScriptHash()),
|
||||||
zap.String("logs", msg))
|
zap.String("logs", msg))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtimeGetTime returns timestamp of the block being verified, or the latest
|
// runtimeGetTime returns timestamp of the block being verified, or the latest
|
||||||
// one in the blockchain if no block is given to Context.
|
// one in the blockchain if no block is given to Context.
|
||||||
func runtimeGetTime(ic *interop.Context, v *vm.VM) error {
|
func runtimeGetTime(ic *interop.Context) error {
|
||||||
var header *block.Header
|
var header *block.Header
|
||||||
if ic.Block == nil {
|
if ic.Block == nil {
|
||||||
var err error
|
var err error
|
||||||
|
@ -314,13 +314,13 @@ func runtimeGetTime(ic *interop.Context, v *vm.VM) error {
|
||||||
} else {
|
} else {
|
||||||
header = ic.Block.Header()
|
header = ic.Block.Header()
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(header.Timestamp)
|
ic.VM.Estack().PushVal(header.Timestamp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageDelete deletes stored key-value pair.
|
// storageDelete deletes stored key-value pair.
|
||||||
func storageDelete(ic *interop.Context, v *vm.VM) error {
|
func storageDelete(ic *interop.Context) error {
|
||||||
stcInterface := v.Estack().Pop().Value()
|
stcInterface := ic.VM.Estack().Pop().Value()
|
||||||
stc, ok := stcInterface.(*StorageContext)
|
stc, ok := stcInterface.(*StorageContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||||
|
@ -328,7 +328,7 @@ func storageDelete(ic *interop.Context, v *vm.VM) error {
|
||||||
if stc.ReadOnly {
|
if stc.ReadOnly {
|
||||||
return errors.New("StorageContext is read only")
|
return errors.New("StorageContext is read only")
|
||||||
}
|
}
|
||||||
key := v.Estack().Pop().Bytes()
|
key := ic.VM.Estack().Pop().Bytes()
|
||||||
si := ic.DAO.GetStorageItem(stc.ID, key)
|
si := ic.DAO.GetStorageItem(stc.ID, key)
|
||||||
if si != nil && si.IsConst {
|
if si != nil && si.IsConst {
|
||||||
return errors.New("storage item is constant")
|
return errors.New("storage item is constant")
|
||||||
|
@ -337,36 +337,36 @@ func storageDelete(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageGet returns stored key-value pair.
|
// storageGet returns stored key-value pair.
|
||||||
func storageGet(ic *interop.Context, v *vm.VM) error {
|
func storageGet(ic *interop.Context) error {
|
||||||
stcInterface := v.Estack().Pop().Value()
|
stcInterface := ic.VM.Estack().Pop().Value()
|
||||||
stc, ok := stcInterface.(*StorageContext)
|
stc, ok := stcInterface.(*StorageContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||||
}
|
}
|
||||||
key := v.Estack().Pop().Bytes()
|
key := ic.VM.Estack().Pop().Bytes()
|
||||||
si := ic.DAO.GetStorageItem(stc.ID, key)
|
si := ic.DAO.GetStorageItem(stc.ID, key)
|
||||||
if si != nil && si.Value != nil {
|
if si != nil && si.Value != nil {
|
||||||
v.Estack().PushVal(si.Value)
|
ic.VM.Estack().PushVal(si.Value)
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushVal(stackitem.Null{})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageGetContext returns storage context (scripthash).
|
// storageGetContext returns storage context (scripthash).
|
||||||
func storageGetContext(ic *interop.Context, v *vm.VM) error {
|
func storageGetContext(ic *interop.Context) error {
|
||||||
return storageGetContextInternal(ic, v, false)
|
return storageGetContextInternal(ic, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageGetReadOnlyContext returns read-only context (scripthash).
|
// storageGetReadOnlyContext returns read-only context (scripthash).
|
||||||
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
|
func storageGetReadOnlyContext(ic *interop.Context) error {
|
||||||
return storageGetContextInternal(ic, v, true)
|
return storageGetContextInternal(ic, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageGetContextInternal is internal version of storageGetContext and
|
// storageGetContextInternal is internal version of storageGetContext and
|
||||||
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
|
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
|
||||||
func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) error {
|
func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
|
||||||
contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
contract, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -377,11 +377,11 @@ func storageGetContextInternal(ic *interop.Context, v *vm.VM, isReadOnly bool) e
|
||||||
ID: contract.ID,
|
ID: contract.ID,
|
||||||
ReadOnly: isReadOnly,
|
ReadOnly: isReadOnly,
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(stackitem.NewInterop(sc))
|
ic.VM.Estack().PushVal(stackitem.NewInterop(sc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext, key []byte, value []byte, isConst bool) error {
|
func putWithContextAndFlags(ic *interop.Context, stc *StorageContext, key []byte, value []byte, isConst bool) error {
|
||||||
if len(key) > MaxStorageKeyLen {
|
if len(key) > MaxStorageKeyLen {
|
||||||
return errors.New("key is too big")
|
return errors.New("key is too big")
|
||||||
}
|
}
|
||||||
|
@ -402,7 +402,7 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext,
|
||||||
if len(value) > len(si.Value) {
|
if len(value) > len(si.Value) {
|
||||||
sizeInc = len(value) - len(si.Value)
|
sizeInc = len(value) - len(si.Value)
|
||||||
}
|
}
|
||||||
if !v.AddGas(int64(sizeInc) * StoragePrice) {
|
if !ic.VM.AddGas(int64(sizeInc) * StoragePrice) {
|
||||||
return errGasLimitExceeded
|
return errGasLimitExceeded
|
||||||
}
|
}
|
||||||
si.Value = value
|
si.Value = value
|
||||||
|
@ -411,34 +411,34 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
// storagePutInternal is a unified implementation of storagePut and storagePutEx.
|
// storagePutInternal is a unified implementation of storagePut and storagePutEx.
|
||||||
func storagePutInternal(ic *interop.Context, v *vm.VM, getFlag bool) error {
|
func storagePutInternal(ic *interop.Context, getFlag bool) error {
|
||||||
stcInterface := v.Estack().Pop().Value()
|
stcInterface := ic.VM.Estack().Pop().Value()
|
||||||
stc, ok := stcInterface.(*StorageContext)
|
stc, ok := stcInterface.(*StorageContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||||
}
|
}
|
||||||
key := v.Estack().Pop().Bytes()
|
key := ic.VM.Estack().Pop().Bytes()
|
||||||
value := v.Estack().Pop().Bytes()
|
value := ic.VM.Estack().Pop().Bytes()
|
||||||
var flag int
|
var flag int
|
||||||
if getFlag {
|
if getFlag {
|
||||||
flag = int(v.Estack().Pop().BigInt().Int64())
|
flag = int(ic.VM.Estack().Pop().BigInt().Int64())
|
||||||
}
|
}
|
||||||
return putWithContextAndFlags(ic, v, stc, key, value, int(Constant)&flag != 0)
|
return putWithContextAndFlags(ic, stc, key, value, int(Constant)&flag != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// storagePut puts key-value pair into the storage.
|
// storagePut puts key-value pair into the storage.
|
||||||
func storagePut(ic *interop.Context, v *vm.VM) error {
|
func storagePut(ic *interop.Context) error {
|
||||||
return storagePutInternal(ic, v, false)
|
return storagePutInternal(ic, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// storagePutEx puts key-value pair with given flags into the storage.
|
// storagePutEx puts key-value pair with given flags into the storage.
|
||||||
func storagePutEx(ic *interop.Context, v *vm.VM) error {
|
func storagePutEx(ic *interop.Context) error {
|
||||||
return storagePutInternal(ic, v, true)
|
return storagePutInternal(ic, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageContextAsReadOnly sets given context to read-only mode.
|
// storageContextAsReadOnly sets given context to read-only mode.
|
||||||
func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
|
func storageContextAsReadOnly(ic *interop.Context) error {
|
||||||
stcInterface := v.Estack().Pop().Value()
|
stcInterface := ic.VM.Estack().Pop().Value()
|
||||||
stc, ok := stcInterface.(*StorageContext)
|
stc, ok := stcInterface.(*StorageContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
return fmt.Errorf("%T is not a StorageContext", stcInterface)
|
||||||
|
@ -450,31 +450,31 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
stc = stx
|
stc = stx
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(stackitem.NewInterop(stc))
|
ic.VM.Estack().PushVal(stackitem.NewInterop(stc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractCall calls a contract.
|
// contractCall calls a contract.
|
||||||
func contractCall(ic *interop.Context, v *vm.VM) error {
|
func contractCall(ic *interop.Context) error {
|
||||||
h := v.Estack().Pop().Bytes()
|
h := ic.VM.Estack().Pop().Bytes()
|
||||||
method := v.Estack().Pop().String()
|
method := ic.VM.Estack().Pop().String()
|
||||||
args := v.Estack().Pop().Array()
|
args := ic.VM.Estack().Pop().Array()
|
||||||
return contractCallExInternal(ic, v, h, method, args, smartcontract.All)
|
return contractCallExInternal(ic, h, method, args, smartcontract.All)
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractCallEx calls a contract with flags.
|
// contractCallEx calls a contract with flags.
|
||||||
func contractCallEx(ic *interop.Context, v *vm.VM) error {
|
func contractCallEx(ic *interop.Context) error {
|
||||||
h := v.Estack().Pop().Bytes()
|
h := ic.VM.Estack().Pop().Bytes()
|
||||||
method := v.Estack().Pop().String()
|
method := ic.VM.Estack().Pop().String()
|
||||||
args := v.Estack().Pop().Array()
|
args := ic.VM.Estack().Pop().Array()
|
||||||
flags := smartcontract.CallFlag(int32(v.Estack().Pop().BigInt().Int64()))
|
flags := smartcontract.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64()))
|
||||||
if flags&^smartcontract.All != 0 {
|
if flags&^smartcontract.All != 0 {
|
||||||
return errors.New("call flags out of range")
|
return errors.New("call flags out of range")
|
||||||
}
|
}
|
||||||
return contractCallExInternal(ic, v, h, method, args, flags)
|
return contractCallExInternal(ic, h, method, args, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string, args []stackitem.Item, f smartcontract.CallFlag) error {
|
func contractCallExInternal(ic *interop.Context, h []byte, name string, args []stackitem.Item, f smartcontract.CallFlag) error {
|
||||||
u, err := util.Uint160DecodeBytesBE(h)
|
u, err := util.Uint160DecodeBytesBE(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("invalid contract hash")
|
return errors.New("invalid contract hash")
|
||||||
|
@ -490,7 +490,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
|
||||||
if md == nil {
|
if md == nil {
|
||||||
return fmt.Errorf("method '%s' not found", name)
|
return fmt.Errorf("method '%s' not found", name)
|
||||||
}
|
}
|
||||||
curr, err := ic.DAO.GetContractState(v.GetCurrentScriptHash())
|
curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if !curr.Manifest.CanCall(&cs.Manifest, name) {
|
if !curr.Manifest.CanCall(&cs.Manifest, name) {
|
||||||
return errors.New("disallowed method call")
|
return errors.New("disallowed method call")
|
||||||
|
@ -502,7 +502,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
|
||||||
}
|
}
|
||||||
|
|
||||||
ic.Invocations[u]++
|
ic.Invocations[u]++
|
||||||
v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f)
|
ic.VM.LoadScriptWithHash(cs.Script, u, ic.VM.Context().GetCallFlags()&f)
|
||||||
var isNative bool
|
var isNative bool
|
||||||
for i := range ic.Natives {
|
for i := range ic.Natives {
|
||||||
if ic.Natives[i].Metadata().Hash.Equals(u) {
|
if ic.Natives[i].Metadata().Hash.Equals(u) {
|
||||||
|
@ -511,27 +511,27 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, name string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isNative {
|
if isNative {
|
||||||
v.Estack().PushVal(args)
|
ic.VM.Estack().PushVal(args)
|
||||||
v.Estack().PushVal(name)
|
ic.VM.Estack().PushVal(name)
|
||||||
} else {
|
} else {
|
||||||
for i := len(args) - 1; i >= 0; i-- {
|
for i := len(args) - 1; i >= 0; i-- {
|
||||||
v.Estack().PushVal(args[i])
|
ic.VM.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
// use Jump not Call here because context was loaded in LoadScript above.
|
// use Jump not Call here because context was loaded in LoadScript above.
|
||||||
v.Jump(v.Context(), md.Offset)
|
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
md = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
md = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
||||||
if md != nil {
|
if md != nil {
|
||||||
v.Call(v.Context(), md.Offset)
|
ic.VM.Call(ic.VM.Context(), md.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractDestroy destroys a contract.
|
// contractDestroy destroys a contract.
|
||||||
func contractDestroy(ic *interop.Context, v *vm.VM) error {
|
func contractDestroy(ic *interop.Context) error {
|
||||||
hash := v.GetCurrentScriptHash()
|
hash := ic.VM.GetCurrentScriptHash()
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
cs, err := ic.DAO.GetContractState(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -553,8 +553,8 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractIsStandard checks if contract is standard (sig or multisig) contract.
|
// contractIsStandard checks if contract is standard (sig or multisig) contract.
|
||||||
func contractIsStandard(ic *interop.Context, v *vm.VM) error {
|
func contractIsStandard(ic *interop.Context) error {
|
||||||
h := v.Estack().Pop().Bytes()
|
h := ic.VM.Estack().Pop().Bytes()
|
||||||
u, err := util.Uint160DecodeBytesBE(h)
|
u, err := util.Uint160DecodeBytesBE(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -573,23 +573,23 @@ func contractIsStandard(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(result)
|
ic.VM.Estack().PushVal(result)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractCreateStandardAccount calculates contract scripthash for a given public key.
|
// contractCreateStandardAccount calculates contract scripthash for a given public key.
|
||||||
func contractCreateStandardAccount(ic *interop.Context, v *vm.VM) error {
|
func contractCreateStandardAccount(ic *interop.Context) error {
|
||||||
h := v.Estack().Pop().Bytes()
|
h := ic.VM.Estack().Pop().Bytes()
|
||||||
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
|
p, err := keys.NewPublicKeyFromBytes(h, elliptic.P256())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(p.GetScriptHash().BytesBE())
|
ic.VM.Estack().PushVal(p.GetScriptHash().BytesBE())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractGetCallFlags returns current context calling flags.
|
// contractGetCallFlags returns current context calling flags.
|
||||||
func contractGetCallFlags(_ *interop.Context, v *vm.VM) error {
|
func contractGetCallFlags(ic *interop.Context) error {
|
||||||
v.Estack().PushVal(v.Context().GetCallFlags())
|
ic.VM.Estack().PushVal(ic.VM.Context().GetCallFlags())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ func TestBCGetTransaction(t *testing.T) {
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 0))
|
require.NoError(t, context.DAO.StoreAsTransaction(tx, 0))
|
||||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
v.Estack().PushVal(tx.Hash().BytesBE())
|
||||||
err := bcGetTransaction(context, v)
|
err := bcGetTransaction(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
value := v.Estack().Pop().Value()
|
value := v.Estack().Pop().Value()
|
||||||
|
@ -49,7 +49,7 @@ func TestBCGetTransaction(t *testing.T) {
|
||||||
t.Run("isn't traceable", func(t *testing.T) {
|
t.Run("isn't traceable", func(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
||||||
v.Estack().PushVal(tx.Hash().BytesBE())
|
v.Estack().PushVal(tx.Hash().BytesBE())
|
||||||
err := bcGetTransaction(context, v)
|
err := bcGetTransaction(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||||
|
@ -59,7 +59,7 @@ func TestBCGetTransaction(t *testing.T) {
|
||||||
t.Run("bad hash", func(t *testing.T) {
|
t.Run("bad hash", func(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
require.NoError(t, context.DAO.StoreAsTransaction(tx, 1))
|
||||||
v.Estack().PushVal(tx.Hash().BytesLE())
|
v.Estack().PushVal(tx.Hash().BytesLE())
|
||||||
err := bcGetTransaction(context, v)
|
err := bcGetTransaction(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||||
|
@ -76,7 +76,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
v.Estack().PushVal(0)
|
v.Estack().PushVal(0)
|
||||||
v.Estack().PushVal(block.Hash().BytesBE())
|
v.Estack().PushVal(block.Hash().BytesBE())
|
||||||
err := bcGetTransactionFromBlock(context, v)
|
err := bcGetTransactionFromBlock(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
value := v.Estack().Pop().Value()
|
value := v.Estack().Pop().Value()
|
||||||
|
@ -88,7 +88,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
||||||
t.Run("invalid block hash", func(t *testing.T) {
|
t.Run("invalid block hash", func(t *testing.T) {
|
||||||
v.Estack().PushVal(0)
|
v.Estack().PushVal(0)
|
||||||
v.Estack().PushVal(block.Hash().BytesBE()[:10])
|
v.Estack().PushVal(block.Hash().BytesBE()[:10])
|
||||||
err := bcGetTransactionFromBlock(context, v)
|
err := bcGetTransactionFromBlock(context)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||||
v.Estack().PushVal(0)
|
v.Estack().PushVal(0)
|
||||||
v.Estack().PushVal(block.Hash().BytesBE())
|
v.Estack().PushVal(block.Hash().BytesBE())
|
||||||
err := bcGetTransactionFromBlock(context, v)
|
err := bcGetTransactionFromBlock(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||||
|
@ -109,7 +109,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||||
v.Estack().PushVal(0)
|
v.Estack().PushVal(0)
|
||||||
v.Estack().PushVal(block.Hash().BytesLE())
|
v.Estack().PushVal(block.Hash().BytesLE())
|
||||||
err := bcGetTransactionFromBlock(context, v)
|
err := bcGetTransactionFromBlock(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||||
|
@ -120,7 +120,7 @@ func TestBCGetTransactionFromBlock(t *testing.T) {
|
||||||
require.NoError(t, context.DAO.StoreAsBlock(block))
|
require.NoError(t, context.DAO.StoreAsBlock(block))
|
||||||
v.Estack().PushVal(1)
|
v.Estack().PushVal(1)
|
||||||
v.Estack().PushVal(block.Hash().BytesBE())
|
v.Estack().PushVal(block.Hash().BytesBE())
|
||||||
err := bcGetTransactionFromBlock(context, v)
|
err := bcGetTransactionFromBlock(context)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ func TestBCGetBlock(t *testing.T) {
|
||||||
|
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
v.Estack().PushVal(block.Hash().BytesBE())
|
v.Estack().PushVal(block.Hash().BytesBE())
|
||||||
err := bcGetBlock(context, v)
|
err := bcGetBlock(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
value := v.Estack().Pop().Value()
|
value := v.Estack().Pop().Value()
|
||||||
|
@ -152,7 +152,7 @@ func TestBCGetBlock(t *testing.T) {
|
||||||
|
|
||||||
t.Run("bad hash", func(t *testing.T) {
|
t.Run("bad hash", func(t *testing.T) {
|
||||||
v.Estack().PushVal(block.Hash().BytesLE())
|
v.Estack().PushVal(block.Hash().BytesLE())
|
||||||
err := bcGetTransaction(context, v)
|
err := bcGetTransaction(context)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
_, ok := v.Estack().Pop().Item().(stackitem.Null)
|
||||||
|
@ -180,14 +180,14 @@ func TestContractIsStandard(t *testing.T) {
|
||||||
|
|
||||||
t.Run("true", func(t *testing.T) {
|
t.Run("true", func(t *testing.T) {
|
||||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||||
require.NoError(t, contractIsStandard(ic, v))
|
require.NoError(t, contractIsStandard(ic))
|
||||||
require.True(t, v.Estack().Pop().Bool())
|
require.True(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("false", func(t *testing.T) {
|
t.Run("false", func(t *testing.T) {
|
||||||
tx.Scripts[0].VerificationScript = []byte{9, 8, 7}
|
tx.Scripts[0].VerificationScript = []byte{9, 8, 7}
|
||||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||||
require.NoError(t, contractIsStandard(ic, v))
|
require.NoError(t, contractIsStandard(ic))
|
||||||
require.False(t, v.Estack().Pop().Bool())
|
require.False(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -201,7 +201,7 @@ func TestContractIsStandard(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||||
require.NoError(t, contractIsStandard(ic, v))
|
require.NoError(t, contractIsStandard(ic))
|
||||||
require.True(t, v.Estack().Pop().Bool())
|
require.True(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
t.Run("contract stored, false", func(t *testing.T) {
|
t.Run("contract stored, false", func(t *testing.T) {
|
||||||
|
@ -209,7 +209,7 @@ func TestContractIsStandard(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script}))
|
require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Script: script}))
|
||||||
|
|
||||||
v.Estack().PushVal(crypto.Hash160(script).BytesBE())
|
v.Estack().PushVal(crypto.Hash160(script).BytesBE())
|
||||||
require.NoError(t, contractIsStandard(ic, v))
|
require.NoError(t, contractIsStandard(ic))
|
||||||
require.False(t, v.Estack().Pop().Bool())
|
require.False(t, v.Estack().Pop().Bool())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ func TestContractCreateAccount(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pub := priv.PublicKey()
|
pub := priv.PublicKey()
|
||||||
v.Estack().PushVal(pub.Bytes())
|
v.Estack().PushVal(pub.Bytes())
|
||||||
require.NoError(t, contractCreateStandardAccount(ic, v))
|
require.NoError(t, contractCreateStandardAccount(ic))
|
||||||
|
|
||||||
value := v.Estack().Pop().Bytes()
|
value := v.Estack().Pop().Bytes()
|
||||||
u, err := util.Uint160DecodeBytesBE(value)
|
u, err := util.Uint160DecodeBytesBE(value)
|
||||||
|
@ -231,7 +231,7 @@ func TestContractCreateAccount(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("InvalidKey", func(t *testing.T) {
|
t.Run("InvalidKey", func(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{1, 2, 3})
|
v.Estack().PushVal([]byte{1, 2, 3})
|
||||||
require.Error(t, contractCreateStandardAccount(ic, v))
|
require.Error(t, contractCreateStandardAccount(ic))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ func TestRuntimeGasLeft(t *testing.T) {
|
||||||
|
|
||||||
v.GasLimit = 100
|
v.GasLimit = 100
|
||||||
v.AddGas(58)
|
v.AddGas(58)
|
||||||
require.NoError(t, runtime.GasLeft(ic, v))
|
require.NoError(t, runtime.GasLeft(ic))
|
||||||
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
|
|
||||||
t.Run("NoFilter", func(t *testing.T) {
|
t.Run("NoFilter", func(t *testing.T) {
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
v.Estack().PushVal(stackitem.Null{})
|
||||||
require.NoError(t, runtime.GetNotifications(ic, v))
|
require.NoError(t, runtime.GetNotifications(ic))
|
||||||
|
|
||||||
arr := v.Estack().Pop().Array()
|
arr := v.Estack().Pop().Array()
|
||||||
require.Equal(t, len(ic.Notifications), len(arr))
|
require.Equal(t, len(ic.Notifications), len(arr))
|
||||||
|
@ -274,7 +274,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
t.Run("WithFilter", func(t *testing.T) {
|
t.Run("WithFilter", func(t *testing.T) {
|
||||||
h := util.Uint160{2}.BytesBE()
|
h := util.Uint160{2}.BytesBE()
|
||||||
v.Estack().PushVal(h)
|
v.Estack().PushVal(h)
|
||||||
require.NoError(t, runtime.GetNotifications(ic, v))
|
require.NoError(t, runtime.GetNotifications(ic))
|
||||||
|
|
||||||
arr := v.Estack().Pop().Array()
|
arr := v.Estack().Pop().Array()
|
||||||
require.Equal(t, 1, len(arr))
|
require.Equal(t, 1, len(arr))
|
||||||
|
@ -295,11 +295,11 @@ func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Zero", func(t *testing.T) {
|
t.Run("Zero", func(t *testing.T) {
|
||||||
v.LoadScript([]byte{1})
|
v.LoadScript([]byte{1})
|
||||||
require.Error(t, runtime.GetInvocationCounter(ic, v))
|
require.Error(t, runtime.GetInvocationCounter(ic))
|
||||||
})
|
})
|
||||||
t.Run("NonZero", func(t *testing.T) {
|
t.Run("NonZero", func(t *testing.T) {
|
||||||
v.LoadScript([]byte{2})
|
v.LoadScript([]byte{2})
|
||||||
require.NoError(t, runtime.GetInvocationCounter(ic, v))
|
require.NoError(t, runtime.GetInvocationCounter(ic))
|
||||||
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ func TestBlockchainGetContractState(t *testing.T) {
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
v.Estack().PushVal(cs.ScriptHash().BytesBE())
|
v.Estack().PushVal(cs.ScriptHash().BytesBE())
|
||||||
require.NoError(t, bcGetContract(ic, v))
|
require.NoError(t, bcGetContract(ic))
|
||||||
|
|
||||||
actual := v.Estack().Pop().Item()
|
actual := v.Estack().Pop().Item()
|
||||||
compareContractStates(t, cs, actual)
|
compareContractStates(t, cs, actual)
|
||||||
|
@ -319,7 +319,7 @@ func TestBlockchainGetContractState(t *testing.T) {
|
||||||
|
|
||||||
t.Run("uncknown contract state", func(t *testing.T) {
|
t.Run("uncknown contract state", func(t *testing.T) {
|
||||||
v.Estack().PushVal(util.Uint160{1, 2, 3}.BytesBE())
|
v.Estack().PushVal(util.Uint160{1, 2, 3}.BytesBE())
|
||||||
require.NoError(t, bcGetContract(ic, v))
|
require.NoError(t, bcGetContract(ic))
|
||||||
|
|
||||||
actual := v.Estack().Pop().Item()
|
actual := v.Estack().Pop().Item()
|
||||||
require.Equal(t, stackitem.Null{}, actual)
|
require.Equal(t, stackitem.Null{}, actual)
|
||||||
|
@ -394,14 +394,14 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadScript(script []byte, args ...interface{}) *vm.VM {
|
func loadScript(ic *interop.Context, script []byte, args ...interface{}) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
v.LoadScriptWithFlags(script, smartcontract.AllowCall)
|
v.LoadScriptWithFlags(script, smartcontract.AllowCall)
|
||||||
for i := range args {
|
for i := range args {
|
||||||
v.Estack().PushVal(args[i])
|
v.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
v.GasLimit = -1
|
v.GasLimit = -1
|
||||||
return v
|
ic.VM = v
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContractCall(t *testing.T) {
|
func TestContractCall(t *testing.T) {
|
||||||
|
@ -417,36 +417,36 @@ func TestContractCall(t *testing.T) {
|
||||||
|
|
||||||
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
|
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
|
||||||
t.Run("Good", func(t *testing.T) {
|
t.Run("Good", func(t *testing.T) {
|
||||||
v := loadScript(currScript, 42)
|
loadScript(ic, currScript, 42)
|
||||||
v.Estack().PushVal(addArgs)
|
ic.VM.Estack().PushVal(addArgs)
|
||||||
v.Estack().PushVal("add")
|
ic.VM.Estack().PushVal("add")
|
||||||
v.Estack().PushVal(h.BytesBE())
|
ic.VM.Estack().PushVal(h.BytesBE())
|
||||||
require.NoError(t, contractCall(ic, v))
|
require.NoError(t, contractCall(ic))
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
require.Equal(t, 2, v.Estack().Len())
|
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||||
require.Equal(t, big.NewInt(3), v.Estack().Pop().Value())
|
require.Equal(t, big.NewInt(3), ic.VM.Estack().Pop().Value())
|
||||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("CallExInvalidFlag", func(t *testing.T) {
|
t.Run("CallExInvalidFlag", func(t *testing.T) {
|
||||||
v := loadScript(currScript, 42)
|
loadScript(ic, currScript, 42)
|
||||||
v.Estack().PushVal(byte(0xFF))
|
ic.VM.Estack().PushVal(byte(0xFF))
|
||||||
v.Estack().PushVal(addArgs)
|
ic.VM.Estack().PushVal(addArgs)
|
||||||
v.Estack().PushVal("add")
|
ic.VM.Estack().PushVal("add")
|
||||||
v.Estack().PushVal(h.BytesBE())
|
ic.VM.Estack().PushVal(h.BytesBE())
|
||||||
require.Error(t, contractCallEx(ic, v))
|
require.Error(t, contractCallEx(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
v := loadScript(currScript, 42)
|
loadScript(ic, currScript, 42)
|
||||||
for i := range args {
|
for i := range args {
|
||||||
v.Estack().PushVal(args[i])
|
ic.VM.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
// interops can both return error and panic,
|
// interops can both return error and panic,
|
||||||
// we don't care which kind of error has occured
|
// we don't care which kind of error has occured
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
err := contractCall(ic, v)
|
err := contractCall(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -466,26 +466,26 @@ func TestContractCall(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("IsolatedStack", func(t *testing.T) {
|
t.Run("IsolatedStack", func(t *testing.T) {
|
||||||
v := loadScript(currScript, 42)
|
loadScript(ic, currScript, 42)
|
||||||
v.Estack().PushVal(stackitem.NewArray(nil))
|
ic.VM.Estack().PushVal(stackitem.NewArray(nil))
|
||||||
v.Estack().PushVal("drop")
|
ic.VM.Estack().PushVal("drop")
|
||||||
v.Estack().PushVal(h.BytesBE())
|
ic.VM.Estack().PushVal(h.BytesBE())
|
||||||
require.NoError(t, contractCall(ic, v))
|
require.NoError(t, contractCall(ic))
|
||||||
require.Error(t, v.Run())
|
require.Error(t, ic.VM.Run())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("CallInitialize", func(t *testing.T) {
|
t.Run("CallInitialize", func(t *testing.T) {
|
||||||
t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE()))
|
t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE()))
|
||||||
|
|
||||||
v := loadScript(currScript, 42)
|
loadScript(ic, currScript, 42)
|
||||||
v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)}))
|
ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)}))
|
||||||
v.Estack().PushVal("add3")
|
ic.VM.Estack().PushVal("add3")
|
||||||
v.Estack().PushVal(h.BytesBE())
|
ic.VM.Estack().PushVal(h.BytesBE())
|
||||||
require.NoError(t, contractCall(ic, v))
|
require.NoError(t, contractCall(ic))
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
require.Equal(t, 2, v.Estack().Len())
|
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||||
require.Equal(t, big.NewInt(8), v.Estack().Pop().Value())
|
require.Equal(t, big.NewInt(8), ic.VM.Estack().Pop().Value())
|
||||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +504,7 @@ func TestContractCreate(t *testing.T) {
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
putArgsOnStack()
|
putArgsOnStack()
|
||||||
|
|
||||||
require.NoError(t, contractCreate(ic, v))
|
require.NoError(t, contractCreate(ic))
|
||||||
actual := v.Estack().Pop().Item()
|
actual := v.Estack().Pop().Item()
|
||||||
compareContractStates(t, cs, actual)
|
compareContractStates(t, cs, actual)
|
||||||
})
|
})
|
||||||
|
@ -513,7 +513,7 @@ func TestContractCreate(t *testing.T) {
|
||||||
cs.Script = append(cs.Script, 0x01)
|
cs.Script = append(cs.Script, 0x01)
|
||||||
putArgsOnStack()
|
putArgsOnStack()
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic, v))
|
require.Error(t, contractCreate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("contract already exists", func(t *testing.T) {
|
t.Run("contract already exists", func(t *testing.T) {
|
||||||
|
@ -521,7 +521,7 @@ func TestContractCreate(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
putArgsOnStack()
|
putArgsOnStack()
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic, v))
|
require.Error(t, contractCreate(ic))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,27 +553,27 @@ func TestContractUpdate(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(nil, nil)
|
putArgsOnStack(nil, nil)
|
||||||
require.NoError(t, contractUpdate(ic, v))
|
require.NoError(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("no contract", func(t *testing.T) {
|
t.Run("no contract", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("too large script", func(t *testing.T) {
|
t.Run("too large script", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(make([]byte, MaxContractScriptSize+1), nil)
|
putArgsOnStack(make([]byte, MaxContractScriptSize+1), nil)
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("too large manifest", func(t *testing.T) {
|
t.Run("too large manifest", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(nil, make([]byte, manifest.MaxManifestSize+1))
|
putArgsOnStack(nil, make([]byte, manifest.MaxManifestSize+1))
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("gas limit exceeded", func(t *testing.T) {
|
t.Run("gas limit exceeded", func(t *testing.T) {
|
||||||
|
@ -581,7 +581,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
v.GasLimit = 0
|
v.GasLimit = 0
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack([]byte{1}, []byte{2})
|
putArgsOnStack([]byte{1}, []byte{2})
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update script, the same script", func(t *testing.T) {
|
t.Run("update script, the same script", func(t *testing.T) {
|
||||||
|
@ -590,7 +590,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(cs.Script, nil)
|
putArgsOnStack(cs.Script, nil)
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update script, already exists", func(t *testing.T) {
|
t.Run("update script, already exists", func(t *testing.T) {
|
||||||
|
@ -608,7 +608,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(duplicateScript, nil)
|
putArgsOnStack(duplicateScript, nil)
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update script, positive", func(t *testing.T) {
|
t.Run("update script, positive", func(t *testing.T) {
|
||||||
|
@ -617,7 +617,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
newScript := []byte{9, 8, 7, 6, 5}
|
newScript := []byte{9, 8, 7, 6, 5}
|
||||||
putArgsOnStack(newScript, nil)
|
putArgsOnStack(newScript, nil)
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic, v))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new scripthash
|
// updated contract should have new scripthash
|
||||||
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
||||||
|
@ -641,7 +641,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
||||||
putArgsOnStack(nil, []byte{1, 2, 3})
|
putArgsOnStack(nil, []byte{1, 2, 3})
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update manifest, bad contract hash", func(t *testing.T) {
|
t.Run("update manifest, bad contract hash", func(t *testing.T) {
|
||||||
|
@ -656,7 +656,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
putArgsOnStack(nil, manifestBytes)
|
putArgsOnStack(nil, manifestBytes)
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update manifest, old contract shouldn't have storage", func(t *testing.T) {
|
t.Run("update manifest, old contract shouldn't have storage", func(t *testing.T) {
|
||||||
|
@ -676,7 +676,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
putArgsOnStack(nil, manifestBytes)
|
putArgsOnStack(nil, manifestBytes)
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic, v))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update manifest, positive", func(t *testing.T) {
|
t.Run("update manifest, positive", func(t *testing.T) {
|
||||||
|
@ -693,7 +693,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
putArgsOnStack(nil, manifestBytes)
|
putArgsOnStack(nil, manifestBytes)
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic, v))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new scripthash
|
// updated contract should have new scripthash
|
||||||
actual, err := ic.DAO.GetContractState(cs.ScriptHash())
|
actual, err := ic.DAO.GetContractState(cs.ScriptHash())
|
||||||
|
@ -721,7 +721,7 @@ func TestContractUpdate(t *testing.T) {
|
||||||
|
|
||||||
putArgsOnStack(newScript, newManifestBytes)
|
putArgsOnStack(newScript, newManifestBytes)
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic, v))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new script and manifest
|
// updated contract should have new script and manifest
|
||||||
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
||||||
|
@ -746,7 +746,7 @@ func TestContractGetCallFlags(t *testing.T) {
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, smartcontract.All)
|
||||||
require.NoError(t, contractGetCallFlags(ic, v))
|
require.NoError(t, contractGetCallFlags(ic))
|
||||||
require.Equal(t, int64(smartcontract.All), v.Estack().Pop().Value().(*big.Int).Int64())
|
require.Equal(t, int64(smartcontract.All), v.Estack().Pop().Value().(*big.Int).Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,27 +759,27 @@ func TestPointerCallback(t *testing.T) {
|
||||||
byte(opcode.DIV), byte(opcode.RET),
|
byte(opcode.DIV), byte(opcode.RET),
|
||||||
}
|
}
|
||||||
t.Run("Good", func(t *testing.T) {
|
t.Run("Good", func(t *testing.T) {
|
||||||
v := loadScript(script, 2, stackitem.NewPointer(3, script))
|
loadScript(ic, script, 2, stackitem.NewPointer(3, script))
|
||||||
v.Estack().PushVal(v.Context())
|
ic.VM.Estack().PushVal(ic.VM.Context())
|
||||||
require.NoError(t, callback.Create(ic, v))
|
require.NoError(t, callback.Create(ic))
|
||||||
|
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)})
|
||||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
|
||||||
require.NoError(t, callback.Invoke(ic, v))
|
require.NoError(t, callback.Invoke(ic))
|
||||||
|
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
require.Equal(t, 1, v.Estack().Len())
|
require.Equal(t, 1, ic.VM.Estack().Len())
|
||||||
require.Equal(t, big.NewInt(5), v.Estack().Pop().Item().Value())
|
require.Equal(t, big.NewInt(5), ic.VM.Estack().Pop().Item().Value())
|
||||||
})
|
})
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
t.Run("NotEnoughParameters", func(t *testing.T) {
|
t.Run("NotEnoughParameters", func(t *testing.T) {
|
||||||
v := loadScript(script, 2, stackitem.NewPointer(3, script))
|
loadScript(ic, script, 2, stackitem.NewPointer(3, script))
|
||||||
v.Estack().PushVal(v.Context())
|
ic.VM.Estack().PushVal(ic.VM.Context())
|
||||||
require.NoError(t, callback.Create(ic, v))
|
require.NoError(t, callback.Create(ic))
|
||||||
|
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)})
|
||||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
|
||||||
require.Error(t, callback.Invoke(ic, v))
|
require.Error(t, callback.Invoke(ic))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -799,11 +799,11 @@ func TestMethodCallback(t *testing.T) {
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
v := loadScript(currCs.Script, 42)
|
loadScript(ic, currCs.Script, 42)
|
||||||
for i := range args {
|
for i := range args {
|
||||||
v.Estack().PushVal(args[i])
|
ic.VM.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
require.Error(t, callback.CreateFromMethod(ic, v))
|
require.Error(t, callback.CreateFromMethod(ic))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Run("Hash", runInvalid("add", rawHash[1:]))
|
t.Run("Hash", runInvalid("add", rawHash[1:]))
|
||||||
|
@ -812,37 +812,38 @@ func TestMethodCallback(t *testing.T) {
|
||||||
t.Run("DisallowedMethod", runInvalid("ret7", rawHash))
|
t.Run("DisallowedMethod", runInvalid("ret7", rawHash))
|
||||||
t.Run("Initialize", runInvalid("_initialize", rawHash))
|
t.Run("Initialize", runInvalid("_initialize", rawHash))
|
||||||
t.Run("NotEnoughArguments", func(t *testing.T) {
|
t.Run("NotEnoughArguments", func(t *testing.T) {
|
||||||
v := loadScript(currCs.Script, 42, "add", rawHash)
|
loadScript(ic, currCs.Script, 42, "add", rawHash)
|
||||||
require.NoError(t, callback.CreateFromMethod(ic, v))
|
require.NoError(t, callback.CreateFromMethod(ic))
|
||||||
|
|
||||||
v.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1)
|
ic.VM.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1)
|
||||||
require.Error(t, callback.Invoke(ic, v))
|
require.Error(t, callback.Invoke(ic))
|
||||||
})
|
})
|
||||||
t.Run("CallIsNotAllowed", func(t *testing.T) {
|
t.Run("CallIsNotAllowed", func(t *testing.T) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
|
ic.VM = v
|
||||||
v.Load(currCs.Script)
|
v.Load(currCs.Script)
|
||||||
v.Estack().PushVal("add")
|
v.Estack().PushVal("add")
|
||||||
v.Estack().PushVal(rawHash)
|
v.Estack().PushVal(rawHash)
|
||||||
require.NoError(t, callback.CreateFromMethod(ic, v))
|
require.NoError(t, callback.CreateFromMethod(ic))
|
||||||
|
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
|
||||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
v.Estack().InsertAt(vm.NewElement(args), 1)
|
||||||
require.Error(t, callback.Invoke(ic, v))
|
require.Error(t, callback.Invoke(ic))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Good", func(t *testing.T) {
|
t.Run("Good", func(t *testing.T) {
|
||||||
v := loadScript(currCs.Script, 42, "add", rawHash)
|
loadScript(ic, currCs.Script, 42, "add", rawHash)
|
||||||
require.NoError(t, callback.CreateFromMethod(ic, v))
|
require.NoError(t, callback.CreateFromMethod(ic))
|
||||||
|
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)})
|
||||||
v.Estack().InsertAt(vm.NewElement(args), 1)
|
ic.VM.Estack().InsertAt(vm.NewElement(args), 1)
|
||||||
|
|
||||||
require.NoError(t, callback.Invoke(ic, v))
|
require.NoError(t, callback.Invoke(ic))
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
require.Equal(t, 2, v.Estack().Len())
|
require.Equal(t, 2, ic.VM.Estack().Len())
|
||||||
require.Equal(t, big.NewInt(6), v.Estack().Pop().Item().Value())
|
require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Item().Value())
|
||||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value())
|
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func TestSyscallCallback(t *testing.T) {
|
func TestSyscallCallback(t *testing.T) {
|
||||||
|
@ -852,44 +853,44 @@ func TestSyscallCallback(t *testing.T) {
|
||||||
ic.Functions = append(ic.Functions, []interop.Function{
|
ic.Functions = append(ic.Functions, []interop.Function{
|
||||||
{
|
{
|
||||||
ID: 0x42,
|
ID: 0x42,
|
||||||
Func: func(_ *interop.Context, v *vm.VM) error {
|
Func: func(ic *interop.Context) error {
|
||||||
a := v.Estack().Pop().BigInt()
|
a := ic.VM.Estack().Pop().BigInt()
|
||||||
b := v.Estack().Pop().BigInt()
|
b := ic.VM.Estack().Pop().BigInt()
|
||||||
v.Estack().PushVal(new(big.Int).Add(a, b))
|
ic.VM.Estack().PushVal(new(big.Int).Add(a, b))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
ParamCount: 2,
|
ParamCount: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: 0x53,
|
ID: 0x53,
|
||||||
Func: func(_ *interop.Context, _ *vm.VM) error { return nil },
|
Func: func(_ *interop.Context) error { return nil },
|
||||||
DisallowCallback: true,
|
DisallowCallback: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Good", func(t *testing.T) {
|
t.Run("Good", func(t *testing.T) {
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)})
|
||||||
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42)
|
loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
|
||||||
require.NoError(t, callback.CreateFromSyscall(ic, v))
|
require.NoError(t, callback.CreateFromSyscall(ic))
|
||||||
require.NoError(t, callback.Invoke(ic, v))
|
require.NoError(t, callback.Invoke(ic))
|
||||||
require.Equal(t, 1, v.Estack().Len())
|
require.Equal(t, 1, ic.VM.Estack().Len())
|
||||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Item().Value())
|
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
t.Run("InvalidParameterCount", func(t *testing.T) {
|
t.Run("InvalidParameterCount", func(t *testing.T) {
|
||||||
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)})
|
args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)})
|
||||||
v := loadScript([]byte{byte(opcode.RET)}, args, 0x42)
|
loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42)
|
||||||
require.NoError(t, callback.CreateFromSyscall(ic, v))
|
require.NoError(t, callback.CreateFromSyscall(ic))
|
||||||
require.Error(t, callback.Invoke(ic, v))
|
require.Error(t, callback.Invoke(ic))
|
||||||
})
|
})
|
||||||
t.Run("MissingSyscall", func(t *testing.T) {
|
t.Run("MissingSyscall", func(t *testing.T) {
|
||||||
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43)
|
loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43)
|
||||||
require.Error(t, callback.CreateFromSyscall(ic, v))
|
require.Error(t, callback.CreateFromSyscall(ic))
|
||||||
})
|
})
|
||||||
t.Run("Disallowed", func(t *testing.T) {
|
t.Run("Disallowed", func(t *testing.T) {
|
||||||
v := loadScript([]byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53)
|
loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53)
|
||||||
require.Error(t, callback.CreateFromSyscall(ic, v))
|
require.Error(t, callback.CreateFromSyscall(ic))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,14 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testNonInterop(t *testing.T, value interface{}, f func(*interop.Context, *vm.VM) error) {
|
func testNonInterop(t *testing.T, value interface{}, f func(*interop.Context) error) {
|
||||||
v := vm.New()
|
v := vm.New()
|
||||||
v.Estack().PushVal(value)
|
v.Estack().PushVal(value)
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
require.Error(t, f(context, v))
|
context.VM = v
|
||||||
|
require.Error(t, f(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnexpectedNonInterops(t *testing.T) {
|
func TestUnexpectedNonInterops(t *testing.T) {
|
||||||
|
@ -32,7 +33,7 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// All of these functions expect an interop item on the stack.
|
// All of these functions expect an interop item on the stack.
|
||||||
funcs := []func(*interop.Context, *vm.VM) error{
|
funcs := []func(*interop.Context) error{
|
||||||
storageContextAsReadOnly,
|
storageContextAsReadOnly,
|
||||||
storageDelete,
|
storageDelete,
|
||||||
storageFind,
|
storageFind,
|
||||||
|
|
|
@ -6,11 +6,10 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deploy deploys native contract.
|
// Deploy deploys native contract.
|
||||||
func Deploy(ic *interop.Context, _ *vm.VM) error {
|
func Deploy(ic *interop.Context) error {
|
||||||
if ic.Block == nil || ic.Block.Index != 0 {
|
if ic.Block == nil || ic.Block.Index != 0 {
|
||||||
return errors.New("native contracts can be deployed only at 0 block")
|
return errors.New("native contracts can be deployed only at 0 block")
|
||||||
}
|
}
|
||||||
|
@ -34,8 +33,8 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call calls specified native contract method.
|
// Call calls specified native contract method.
|
||||||
func Call(ic *interop.Context, v *vm.VM) error {
|
func Call(ic *interop.Context) error {
|
||||||
name := v.Estack().Pop().String()
|
name := ic.VM.Estack().Pop().String()
|
||||||
var c interop.Contract
|
var c interop.Contract
|
||||||
for _, ctr := range ic.Natives {
|
for _, ctr := range ic.Natives {
|
||||||
if ctr.Metadata().Name == name {
|
if ctr.Metadata().Name == name {
|
||||||
|
@ -46,23 +45,23 @@ func Call(ic *interop.Context, v *vm.VM) error {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return fmt.Errorf("native contract %s not found", name)
|
return fmt.Errorf("native contract %s not found", name)
|
||||||
}
|
}
|
||||||
h := v.GetCurrentScriptHash()
|
h := ic.VM.GetCurrentScriptHash()
|
||||||
if !h.Equals(c.Metadata().Hash) {
|
if !h.Equals(c.Metadata().Hash) {
|
||||||
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
|
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
|
||||||
}
|
}
|
||||||
operation := v.Estack().Pop().String()
|
operation := ic.VM.Estack().Pop().String()
|
||||||
args := v.Estack().Pop().Array()
|
args := ic.VM.Estack().Pop().Array()
|
||||||
m, ok := c.Metadata().Methods[operation]
|
m, ok := c.Metadata().Methods[operation]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("method %s not found", operation)
|
return fmt.Errorf("method %s not found", operation)
|
||||||
}
|
}
|
||||||
if !v.Context().GetCallFlags().Has(m.RequiredFlags) {
|
if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) {
|
||||||
return errors.New("missing call flags")
|
return errors.New("missing call flags")
|
||||||
}
|
}
|
||||||
if !v.AddGas(m.Price) {
|
if !ic.VM.AddGas(m.Price) {
|
||||||
return errors.New("gas limit exceeded")
|
return errors.New("gas limit exceeded")
|
||||||
}
|
}
|
||||||
result := m.Func(ic, args)
|
result := m.Func(ic, args)
|
||||||
v.Estack().PushVal(result)
|
ic.VM.Estack().PushVal(result)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Ui
|
||||||
return errors.New("negative amount")
|
return errors.New("negative amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
caller := ic.ScriptGetter.GetCallingScriptHash()
|
caller := ic.VM.GetCallingScriptHash()
|
||||||
if caller.Equals(util.Uint160{}) || !from.Equals(caller) {
|
if caller.Equals(util.Uint160{}) || !from.Equals(caller) {
|
||||||
ok, err := runtime.CheckHashedWitness(ic, from)
|
ok, err := runtime.CheckHashedWitness(ic, from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -76,6 +78,35 @@ func newTestNative() *testNative {
|
||||||
}
|
}
|
||||||
tn.meta.AddMethod(md, desc, true)
|
tn.meta.AddMethod(md, desc, true)
|
||||||
|
|
||||||
|
desc = &manifest.Method{
|
||||||
|
Name: "callOtherContractWithoutArgs",
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
|
||||||
|
manifest.NewParameter("method", smartcontract.StringType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.AnyType,
|
||||||
|
}
|
||||||
|
md = &interop.MethodAndPrice{
|
||||||
|
Func: tn.callOtherContractWithoutArgs,
|
||||||
|
Price: testSumPrice,
|
||||||
|
RequiredFlags: smartcontract.NoneFlag}
|
||||||
|
tn.meta.AddMethod(md, desc, true)
|
||||||
|
|
||||||
|
desc = &manifest.Method{
|
||||||
|
Name: "callOtherContractWithArg",
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
|
||||||
|
manifest.NewParameter("method", smartcontract.StringType),
|
||||||
|
manifest.NewParameter("arg", smartcontract.ArrayType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.AnyType,
|
||||||
|
}
|
||||||
|
md = &interop.MethodAndPrice{
|
||||||
|
Func: tn.callOtherContractWithArg,
|
||||||
|
Price: testSumPrice,
|
||||||
|
RequiredFlags: smartcontract.NoneFlag}
|
||||||
|
tn.meta.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType}
|
desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType}
|
||||||
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates}
|
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates}
|
||||||
tn.meta.AddMethod(md, desc, false)
|
tn.meta.AddMethod(md, desc, false)
|
||||||
|
@ -95,6 +126,38 @@ func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.I
|
||||||
return stackitem.NewBigInteger(s1.Add(s1, s2))
|
return stackitem.NewBigInteger(s1.Add(s1, s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tn *testNative) callOtherContractWithoutArgs(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
vm := ic.VM
|
||||||
|
vm.Estack().PushVal(stackitem.NewArray([]stackitem.Item{})) // no args
|
||||||
|
vm.Estack().PushVal(args[1]) // method
|
||||||
|
vm.Estack().PushVal(args[0]) // contract hash
|
||||||
|
err := contractCall(ic)
|
||||||
|
if err != nil {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(-1))
|
||||||
|
}
|
||||||
|
_ = vm.Run()
|
||||||
|
if vm.HasFailed() {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(-2))
|
||||||
|
}
|
||||||
|
return vm.Estack().Pop().Item()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tn *testNative) callOtherContractWithArg(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
vm := ic.VM
|
||||||
|
vm.Estack().PushVal(stackitem.NewArray([]stackitem.Item{args[2]})) // arg
|
||||||
|
vm.Estack().PushVal(args[1]) // method
|
||||||
|
vm.Estack().PushVal(args[0]) // contract hash
|
||||||
|
err := contractCall(ic)
|
||||||
|
if err != nil {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(-1))
|
||||||
|
}
|
||||||
|
_ = vm.Run()
|
||||||
|
if vm.HasFailed() {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(-2))
|
||||||
|
}
|
||||||
|
return vm.Estack().Pop().Item()
|
||||||
|
}
|
||||||
|
|
||||||
func TestNativeContract_Invoke(t *testing.T) {
|
func TestNativeContract_Invoke(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
@ -159,10 +222,9 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
v := vm.New()
|
|
||||||
v.GasLimit = -1
|
|
||||||
ic := chain.newInteropContext(trigger.Application,
|
ic := chain.newInteropContext(trigger.Application,
|
||||||
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil)
|
||||||
|
v := ic.SpawnVM()
|
||||||
|
|
||||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
t.Run("fail, bad current script hash", func(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, smartcontract.All)
|
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, smartcontract.All)
|
||||||
|
@ -171,7 +233,7 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
v.Estack().PushVal(tn.Metadata().Name)
|
v.Estack().PushVal(tn.Metadata().Name)
|
||||||
|
|
||||||
// it's prohibited to call natives directly
|
// it's prohibited to call natives directly
|
||||||
require.Error(t, native.Call(ic, v))
|
require.Error(t, native.Call(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
|
@ -180,9 +242,117 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
v.Estack().PushVal("sum")
|
v.Estack().PushVal("sum")
|
||||||
v.Estack().PushVal(tn.Metadata().Name)
|
v.Estack().PushVal(tn.Metadata().Name)
|
||||||
|
|
||||||
require.NoError(t, native.Call(ic, v))
|
require.NoError(t, native.Call(ic))
|
||||||
|
|
||||||
value := v.Estack().Pop().BigInt()
|
value := v.Estack().Pop().BigInt()
|
||||||
require.Equal(t, int64(42), value.Int64())
|
require.Equal(t, int64(42), value.Int64())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
||||||
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
tn := newTestNative()
|
||||||
|
chain.registerNative(tn)
|
||||||
|
|
||||||
|
err := chain.dao.PutContractState(&state.Contract{
|
||||||
|
Script: tn.meta.Script,
|
||||||
|
Manifest: tn.meta.Manifest,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("native Policy, getFeePerByte", func(t *testing.T) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithoutArgs", chain.contracts.Policy.Hash, "getFeePerByte")
|
||||||
|
require.NoError(t, w.Err)
|
||||||
|
script := w.Bytes()
|
||||||
|
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*4+10000)
|
||||||
|
validUntil := chain.blockHeight + 1
|
||||||
|
tx.ValidUntilBlock = validUntil
|
||||||
|
addSigners(tx)
|
||||||
|
require.NoError(t, signTx(chain, tx))
|
||||||
|
|
||||||
|
b := chain.newBlock(tx)
|
||||||
|
require.NoError(t, chain.AddBlock(b))
|
||||||
|
|
||||||
|
res, err := chain.GetAppExecResult(tx.Hash())
|
||||||
|
require.NoError(t, err)
|
||||||
|
// we expect it to be FeePerByte from Policy contract
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
require.Equal(t, big.NewInt(1000), res.Stack[0].Value())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("native Policy, setFeePerByte", func(t *testing.T) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithArg", chain.contracts.Policy.Hash, "setFeePerByte", int64(500))
|
||||||
|
require.NoError(t, w.Err)
|
||||||
|
script := w.Bytes()
|
||||||
|
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*5+10000)
|
||||||
|
validUntil := chain.blockHeight + 1
|
||||||
|
tx.ValidUntilBlock = validUntil
|
||||||
|
addSigners(tx)
|
||||||
|
// to pass policy.checkValidators
|
||||||
|
tx.Signers[0].Scopes = transaction.Global
|
||||||
|
require.NoError(t, signTx(chain, tx))
|
||||||
|
|
||||||
|
b := chain.newBlock(tx)
|
||||||
|
require.NoError(t, chain.AddBlock(b))
|
||||||
|
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
|
||||||
|
res, err := chain.GetAppExecResult(tx.Hash())
|
||||||
|
require.NoError(t, err)
|
||||||
|
// we expect it to be `true` which means that native policy value was successfully updated
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
require.Equal(t, true, res.Stack[0].Value())
|
||||||
|
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
|
||||||
|
// check that feePerByte was updated
|
||||||
|
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
|
||||||
|
require.Equal(t, 500, int(n))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non-native contract", func(t *testing.T) {
|
||||||
|
// put some other contract into chain (this contract just pushes `5` on stack)
|
||||||
|
avm := []byte{byte(opcode.PUSH5), byte(opcode.RET)}
|
||||||
|
contractHash := hash.Hash160(avm)
|
||||||
|
m := manifest.NewManifest(contractHash)
|
||||||
|
m.ABI.Methods = []manifest.Method{
|
||||||
|
{
|
||||||
|
Name: "five",
|
||||||
|
Offset: 0,
|
||||||
|
Parameters: []manifest.Parameter{},
|
||||||
|
ReturnType: smartcontract.IntegerType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = chain.dao.PutContractState(&state.Contract{
|
||||||
|
Script: avm,
|
||||||
|
Manifest: *m,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "callOtherContractWithoutArgs", contractHash, "five")
|
||||||
|
require.NoError(t, w.Err)
|
||||||
|
script := w.Bytes()
|
||||||
|
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*4+10000)
|
||||||
|
validUntil := chain.blockHeight + 1
|
||||||
|
tx.ValidUntilBlock = validUntil
|
||||||
|
addSigners(tx)
|
||||||
|
require.NoError(t, signTx(chain, tx))
|
||||||
|
|
||||||
|
b := chain.newBlock(tx)
|
||||||
|
require.NoError(t, chain.AddBlock(b))
|
||||||
|
|
||||||
|
res, err := chain.GetAppExecResult(tx.Hash())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
require.Equal(t, int64(5), res.Stack[0].Value().(*big.Int).Int64())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -40,13 +40,6 @@ func newError(ip int, op opcode.Opcode, err interface{}) *errorAtInstruct {
|
||||||
// StateMessage is a vm state message which could be used as additional info for example by cli.
|
// StateMessage is a vm state message which could be used as additional info for example by cli.
|
||||||
type StateMessage string
|
type StateMessage string
|
||||||
|
|
||||||
// ScriptHashGetter defines an interface for getting calling, entry and current script hashes.
|
|
||||||
type ScriptHashGetter interface {
|
|
||||||
GetCallingScriptHash() util.Uint160
|
|
||||||
GetEntryScriptHash() util.Uint160
|
|
||||||
GetCurrentScriptHash() util.Uint160
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
||||||
MaxInvocationStackSize = 1024
|
MaxInvocationStackSize = 1024
|
||||||
|
|
Loading…
Reference in a new issue