From 6bdaefcfa43d400a06d738fa6a0f9da0321a0776 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 17 Aug 2020 16:49:47 +0300 Subject: [PATCH] rpc/client: use `CreateTxFromScript` where possible There is substantial overlap between `CreateTxFromScript` and `SignAndPushInvocationTx`. This commit refactors both of them to reuse common code. --- cli/wallet/validator.go | 4 ++-- pkg/rpc/client/nep5.go | 45 +++++++++++++++++------------------ pkg/rpc/client/rpc.go | 22 +---------------- pkg/rpc/server/client_test.go | 29 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 2e9a4c8be..fc03899cb 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -100,7 +100,7 @@ func handleCandidate(ctx *cli.Context, method string) error { w := io.NewBufBinWriter() emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, method, acc.PrivateKey().PublicKey().Bytes()) emit.Opcode(w.BinWriter, opcode.ASSERT) - tx, err := c.CreateTxFromScript(w.Bytes(), acc, int64(gas)) + tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas)) if err != nil { return cli.NewExitError(err, 1) } else if err = acc.SignTx(tx); err != nil { @@ -155,7 +155,7 @@ func handleVote(ctx *cli.Context) error { emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, "vote", addr.BytesBE(), pubArg) emit.Opcode(w.BinWriter, opcode.ASSERT) - tx, err := c.CreateTxFromScript(w.Bytes(), acc, int64(gas)) + tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas)) if err != nil { return cli.NewExitError(err, 1) } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 000376901..7a1f22cc5 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -134,39 +134,38 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recip recipients[i].Address, recipients[i].Amount) emit.Opcode(w.BinWriter, opcode.ASSERT) } - return c.CreateTxFromScript(w.Bytes(), acc, gas) + return c.CreateTxFromScript(w.Bytes(), acc, -1, gas) } // CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee. -func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, gas int64) (*transaction.Transaction, error) { +// If sysFee <= 0, it is determined via result of `invokescript` RPC. +func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64, + cosigners ...transaction.Signer) (*transaction.Transaction, error) { from, err := address.StringToUint160(acc.Address) if err != nil { return nil, fmt.Errorf("bad account address: %v", err) } - result, err := c.InvokeScript(script, []transaction.Signer{ - { - Account: from, - Scopes: transaction.CalledByEntry, - }, - }) - if err != nil { - return nil, fmt.Errorf("can't add system fee to transaction: %w", err) - } - tx := transaction.New(c.opts.Network, script, result.GasConsumed) - tx.Signers = []transaction.Signer{ - { - Account: from, - Scopes: transaction.CalledByEntry, - }, - } - tx.ValidUntilBlock, err = c.CalculateValidUntilBlock() - if err != nil { - return nil, fmt.Errorf("can't calculate validUntilBlock: %w", err) + + signers := getSigners(from, cosigners) + if sysFee < 0 { + result, err := c.InvokeScript(script, signers) + if err != nil { + return nil, fmt.Errorf("can't add system fee to transaction: %w", err) + } + sysFee = result.GasConsumed } - err = c.AddNetworkFee(tx, gas, acc) + tx := transaction.New(c.opts.Network, script, sysFee) + tx.Signers = signers + + tx.ValidUntilBlock, err = c.CalculateValidUntilBlock() if err != nil { - return nil, fmt.Errorf("can't add network fee to transaction: %w", err) + return nil, fmt.Errorf("failed to add validUntilBlock to transaction: %w", err) + } + + err = c.AddNetworkFee(tx, netFee, acc) + if err != nil { + return nil, fmt.Errorf("failed to add network fee: %w", err) } return tx, nil diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index 32bdbceb6..f45363f6e 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -9,7 +9,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" - "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" @@ -410,26 +409,7 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys var txHash util.Uint256 var err error - tx := transaction.New(c.opts.Network, script, sysfee) - tx.SystemFee = sysfee - - addr, err := address.StringToUint160(acc.Address) - if err != nil { - return txHash, fmt.Errorf("failed to get address: %w", err) - } - tx.Signers = getSigners(addr, cosigners) - - validUntilBlock, err := c.CalculateValidUntilBlock() - if err != nil { - return txHash, fmt.Errorf("failed to add validUntilBlock to transaction: %w", err) - } - tx.ValidUntilBlock = validUntilBlock - - err = c.AddNetworkFee(tx, int64(netfee), acc) - if err != nil { - return txHash, fmt.Errorf("failed to add network fee: %w", err) - } - + tx, err := c.CreateTxFromScript(script, acc, sysfee, int64(netfee), cosigners...) if err = acc.SignTx(tx); err != nil { return txHash, fmt.Errorf("failed to sign tx: %w", err) } diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index 22374d282..059a3c7b0 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -169,3 +169,32 @@ func TestPing(t *testing.T) { httpSrv.Close() require.Error(t, c.Ping()) } + +func TestCreateTxFromScript(t *testing.T) { + chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) + defer chain.Close() + defer rpcSrv.Shutdown() + + c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) + require.NoError(t, err) + + priv := testchain.PrivateKey(0) + acc, err := wallet.NewAccountFromWIF(priv.WIF()) + require.NoError(t, err) + t.Run("NoSystemFee", func(t *testing.T) { + tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10) + require.NoError(t, err) + require.True(t, tx.ValidUntilBlock > chain.BlockHeight()) + require.EqualValues(t, 30, tx.SystemFee) // PUSH1 + require.True(t, len(tx.Signers) == 1) + require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account) + }) + t.Run("ProvideSystemFee", func(t *testing.T) { + tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, 123, 10) + require.NoError(t, err) + require.True(t, tx.ValidUntilBlock > chain.BlockHeight()) + require.EqualValues(t, 123, tx.SystemFee) + require.True(t, len(tx.Signers) == 1) + require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account) + }) +}