core/rawdb, triedb/pathdb: use account hash as the database key

This commit is contained in:
Gary Rong 2025-06-09 17:52:42 +08:00
parent 1370ed79fb
commit aa65b94969
9 changed files with 140 additions and 124 deletions

View file

@ -47,8 +47,8 @@ func DeleteStateHistoryIndexMetadata(db ethdb.KeyValueWriter) {
// ReadAccountHistoryIndex retrieves the account history index with the provided // ReadAccountHistoryIndex retrieves the account history index with the provided
// account address. // account address.
func ReadAccountHistoryIndex(db ethdb.KeyValueReader, address common.Address) []byte { func ReadAccountHistoryIndex(db ethdb.KeyValueReader, addressHash common.Hash) []byte {
data, err := db.Get(accountHistoryIndexKey(address)) data, err := db.Get(accountHistoryIndexKey(addressHash))
if err != nil || len(data) == 0 { if err != nil || len(data) == 0 {
return nil return nil
} }
@ -56,24 +56,24 @@ func ReadAccountHistoryIndex(db ethdb.KeyValueReader, address common.Address) []
} }
// WriteAccountHistoryIndex writes the provided account history index into database. // WriteAccountHistoryIndex writes the provided account history index into database.
func WriteAccountHistoryIndex(db ethdb.KeyValueWriter, address common.Address, data []byte) { func WriteAccountHistoryIndex(db ethdb.KeyValueWriter, addressHash common.Hash, data []byte) {
if err := db.Put(accountHistoryIndexKey(address), data); err != nil { if err := db.Put(accountHistoryIndexKey(addressHash), data); err != nil {
log.Crit("Failed to store account history index", "err", err) log.Crit("Failed to store account history index", "err", err)
} }
} }
// DeleteAccountHistoryIndex deletes the specified account history index from // DeleteAccountHistoryIndex deletes the specified account history index from
// the database. // the database.
func DeleteAccountHistoryIndex(db ethdb.KeyValueWriter, address common.Address) { func DeleteAccountHistoryIndex(db ethdb.KeyValueWriter, addressHash common.Hash) {
if err := db.Delete(accountHistoryIndexKey(address)); err != nil { if err := db.Delete(accountHistoryIndexKey(addressHash)); err != nil {
log.Crit("Failed to delete account history index", "err", err) log.Crit("Failed to delete account history index", "err", err)
} }
} }
// ReadStorageHistoryIndex retrieves the storage history index with the provided // ReadStorageHistoryIndex retrieves the storage history index with the provided
// account address and storage key hash. // account address and storage key hash.
func ReadStorageHistoryIndex(db ethdb.KeyValueReader, address common.Address, storageHash common.Hash) []byte { func ReadStorageHistoryIndex(db ethdb.KeyValueReader, addressHash common.Hash, storageHash common.Hash) []byte {
data, err := db.Get(storageHistoryIndexKey(address, storageHash)) data, err := db.Get(storageHistoryIndexKey(addressHash, storageHash))
if err != nil || len(data) == 0 { if err != nil || len(data) == 0 {
return nil return nil
} }
@ -81,23 +81,23 @@ func ReadStorageHistoryIndex(db ethdb.KeyValueReader, address common.Address, st
} }
// WriteStorageHistoryIndex writes the provided storage history index into database. // WriteStorageHistoryIndex writes the provided storage history index into database.
func WriteStorageHistoryIndex(db ethdb.KeyValueWriter, address common.Address, storageHash common.Hash, data []byte) { func WriteStorageHistoryIndex(db ethdb.KeyValueWriter, addressHash common.Hash, storageHash common.Hash, data []byte) {
if err := db.Put(storageHistoryIndexKey(address, storageHash), data); err != nil { if err := db.Put(storageHistoryIndexKey(addressHash, storageHash), data); err != nil {
log.Crit("Failed to store storage history index", "err", err) log.Crit("Failed to store storage history index", "err", err)
} }
} }
// DeleteStorageHistoryIndex deletes the specified state index from the database. // DeleteStorageHistoryIndex deletes the specified state index from the database.
func DeleteStorageHistoryIndex(db ethdb.KeyValueWriter, address common.Address, storageHash common.Hash) { func DeleteStorageHistoryIndex(db ethdb.KeyValueWriter, addressHash common.Hash, storageHash common.Hash) {
if err := db.Delete(storageHistoryIndexKey(address, storageHash)); err != nil { if err := db.Delete(storageHistoryIndexKey(addressHash, storageHash)); err != nil {
log.Crit("Failed to delete storage history index", "err", err) log.Crit("Failed to delete storage history index", "err", err)
} }
} }
// ReadAccountHistoryIndexBlock retrieves the index block with the provided // ReadAccountHistoryIndexBlock retrieves the index block with the provided
// account address along with the block id. // account address along with the block id.
func ReadAccountHistoryIndexBlock(db ethdb.KeyValueReader, address common.Address, blockID uint32) []byte { func ReadAccountHistoryIndexBlock(db ethdb.KeyValueReader, addressHash common.Hash, blockID uint32) []byte {
data, err := db.Get(accountHistoryIndexBlockKey(address, blockID)) data, err := db.Get(accountHistoryIndexBlockKey(addressHash, blockID))
if err != nil || len(data) == 0 { if err != nil || len(data) == 0 {
return nil return nil
} }
@ -105,23 +105,23 @@ func ReadAccountHistoryIndexBlock(db ethdb.KeyValueReader, address common.Addres
} }
// WriteAccountHistoryIndexBlock writes the provided index block into database. // WriteAccountHistoryIndexBlock writes the provided index block into database.
func WriteAccountHistoryIndexBlock(db ethdb.KeyValueWriter, address common.Address, blockID uint32, data []byte) { func WriteAccountHistoryIndexBlock(db ethdb.KeyValueWriter, addressHash common.Hash, blockID uint32, data []byte) {
if err := db.Put(accountHistoryIndexBlockKey(address, blockID), data); err != nil { if err := db.Put(accountHistoryIndexBlockKey(addressHash, blockID), data); err != nil {
log.Crit("Failed to store account index block", "err", err) log.Crit("Failed to store account index block", "err", err)
} }
} }
// DeleteAccountHistoryIndexBlock deletes the specified index block from the database. // DeleteAccountHistoryIndexBlock deletes the specified index block from the database.
func DeleteAccountHistoryIndexBlock(db ethdb.KeyValueWriter, address common.Address, blockID uint32) { func DeleteAccountHistoryIndexBlock(db ethdb.KeyValueWriter, addressHash common.Hash, blockID uint32) {
if err := db.Delete(accountHistoryIndexBlockKey(address, blockID)); err != nil { if err := db.Delete(accountHistoryIndexBlockKey(addressHash, blockID)); err != nil {
log.Crit("Failed to delete account index block", "err", err) log.Crit("Failed to delete account index block", "err", err)
} }
} }
// ReadStorageHistoryIndexBlock retrieves the index block with the provided state // ReadStorageHistoryIndexBlock retrieves the index block with the provided state
// identifier along with the block id. // identifier along with the block id.
func ReadStorageHistoryIndexBlock(db ethdb.KeyValueReader, address common.Address, storageHash common.Hash, blockID uint32) []byte { func ReadStorageHistoryIndexBlock(db ethdb.KeyValueReader, addressHash common.Hash, storageHash common.Hash, blockID uint32) []byte {
data, err := db.Get(storageHistoryIndexBlockKey(address, storageHash, blockID)) data, err := db.Get(storageHistoryIndexBlockKey(addressHash, storageHash, blockID))
if err != nil || len(data) == 0 { if err != nil || len(data) == 0 {
return nil return nil
} }
@ -129,15 +129,15 @@ func ReadStorageHistoryIndexBlock(db ethdb.KeyValueReader, address common.Addres
} }
// WriteStorageHistoryIndexBlock writes the provided index block into database. // WriteStorageHistoryIndexBlock writes the provided index block into database.
func WriteStorageHistoryIndexBlock(db ethdb.KeyValueWriter, address common.Address, storageHash common.Hash, id uint32, data []byte) { func WriteStorageHistoryIndexBlock(db ethdb.KeyValueWriter, addressHash common.Hash, storageHash common.Hash, id uint32, data []byte) {
if err := db.Put(storageHistoryIndexBlockKey(address, storageHash, id), data); err != nil { if err := db.Put(storageHistoryIndexBlockKey(addressHash, storageHash, id), data); err != nil {
log.Crit("Failed to store storage index block", "err", err) log.Crit("Failed to store storage index block", "err", err)
} }
} }
// DeleteStorageHistoryIndexBlock deletes the specified index block from the database. // DeleteStorageHistoryIndexBlock deletes the specified index block from the database.
func DeleteStorageHistoryIndexBlock(db ethdb.KeyValueWriter, address common.Address, state common.Hash, id uint32) { func DeleteStorageHistoryIndexBlock(db ethdb.KeyValueWriter, addressHash common.Hash, storageHash common.Hash, id uint32) {
if err := db.Delete(storageHistoryIndexBlockKey(address, state, id)); err != nil { if err := db.Delete(storageHistoryIndexBlockKey(addressHash, storageHash, id)); err != nil {
log.Crit("Failed to delete storage index block", "err", err) log.Crit("Failed to delete storage index block", "err", err)
} }
} }

View file

@ -493,7 +493,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
bloomBits.Add(size) bloomBits.Add(size)
// Path-based historic state indexes // Path-based historic state indexes
case bytes.HasPrefix(key, StateHistoryIndexPrefix) && len(key) >= len(StateHistoryIndexPrefix)+common.AddressLength: case bytes.HasPrefix(key, StateHistoryIndexPrefix) && len(key) >= len(StateHistoryIndexPrefix)+common.HashLength:
stateIndex.Add(size) stateIndex.Add(size)
// Verkle trie data is detected, determine the sub-category // Verkle trie data is detected, determine the sub-category

View file

@ -122,7 +122,7 @@ var (
stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
// State history indexing within path-based storage scheme // State history indexing within path-based storage scheme
StateHistoryIndexPrefix = []byte("m") // StateHistoryIndexPrefix + account address or (account address + slotHash) -> index StateHistoryIndexPrefix = []byte("m") // StateHistoryIndexPrefix + account address hash or (account address hash + slotHash) -> index
// VerklePrefix is the database prefix for Verkle trie data, which includes: // VerklePrefix is the database prefix for Verkle trie data, which includes:
// (a) Trie nodes // (a) Trie nodes
@ -370,26 +370,26 @@ func filterMapBlockLVKey(number uint64) []byte {
return key return key
} }
// accountHistoryIndexKey = StateHistoryIndexPrefix + address // accountHistoryIndexKey = StateHistoryIndexPrefix + addressHash
func accountHistoryIndexKey(address common.Address) []byte { func accountHistoryIndexKey(addressHash common.Hash) []byte {
return append(StateHistoryIndexPrefix, address.Bytes()...) return append(StateHistoryIndexPrefix, addressHash.Bytes()...)
} }
// storageHistoryIndexKey = StateHistoryIndexPrefix + address + storageHash // storageHistoryIndexKey = StateHistoryIndexPrefix + addressHash + storageHash
func storageHistoryIndexKey(address common.Address, storageHash common.Hash) []byte { func storageHistoryIndexKey(addressHash common.Hash, storageHash common.Hash) []byte {
return append(append(StateHistoryIndexPrefix, address.Bytes()...), storageHash.Bytes()...) return append(append(StateHistoryIndexPrefix, addressHash.Bytes()...), storageHash.Bytes()...)
} }
// accountHistoryIndexBlockKey = StateHistoryIndexPrefix + address + blockID // accountHistoryIndexBlockKey = StateHistoryIndexPrefix + addressHash + blockID
func accountHistoryIndexBlockKey(address common.Address, blockID uint32) []byte { func accountHistoryIndexBlockKey(addressHash common.Hash, blockID uint32) []byte {
var buf [4]byte var buf [4]byte
binary.BigEndian.PutUint32(buf[:], blockID) binary.BigEndian.PutUint32(buf[:], blockID)
return append(append(StateHistoryIndexPrefix, address.Bytes()...), buf[:]...) return append(append(StateHistoryIndexPrefix, addressHash.Bytes()...), buf[:]...)
} }
// storageHistoryIndexBlockKey = StateHistoryIndexPrefix + address + storageHash + blockID // storageHistoryIndexBlockKey = StateHistoryIndexPrefix + addressHash + storageHash + blockID
func storageHistoryIndexBlockKey(address common.Address, storageHash common.Hash, blockID uint32) []byte { func storageHistoryIndexBlockKey(addressHash common.Hash, storageHash common.Hash, blockID uint32) []byte {
var buf [4]byte var buf [4]byte
binary.BigEndian.PutUint32(buf[:], blockID) binary.BigEndian.PutUint32(buf[:], blockID)
return append(append(append(StateHistoryIndexPrefix, address.Bytes()...), storageHash.Bytes()...), buf[:]...) return append(append(append(StateHistoryIndexPrefix, addressHash.Bytes()...), storageHash.Bytes()...), buf[:]...)
} }

View file

@ -80,9 +80,9 @@ type indexReader struct {
func loadIndexData(db ethdb.KeyValueReader, state stateIdent) ([]*indexBlockDesc, error) { func loadIndexData(db ethdb.KeyValueReader, state stateIdent) ([]*indexBlockDesc, error) {
var blob []byte var blob []byte
if state.account { if state.account {
blob = rawdb.ReadAccountHistoryIndex(db, state.address) blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash)
} else { } else {
blob = rawdb.ReadStorageHistoryIndex(db, state.address, state.storageHash) blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash)
} }
if len(blob) == 0 { if len(blob) == 0 {
return nil, nil return nil, nil
@ -142,9 +142,9 @@ func (r *indexReader) readGreaterThan(id uint64) (uint64, error) {
blob []byte blob []byte
) )
if r.state.account { if r.state.account {
blob = rawdb.ReadAccountHistoryIndexBlock(r.db, r.state.address, desc.id) blob = rawdb.ReadAccountHistoryIndexBlock(r.db, r.state.addressHash, desc.id)
} else { } else {
blob = rawdb.ReadStorageHistoryIndexBlock(r.db, r.state.address, r.state.storageHash, desc.id) blob = rawdb.ReadStorageHistoryIndexBlock(r.db, r.state.addressHash, r.state.storageHash, desc.id)
} }
br, err = newBlockReader(blob) br, err = newBlockReader(blob)
if err != nil { if err != nil {
@ -176,9 +176,9 @@ type indexWriter struct {
func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, error) { func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, error) {
var blob []byte var blob []byte
if state.account { if state.account {
blob = rawdb.ReadAccountHistoryIndex(db, state.address) blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash)
} else { } else {
blob = rawdb.ReadStorageHistoryIndex(db, state.address, state.storageHash) blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash)
} }
if len(blob) == 0 { if len(blob) == 0 {
desc := newIndexBlockDesc(0) desc := newIndexBlockDesc(0)
@ -199,9 +199,9 @@ func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, er
lastDesc = descList[len(descList)-1] lastDesc = descList[len(descList)-1]
) )
if state.account { if state.account {
indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.address, lastDesc.id) indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id)
} else { } else {
indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.address, state.storageHash, lastDesc.id) indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id)
} }
bw, err := newBlockWriter(indexBlock, lastDesc) bw, err := newBlockWriter(indexBlock, lastDesc)
if err != nil { if err != nil {
@ -271,9 +271,9 @@ func (w *indexWriter) finish(batch ethdb.Batch) {
} }
for _, bw := range writers { for _, bw := range writers {
if w.state.account { if w.state.account {
rawdb.WriteAccountHistoryIndexBlock(batch, w.state.address, bw.desc.id, bw.finish()) rawdb.WriteAccountHistoryIndexBlock(batch, w.state.addressHash, bw.desc.id, bw.finish())
} else { } else {
rawdb.WriteStorageHistoryIndexBlock(batch, w.state.address, w.state.storageHash, bw.desc.id, bw.finish()) rawdb.WriteStorageHistoryIndexBlock(batch, w.state.addressHash, w.state.storageHash, bw.desc.id, bw.finish())
} }
} }
w.frozen = nil // release all the frozen writers w.frozen = nil // release all the frozen writers
@ -283,9 +283,9 @@ func (w *indexWriter) finish(batch ethdb.Batch) {
buf = append(buf, desc.encode()...) buf = append(buf, desc.encode()...)
} }
if w.state.account { if w.state.account {
rawdb.WriteAccountHistoryIndex(batch, w.state.address, buf) rawdb.WriteAccountHistoryIndex(batch, w.state.addressHash, buf)
} else { } else {
rawdb.WriteStorageHistoryIndex(batch, w.state.address, w.state.storageHash, buf) rawdb.WriteStorageHistoryIndex(batch, w.state.addressHash, w.state.storageHash, buf)
} }
} }
@ -303,9 +303,9 @@ type indexDeleter struct {
func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, error) { func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, error) {
var blob []byte var blob []byte
if state.account { if state.account {
blob = rawdb.ReadAccountHistoryIndex(db, state.address) blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash)
} else { } else {
blob = rawdb.ReadStorageHistoryIndex(db, state.address, state.storageHash) blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash)
} }
if len(blob) == 0 { if len(blob) == 0 {
// TODO(rjl493456442) we can probably return an error here, // TODO(rjl493456442) we can probably return an error here,
@ -328,9 +328,9 @@ func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter,
lastDesc = descList[len(descList)-1] lastDesc = descList[len(descList)-1]
) )
if state.account { if state.account {
indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.address, lastDesc.id) indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id)
} else { } else {
indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.address, state.storageHash, lastDesc.id) indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id)
} }
bw, err := newBlockWriter(indexBlock, lastDesc) bw, err := newBlockWriter(indexBlock, lastDesc)
if err != nil { if err != nil {
@ -381,9 +381,9 @@ func (d *indexDeleter) pop(id uint64) error {
lastDesc = d.descList[len(d.descList)-1] lastDesc = d.descList[len(d.descList)-1]
) )
if d.state.account { if d.state.account {
indexBlock = rawdb.ReadAccountHistoryIndexBlock(d.db, d.state.address, lastDesc.id) indexBlock = rawdb.ReadAccountHistoryIndexBlock(d.db, d.state.addressHash, lastDesc.id)
} else { } else {
indexBlock = rawdb.ReadStorageHistoryIndexBlock(d.db, d.state.address, d.state.storageHash, lastDesc.id) indexBlock = rawdb.ReadStorageHistoryIndexBlock(d.db, d.state.addressHash, d.state.storageHash, lastDesc.id)
} }
bw, err := newBlockWriter(indexBlock, lastDesc) bw, err := newBlockWriter(indexBlock, lastDesc)
if err != nil { if err != nil {
@ -400,9 +400,9 @@ func (d *indexDeleter) pop(id uint64) error {
func (d *indexDeleter) finish(batch ethdb.Batch) { func (d *indexDeleter) finish(batch ethdb.Batch) {
for _, id := range d.dropped { for _, id := range d.dropped {
if d.state.account { if d.state.account {
rawdb.DeleteAccountHistoryIndexBlock(batch, d.state.address, id) rawdb.DeleteAccountHistoryIndexBlock(batch, d.state.addressHash, id)
} else { } else {
rawdb.DeleteStorageHistoryIndexBlock(batch, d.state.address, d.state.storageHash, id) rawdb.DeleteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, id)
} }
} }
d.dropped = nil d.dropped = nil
@ -410,17 +410,17 @@ func (d *indexDeleter) finish(batch ethdb.Batch) {
// Flush the content of last block writer, regardless it's dirty or not // Flush the content of last block writer, regardless it's dirty or not
if !d.bw.empty() { if !d.bw.empty() {
if d.state.account { if d.state.account {
rawdb.WriteAccountHistoryIndexBlock(batch, d.state.address, d.bw.desc.id, d.bw.finish()) rawdb.WriteAccountHistoryIndexBlock(batch, d.state.addressHash, d.bw.desc.id, d.bw.finish())
} else { } else {
rawdb.WriteStorageHistoryIndexBlock(batch, d.state.address, d.state.storageHash, d.bw.desc.id, d.bw.finish()) rawdb.WriteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, d.bw.desc.id, d.bw.finish())
} }
} }
// Flush the index metadata into the supplied batch // Flush the index metadata into the supplied batch
if d.empty() { if d.empty() {
if d.state.account { if d.state.account {
rawdb.DeleteAccountHistoryIndex(batch, d.state.address) rawdb.DeleteAccountHistoryIndex(batch, d.state.addressHash)
} else { } else {
rawdb.DeleteStorageHistoryIndex(batch, d.state.address, d.state.storageHash) rawdb.DeleteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash)
} }
} else { } else {
buf := make([]byte, 0, indexBlockDescSize*len(d.descList)) buf := make([]byte, 0, indexBlockDescSize*len(d.descList))
@ -428,9 +428,9 @@ func (d *indexDeleter) finish(batch ethdb.Batch) {
buf = append(buf, desc.encode()...) buf = append(buf, desc.encode()...)
} }
if d.state.account { if d.state.account {
rawdb.WriteAccountHistoryIndex(batch, d.state.address, buf) rawdb.WriteAccountHistoryIndex(batch, d.state.addressHash, buf)
} else { } else {
rawdb.WriteStorageHistoryIndex(batch, d.state.address, d.state.storageHash, buf) rawdb.WriteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash, buf)
} }
} }
} }

