From feed0d8a3a7cf39e1483c706d147aa83e59ae0fa Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Wed, 18 Jun 2025 09:31:31 +0800 Subject: [PATCH] core/rawdb: add more unit tests --- core/rawdb/accessors_indexes.go | 74 +++++++++++-------- core/rawdb/accessors_indexes_test.go | 102 +++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 31 deletions(-) diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 39686f3bc8..4ac56a869b 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -129,6 +129,46 @@ func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash } } +// traverseBlockBody traverses the given RLP-encoded block body, searching for +// the transaction specified by its hash. +func traverseBlockBody(data rlp.RawValue, target common.Hash) (*types.Transaction, uint64, error) { + txnListRLP, _, err := rlp.SplitList(data) + if err != nil { + return nil, 0, err + } + iter, err := rlp.NewListIterator(txnListRLP) + if err != nil { + return nil, 0, err + } + txIndex := uint64(0) + for iter.Next() { + if iter.Err() != nil { + return nil, 0, err + } + // The preimage for the hash calculation of legacy transactions + // is just their RLP encoding. For typed (EIP-2718) transactions, + // which are encoded as byte arrays, the preimage is the content of + // the byte array, so trim their prefix here. + txRLP := iter.Value() + kind, txHashPayload, _, err := rlp.Split(txRLP) + if err != nil { + return nil, 0, err + } + if kind == rlp.List { // Legacy transaction + txHashPayload = txRLP + } + if crypto.Keccak256Hash(txHashPayload) == target { + var tx types.Transaction + if err := rlp.DecodeBytes(txRLP, &tx); err != nil { + return nil, 0, err + } + return &tx, txIndex, nil + } + txIndex++ + } + return nil, 0, errors.New("transaction not found") +} + // ReadTransaction retrieves a specific transaction from the database, along with // its added positional metadata. func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { @@ -145,40 +185,12 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash) return nil, common.Hash{}, 0, 0 } - txnListRLP, _, err := rlp.SplitList(bodyRLP) + tx, txIndex, err := traverseBlockBody(bodyRLP, hash) if err != nil { + log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash, "err", err) return nil, common.Hash{}, 0, 0 } - txnIterator, err := rlp.NewListIterator(txnListRLP) - if err != nil { - return nil, common.Hash{}, 0, 0 - } - - txIndex := uint64(0) - for txnIterator.Next() && txnIterator.Err() == nil { - // The preimage for the hash calculation of legacy transactions - // is just their RLP encoding. For typed (EIP-2718) transactions, - // which are encoded as byte arrays, the preimage is the content of - // the byte array, so trim their prefix here. - txRLP := txnIterator.Value() - kind, txHashPayload, _, err := rlp.Split(txRLP) - if err != nil { - return nil, common.Hash{}, 0, 0 - } - if kind == rlp.List { - txHashPayload = txRLP - } - if crypto.Keccak256Hash(txHashPayload) == hash { - var tx types.Transaction - if err := rlp.DecodeBytes(txRLP, &tx); err != nil { - return nil, common.Hash{}, 0, 0 - } - return &tx, blockHash, *blockNumber, txIndex - } - txIndex++ - } - log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash) - return nil, common.Hash{}, 0, 0 + return tx, blockHash, *blockNumber, txIndex } // ReadReceipt retrieves a specific transaction receipt from the database, along with diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index 1b29f67d49..418920d287 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -17,6 +17,8 @@ package rawdb import ( + "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" "math/big" "testing" @@ -117,3 +119,103 @@ func TestLookupStorage(t *testing.T) { }) } } + +func TestTraverseBlockBody(t *testing.T) { + tx1 := types.NewTx(&types.LegacyTx{ + Nonce: 1, + GasPrice: big.NewInt(1), + Gas: 1, + To: new(common.Address), + Value: big.NewInt(5), + Data: []byte{0x11, 0x11, 0x11}, + }) + tx2 := types.NewTx(&types.AccessListTx{ + Nonce: 1, + GasPrice: big.NewInt(1), + Gas: 1, + To: new(common.Address), + Value: big.NewInt(5), + Data: []byte{0x11, 0x11, 0x11}, + AccessList: []types.AccessTuple{ + { + Address: common.Address{0x1}, + StorageKeys: []common.Hash{{0x1}, {0x2}}, + }, + }, + }) + tx3 := types.NewTx(&types.DynamicFeeTx{ + Nonce: 1, + Gas: 1, + To: new(common.Address), + Value: big.NewInt(5), + Data: []byte{0x11, 0x11, 0x11}, + GasTipCap: big.NewInt(55), + GasFeeCap: big.NewInt(1055), + AccessList: []types.AccessTuple{ + { + Address: common.Address{0x1}, + StorageKeys: []common.Hash{{0x1}, {0x2}}, + }, + }, + }) + tx4 := types.NewTx(&types.BlobTx{ + Nonce: 1, + Gas: 1, + To: common.Address{0x1}, + Value: uint256.NewInt(5), + Data: []byte{0x11, 0x11, 0x11}, + GasTipCap: uint256.NewInt(55), + GasFeeCap: uint256.NewInt(1055), + AccessList: []types.AccessTuple{ + { + Address: common.Address{0x1}, + StorageKeys: []common.Hash{{0x1}, {0x2}}, + }, + }, + BlobFeeCap: uint256.NewInt(1), + BlobHashes: []common.Hash{{0x1}, {0x2}}, + }) + tx5 := types.NewTx(&types.SetCodeTx{ + Nonce: 1, + Gas: 1, + To: common.Address{0x1}, + Value: uint256.NewInt(5), + Data: []byte{0x11, 0x11, 0x11}, + GasTipCap: uint256.NewInt(55), + GasFeeCap: uint256.NewInt(1055), + AccessList: []types.AccessTuple{ + { + Address: common.Address{0x1}, + StorageKeys: []common.Hash{{0x1}, {0x2}}, + }, + }, + AuthList: []types.SetCodeAuthorization{ + { + ChainID: uint256.Int{1}, + Address: common.Address{0x1}, + }, + }, + }) + + txs := []*types.Transaction{tx1, tx2, tx3, tx4, tx5} + + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher()) + db := NewMemoryDatabase() + WriteBlock(db, block) + + rlp := ReadBodyRLP(db, block.Hash(), block.NumberU64()) + for i := 0; i < len(txs); i++ { + tx, txIndex, err := traverseBlockBody(rlp, txs[i].Hash()) + if err != nil { + t.Fatalf("Failed to retrieve tx, err: %v", err) + } + if txIndex != uint64(i) { + t.Fatalf("Unexpected transaction index, want: %d, got: %d", i, txIndex) + } + if tx.Hash() != txs[i].Hash() { + want := spew.Sdump(txs[i]) + got := spew.Sdump(tx) + t.Fatalf("Unexpected transaction, want: %s, got: %s", want, got) + } + } +}