From 31cf71fb62f391537125e103b1ec535302590ef2 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 13 Nov 2020 12:28:31 +0300 Subject: [PATCH 1/3] rpc: use default Uint160 marshaller for result.NEP5Balance.Asset To match C# behaviour we should marshal asset hash with `0x` prefix. --- pkg/rpc/response/result/nep5.go | 35 --------------------------------- 1 file changed, 35 deletions(-) diff --git a/pkg/rpc/response/result/nep5.go b/pkg/rpc/response/result/nep5.go index fea5fa07a..63e995f25 100644 --- a/pkg/rpc/response/result/nep5.go +++ b/pkg/rpc/response/result/nep5.go @@ -1,8 +1,6 @@ package result import ( - "encoding/json" - "github.com/nspcc-dev/neo-go/pkg/util" ) @@ -19,13 +17,6 @@ type NEP5Balance struct { LastUpdated uint32 `json:"lastupdatedblock"` } -// nep5Balance is an auxiliary struct for proper Asset marshaling. -type nep5Balance struct { - Asset string `json:"assethash"` - Amount string `json:"amount"` - LastUpdated uint32 `json:"lastupdatedblock"` -} - // NEP5Transfers is a result for the getnep5transfers RPC. type NEP5Transfers struct { Sent []NEP5Transfer `json:"sent"` @@ -43,29 +34,3 @@ type NEP5Transfer struct { NotifyIndex uint32 `json:"transfernotifyindex"` TxHash util.Uint256 `json:"txhash"` } - -// MarshalJSON implements json.Marshaler interface. -func (b *NEP5Balance) MarshalJSON() ([]byte, error) { - s := &nep5Balance{ - Asset: b.Asset.StringLE(), - Amount: b.Amount, - LastUpdated: b.LastUpdated, - } - return json.Marshal(s) -} - -// UnmarshalJSON implements json.Unmarshaler interface. -func (b *NEP5Balance) UnmarshalJSON(data []byte) error { - s := new(nep5Balance) - if err := json.Unmarshal(data, s); err != nil { - return err - } - asset, err := util.Uint160DecodeStringLE(s.Asset) - if err != nil { - return err - } - b.Amount = s.Amount - b.Asset = asset - b.LastUpdated = s.LastUpdated - return nil -} From 860d2ca7a79a9a579457b583d92bb5f25b429dd0 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 13 Nov 2020 12:43:56 +0300 Subject: [PATCH 2/3] cli: prettify `transfer` error Let the user know what's wrong when cannot find suitable token. --- cli/wallet/nep5.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index 7ad69463d..35f228712 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -445,7 +445,7 @@ func transferNEP5(ctx *cli.Context) error { fmt.Fprintln(ctx.App.ErrWriter, "Can't find matching token in the wallet. Querying RPC-node for balances.") token, err = getMatchingTokenRPC(ctx, c, from, ctx.String("token")) if err != nil { - return cli.NewExitError(err, 1) + return cli.NewExitError(fmt.Errorf("failed to get matching token: %w", err), 1) } } From 083879838c2015bbc2392d7c29eccc437609a65e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 13 Nov 2020 16:54:38 +0300 Subject: [PATCH 3/3] rpc: adjust `getrawtransaction` and `gettransactionheight` RPC call We should return verbose transaction in case if it is in the mempool from `getrawtransaction`. We also shouldn't return height from `gettransactionheight` in case if transaction is in the mempool. --- cli/executor_test.go | 3 +- pkg/core/blockchain.go | 5 ++-- pkg/rpc/response/result/tx_raw_output.go | 35 ++++++++++-------------- pkg/rpc/server/server.go | 33 +++++++++++----------- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/cli/executor_test.go b/cli/executor_test.go index a8e489552..df8eb7529 100644 --- a/cli/executor_test.go +++ b/cli/executor_test.go @@ -6,6 +6,7 @@ import ( "errors" "io" "io/ioutil" + "math" "strings" "testing" "time" @@ -123,7 +124,7 @@ func (e *executor) GetTransaction(t *testing.T, h util.Uint256) (*transaction.Tr require.Eventually(t, func() bool { var err error tx, height, err = e.Chain.GetTransaction(h) - return err == nil && height != 0 + return err == nil && height != math.MaxUint32 }, time.Second*2, time.Millisecond*100, "too long time waiting for block") return tx, height } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 82f1ed459..21cff195d 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "math" "math/big" "sort" "sync" @@ -942,10 +943,10 @@ func (bc *Blockchain) persist() error { return nil } -// GetTransaction returns a TX and its height by the given hash. +// GetTransaction returns a TX and its height by the given hash. The height is MaxUint32 if tx is in the mempool. func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) { if tx, ok := bc.memPool.TryGetValue(hash); ok { - return tx, 0, nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case. + return tx, math.MaxUint32, nil // the height is not actually defined for memPool transaction. } return bc.dao.GetTransaction(hash) } diff --git a/pkg/rpc/response/result/tx_raw_output.go b/pkg/rpc/response/result/tx_raw_output.go index 7445074da..5d1d6335a 100644 --- a/pkg/rpc/response/result/tx_raw_output.go +++ b/pkg/rpc/response/result/tx_raw_output.go @@ -23,32 +23,31 @@ type TransactionMetadata struct { Blockhash util.Uint256 `json:"blockhash,omitempty"` Confirmations int `json:"confirmations,omitempty"` Timestamp uint64 `json:"blocktime,omitempty"` - VMState string `json:"vmstate"` + VMState string `json:"vmstate,omitempty"` } // NewTransactionOutputRaw returns a new ransactionOutputRaw object. func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, appExecResult *state.AppExecResult, chain blockchainer.Blockchainer) TransactionOutputRaw { + result := TransactionOutputRaw{ + Transaction: *tx, + } + if header == nil { + return result + } // confirmations formula confirmations := int(chain.BlockHeight() - header.Base.Index + 1) - return TransactionOutputRaw{ - Transaction: *tx, - TransactionMetadata: TransactionMetadata{ - Blockhash: header.Hash(), - Confirmations: confirmations, - Timestamp: header.Timestamp, - VMState: appExecResult.VMState.String(), - }, + result.TransactionMetadata = TransactionMetadata{ + Blockhash: header.Hash(), + Confirmations: confirmations, + Timestamp: header.Timestamp, + VMState: appExecResult.VMState.String(), } + return result } // MarshalJSON implements json.Marshaler interface. func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) { - output, err := json.Marshal(TransactionMetadata{ - Blockhash: t.Blockhash, - Confirmations: t.Confirmations, - Timestamp: t.Timestamp, - VMState: t.VMState, - }) + output, err := json.Marshal(t.TransactionMetadata) if err != nil { return nil, err } @@ -76,10 +75,6 @@ func (t *TransactionOutputRaw) UnmarshalJSON(data []byte) error { if err != nil { return err } - t.Blockhash = output.Blockhash - t.Confirmations = output.Confirmations - t.Timestamp = output.Timestamp - t.VMState = output.VMState - + t.TransactionMetadata = *output return json.Unmarshal(data, &t.Transaction) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 71a22bffe..4f7f05906 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -846,33 +846,34 @@ func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) { } func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, *response.Error) { - var resultsErr *response.Error - var results interface{} - - if txHash, err := reqParams.Value(0).GetUint256(); err != nil { - resultsErr = response.ErrInvalidParams - } else if tx, height, err := s.chain.GetTransaction(txHash); err != nil { + txHash, err := reqParams.Value(0).GetUint256() + if err != nil { + return nil, response.ErrInvalidParams + } + tx, height, err := s.chain.GetTransaction(txHash) + if err != nil { err = fmt.Errorf("invalid transaction %s: %w", txHash, err) return nil, response.NewRPCError("Unknown transaction", err.Error(), err) - } else if reqParams.Value(1).GetBoolean() { + } + if reqParams.Value(1).GetBoolean() { + if height == math.MaxUint32 { + return result.NewTransactionOutputRaw(tx, nil, nil, s.chain), nil + } _header := s.chain.GetHeaderHash(int(height)) header, err := s.chain.GetHeader(_header) if err != nil { - return nil, response.NewInvalidParamsError(err.Error(), err) + return nil, response.NewRPCError("Failed to get header for the transaction", err.Error(), err) } aers, err := s.chain.GetAppExecResults(txHash, trigger.Application) if err != nil { - return nil, response.NewRPCError("Unknown transaction", err.Error(), err) + return nil, response.NewRPCError("Failed to get application log for the transaction", err.Error(), err) } if len(aers) == 0 { - return nil, response.NewRPCError("Unknown transaction", "", nil) + return nil, response.NewRPCError("Application log for the transaction is empty", "", nil) } - results = result.NewTransactionOutputRaw(tx, header, &aers[0], s.chain) - } else { - results = tx.Bytes() + return result.NewTransactionOutputRaw(tx, header, &aers[0], s.chain), nil } - - return results, resultsErr + return tx.Bytes(), nil } func (s *Server) getTransactionHeight(ps request.Params) (interface{}, *response.Error) { @@ -882,7 +883,7 @@ func (s *Server) getTransactionHeight(ps request.Params) (interface{}, *response } _, height, err := s.chain.GetTransaction(h) - if err != nil { + if err != nil || height == math.MaxUint32 { return nil, response.NewRPCError("unknown transaction", "", nil) }