From 4e0eb8b24d31b83f6b6b0f260bbdb5ca830dadb3 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Fri, 19 Jan 2024 14:06:18 +0800 Subject: [PATCH] internal, eth, core: return RPC error --- core/blockchain_reader.go | 2 +- eth/tracers/api.go | 2 +- eth/tracers/api_test.go | 4 +- internal/ethapi/api.go | 57 ++++++----------- internal/ethapi/api_test.go | 4 +- internal/ethapi/errors.go | 78 ++++++++++++++++++++++++ internal/ethapi/transaction_args_test.go | 4 +- 7 files changed, 103 insertions(+), 48 deletions(-) create mode 100644 internal/ethapi/errors.go diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index b761876b3b..7e4eb8daa0 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -279,7 +279,7 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo // The transaction indexing is not finished yet, returning an // error to explicitly indicate it. if !progress.done() { - return nil, nil, errors.New("transaction is still indexing") + return nil, nil, errors.New("transaction indexing still in progress") } // The transaction is already indexed, the transaction is either // not existent or not in the range of index, returning null. diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 67b7d21a40..4d4428f6c6 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -828,7 +828,7 @@ func containsTx(block *types.Block, hash common.Hash) bool { func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) if err != nil { - return nil, err + return nil, ethapi.NewTxIndexingError() } // Only mined txes are supported if !found { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 49c3ebb67d..c72e5eaaac 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -113,9 +113,9 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) return b.chain.GetBlockByNumber(uint64(number)), nil } -func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { +func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) - return tx, hash, blockNumber, index, nil + return true, tx, hash, blockNumber, index, nil } func (b *testBackend) RPCGasCap() uint64 { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6d33f2053d..f2ba2b4aac 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -27,7 +27,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/common" @@ -1133,37 +1132,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash return doCall(ctx, b, args, state, header, overrides, blockOverrides, timeout, globalGasCap) } -func newRevertError(revert []byte) *revertError { - err := vm.ErrExecutionReverted - - reason, errUnpack := abi.UnpackRevert(revert) - if errUnpack == nil { - err = fmt.Errorf("%w: %v", vm.ErrExecutionReverted, reason) - } - return &revertError{ - error: err, - reason: hexutil.Encode(revert), - } -} - -// revertError is an API error that encompasses an EVM revertal with JSON error -// code and a binary data blob. -type revertError struct { - error - reason string // revert reason hex encoded -} - -// ErrorCode returns the JSON error code for a revertal. -// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal -func (e *revertError) ErrorCode() int { - return 3 -} - -// ErrorData returns the hex encoded revert reason. -func (e *revertError) ErrorData() interface{} { - return e.reason -} - // Call executes the given transaction on the state for the given block number. // // Additionally, the caller can specify a batch of contract for fields overriding. @@ -1658,7 +1626,10 @@ func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.H if tx := s.b.GetPoolTransaction(hash); tx != nil { return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil } - return nil, err + if err == nil { + return nil, nil + } + return nil, NewTxIndexingError() } header, err := s.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1672,11 +1643,14 @@ func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash commo // Retrieve a finalized transaction, or a pooled otherwise found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) if !found { - if tx = s.b.GetPoolTransaction(hash); tx == nil { - return nil, err + if tx = s.b.GetPoolTransaction(hash); tx != nil { + return tx.MarshalBinary() } + if err == nil { + return nil, nil + } + return nil, NewTxIndexingError() } - // Serialize to RLP and return return tx.MarshalBinary() } @@ -1684,7 +1658,7 @@ func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash commo func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) if err != nil { - return nil, err // transaction is not fully indexed + return nil, NewTxIndexingError() // transaction is not fully indexed } if !found { return nil, nil // transaction is not existent or reachable @@ -2079,10 +2053,13 @@ func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hex // Retrieve a finalized transaction, or a pooled otherwise found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) if !found { - if tx = s.b.GetPoolTransaction(hash); tx == nil { - // Transaction not found anywhere, abort - return nil, err + if tx = s.b.GetPoolTransaction(hash); tx != nil { + return tx.MarshalBinary() } + if err == nil { + return nil, nil + } + return nil, NewTxIndexingError() } return tx.MarshalBinary() } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index fd68650193..623aa1fe42 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -583,9 +583,9 @@ func (b testBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) even func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { panic("implement me") } -func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { +func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash) - return tx, blockHash, blockNumber, index, nil + return true, tx, blockHash, blockNumber, index, nil } func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") } func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") } diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go new file mode 100644 index 0000000000..6171cc4d6b --- /dev/null +++ b/internal/ethapi/errors.go @@ -0,0 +1,78 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" +) + +// revertError is an API error that encompasses an EVM revert with JSON error +// code and a binary data blob. +type revertError struct { + error + reason string // revert reason hex encoded +} + +// ErrorCode returns the JSON error code for a revert. +// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +func (e *revertError) ErrorCode() int { + return 3 +} + +// ErrorData returns the hex encoded revert reason. +func (e *revertError) ErrorData() interface{} { + return e.reason +} + +// newRevertError creates a revertError instance with the provided revert data. +func newRevertError(revert []byte) *revertError { + err := vm.ErrExecutionReverted + + reason, errUnpack := abi.UnpackRevert(revert) + if errUnpack == nil { + err = fmt.Errorf("%w: %v", vm.ErrExecutionReverted, reason) + } + return &revertError{ + error: err, + reason: hexutil.Encode(revert), + } +} + +// TxIndexingError is an API error that indicates the transaction indexing is not +// fully finished yet with JSON error code and a binary data blob. +type TxIndexingError struct{} + +// NewTxIndexingError creates a TxIndexingError instance. +func NewTxIndexingError() *TxIndexingError { return &TxIndexingError{} } + +// Error implement error interface, returning the error message. +func (e *TxIndexingError) Error() string { + return "transaction indexing is in progress" +} + +// ErrorCode returns the JSON error code for a revert. +// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +func (e *TxIndexingError) ErrorCode() int { + return 3 // TODO tbd +} + +// ErrorData returns the hex encoded revert reason. +func (e *TxIndexingError) ErrorData() interface{} { return "transaction indexing is in progress" } diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 8651da4020..f0fdb6d8ee 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -379,8 +379,8 @@ func (b *backendMock) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) eve return nil } func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil } -func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { - return nil, [32]byte{}, 0, 0, nil +func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { + return false, nil, [32]byte{}, 0, 0, nil } func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil } func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }