diff --git a/jsonrpc/eth_blockchain_test.go b/jsonrpc/eth_blockchain_test.go index e1162f98bc..ad1f1c2e0a 100644 --- a/jsonrpc/eth_blockchain_test.go +++ b/jsonrpc/eth_blockchain_test.go @@ -83,6 +83,25 @@ func TestEth_Block_BlockNumber(t *testing.T) { assert.Equal(t, argUintPtr(10), num) } +func TestEth_Block_GetBlockTransactionCountByHash(t *testing.T) { + store := &mockBlockStore{} + block := newTestBlock(1, hash1) + + for i := 0; i < 10; i++ { + block.Transactions = append(block.Transactions, []*types.Transaction{ + types.NewTx(&types.LegacyTx{BaseTx: &types.BaseTx{Nonce: 0, From: addr0}})}...) + } + store.add(block) + + eth := newTestEthEndpoint(store) + + res, err := eth.GetBlockTransactionCountByHash(block.Header.Hash) + + assert.NoError(t, err) + assert.NotNil(t, res, "expected to return block, but got nil") + assert.Equal(t, "0xa", res) +} + func TestEth_Block_GetBlockTransactionCountByNumber(t *testing.T) { store := &mockBlockStore{} block := newTestBlock(1, hash1) @@ -102,6 +121,58 @@ func TestEth_Block_GetBlockTransactionCountByNumber(t *testing.T) { assert.Equal(t, "0xa", res) } +func TestEth_Block_GetTransactionByBlockNumberAndIndex(t *testing.T) { + store := &mockBlockStore{} + eth := newTestEthEndpoint(store) + block := newTestBlock(1, hash1) + + for i := 0; i < 10; i++ { + txn := newTestTransaction(uint64(i), addr0) + block.Transactions = append(block.Transactions, txn) + } + store.add(block) + + testIndex := 5 + res, err := eth.GetTransactionByBlockNumberAndIndex(BlockNumber(block.Header.Number), argUint64(testIndex)) + + transaction := toTransaction( + block.Transactions[testIndex], + argUintPtr(block.Number()), + argHashPtr(block.Hash()), + &testIndex, + ) + + assert.NoError(t, err) + assert.NotNil(t, res, "expected to return transaction, but got nil") + assert.Equal(t, transaction, res) +} + +func TestEth_Block_GetTransactionByBlockHashAndIndex(t *testing.T) { + store := &mockBlockStore{} + block := newTestBlock(1, hash1) + + for i := 0; i < 10; i++ { + txn := newTestTransaction(uint64(i), addr0) + block.Transactions = append(block.Transactions, txn) + } + store.add(block) + + eth := newTestEthEndpoint(store) + testIndex := 5 + res, err := eth.GetTransactionByBlockHashAndIndex(block.Header.Hash, argUint64(testIndex)) + + transaction := toTransaction( + block.Transactions[testIndex], + argUintPtr(block.Number()), + argHashPtr(block.Hash()), + &testIndex, + ) + + assert.NoError(t, err) + assert.NotNil(t, res, "expected to return transaction, but got nil") + assert.Equal(t, transaction, res) +} + func TestEth_GetTransactionByHash(t *testing.T) { t.Parallel() diff --git a/jsonrpc/eth_endpoint.go b/jsonrpc/eth_endpoint.go index ca90e8455a..4527fe32a0 100644 --- a/jsonrpc/eth_endpoint.go +++ b/jsonrpc/eth_endpoint.go @@ -100,10 +100,6 @@ type Eth struct { priceLimit uint64 } -var ( - ErrInsufficientFunds = errors.New("insufficient funds for execution") -) - // ChainId returns the chain id of the client // //nolint:stylecheck @@ -176,6 +172,16 @@ func (e *Eth) filterExtra(block *types.Block) error { return nil } +// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. +func (e *Eth) GetBlockTransactionCountByHash(blockHash types.Hash) (interface{}, error) { + block, ok := e.store.GetBlockByHash(blockHash, true) + if !ok { + return nil, nil + } + + return *common.EncodeUint64(uint64(len(block.Transactions))), nil +} + func (e *Eth) GetBlockTransactionCountByNumber(number BlockNumber) (interface{}, error) { num, err := GetNumericBlockNumber(number, e.store) if err != nil { @@ -191,6 +197,31 @@ func (e *Eth) GetBlockTransactionCountByNumber(number BlockNumber) (interface{}, return *common.EncodeUint64(uint64(len(block.Transactions))), nil } +// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. +func (e *Eth) GetTransactionByBlockNumberAndIndex(number BlockNumber, index argUint64) (interface{}, error) { + num, err := GetNumericBlockNumber(number, e.store) + if err != nil { + return nil, err + } + + block, ok := e.store.GetBlockByNumber(num, true) + if !ok { + return nil, nil + } + + return GetTransactionByBlockAndIndex(block, index) +} + +// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. +func (e *Eth) GetTransactionByBlockHashAndIndex(blockHash types.Hash, index argUint64) (interface{}, error) { + block, ok := e.store.GetBlockByHash(blockHash, true) + if !ok { + return nil, nil + } + + return GetTransactionByBlockAndIndex(block, index) +} + // BlockNumber returns current block number func (e *Eth) BlockNumber() (interface{}, error) { h := e.store.Header() diff --git a/jsonrpc/helper.go b/jsonrpc/helper.go index 618739b548..becff911b4 100644 --- a/jsonrpc/helper.go +++ b/jsonrpc/helper.go @@ -14,6 +14,8 @@ var ( ErrNegativeBlockNumber = errors.New("invalid argument 0: block number must not be negative") ErrFailedFetchGenesis = errors.New("error fetching genesis block header") ErrNoDataInContractCreation = errors.New("contract creation without data provided") + ErrIndexOutOfRange = errors.New("the index is invalid, it is out of range of expected values") + ErrInsufficientFunds = errors.New("insufficient funds for execution") ) type latestHeaderGetter interface { @@ -43,6 +45,23 @@ func GetNumericBlockNumber(number BlockNumber, store latestHeaderGetter) (uint64 } } +// GetTransactionByBlockAndIndex returns the transaction for the given block and index. +func GetTransactionByBlockAndIndex(block *types.Block, index argUint64) (interface{}, error) { + idx := int(index) + size := len(block.Transactions) + + if size == 0 || size < idx { + return nil, ErrIndexOutOfRange + } + + return toTransaction( + block.Transactions[index], + argUintPtr(block.Number()), + argHashPtr(block.Hash()), + &idx, + ), nil +} + type headerGetter interface { Header() *types.Header GetHeaderByNumber(uint64) (*types.Header, bool)