rpc: refactor invokecontractverify
In `(c *Client) AddNetworkFee` we define network fee for contract witness verification via `invokecontractverify` RPC call, and that's the initial purpose of this RPC method. But it was not implemented correctly. It used `System.Contract.Call` instead of beheiving like `initVerificationVM`. During real contract witness verification the whole contract's script is loaded into VM, and then we jump to the `verify` method. Thus, to define exact contract verification price, we should act like this (and not just perform `System.Contract.Call` of `verify` method). Tests are added. This bug is the reason of adding extra GAS (c.notary.extraVerifyFee) to pre-calculated value in https://github.com/nspcc-dev/neofs-node/pull/404/files#diff-639db437ca2578db46c9e8cbf18f9aa01f8ca5aee30e0fa7e70ba0354822d7b3R237
This commit is contained in:
parent
1261dd5306
commit
edfca68a17
8 changed files with 207 additions and 71 deletions
|
@ -451,6 +451,15 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
|||
bc.setNodesByRole(t, true, native.RoleP2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()})
|
||||
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
|
||||
|
||||
// Push verification contract with arguments into the chain.
|
||||
txDeploy3, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_with_args_contract.go", "VerifyWithArgs")
|
||||
txDeploy3.Nonce = getNextNonce()
|
||||
txDeploy3.ValidUntilBlock = validUntilBlock
|
||||
require.NoError(t, addNetworkFee(bc, txDeploy3, acc0))
|
||||
require.NoError(t, acc0.SignTx(txDeploy3))
|
||||
b = bc.newBlock(txDeploy3)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
|
||||
// Compile contract to test `invokescript` RPC call
|
||||
_, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest")
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
)
|
||||
|
||||
// expandArrayIntoScript pushes all FuncParam parameters from the given array
|
||||
// ExpandArrayIntoScript pushes all FuncParam parameters from the given array
|
||||
// into the given buffer in reverse order.
|
||||
func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
||||
func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
||||
for j := len(slice) - 1; j >= 0; j-- {
|
||||
fp, err := slice[j].GetFuncParam()
|
||||
if err != nil {
|
||||
|
@ -87,7 +87,7 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = expandArrayIntoScript(script, val)
|
||||
err = ExpandArrayIntoScript(script, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, method string, params
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = expandArrayIntoScript(script.BinWriter, slice)
|
||||
err = ExpandArrayIntoScript(script.BinWriter, slice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ func TestExpandArrayIntoScript(t *testing.T) {
|
|||
}
|
||||
for _, c := range testCases {
|
||||
script := io.NewBufBinWriter()
|
||||
err := expandArrayIntoScript(script.BinWriter, c.Input)
|
||||
err := ExpandArrayIntoScript(script.BinWriter, c.Input)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.Expected, script.Bytes())
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func TestExpandArrayIntoScript(t *testing.T) {
|
|||
}
|
||||
for _, c := range errorCases {
|
||||
script := io.NewBufBinWriter()
|
||||
err := expandArrayIntoScript(script.BinWriter, c)
|
||||
err := ExpandArrayIntoScript(script.BinWriter, c)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
const extraFee = 10
|
||||
|
||||
c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
|
||||
require.NoError(t, err)
|
||||
|
@ -95,7 +96,7 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
Account: accs[0].PrivateKey().GetScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
}}
|
||||
require.Error(t, c.AddNetworkFee(tx, 10, accs[0], accs[1]))
|
||||
require.Error(t, c.AddNetworkFee(tx, extraFee, accs[0], accs[1]))
|
||||
})
|
||||
t.Run("Simple", func(t *testing.T) {
|
||||
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
|
||||
|
@ -107,7 +108,7 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0]))
|
||||
require.NoError(t, accs[0].SignTx(tx))
|
||||
cFee, _ := fee.Calculate(chain.GetBaseExecFee(), accs[0].Contract.Script)
|
||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee)
|
||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+extraFee, tx.NetworkFee)
|
||||
})
|
||||
|
||||
t.Run("Multi", func(t *testing.T) {
|
||||
|
@ -126,43 +127,69 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
Scopes: transaction.Global,
|
||||
},
|
||||
}
|
||||
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0], accs[1]))
|
||||
require.NoError(t, c.AddNetworkFee(tx, extraFee, accs[0], accs[1]))
|
||||
require.NoError(t, accs[0].SignTx(tx))
|
||||
require.NoError(t, accs[1].SignTx(tx))
|
||||
require.NoError(t, accs[2].SignTx(tx))
|
||||
cFee, _ := fee.Calculate(chain.GetBaseExecFee(), accs[0].Contract.Script)
|
||||
cFeeM, _ := fee.Calculate(chain.GetBaseExecFee(), accs[1].Contract.Script)
|
||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee)
|
||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+extraFee, tx.NetworkFee)
|
||||
})
|
||||
t.Run("Contract", func(t *testing.T) {
|
||||
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
|
||||
priv := testchain.PrivateKeyByID(0)
|
||||
acc1 := wallet.NewAccountFromPrivateKey(priv)
|
||||
acc1.Contract.Deployed = true
|
||||
acc1.Contract.Script, err = base64.StdEncoding.DecodeString(verifyContractAVM)
|
||||
require.NoError(t, err)
|
||||
h, err := util.Uint160DecodeStringLE(verifyContractHash)
|
||||
require.NoError(t, err)
|
||||
tx.ValidUntilBlock = chain.BlockHeight() + 10
|
||||
priv := testchain.PrivateKeyByID(0)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv)
|
||||
acc1 := wallet.NewAccountFromPrivateKey(priv) // contract account
|
||||
acc1.Contract.Deployed = true
|
||||
acc1.Contract.Script, err = base64.StdEncoding.DecodeString(verifyContractAVM)
|
||||
|
||||
newTx := func(t *testing.T) *transaction.Transaction {
|
||||
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
|
||||
require.NoError(t, err)
|
||||
tx.ValidUntilBlock = chain.BlockHeight() + 10
|
||||
return tx
|
||||
}
|
||||
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv)
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: acc0.PrivateKey().GetScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
{
|
||||
Account: h,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
completeTx := func(t *testing.T) *transaction.Transaction {
|
||||
tx := newTx(t)
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: acc0.PrivateKey().GetScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
{
|
||||
Account: h,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
}
|
||||
require.NoError(t, c.AddNetworkFee(tx, extraFee, acc0, acc1))
|
||||
return tx
|
||||
}
|
||||
require.NoError(t, c.AddNetworkFee(tx, 10, acc0, acc1))
|
||||
require.NoError(t, acc0.SignTx(tx))
|
||||
tx.Scripts = append(tx.Scripts, transaction.Witness{})
|
||||
require.NoError(t, chain.VerifyTx(tx))
|
||||
|
||||
// check that network fee with extra value is enough
|
||||
tx1 := completeTx(t)
|
||||
require.NoError(t, acc0.SignTx(tx1))
|
||||
tx1.Scripts = append(tx1.Scripts, transaction.Witness{})
|
||||
require.NoError(t, chain.VerifyTx(tx1))
|
||||
|
||||
// check that network fee without extra value is enough
|
||||
tx2 := completeTx(t)
|
||||
tx2.NetworkFee -= extraFee
|
||||
require.NoError(t, acc0.SignTx(tx2))
|
||||
tx2.Scripts = append(tx2.Scripts, transaction.Witness{})
|
||||
require.NoError(t, chain.VerifyTx(tx2))
|
||||
|
||||
// check that we don't add unexpected extra GAS
|
||||
tx3 := completeTx(t)
|
||||
tx3.NetworkFee -= extraFee + 1
|
||||
require.NoError(t, acc0.SignTx(tx3))
|
||||
tx3.Scripts = append(tx3.Scripts, transaction.Witness{})
|
||||
require.Error(t, chain.VerifyTx(tx3))
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
tx := newTx(t)
|
||||
acc0, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
tx.Signers = []transaction.Signer{
|
||||
|
@ -178,6 +205,7 @@ func TestAddNetworkFee(t *testing.T) {
|
|||
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1))
|
||||
})
|
||||
t.Run("InvalidContract", func(t *testing.T) {
|
||||
tx := newTx(t)
|
||||
acc0 := wallet.NewAccountFromPrivateKey(priv)
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
|
|
|
@ -36,9 +36,9 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
|
||||
"github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -1118,7 +1118,7 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
|
|||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||
}
|
||||
tx.Script = script
|
||||
return s.runScriptInVM(trigger.Application, script, tx)
|
||||
return s.runScriptInVM(trigger.Application, script, util.Uint160{}, tx)
|
||||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
|
@ -1144,7 +1144,7 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
|
|||
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
|
||||
}
|
||||
tx.Script = script
|
||||
return s.runScriptInVM(trigger.Application, script, tx)
|
||||
return s.runScriptInVM(trigger.Application, script, util.Uint160{}, tx)
|
||||
}
|
||||
|
||||
// invokeContractVerify implements the `invokecontractverify` RPC call.
|
||||
|
@ -1154,36 +1154,43 @@ func (s *Server) invokeContractVerify(reqParams request.Params) (interface{}, *r
|
|||
return nil, responseErr
|
||||
}
|
||||
|
||||
args := reqParams[1:2]
|
||||
var tx *transaction.Transaction
|
||||
bw := io.NewBufBinWriter()
|
||||
if len(reqParams) > 1 {
|
||||
args, err := reqParams[1].GetArray() // second `invokecontractverify` parameter is an array of arguments for `verify` method
|
||||
if err != nil {
|
||||
return nil, response.WrapErrorWithData(response.ErrInvalidParams, err)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
err := request.ExpandArrayIntoScript(bw.BinWriter, args)
|
||||
if err != nil {
|
||||
return nil, response.NewRPCError("can't create witness invocation script", err.Error(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
invocationScript := bw.Bytes()
|
||||
|
||||
tx := &transaction.Transaction{Script: []byte{byte(opcode.RET)}} // need something in script
|
||||
if len(reqParams) > 2 {
|
||||
signers, witnesses, err := reqParams[2].GetSignersWithWitnesses()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx = &transaction.Transaction{
|
||||
Signers: signers,
|
||||
Scripts: witnesses,
|
||||
}
|
||||
tx.Signers = signers
|
||||
tx.Scripts = witnesses
|
||||
} else { // fill the only known signer - the contract with `verify` method
|
||||
tx.Signers = []transaction.Signer{{Account: scriptHash}}
|
||||
tx.Scripts = []transaction.Witness{{InvocationScript: invocationScript, VerificationScript: []byte{}}}
|
||||
}
|
||||
|
||||
cs := s.chain.GetContractState(scriptHash)
|
||||
if cs == nil {
|
||||
return nil, response.NewRPCError("unknown contract", scriptHash.StringBE(), nil)
|
||||
}
|
||||
script, err := request.CreateFunctionInvocationScript(cs.Hash, manifest.MethodVerify, args)
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||
}
|
||||
if tx != nil {
|
||||
tx.Script = script
|
||||
}
|
||||
return s.runScriptInVM(trigger.Verification, script, tx)
|
||||
return s.runScriptInVM(trigger.Verification, invocationScript, scriptHash, tx)
|
||||
}
|
||||
|
||||
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||
// result.
|
||||
func (s *Server) runScriptInVM(t trigger.Type, script []byte, tx *transaction.Transaction) (*result.Invoke, *response.Error) {
|
||||
// result. The script is either a simple script in case of `application` trigger
|
||||
// witness invocation script in case of `verification` trigger (it pushes `verify`
|
||||
// arguments on stack before verification). In case of contract verification
|
||||
// contractScriptHash should be specified.
|
||||
func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction) (*result.Invoke, *response.Error) {
|
||||
// When transferring funds, script execution does no auto GAS claim,
|
||||
// because it depends on persisting tx height.
|
||||
// This is why we provide block here.
|
||||
|
@ -1197,7 +1204,27 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, tx *transaction.Tr
|
|||
|
||||
vm := s.chain.GetTestVM(t, tx, b)
|
||||
vm.GasLimit = int64(s.config.MaxGasInvoke)
|
||||
vm.LoadScriptWithFlags(script, callflag.All)
|
||||
if t == trigger.Verification {
|
||||
// We need this special case because witnesses verification is not the simple System.Contract.Call,
|
||||
// and we need to define exactly the amount of gas consumed for a contract witness verification.
|
||||
gasPolicy := s.chain.GetPolicer().GetMaxVerificationGAS()
|
||||
if vm.GasLimit > gasPolicy {
|
||||
vm.GasLimit = gasPolicy
|
||||
}
|
||||
|
||||
err := s.chain.InitVerificationVM(vm, func(h util.Uint160) (*state.Contract, error) {
|
||||
res := s.chain.GetContractState(h)
|
||||
if res == nil {
|
||||
return nil, fmt.Errorf("unknown contract: %s", h.StringBE())
|
||||
}
|
||||
return res, nil
|
||||
}, contractScriptHash, &transaction.Witness{InvocationScript: script, VerificationScript: []byte{}})
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't prepare verification VM", err)
|
||||
}
|
||||
} else {
|
||||
vm.LoadScriptWithFlags(script, callflag.All)
|
||||
}
|
||||
err = vm.Run()
|
||||
var faultException string
|
||||
if err != nil {
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -60,11 +61,13 @@ type rpcTestCase struct {
|
|||
}
|
||||
|
||||
const testContractHash = "1e1c3024bd955ff3baf7cb92e3b7608c7bb3712b"
|
||||
const deploymentTxHash = "b67eb38f1e6805d60b68cbec9cf8db7e4a71313b6f53ff8545c578b51ce874c5"
|
||||
const deploymentTxHash = "298092d1619585b2fcd3045c8e5a749ddbe14a6fe41569a69b50f47b812112d9"
|
||||
const genesisBlockHash = "5b60644c6c6f58faca72c70689d7ed1f40c2e795772bd0de5a88e983ad55080c"
|
||||
|
||||
const verifyContractHash = "5bb4bac40e961e334ba7bd36d2496010f67e246e"
|
||||
const verifyContractAVM = "VwMAQS1RCDAhcAwUVVQtU+0PVUb61E1umZEoZwIvzl7bMHFoE87bKGnbKJdA"
|
||||
const verifyWithArgsContractHash = "59b08e81dcf94f6ddbef5c2d84a4c1a098b9a984"
|
||||
const verifyWithArgsContractAVM = "VwIDeAwLZ29vZF9zdHJpbmeXJA15FSgJehHbIJciBRHbIHBoQA=="
|
||||
const invokescriptContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGhB+CfsjCGqJgQRQAwUDQ8DAgkAAgEDBwMEBQIBAA4GDAnbMHFpQfgn7IwhqiYEEkATQA=="
|
||||
|
||||
var rpcTestCases = map[string][]rpcTestCase{
|
||||
|
@ -651,7 +654,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
require.True(t, ok)
|
||||
expected := result.UnclaimedGas{
|
||||
Address: testchain.MultisigScriptHash(),
|
||||
Unclaimed: *big.NewInt(4500),
|
||||
Unclaimed: *big.NewInt(5000),
|
||||
}
|
||||
assert.Equal(t, expected, *actual)
|
||||
},
|
||||
|
@ -814,7 +817,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.NotNil(t, res.Script)
|
||||
assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address()))
|
||||
|
@ -827,25 +830,74 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.NotNil(t, res.Script)
|
||||
assert.Nil(t, res.Script)
|
||||
assert.Equal(t, "HALT", res.State, res.FaultException)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, false, res.Stack[0].Value().(bool))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, with scripts",
|
||||
name: "positive, no arguments",
|
||||
params: fmt.Sprintf(`["%s"]`, verifyContractHash),
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Nil(t, res.Script)
|
||||
assert.Equal(t, "HALT", res.State, res.FaultException)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, false, res.Stack[0].Value().(bool))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, with signers and scripts",
|
||||
params: fmt.Sprintf(`["%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.NotNil(t, res.Script)
|
||||
assert.Nil(t, res.Script)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, true, res.Stack[0].Value().(bool))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, with arguments, result=true",
|
||||
params: fmt.Sprintf(`["%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": "false"}]]`, verifyWithArgsContractHash),
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
expectedInvScript := io.NewBufBinWriter()
|
||||
emit.Int(expectedInvScript.BinWriter, 0)
|
||||
emit.Int(expectedInvScript.BinWriter, int64(4))
|
||||
emit.String(expectedInvScript.BinWriter, "good_string")
|
||||
require.NoError(t, expectedInvScript.Err)
|
||||
assert.Equal(t, expectedInvScript.Bytes(), res.Script) // witness invocation script (pushes args of `verify` on stack)
|
||||
assert.Equal(t, "HALT", res.State, res.FaultException)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, true, res.Stack[0].Value().(bool))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, with arguments, result=false",
|
||||
params: fmt.Sprintf(`["%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": "false"}]]`, verifyWithArgsContractHash),
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
expectedInvScript := io.NewBufBinWriter()
|
||||
emit.Int(expectedInvScript.BinWriter, 0)
|
||||
emit.Int(expectedInvScript.BinWriter, int64(4))
|
||||
emit.String(expectedInvScript.BinWriter, "invalid_string")
|
||||
require.NoError(t, expectedInvScript.Err)
|
||||
assert.Equal(t, expectedInvScript.Bytes(), res.Script)
|
||||
assert.Equal(t, "HALT", res.State, res.FaultException)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
assert.Equal(t, false, res.Stack[0].Value().(bool))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown contract",
|
||||
params: fmt.Sprintf(`["%s", []]`, util.Uint160{}.String()),
|
||||
|
@ -1369,7 +1421,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
||||
|
||||
assert.Equal(t, *block.Transactions[0], actual.Transaction)
|
||||
assert.Equal(t, 10, actual.Confirmations)
|
||||
assert.Equal(t, 11, actual.Confirmations)
|
||||
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
||||
})
|
||||
|
||||
|
@ -1487,12 +1539,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoError(t, json.Unmarshal(res, actual))
|
||||
checkNep17TransfersAux(t, e, actual, sent, rcvd)
|
||||
}
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{5, 6, 7, 8}, []int{1, 2}) })
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{6, 7, 8, 9}, []int{1, 2}) })
|
||||
t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{2, 3}, []int{0}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{5}, []int{1}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{4, 5}, []int{1}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{6, 7}, []int{2}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{3, 4}, []int{0}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{6}, []int{1}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{5, 6}, []int{1}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{7, 8}, []int{2}) })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1597,8 +1649,8 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
|||
},
|
||||
{
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Amount: "78994302340",
|
||||
LastUpdated: 8,
|
||||
Amount: "68992647820",
|
||||
LastUpdated: 10,
|
||||
}},
|
||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||
}
|
||||
|
@ -1607,7 +1659,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
|||
}
|
||||
|
||||
func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) {
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []int{0, 1, 2, 3, 4, 5, 6})
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, []int{0, 1, 2, 3, 4, 5, 6})
|
||||
}
|
||||
|
||||
func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) {
|
||||
|
@ -1616,12 +1668,17 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockDeploy3, err := e.chain.GetBlock(e.chain.GetHeaderHash(10)) // deploy verification_with_args_contract.go
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockDeploy3.Transactions))
|
||||
txDeploy3 := blockDeploy3.Transactions[0]
|
||||
|
||||
blockDepositGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(8))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockDepositGAS.Transactions))
|
||||
txDepositGAS := blockDepositGAS.Transactions[0]
|
||||
|
||||
blockDeploy2, err := e.chain.GetBlock(e.chain.GetHeaderHash(7))
|
||||
blockDeploy2, err := e.chain.GetBlock(e.chain.GetHeaderHash(7)) // deploy verification_contract.go
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockDeploy2.Transactions))
|
||||
txDeploy2 := blockDeploy2.Transactions[0]
|
||||
|
@ -1669,6 +1726,14 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
// duplicate the Server method.
|
||||
expected := result.NEP17Transfers{
|
||||
Sent: []result.NEP17Transfer{
|
||||
{
|
||||
Timestamp: blockDeploy3.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: "", // burn
|
||||
Amount: big.NewInt(txDeploy3.SystemFee + txDeploy3.NetworkFee).String(),
|
||||
Index: 10,
|
||||
TxHash: blockDeploy3.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockDepositGAS.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
7
pkg/rpc/server/testdata/verification_with_args_contract.go
vendored
Normal file
7
pkg/rpc/server/testdata/verification_with_args_contract.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package testdata
|
||||
|
||||
// Verify is a verification contract method which takes several arguments.
|
||||
func Verify(argString string, argInt int, argBool bool) bool {
|
||||
isOK := argString == "good_string" || argInt == 5 || argBool == true
|
||||
return isOK
|
||||
}
|
Loading…
Reference in a new issue