Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented GetHeaderByNumber and GetHeaderByHash #149

Merged
merged 29 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
07ada94
implemented methods: eth_getHeaderByNumber
novosandara Mar 8, 2024
22a5ece
The following methods were created: eth_createAccessList, eth_coinbas…
novosandara Mar 10, 2024
202ae4a
fix lint
novosandara Mar 10, 2024
c3cbd86
CR fix, part 1
novosandara Mar 11, 2024
8df5959
CR fix, part 2
novosandara Mar 12, 2024
d07f2e4
lint fix
novosandara Mar 12, 2024
b0115c6
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 14, 2024
1fc6f10
created UTs for methods: eth_createAccessList, eth_getBlockReceipts, …
novosandara Mar 14, 2024
681665b
e2e test for eth_getBlockReceipts
novosandara Mar 14, 2024
297ea34
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 14, 2024
c7960fe
e2e remove
novosandara Mar 15, 2024
5847e35
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 15, 2024
775749e
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 18, 2024
ace4b58
CR fix
novosandara Mar 19, 2024
4ece04e
Remove dummy comment
Stefan-Ethernal Mar 20, 2024
29f7454
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 20, 2024
a2ce3d5
CR fix
novosandara Mar 20, 2024
8df9653
lint fix
novosandara Mar 22, 2024
e1bbc87
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 22, 2024
6f9b1d3
CR fix and new tests e2e have been added
novosandara Mar 25, 2024
fe5e7f0
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 25, 2024
685e4e0
Address comments
Stefan-Ethernal Mar 26, 2024
b95b8f4
Minor simplification
Stefan-Ethernal Mar 26, 2024
845579b
Minor changes in eth_getBlockReceipts
Stefan-Ethernal Mar 26, 2024
fb0ee29
e2e test fix and cr fix
novosandara Mar 26, 2024
469ca4f
lint fix
novosandara Mar 26, 2024
329f1b9
e2e
novosandara Mar 26, 2024
3421560
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 26, 2024
1295b73
Merge branch 'develop' into implementing_methods_eth_txpool
novosandara Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions jsonrpc/eth_blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,59 @@ func TestEth_Block_GetBlockByHash(t *testing.T) {
assert.Nil(t, res)
}

func TestEth_Block_GetHeaderByNumber(t *testing.T) {
store := &mockBlockStore{}
for i := 0; i < 10; i++ {
store.add(newTestBlock(uint64(i), hash1))
}

eth := newTestEthEndpoint(store)

cases := []struct {
description string
blockNum BlockNumber
isNotNil bool
err bool
}{
{"should be able to get the latest block number", LatestBlockNumber, true, false},
{"should be able to get the earliest block number", EarliestBlockNumber, true, false},
{"should not be able to get block with negative number", BlockNumber(-50), false, true},
{"should be able to get block with number 0", BlockNumber(0), true, false},
{"should be able to get block with number 2", BlockNumber(2), true, false},
{"should be able to get block with number greater than latest block", BlockNumber(50), false, false},
}
for _, c := range cases {
res, err := eth.GetHeadarByNumber(c.blockNum)

if c.isNotNil {
assert.NotNil(t, res, "expected to return block, but got nil")
} else {
assert.Nil(t, res, "expected to return nil, but got data")
}

if c.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
}
}

func TestEth_Block_GetHeaderByHash(t *testing.T) {
store := &mockBlockStore{}
store.add(newTestBlock(1, hash1))

eth := newTestEthEndpoint(store)

res, err := eth.GetHeadarByHash(hash1)
assert.NoError(t, err)
assert.NotNil(t, res)

res, err = eth.GetHeadarByHash(hash2)
assert.NoError(t, err)
assert.Nil(t, res)
}

func TestEth_Block_BlockNumber(t *testing.T) {
store := &mockBlockStore{}
store.add(&types.Block{
Expand Down
124 changes: 123 additions & 1 deletion jsonrpc/eth_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,41 @@ func (e *Eth) GetBlockByHash(hash types.Hash, fullTx bool) (interface{}, error)
return toBlock(block, fullTx), nil
}

// GetHeaderByNumber returns the requested canonical block header.
// * When blockNr is -1 the chain head is returned.
// * When blockNr is -2 the pending chain head is returned.
novosandara marked this conversation as resolved.
Show resolved Hide resolved
func (e *Eth) GetHeadarByNumber(number BlockNumber) (interface{}, error) {
novosandara marked this conversation as resolved.
Show resolved Hide resolved
num, err := GetNumericBlockNumber(number, e.store)
if err != nil {
return nil, err
}

block, ok := e.store.GetBlockByNumber(num, true)
novosandara marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
return nil, nil
}

if err := e.filterExtra(block); err != nil {
return nil, err
}

return toHeader(block.Header), nil
}

// GetHeaderByHash returns the requested header by hash.
func (e *Eth) GetHeadarByHash(hash types.Hash) (interface{}, error) {
novosandara marked this conversation as resolved.
Show resolved Hide resolved
block, ok := e.store.GetBlockByHash(hash, true)
novosandara marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
return nil, nil
}

if err := e.filterExtra(block); err != nil {
return nil, err
}

return toHeader(block.Header), nil
}