View file

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
) )
func TestIndexReaderBasic(t *testing.T) { func TestIndexReaderBasic(t *testing.T) {
@ -32,7 +33,7 @@ func TestIndexReaderBasic(t *testing.T) {
1, 5, 10, 11, 20, 1, 5, 10, 11, 20,
} }
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
bw, _ := newIndexWriter(db, newAccountIdent(common.Address{0xa})) bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}))
for i := 0; i < len(elements); i++ { for i := 0; i < len(elements); i++ {
bw.append(elements[i]) bw.append(elements[i])
} }
@ -40,7 +41,7 @@ func TestIndexReaderBasic(t *testing.T) {
bw.finish(batch) bw.finish(batch)
batch.Write() batch.Write()
br, err := newIndexReader(db, newAccountIdent(common.Address{0xa})) br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa}))
if err != nil { if err != nil {
t.Fatalf("Failed to construct the index reader, %v", err) t.Fatalf("Failed to construct the index reader, %v", err)
} }
@ -74,7 +75,7 @@ func TestIndexReaderLarge(t *testing.T) {
slices.Sort(elements) slices.Sort(elements)
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
bw, _ := newIndexWriter(db, newAccountIdent(common.Address{0xa})) bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}))
for i := 0; i < len(elements); i++ { for i := 0; i < len(elements); i++ {
bw.append(elements[i]) bw.append(elements[i])
} }
@ -82,7 +83,7 @@ func TestIndexReaderLarge(t *testing.T) {
bw.finish(batch) bw.finish(batch)
batch.Write() batch.Write()
br, err := newIndexReader(db, newAccountIdent(common.Address{0xa})) br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa}))
if err != nil { if err != nil {
t.Fatalf("Failed to construct the index reader, %v", err) t.Fatalf("Failed to construct the index reader, %v", err)
} }
@ -106,7 +107,7 @@ func TestIndexReaderLarge(t *testing.T) {
} }
func TestEmptyIndexReader(t *testing.T) { func TestEmptyIndexReader(t *testing.T) {
br, err := newIndexReader(rawdb.NewMemoryDatabase(), newAccountIdent(common.Address{0xa})) br, err := newIndexReader(rawdb.NewMemoryDatabase(), newAccountIdent(common.Hash{0xa}))
if err != nil { if err != nil {
t.Fatalf("Failed to construct the index reader, %v", err) t.Fatalf("Failed to construct the index reader, %v", err)
} }
@ -121,7 +122,7 @@ func TestEmptyIndexReader(t *testing.T) {
func TestIndexWriterBasic(t *testing.T) { func TestIndexWriterBasic(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
iw, _ := newIndexWriter(db, newAccountIdent(common.Address{0xa})) iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}))
iw.append(2) iw.append(2)
if err := iw.append(1); err == nil { if err := iw.append(1); err == nil {
t.Fatal("out-of-order insertion is not expected") t.Fatal("out-of-order insertion is not expected")
@ -133,7 +134,7 @@ func TestIndexWriterBasic(t *testing.T) {
iw.finish(batch) iw.finish(batch)
batch.Write() batch.Write()
iw, err := newIndexWriter(db, newAccountIdent(common.Address{0xa})) iw, err := newIndexWriter(db, newAccountIdent(common.Hash{0xa}))
if err != nil { if err != nil {
t.Fatalf("Failed to construct the block writer, %v", err) t.Fatalf("Failed to construct the block writer, %v", err)
} }
@ -147,7 +148,7 @@ func TestIndexWriterBasic(t *testing.T) {
func TestIndexWriterDelete(t *testing.T) { func TestIndexWriterDelete(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
iw, _ := newIndexWriter(db, newAccountIdent(common.Address{0xa})) iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}))
for i := 0; i < indexBlockEntriesCap*4; i++ { for i := 0; i < indexBlockEntriesCap*4; i++ {
iw.append(uint64(i + 1)) iw.append(uint64(i + 1))
} }
@ -156,7 +157,7 @@ func TestIndexWriterDelete(t *testing.T) {
batch.Write() batch.Write()
// Delete unknown id, the request should be rejected // Delete unknown id, the request should be rejected
id, _ := newIndexDeleter(db, newAccountIdent(common.Address{0xa})) id, _ := newIndexDeleter(db, newAccountIdent(common.Hash{0xa}))
if err := id.pop(indexBlockEntriesCap * 5); err == nil { if err := id.pop(indexBlockEntriesCap * 5); err == nil {
t.Fatal("Expect error to occur for unknown id") t.Fatal("Expect error to occur for unknown id")
} }
@ -194,23 +195,24 @@ func TestBatchIndexerWrite(t *testing.T) {
t.Fatal("Unexpected index position") t.Fatal("Unexpected index position")
} }
var ( var (
accounts = make(map[common.Address][]uint64) accounts = make(map[common.Hash][]uint64)
storages = make(map[common.Address]map[common.Hash][]uint64) storages = make(map[common.Hash]map[common.Hash][]uint64)
) )
for i, h := range histories { for i, h := range histories {
for _, addr := range h.accountList { for _, addr := range h.accountList {
accounts[addr] = append(accounts[addr], uint64(i+1)) addrHash := crypto.Keccak256Hash(addr.Bytes())
accounts[addrHash] = append(accounts[addrHash], uint64(i+1))
if _, ok := storages[addr]; !ok { if _, ok := storages[addrHash]; !ok {
storages[addr] = make(map[common.Hash][]uint64) storages[addrHash] = make(map[common.Hash][]uint64)
} }
for _, slot := range h.storageList[addr] { for _, slot := range h.storageList[addr] {
storages[addr][slot] = append(storages[addr][slot], uint64(i+1)) storages[addrHash][slot] = append(storages[addrHash][slot], uint64(i+1))
} }
} }
} }
for addr, indexes := range accounts { for addrHash, indexes := range accounts {
ir, _ := newIndexReader(db, newAccountIdent(addr)) ir, _ := newIndexReader(db, newAccountIdent(addrHash))
for i := 0; i < len(indexes)-1; i++ { for i := 0; i < len(indexes)-1; i++ {
n, err := ir.readGreaterThan(indexes[i]) n, err := ir.readGreaterThan(indexes[i])
if err != nil { if err != nil {
@ -228,9 +230,9 @@ func TestBatchIndexerWrite(t *testing.T) {
t.Fatalf("Unexpected result, want math.MaxUint64, got %d", n) t.Fatalf("Unexpected result, want math.MaxUint64, got %d", n)
} }
} }
for addr, slots := range storages { for addrHash, slots := range storages {
for slotHash, indexes := range slots { for slotHash, indexes := range slots {
ir, _ := newIndexReader(db, newStorageIdent(addr, slotHash)) ir, _ := newIndexReader(db, newStorageIdent(addrHash, slotHash))
for i := 0; i < len(indexes)-1; i++ { for i := 0; i < len(indexes)-1; i++ {
n, err := ir.readGreaterThan(indexes[i]) n, err := ir.readGreaterThan(indexes[i])
if err != nil { if err != nil {

View file

@ -71,19 +71,19 @@ func storeIndexMetadata(db ethdb.KeyValueWriter, last uint64) {
// batchIndexer is a structure designed to perform batch indexing or unindexing // batchIndexer is a structure designed to perform batch indexing or unindexing
// of state histories atomically. // of state histories atomically.
type batchIndexer struct { type batchIndexer struct {
accounts map[common.Address][]uint64 // History ID list, Keyed by account address accounts map[common.Hash][]uint64 // History ID list, Keyed by account address
storages map[common.Address]map[common.Hash][]uint64 // History ID list, Keyed by account address and the hash of raw storage key storages map[common.Hash]map[common.Hash][]uint64 // History ID list, Keyed by account address and the hash of raw storage key
counter int // The counter of processed states counter int // The counter of processed states
delete bool // Index or unindex mode delete bool // Index or unindex mode
lastID uint64 // The ID of latest processed history lastID uint64 // The ID of latest processed history
db ethdb.KeyValueStore db ethdb.KeyValueStore
} }
// newBatchIndexer constructs the batch indexer with the supplied mode. // newBatchIndexer constructs the batch indexer with the supplied mode.
func newBatchIndexer(db ethdb.KeyValueStore, delete bool) *batchIndexer { func newBatchIndexer(db ethdb.KeyValueStore, delete bool) *batchIndexer {
return &batchIndexer{ return &batchIndexer{
accounts: make(map[common.Address][]uint64), accounts: make(map[common.Hash][]uint64),
storages: make(map[common.Address]map[common.Hash][]uint64), storages: make(map[common.Hash]map[common.Hash][]uint64),
delete: delete, delete: delete,
db: db, db: db,
} }
@ -93,13 +93,14 @@ func newBatchIndexer(db ethdb.KeyValueStore, delete bool) *batchIndexer {
// state history, tracking the mapping between state and history IDs. // state history, tracking the mapping between state and history IDs.
func (b *batchIndexer) process(h *history, historyID uint64) error { func (b *batchIndexer) process(h *history, historyID uint64) error {
for _, address := range h.accountList { for _, address := range h.accountList {
addrHash := crypto.Keccak256Hash(address.Bytes())
b.counter += 1 b.counter += 1
b.accounts[address] = append(b.accounts[address], historyID) b.accounts[addrHash] = append(b.accounts[addrHash], historyID)
for _, slotKey := range h.storageList[address] { for _, slotKey := range h.storageList[address] {
b.counter += 1 b.counter += 1
if _, ok := b.storages[address]; !ok { if _, ok := b.storages[addrHash]; !ok {
b.storages[address] = make(map[common.Hash][]uint64) b.storages[addrHash] = make(map[common.Hash][]uint64)
} }
// The hash of the storage slot key is used as the identifier because the // The hash of the storage slot key is used as the identifier because the
// legacy history does not include the raw storage key, therefore, the // legacy history does not include the raw storage key, therefore, the
@ -108,7 +109,7 @@ func (b *batchIndexer) process(h *history, historyID uint64) error {
if h.meta.version != stateHistoryV0 { if h.meta.version != stateHistoryV0 {
slotHash = crypto.Keccak256Hash(slotKey.Bytes()) slotHash = crypto.Keccak256Hash(slotKey.Bytes())
} }
b.storages[address][slotHash] = append(b.storages[address][slotHash], historyID) b.storages[addrHash][slotHash] = append(b.storages[addrHash][slotHash], historyID)
} }
} }
b.lastID = historyID b.lastID = historyID
@ -125,9 +126,9 @@ func (b *batchIndexer) finish(force bool) error {
return nil return nil
} }
batch := b.db.NewBatch() batch := b.db.NewBatch()
for address, idList := range b.accounts { for addrHash, idList := range b.accounts {
if !b.delete { if !b.delete {
iw, err := newIndexWriter(b.db, newAccountIdent(address)) iw, err := newIndexWriter(b.db, newAccountIdent(addrHash))
if err != nil { if err != nil {
return err return err
} }
@ -138,7 +139,7 @@ func (b *batchIndexer) finish(force bool) error {
} }
iw.finish(batch) iw.finish(batch)
} else { } else {
id, err := newIndexDeleter(b.db, newAccountIdent(address)) id, err := newIndexDeleter(b.db, newAccountIdent(addrHash))
if err != nil { if err != nil {
return err return err
} }
@ -150,10 +151,10 @@ func (b *batchIndexer) finish(force bool) error {
id.finish(batch) id.finish(batch)
} }
} }
for address, slots := range b.storages { for addrHash, slots := range b.storages {
for storageHash, idList := range slots { for storageHash, idList := range slots {
if !b.delete { if !b.delete {
iw, err := newIndexWriter(b.db, newStorageIdent(address, storageHash)) iw, err := newIndexWriter(b.db, newStorageIdent(addrHash, storageHash))
if err != nil { if err != nil {
return err return err
} }
@ -164,7 +165,7 @@ func (b *batchIndexer) finish(force bool) error {
} }
iw.finish(batch) iw.finish(batch)
} else { } else {
id, err := newIndexDeleter(b.db, newStorageIdent(address, storageHash)) id, err := newIndexDeleter(b.db, newStorageIdent(addrHash, storageHash))
if err != nil { if err != nil {
return err return err
} }
@ -191,8 +192,8 @@ func (b *batchIndexer) finish(force bool) error {
return err return err
} }
b.counter = 0 b.counter = 0
b.accounts = make(map[common.Address][]uint64) b.accounts = make(map[common.Hash][]uint64)
b.storages = make(map[common.Address]map[common.Hash][]uint64) b.storages = make(map[common.Hash]map[common.Hash][]uint64)
return nil return nil
} }

View file

@ -33,13 +33,19 @@ import (
// either an account or a storage slot. // either an account or a storage slot.
type stateIdent struct { type stateIdent struct {
account bool account bool
address common.Address
// The hash of the account address. This is used instead of the raw account
// address is to align the traversal order with the Merkle-Patricia-Trie.
addressHash common.Hash
// The hash of the storage slot key. This is used instead of the raw slot key // The hash of the storage slot key. This is used instead of the raw slot key
// because, in legacy state histories (prior to the Cancun fork), the slot // because, in legacy state histories (prior to the Cancun fork), the slot
// identifier is the hash of the key, and the original key (preimage) cannot // identifier is the hash of the key, and the original key (preimage) cannot
// be recovered. To maintain backward compatibility, the key hash is used. // be recovered. To maintain backward compatibility, the key hash is used.
// //
// Meanwhile, using the storage key hash also preserve the traversal order
// with Merkle-Patricia-Trie.
//
// This field is null if the identifier refers to account data. // This field is null if the identifier refers to account data.
storageHash common.Hash storageHash common.Hash
} }
@ -47,25 +53,25 @@ type stateIdent struct {
// String returns the string format state identifier. // String returns the string format state identifier.
func (ident stateIdent) String() string { func (ident stateIdent) String() string {
if ident.account { if ident.account {
return ident.address.Hex() return ident.addressHash.Hex()
} }
return ident.address.Hex() + ident.storageHash.Hex() return ident.addressHash.Hex() + ident.storageHash.Hex()
} }
// newAccountIdent constructs a state identifier for an account. // newAccountIdent constructs a state identifier for an account.
func newAccountIdent(address common.Address) stateIdent { func newAccountIdent(addressHash common.Hash) stateIdent {
return stateIdent{ return stateIdent{
account: true, account: true,
address: address, addressHash: addressHash,
} }
} }
// newStorageIdent constructs a state identifier for a storage slot. // newStorageIdent constructs a state identifier for a storage slot.
// The address denotes the address of the associated account; // The address denotes the address of the associated account;
// the storageHash denotes the hash of the raw storage slot key; // the storageHash denotes the hash of the raw storage slot key;
func newStorageIdent(address common.Address, storageHash common.Hash) stateIdent { func newStorageIdent(addressHash common.Hash, storageHash common.Hash) stateIdent {
return stateIdent{ return stateIdent{
address: address, addressHash: addressHash,
storageHash: storageHash, storageHash: storageHash,
} }
} }
@ -73,29 +79,34 @@ func newStorageIdent(address common.Address, storageHash common.Hash) stateIdent
// stateIdentQuery is the extension of stateIdent by adding the raw storage key. // stateIdentQuery is the extension of stateIdent by adding the raw storage key.
type stateIdentQuery struct { type stateIdentQuery struct {
stateIdent stateIdent
address common.Address
storageKey common.Hash storageKey common.Hash
} }
// newAccountIdentQuery constructs a state identifier for an account. // newAccountIdentQuery constructs a state identifier for an account.
func newAccountIdentQuery(address common.Address) stateIdentQuery { func newAccountIdentQuery(address common.Address, addressHash common.Hash) stateIdentQuery {
return stateIdentQuery{ return stateIdentQuery{
stateIdent: stateIdent{ stateIdent: stateIdent{
account: true, account: true,
address: address, addressHash: addressHash,
}, },
address: address,
} }
} }
// newStorageIdentQuery constructs a state identifier for a storage slot. // newStorageIdentQuery constructs a state identifier for a storage slot.
// The address denotes the address of the associated account; // the address denotes the address of the associated account;
// the addressHash denotes the address hash of the associated account;
// the storageKey denotes the raw storage slot key; // the storageKey denotes the raw storage slot key;
// the storageHash denotes the hash of the raw storage slot key; // the storageHash denotes the hash of the raw storage slot key;
func newStorageIdentQuery(address common.Address, storageKey common.Hash, storageHash common.Hash) stateIdentQuery { func newStorageIdentQuery(address common.Address, addressHash common.Hash, storageKey common.Hash, storageHash common.Hash) stateIdentQuery {
return stateIdentQuery{ return stateIdentQuery{
stateIdent: stateIdent{ stateIdent: stateIdent{
address: address, addressHash: addressHash,
storageHash: storageHash, storageHash: storageHash,
}, },
address: address,
storageKey: storageKey, storageKey: storageKey,
} }
} }

View file

@ -49,7 +49,7 @@ func checkHistoricState(env *tester, root common.Hash, hr *historyReader) error
) )
for addrHash, accountData := range accounts { for addrHash, accountData := range accounts {
latest, _ := dl.account(addrHash, 0) latest, _ := dl.account(addrHash, 0)
blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash)), *stateID, dl.stateID(), latest) blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash), addrHash), *stateID, dl.stateID(), latest)
if err != nil { if err != nil {
return err return err
} }
@ -65,7 +65,7 @@ func checkHistoricState(env *tester, root common.Hash, hr *historyReader) error
for addrHash := range env.snapAccounts[env.roots[i]] { for addrHash := range env.snapAccounts[env.roots[i]] {
if _, ok := accounts[addrHash]; !ok { if _, ok := accounts[addrHash]; !ok {
latest, _ := dl.account(addrHash, 0) latest, _ := dl.account(addrHash, 0)
blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash)), *stateID, dl.stateID(), latest) blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash), addrHash), *stateID, dl.stateID(), latest)
if err != nil { if err != nil {
return err return err
} }
@ -78,7 +78,7 @@ func checkHistoricState(env *tester, root common.Hash, hr *historyReader) error
for addrHash, slots := range storages { for addrHash, slots := range storages {
for slotHash, slotData := range slots { for slotHash, slotData := range slots {
latest, _ := dl.storage(addrHash, slotHash, 0) latest, _ := dl.storage(addrHash, slotHash, 0)
blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest) blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), addrHash, env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest)
if err != nil { if err != nil {
return err return err
} }
@ -100,7 +100,7 @@ func checkHistoricState(env *tester, root common.Hash, hr *historyReader) error
} }
if !ok { if !ok {
latest, _ := dl.storage(addrHash, slotHash, 0) latest, _ := dl.storage(addrHash, slotHash, 0)
blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest) blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), addrHash, env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest)
if err != nil { if err != nil {
return err return err
} }

View file

@ -256,11 +256,12 @@ func (r *HistoricalStateReader) AccountRLP(address common.Address) ([]byte, erro
// and try to define a low granularity lock if the current approach doesn't // and try to define a low granularity lock if the current approach doesn't
// work later. // work later.
dl := r.db.tree.bottom() dl := r.db.tree.bottom()
latest, err := dl.account(crypto.Keccak256Hash(address.Bytes()), 0) hash := crypto.Keccak256Hash(address.Bytes())
latest, err := dl.account(hash, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return r.reader.read(newAccountIdentQuery(address), r.id, dl.stateID(), latest) return r.reader.read(newAccountIdentQuery(address, hash), r.id, dl.stateID(), latest)
} }
// Account directly retrieves the account associated with a particular address in // Account directly retrieves the account associated with a particular address in
@ -305,10 +306,11 @@ func (r *HistoricalStateReader) Storage(address common.Address, key common.Hash)
// and try to define a low granularity lock if the current approach doesn't // and try to define a low granularity lock if the current approach doesn't
// work later. // work later.
dl := r.db.tree.bottom() dl := r.db.tree.bottom()
addrHash := crypto.Keccak256Hash(address.Bytes())
keyHash := crypto.Keccak256Hash(key.Bytes()) keyHash := crypto.Keccak256Hash(key.Bytes())
latest, err := dl.storage(crypto.Keccak256Hash(address.Bytes()), keyHash, 0) latest, err := dl.storage(addrHash, keyHash, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return r.reader.read(newStorageIdentQuery(address, key, keyHash), r.id, dl.stateID(), latest) return r.reader.read(newStorageIdentQuery(address, addrHash, key, keyHash), r.id, dl.stateID(), latest)
} }