From 9c7168e4e80b186be5a7e765f2dd2f509ee68e02 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 24 Aug 2020 14:00:05 +0300 Subject: [PATCH] rpc/client: allow to use contract accounts in AddNetworkFee --- pkg/core/helper_test.go | 52 +++++++++------ pkg/rpc/client/rpc.go | 20 +++--- pkg/rpc/server/client_test.go | 60 ++++++++++++++++++ pkg/rpc/server/server_test.go | 15 +++-- pkg/rpc/server/testdata/testblocks.acc | Bin 6681 -> 7725 bytes .../server/testdata/verification_contract.go | 15 +++++ 6 files changed, 129 insertions(+), 33 deletions(-) create mode 100644 pkg/rpc/server/testdata/verification_contract.go diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 36a2f36c5..bb0032d14 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -226,24 +226,7 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, err) // Push some contract into the chain. - name := prefix + "test_contract.go" - c, err := ioutil.ReadFile(name) - require.NoError(t, err) - avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c)) - require.NoError(t, err) - t.Logf("contractHash: %s", hash.Hash160(avm).StringLE()) - - script := io.NewBufBinWriter() - m, err := di.ConvertToManifest(smartcontract.HasStorage, nil) - require.NoError(t, err) - bs, err := m.MarshalJSON() - require.NoError(t, err) - emit.Bytes(script.BinWriter, bs) - emit.Bytes(script.BinWriter, avm) - emit.Syscall(script.BinWriter, interopnames.SystemContractCreate) - txScript := script.Bytes() - - txDeploy := transaction.New(testchain.Network(), txScript, 100*native.GASFactor) + txDeploy, avm := newDeployTx(t, prefix+"test_contract.go") txDeploy.Nonce = getNextNonce() txDeploy.ValidUntilBlock = validUntilBlock txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}} @@ -255,7 +238,7 @@ func TestCreateBasicChain(t *testing.T) { t.Logf("Block2 hash: %s", b.Hash().StringLE()) // Now invoke this contract. - script = io.NewBufBinWriter() + script := io.NewBufBinWriter() emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "putValue", "testkey", "testvalue") txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor) @@ -330,6 +313,16 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, bc.AddBlock(b)) t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE()) + // Push verification contract into the chain. + txDeploy2, _ := newDeployTx(t, prefix+"verification_contract.go") + txDeploy2.Nonce = getNextNonce() + txDeploy2.ValidUntilBlock = validUntilBlock + txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}} + require.NoError(t, addNetworkFee(bc, txDeploy2, acc0)) + require.NoError(t, acc0.SignTx(txDeploy2)) + b = bc.newBlock(txDeploy2) + require.NoError(t, bc.AddBlock(b)) + if saveChain { outStream, err := os.Create(prefix + "testblocks.acc") require.NoError(t, err) @@ -378,6 +371,27 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans return transaction.New(testchain.Network(), script, 10000000) } +func newDeployTx(t *testing.T, name string) (*transaction.Transaction, []byte) { + c, err := ioutil.ReadFile(name) + require.NoError(t, err) + avm, di, err := compiler.CompileWithDebugInfo(name, bytes.NewReader(c)) + require.NoError(t, err) + t.Logf("contractHash (%s): %s", name, hash.Hash160(avm).StringLE()) + t.Logf("contractScript: %x", avm) + + script := io.NewBufBinWriter() + m, err := di.ConvertToManifest(smartcontract.HasStorage, nil) + require.NoError(t, err) + bs, err := m.MarshalJSON() + require.NoError(t, err) + emit.Bytes(script.BinWriter, bs) + emit.Bytes(script.BinWriter, avm) + emit.Syscall(script.BinWriter, interopnames.SystemContractCreate) + txScript := script.Bytes() + + return transaction.New(testchain.Network(), txScript, 100*native.GASFactor), avm +} + func addSigners(txs ...*transaction.Transaction) { for _, tx := range txs { tx.Signers = []transaction.Signer{{ diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index f45363f6e..fb6146439 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" ) @@ -499,16 +500,19 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs } size := io.GetVarSize(tx) for i, cosigner := range tx.Signers { - if accs[i].Contract.Script == nil { - contract, err := c.GetContractState(cosigner.Account) - if err == nil { - if contract == nil { - continue + if accs[i].Contract.Deployed { + res, err := c.InvokeFunction(cosigner.Account, manifest.MethodVerify, []smartcontract.Parameter{}, tx.Signers) + if err == nil && res.State == "HALT" && len(res.Stack) == 1 { + r, err := topIntFromStack(res.Stack) + if err != nil || r == 0 { + return core.ErrVerificationFailed } - netFee, sizeDelta := core.CalculateNetworkFee(contract.Script) - tx.NetworkFee += netFee - size += sizeDelta + } else { + return core.ErrVerificationFailed } + tx.NetworkFee += res.GasConsumed + size += io.GetVarSize([]byte{}) * 2 // both scripts are empty + continue } netFee, sizeDelta := core.CalculateNetworkFee(accs[i].Contract.Script) tx.NetworkFee += netFee diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index 91f70fa19..9ad40480d 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -2,6 +2,7 @@ package server import ( "context" + "encoding/hex" "testing" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -132,6 +133,65 @@ func TestAddNetworkFee(t *testing.T) { cFeeM, _ := core.CalculateNetworkFee(accs[1].Contract.Script) require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee) }) + t.Run("Contract", func(t *testing.T) { + tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) + priv := testchain.PrivateKeyByID(0) + acc1, err := wallet.NewAccountFromWIF(priv.WIF()) + require.NoError(t, err) + acc1.Contract.Deployed = true + acc1.Contract.Script, _ = hex.DecodeString(verifyContractAVM) + h, _ := util.Uint160DecodeStringLE(verifyContractHash) + tx.ValidUntilBlock = chain.BlockHeight() + 10 + + t.Run("Valid", func(t *testing.T) { + acc0, err := wallet.NewAccountFromWIF(priv.WIF()) + require.NoError(t, err) + tx.Signers = []transaction.Signer{ + { + Account: acc0.PrivateKey().GetScriptHash(), + Scopes: transaction.CalledByEntry, + }, + { + Account: h, + Scopes: transaction.Global, + }, + } + 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)) + }) + t.Run("Invalid", func(t *testing.T) { + acc0, err := wallet.NewAccount() + require.NoError(t, err) + tx.Signers = []transaction.Signer{ + { + Account: acc0.PrivateKey().GetScriptHash(), + Scopes: transaction.CalledByEntry, + }, + { + Account: h, + Scopes: transaction.Global, + }, + } + require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) + }) + t.Run("InvalidContract", func(t *testing.T) { + acc0, err := wallet.NewAccountFromWIF(priv.WIF()) + require.NoError(t, err) + tx.Signers = []transaction.Signer{ + { + Account: acc0.PrivateKey().GetScriptHash(), + Scopes: transaction.CalledByEntry, + }, + { + Account: util.Uint160{}, + Scopes: transaction.Global, + }, + } + require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) + }) + }) } func TestSignAndPushInvocationTx(t *testing.T) { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index eb7cdd273..a77fcb9cf 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -51,8 +51,11 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "93c4983afe01a75f74c1e56011bd630e9d8cc755" -const deploymentTxHash = "583cf0e49d69d8854869efc3e97ad741061da478292a7280580789351a39a1ac" +const testContractHash = "4546ec6fcdaa1c3ccdb048526b78624b457b60a4" +const deploymentTxHash = "17be1bbb0fdecae18cd4c6a2db19311f47bd540371e2ea479a46b349a66aa0b3" + +const verifyContractHash = "47ef649f9a77cad161ddaa28b39c7e450e5429e7" +const verifyContractAVM = "560340570300412d510830db4121700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { @@ -438,7 +441,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) expected := result.UnclaimedGas{ Address: testchain.MultisigScriptHash(), - Unclaimed: *big.NewInt(36000), + Unclaimed: *big.NewInt(42000), } assert.Equal(t, expected, *actual) }, @@ -814,7 +817,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, 8, actual.Confirmations) + assert.Equal(t, 9, actual.Confirmations) assert.Equal(t, TXHash, actual.Transaction.Hash()) }) @@ -999,8 +1002,8 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "915.61059740", - LastUpdated: 6, + Amount: "815.59478530", + LastUpdated: 7, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), } diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index baacf2786d048814a60ab0e9c4c5e04db2849564..decc2c137cf5a26a76a83817be07e93e54d91c3f 100644 GIT binary patch delta 2947 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 delta 2443 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