func (e *Eth) filterExtra(block *types.Block) error {
// we need to copy it because the store returns header from storage directly
// and not a copy, so changing it, actually changes it in storage as well
Expand All @@ -172,6 +207,37 @@ func (e *Eth) filterExtra(block *types.Block) error {
return nil
}

// CreateAccessList creates a EIP-2930 type AccessList for the given transaction.
// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state.
func (e *Eth) CreateAccessList(arg *txnArgs, filter BlockNumberOrHash) (interface{}, error) {
novosandara marked this conversation as resolved.
Show resolved Hide resolved
header, err := GetHeaderFromBlockNumberOrHash(filter, e.store)
if err != nil {
return nil, err
}

transaction, err := DecodeTxn(arg, header.Number, e.store, true)
if err != nil {
return nil, err
}

res := &accessListResult{
Accesslist: transaction.AccessList(),
GasUsed: argUint64(header.GasUsed),
}

return res, nil
}

// Returns the client coinbase address.
func (e *Eth) Coinbase() (interface{}, error) {
h := e.store.Header()
if h == nil {
return nil, ErrHeaderNotFound
}

return types.BytesToAddress(h.Miner), 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)
Expand Down Expand Up @@ -226,7 +292,7 @@ func (e *Eth) GetTransactionByBlockHashAndIndex(blockHash types.Hash, index argU
func (e *Eth) BlockNumber() (interface{}, error) {
h := e.store.Header()
if h == nil {
return nil, fmt.Errorf("header has a nil value")
return nil, ErrHeaderNotFound
}

return argUintPtr(h.Number), nil
Expand Down Expand Up @@ -375,6 +441,62 @@ func (e *Eth) GetTransactionReceipt(hash types.Hash) (interface{}, error) {
return toReceipt(raw, txn, uint64(txIndex), block.Header, logs), nil
}

// GetBlockReceipts returns all transaction receipts for a given block.
func (e *Eth) GetBlockReceipts(number BlockNumber) (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
}

blockHash := block.Hash()
if len(block.Transactions) == 0 {
e.logger.Warn(
novosandara marked this conversation as resolved.
Show resolved Hide resolved
fmt.Sprintf("No transactions found for block with hash [%s]", blockHash.String()),
)

return nil, nil
}

receipts, errR := e.store.GetReceiptsByHash(blockHash)
if errR != nil {
// block receipts not found
e.logger.Warn(
novosandara marked this conversation as resolved.
Show resolved Hide resolved
fmt.Sprintf("Receipts for block with hash [%s] not found", blockHash.String()),
)

return nil, errR
}

numberOfReceipts := len(receipts)
if numberOfReceipts == 0 {
// Receipts not written yet on the db
e.logger.Warn(
novosandara marked this conversation as resolved.
Show resolved Hide resolved
fmt.Sprintf("No receipts found for block with hash [%s]", blockHash.String()),
)

return nil, nil
}

resReceipts := make([]*receipt, numberOfReceipts)
logIndex := 0

for i, transaction := range block.Transactions {
raw := receipts[i]
// accumulate receipt logs indexes from block transactions
// that are before the desired transaction
logIndex += len(receipts[i].Logs)
novosandara marked this conversation as resolved.
Show resolved Hide resolved
logs := toLogs(raw.Logs, uint64(logIndex), uint64(i), block.Header, raw.TxHash)
resReceipts[i] = toReceipt(raw, transaction, uint64(i), block.Header, logs)
}

return resReceipts, nil
}

// GetStorageAt returns the contract storage at the index position
func (e *Eth) GetStorageAt(
address types.Address,
Expand Down
8 changes: 4 additions & 4 deletions jsonrpc/filter_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func (f *blockFilter) getUpdates() (interface{}, error) {

updates := make([]string, len(headers))
for index, header := range headers {
updates[index] = header.Hash.String()
updates[index] = header.BlockHeader.Hash.String()
}

return updates, nil
Expand Down Expand Up @@ -772,7 +772,7 @@ func (f *FilterManager) processBlockEvent(evnt *blockchain.Event) {

// appendLogsToFilters makes each LogFilters append logs in the header
func (f *FilterManager) appendLogsToFilters(header *block) error {
receipts, err := f.store.GetReceiptsByHash(header.Hash)
receipts, err := f.store.GetReceiptsByHash(header.BlockHeader.Hash)
if err != nil {
return err
}
Expand All @@ -790,9 +790,9 @@ func (f *FilterManager) appendLogsToFilters(header *block) error {
return nil
}

block, ok := f.store.GetBlockByHash(header.Hash, true)
block, ok := f.store.GetBlockByHash(header.BlockHeader.Hash, true)
if !ok {
f.logger.Error("could not find block in store", "hash", header.Hash.String())
f.logger.Error("could not find block in store", "hash", header.BlockHeader.Hash.String())

return nil
}
Expand Down
24 changes: 15 additions & 9 deletions jsonrpc/filter_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,19 +661,23 @@ func newMockWsConnWithMsgCh() (*mockWsConn, <-chan []byte) {
func TestHeadStream_Basic(t *testing.T) {
t.Parallel()

b := newBlockStream(&block{Hash: types.StringToHash("1")})
b.push(&block{Hash: types.StringToHash("2")})
blockHeader1 := &header{Hash: types.StringToHash("1")}
blockHeader2 := &header{Hash: types.StringToHash("2")}
blockHeader3 := &header{Hash: types.StringToHash("3")}
blockHeader4 := &header{Hash: types.StringToHash("4")}
b := newBlockStream(&block{BlockHeader: blockHeader1})

b.push(&block{BlockHeader: blockHeader2})
cur := b.getHead()

b.push(&block{Hash: types.StringToHash("3")})
b.push(&block{Hash: types.StringToHash("4")})
b.push(&block{BlockHeader: blockHeader3})
b.push(&block{BlockHeader: blockHeader4})

// get the updates, there are two new entries
updates, next := cur.getUpdates()

assert.Equal(t, updates[0].Hash.String(), types.StringToHash("3").String())
assert.Equal(t, updates[1].Hash.String(), types.StringToHash("4").String())
assert.Equal(t, updates[0].BlockHeader.Hash.String(), types.StringToHash("3").String())
assert.Equal(t, updates[1].BlockHeader.Hash.String(), types.StringToHash("4").String())

// there are no new entries
updates, _ = next.getUpdates()
Expand All @@ -686,7 +690,8 @@ func TestHeadStream_Concurrent(t *testing.T) {
nReaders := 20
nMessages := 10

b := newBlockStream(&block{Number: 0})
blockHeader := &header{Number: 0}
b := newBlockStream(&block{BlockHeader: blockHeader})

// Write co-routine with jitter
go func() {
Expand All @@ -696,7 +701,8 @@ func TestHeadStream_Concurrent(t *testing.T) {
z := rand.NewZipf(rand.New(rand.NewSource(seed)), 1.5, 1.5, 50)

for i := 0; i < nMessages; i++ {
b.push(&block{Number: argUint64(i)})
bHeader := &header{Number: argUint64(i)}
b.push(&block{BlockHeader: bHeader})

wait := time.Duration(z.Uint64()) * time.Millisecond
time.Sleep(wait)
Expand All @@ -718,7 +724,7 @@ func TestHeadStream_Concurrent(t *testing.T) {
blocks, next := item.getUpdates()

for _, block := range blocks {
if num := uint64(block.Number); num != expect {
if num := uint64(block.BlockHeader.Number); num != expect {
errCh <- fmt.Errorf("subscriber %05d bad event want=%d, got=%d", i, num, expect)

return
Expand Down
36 changes: 36 additions & 0 deletions jsonrpc/txpool_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ type ContentResponse struct {
Queued map[types.Address]map[uint64]*transaction `json:"queued"`
}

type ContentAddressResponse struct {
Pending map[uint64]*transaction `json:"pending"`
Queued map[uint64]*transaction `json:"queued"`
}

type InspectResponse struct {
Pending map[string]map[string]string `json:"pending"`
Queued map[string]map[string]string `json:"queued"`
Expand All @@ -41,6 +46,37 @@ type StatusResponse struct {
Queued uint64 `json:"queued"`
}

// ContentFrom returns the transactions contained within the transaction pool.
func (t *TxPool) ContentFrom(addr types.Address) (interface{}, error) {
convertTxMap := func(txMap []*types.Transaction) map[uint64]*transaction {
result := make(map[uint64]*transaction, len(txMap))
for key, tx := range txMap {
result[uint64(key)] = toTransaction(tx, nil, &types.ZeroHash, nil)
novosandara marked this conversation as resolved.
Show resolved Hide resolved
}

return result
}

pendingTxs, queuedTxs := t.store.GetTxs(true)

pTxs, ok := pendingTxs[addr]
if !ok {
return nil, nil
}

qTxs, ok := queuedTxs[addr]
if !ok {
return nil, nil
}

resp := ContentAddressResponse{
Pending: convertTxMap(pTxs),
Queued: convertTxMap(qTxs),
}

return resp, nil
}

// Create response for txpool_content request.
// See https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content.
func (t *TxPool) Content() (interface{}, error) {
Expand Down
Loading
Loading