core/rawdb: fix transaction indexing/unindexing hashing error (#22457)

* core/rawdb: more verbose error logs + better hashing

* core/rawdb: add failing testcase

* core/rawdb: properly hash transactions while indexing/unindexing

* core/rawdb: exit on error + better log msg
This commit is contained in:
Martin Holst Swende 2021-03-16 11:15:14 +01:00 committed by GitHub
parent 7cbf1d70a7
commit 94ab4ea341
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 38 deletions

@ -23,10 +23,10 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
) )
// InitDatabaseFromFreezer reinitializes an empty database from a previous batch // InitDatabaseFromFreezer reinitializes an empty database from a previous batch
@ -135,32 +135,15 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
close(hashesCh) close(hashesCh)
} }
}() }()
var hasher = sha3.NewLegacyKeccak256()
for data := range rlpCh { for data := range rlpCh {
it, err := rlp.NewListIterator(data.rlp) var body types.Body
if err != nil { if err := rlp.DecodeBytes(data.rlp, &body); err != nil {
log.Warn("tx iteration error", "error", err) log.Warn("Failed to decode block body", "block", data.number, "error", err)
return
}
it.Next()
txs := it.Value()
txIt, err := rlp.NewListIterator(txs)
if err != nil {
log.Warn("tx iteration error", "error", err)
return return
} }
var hashes []common.Hash var hashes []common.Hash
for txIt.Next() { for _, tx := range body.Transactions {
if err := txIt.Err(); err != nil { hashes = append(hashes, tx.Hash())
log.Warn("tx iteration error", "error", err)
return
}
var txHash common.Hash
hasher.Reset()
hasher.Write(txIt.Value())
hasher.Sum(txHash[:0])
hashes = append(hashes, txHash)
} }
result := &blockTxHashes{ result := &blockTxHashes{
hashes: hashes, hashes: hashes,

@ -33,14 +33,34 @@ func TestChainIterator(t *testing.T) {
var block *types.Block var block *types.Block
var txs []*types.Transaction var txs []*types.Transaction
for i := uint64(0); i <= 10; i++ { to := common.BytesToAddress([]byte{0x11})
if i == 0 { block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) // Empty genesis block
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
for i := uint64(1); i <= 10; i++ {
var tx *types.Transaction
if i%2 == 0 {
tx = types.NewTx(&types.LegacyTx{
Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
} else { } else {
tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx = types.NewTx(&types.AccessListTx{
txs = append(txs, tx) ChainID: big.NewInt(1337),
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
} }
txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher())
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
} }
@ -66,7 +86,7 @@ func TestChainIterator(t *testing.T) {
numbers = append(numbers, int(h.number)) numbers = append(numbers, int(h.number))
if len(h.hashes) > 0 { if len(h.hashes) > 0 {
if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp { if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp {
t.Fatalf("hash wrong, got %x exp %x", got, exp) t.Fatalf("block %d: hash wrong, got %x exp %x", h.number, got, exp)
} }
} }
} }
@ -88,14 +108,37 @@ func TestIndexTransactions(t *testing.T) {
var block *types.Block var block *types.Block
var txs []*types.Transaction var txs []*types.Transaction
for i := uint64(0); i <= 10; i++ { to := common.BytesToAddress([]byte{0x11})
if i == 0 {
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block // Write empty genesis block
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher())
WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
for i := uint64(1); i <= 10; i++ {
var tx *types.Transaction
if i%2 == 0 {
tx = types.NewTx(&types.LegacyTx{
Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
} else { } else {
tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx = types.NewTx(&types.AccessListTx{
txs = append(txs, tx) ChainID: big.NewInt(1337),
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
} }
txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher())
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
} }
@ -108,10 +151,10 @@ func TestIndexTransactions(t *testing.T) {
} }
number := ReadTxLookupEntry(chainDb, txs[i-1].Hash()) number := ReadTxLookupEntry(chainDb, txs[i-1].Hash())
if exist && number == nil { if exist && number == nil {
t.Fatalf("Transaction indice missing") t.Fatalf("Transaction index %d missing", i)
} }
if !exist && number != nil { if !exist && number != nil {
t.Fatalf("Transaction indice is not deleted") t.Fatalf("Transaction index %d is not deleted", i)
} }
} }
number := ReadTxIndexTail(chainDb) number := ReadTxIndexTail(chainDb)

@ -23,6 +23,7 @@ type listIterator struct {
} }
// NewListIterator creates an iterator for the (list) represented by data // NewListIterator creates an iterator for the (list) represented by data
// TODO: Consider removing this implementation, as it is no longer used.
func NewListIterator(data RawValue) (*listIterator, error) { func NewListIterator(data RawValue) (*listIterator, error) {
k, t, c, err := readKind(data) k, t, c, err := readKind(data)
if err != nil { if err != nil {