From 19a2e3bb91d575f27f6fd3f74509464cecc7ed98 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 22 Sep 2020 12:03:19 +0300 Subject: [PATCH 1/2] rpc: add transaction field to InvokeResult --- pkg/rpc/client/rpc_test.go | 47 ++++++++++++++++++------------- pkg/rpc/response/result/invoke.go | 35 ++++++++++++++++++++++- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 01b907994..1d5669ecd 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -347,7 +347,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ invoke: func(c *Client) (interface{}, error) { return c.GetFeePerByte() }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0d676574466565506572427974650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"1000"}]}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0d676574466565506572427974650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`, result: func(c *Client) interface{} { return int64(1000) }, @@ -359,7 +359,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ invoke: func(c *Client) (interface{}, error) { return c.GetMaxTransactionsPerBlock() }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c1a6765744d61785472616e73616374696f6e73506572426c6f636b0c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"512"}]}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c1a6765744d61785472616e73616374696f6e73506572426c6f636b0c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"512"}],"tx":null}}`, result: func(c *Client) interface{} { return int64(512) }, @@ -371,7 +371,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ invoke: func(c *Client) (interface{}, error) { return c.GetMaxBlockSize() }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0f6765744d6178426c6f636b53697a650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"262144"}]}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0f6765744d6178426c6f636b53697a650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`, result: func(c *Client) interface{} { return int64(262144) }, @@ -383,7 +383,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ invoke: func(c *Client) (interface{}, error) { return c.GetBlockedAccounts() }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c12676574426c6f636b65644163636f756e74730c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Array","value":[]}]}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c12676574426c6f636b65644163636f756e74730c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Array","value":[]}],"tx":null}}`, result: func(c *Client) interface{} { return native.BlockedAccounts{} }, @@ -663,18 +663,22 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Account: util.Uint160{1, 2, 3}, }}) }, - serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"}}`, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"01e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b5238","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"}}`, result: func(c *Client) interface{} { + return &result.Invoke{} + }, + check: func(t *testing.T, c *Client, uns interface{}) { + res, ok := uns.(*result.Invoke) + require.True(t, ok) bytes, err := hex.DecodeString("262bec084432") if err != nil { panic(err) } - return &result.Invoke{ - State: "HALT", - GasConsumed: 31100000, - Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf", - Stack: []stackitem.Item{stackitem.NewByteArray(bytes)}, - } + assert.Equal(t, "HALT", res.State) + assert.Equal(t, int64(31100000), res.GasConsumed) + assert.Equal(t, "01e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b5238", res.Script) + assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack) + assert.NotNil(t, res.Transaction) }, }, { @@ -697,19 +701,22 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Account: util.Uint160{1, 2, 3}, }}) }, - serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"FAULT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000","exception":"gas limit exceeded"}}`, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"FAULT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78","exception":"gas limit exceeded"}}`, result: func(c *Client) interface{} { + return &result.Invoke{} + }, + check: func(t *testing.T, c *Client, uns interface{}) { + res, ok := uns.(*result.Invoke) + require.True(t, ok) bytes, err := hex.DecodeString("262bec084432") if err != nil { panic(err) } - return &result.Invoke{ - State: "FAULT", - GasConsumed: 31100000, - Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf", - Stack: []stackitem.Item{stackitem.NewByteArray(bytes)}, - FaultException: "gas limit exceeded", - } + assert.Equal(t, "FAULT", res.State) + assert.Equal(t, int64(31100000), res.GasConsumed) + assert.Equal(t, "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf", res.Script) + assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack) + assert.NotNil(t, res.Transaction) }, }, }, @@ -725,7 +732,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Account: util.Uint160{1, 2, 3}, }}) }, - serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteString","value":"TkVQNSBHQVM="}],"tx":"d1011b00046e616d656724058e5e1b6008847cd662728549088a9ee82191000000000000000000000000"}}`, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteString","value":"TkVQNSBHQVM="}],"tx":null}}`, result: func(c *Client) interface{} { bytes, err := hex.DecodeString("4e45503520474153") if err != nil { diff --git a/pkg/rpc/response/result/invoke.go b/pkg/rpc/response/result/invoke.go index 17439ed95..52c9d937d 100644 --- a/pkg/rpc/response/result/invoke.go +++ b/pkg/rpc/response/result/invoke.go @@ -1,19 +1,28 @@ package result import ( + "encoding/hex" "encoding/json" + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // Invoke represents code invocation result and is used by several RPC calls -// that invoke functions, scripts and generic bytecode. +// that invoke functions, scripts and generic bytecode. Transaction is +// represented in raw serialized format, use transaction.NewTransactionFromBytes +// or GetTransaction method to deserialize it. type Invoke struct { State string GasConsumed int64 Script string Stack []stackitem.Item FaultException string + // Transaction represents transaction bytes. Use GetTransaction method to decode it. + Transaction []byte } type invokeAux struct { @@ -22,6 +31,7 @@ type invokeAux struct { Script string `json:"script"` Stack json.RawMessage `json:"stack"` FaultException string `json:"exception,omitempty"` + Transaction string `json:"tx,omitempty"` } // MarshalJSON implements json.Marshaler. @@ -44,12 +54,20 @@ func (r Invoke) MarshalJSON() ([]byte, error) { return nil, err } } + + var tx string + if r.Transaction != nil { + st := hex.EncodeToString(r.Transaction) + tx = st + } + return json.Marshal(&invokeAux{ GasConsumed: r.GasConsumed, Script: r.Script, State: r.State, Stack: st, FaultException: r.FaultException, + Transaction: tx, }) } @@ -72,9 +90,24 @@ func (r *Invoke) UnmarshalJSON(data []byte) error { r.Stack = st } } + if aux.Transaction != "" { + bytes, err := hex.DecodeString(aux.Transaction) + if err != nil { + return fmt.Errorf("failed to decode transaction bytes from hex: %w", err) + } + r.Transaction = bytes + } r.GasConsumed = aux.GasConsumed r.Script = aux.Script r.State = aux.State r.FaultException = aux.FaultException return nil } + +// GetTransaction returns decoded transaction from Invoke.Transaction bytes. +func (r *Invoke) GetTransaction(magic netmode.Magic) (*transaction.Transaction, error) { + if r.Transaction == nil { + return nil, errors.New("empty transaction") + } + return transaction.NewTransactionFromBytes(magic, r.Transaction) +} From ceb7ea97374dda902be38e4c87cfbea66ce1e6af Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 21 Sep 2020 17:25:43 +0300 Subject: [PATCH 2/2] rpc: allow both base58 and hex for Uint160 RPC server parameters `getunclaimedgas` RPC-call allows to use both base58 and hex representations for uint160. --- pkg/rpc/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 4be7e6f44..5d9f18bac 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -894,7 +894,7 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *respons // getUnclaimedGas returns unclaimed GAS amount of the specified address. func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Error) { - u, err := ps.ValueWithType(0, request.StringT).GetUint160FromAddress() + u, err := ps.ValueWithType(0, request.StringT).GetUint160FromAddressOrHex() if err != nil { return nil, response.ErrInvalidParams }