core/state: add account address to Trie slot accessors (#26934)
This changes the Trie interface to add the plain account address as a parameter to all storage-related methods. After the introduction of the TryAccount* functions, TryGet, TryUpdate and TryDelete are now only meant to read an account's storage. In their current form, they assume that an account storage is stored in a separate trie, and that the hashing of the slot is independent of its account's address. The proposed structure for a stateless storage breaks these two assumptions: the hashing of a slot key requires the address and all slots and accounts are stored in a single trie. This PR therefore adds an address parameter to the interface. It is ignored in the MPT version, so this change has no functional impact, however it will reduce the diff size when merging verkle trees.
This commit is contained in:
parent
37ecff0967
commit
8990c92aea
@ -68,10 +68,10 @@ type Trie interface {
|
|||||||
// TODO(fjl): remove this when StateTrie is removed
|
// TODO(fjl): remove this when StateTrie is removed
|
||||||
GetKey([]byte) []byte
|
GetKey([]byte) []byte
|
||||||
|
|
||||||
// TryGet returns the value for key stored in the trie. The value bytes must
|
// TryGetStorage returns the value for key stored in the trie. The value bytes
|
||||||
// not be modified by the caller. If a node was not found in the database, a
|
// must not be modified by the caller. If a node was not found in the database,
|
||||||
// trie.MissingNodeError is returned.
|
// a trie.MissingNodeError is returned.
|
||||||
TryGet(key []byte) ([]byte, error)
|
TryGetStorage(addr common.Address, key []byte) ([]byte, error)
|
||||||
|
|
||||||
// TryGetAccount abstracts an account read from the trie. It retrieves the
|
// TryGetAccount abstracts an account read from the trie. It retrieves the
|
||||||
// account blob from the trie with provided account address and decodes it
|
// account blob from the trie with provided account address and decodes it
|
||||||
@ -81,20 +81,20 @@ type Trie interface {
|
|||||||
// be returned.
|
// be returned.
|
||||||
TryGetAccount(address common.Address) (*types.StateAccount, error)
|
TryGetAccount(address common.Address) (*types.StateAccount, error)
|
||||||
|
|
||||||
// TryUpdate associates key with value in the trie. If value has length zero, any
|
// TryUpdateStorage associates key with value in the trie. If value has length zero,
|
||||||
// existing value is deleted from the trie. The value bytes must not be modified
|
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||||
// by the caller while they are stored in the trie. If a node was not found in the
|
// by the caller while they are stored in the trie. If a node was not found in the
|
||||||
// database, a trie.MissingNodeError is returned.
|
// database, a trie.MissingNodeError is returned.
|
||||||
TryUpdate(key, value []byte) error
|
TryUpdateStorage(addr common.Address, key, value []byte) error
|
||||||
|
|
||||||
// TryUpdateAccount abstracts an account write to the trie. It encodes the
|
// TryUpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
// in the trie with provided address.
|
// in the trie with provided address.
|
||||||
TryUpdateAccount(address common.Address, account *types.StateAccount) error
|
TryUpdateAccount(address common.Address, account *types.StateAccount) error
|
||||||
|
|
||||||
// TryDelete removes any existing value for key from the trie. If a node was not
|
// TryDeleteStorage removes any existing value for key from the trie. If a node
|
||||||
// found in the database, a trie.MissingNodeError is returned.
|
// was not found in the database, a trie.MissingNodeError is returned.
|
||||||
TryDelete(key []byte) error
|
TryDeleteStorage(addr common.Address, key []byte) error
|
||||||
|
|
||||||
// TryDeleteAccount abstracts an account deletion from the trie.
|
// TryDeleteAccount abstracts an account deletion from the trie.
|
||||||
TryDeleteAccount(address common.Address) error
|
TryDeleteAccount(address common.Address) error
|
||||||
|
@ -201,7 +201,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
|||||||
s.db.setError(err)
|
s.db.setError(err)
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
enc, err = tr.TryGet(key.Bytes())
|
enc, err = tr.TryGetStorage(s.address, key.Bytes())
|
||||||
if metrics.EnabledExpensive {
|
if metrics.EnabledExpensive {
|
||||||
s.db.StorageReads += time.Since(start)
|
s.db.StorageReads += time.Since(start)
|
||||||
}
|
}
|
||||||
@ -253,7 +253,7 @@ func (s *stateObject) finalise(prefetch bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
||||||
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, slotsToPrefetch)
|
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch)
|
||||||
}
|
}
|
||||||
if len(s.dirtyStorage) > 0 {
|
if len(s.dirtyStorage) > 0 {
|
||||||
s.dirtyStorage = make(Storage)
|
s.dirtyStorage = make(Storage)
|
||||||
@ -294,7 +294,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||||||
|
|
||||||
var v []byte
|
var v []byte
|
||||||
if (value == common.Hash{}) {
|
if (value == common.Hash{}) {
|
||||||
if err := tr.TryDelete(key[:]); err != nil {
|
if err := tr.TryDeleteStorage(s.address, key[:]); err != nil {
|
||||||
s.db.setError(err)
|
s.db.setError(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -302,7 +302,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||||||
} else {
|
} else {
|
||||||
// Encoding []byte cannot fail, ok to ignore the error.
|
// Encoding []byte cannot fail, ok to ignore the error.
|
||||||
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
|
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
|
||||||
if err := tr.TryUpdate(key[:], v); err != nil {
|
if err := tr.TryUpdateStorage(s.address, key[:], v); err != nil {
|
||||||
s.db.setError(err)
|
s.db.setError(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -871,7 +871,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
|||||||
addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure
|
addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure
|
||||||
}
|
}
|
||||||
if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
|
if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
|
||||||
s.prefetcher.prefetch(common.Hash{}, s.originalRoot, addressesToPrefetch)
|
s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch)
|
||||||
}
|
}
|
||||||
// Invalidate journal because reverting across transactions is not allowed.
|
// Invalidate journal because reverting across transactions is not allowed.
|
||||||
s.clearJournalAndRefund()
|
s.clearJournalAndRefund()
|
||||||
|
@ -141,7 +141,7 @@ func (p *triePrefetcher) copy() *triePrefetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prefetch schedules a batch of trie items to prefetch.
|
// prefetch schedules a batch of trie items to prefetch.
|
||||||
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, keys [][]byte) {
|
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) {
|
||||||
// If the prefetcher is an inactive one, bail out
|
// If the prefetcher is an inactive one, bail out
|
||||||
if p.fetches != nil {
|
if p.fetches != nil {
|
||||||
return
|
return
|
||||||
@ -150,7 +150,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, keys [][]
|
|||||||
id := p.trieID(owner, root)
|
id := p.trieID(owner, root)
|
||||||
fetcher := p.fetchers[id]
|
fetcher := p.fetchers[id]
|
||||||
if fetcher == nil {
|
if fetcher == nil {
|
||||||
fetcher = newSubfetcher(p.db, p.root, owner, root)
|
fetcher = newSubfetcher(p.db, p.root, owner, root, addr)
|
||||||
p.fetchers[id] = fetcher
|
p.fetchers[id] = fetcher
|
||||||
}
|
}
|
||||||
fetcher.schedule(keys)
|
fetcher.schedule(keys)
|
||||||
@ -209,6 +209,7 @@ type subfetcher struct {
|
|||||||
state common.Hash // Root hash of the state to prefetch
|
state common.Hash // Root hash of the state to prefetch
|
||||||
owner common.Hash // Owner of the trie, usually account hash
|
owner common.Hash // Owner of the trie, usually account hash
|
||||||
root common.Hash // Root hash of the trie to prefetch
|
root common.Hash // Root hash of the trie to prefetch
|
||||||
|
addr common.Address // Address of the account that the trie belongs to
|
||||||
trie Trie // Trie being populated with nodes
|
trie Trie // Trie being populated with nodes
|
||||||
|
|
||||||
tasks [][]byte // Items queued up for retrieval
|
tasks [][]byte // Items queued up for retrieval
|
||||||
@ -226,12 +227,13 @@ type subfetcher struct {
|
|||||||
|
|
||||||
// newSubfetcher creates a goroutine to prefetch state items belonging to a
|
// newSubfetcher creates a goroutine to prefetch state items belonging to a
|
||||||
// particular root hash.
|
// particular root hash.
|
||||||
func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash) *subfetcher {
|
func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher {
|
||||||
sf := &subfetcher{
|
sf := &subfetcher{
|
||||||
db: db,
|
db: db,
|
||||||
state: state,
|
state: state,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
root: root,
|
root: root,
|
||||||
|
addr: addr,
|
||||||
wake: make(chan struct{}, 1),
|
wake: make(chan struct{}, 1),
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
term: make(chan struct{}),
|
term: make(chan struct{}),
|
||||||
@ -336,7 +338,11 @@ func (sf *subfetcher) loop() {
|
|||||||
if _, ok := sf.seen[string(task)]; ok {
|
if _, ok := sf.seen[string(task)]; ok {
|
||||||
sf.dups++
|
sf.dups++
|
||||||
} else {
|
} else {
|
||||||
sf.trie.TryGet(task)
|
if len(task) == common.AddressLength {
|
||||||
|
sf.trie.TryGetAccount(common.BytesToAddress(task))
|
||||||
|
} else {
|
||||||
|
sf.trie.TryGetStorage(sf.addr, task)
|
||||||
|
}
|
||||||
sf.seen[string(task)] = struct{}{}
|
sf.seen[string(task)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,19 +47,19 @@ func TestCopyAndClose(t *testing.T) {
|
|||||||
db := filledStateDB()
|
db := filledStateDB()
|
||||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||||
skey := common.HexToHash("aaa")
|
skey := common.HexToHash("aaa")
|
||||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||||
cpy := prefetcher.copy()
|
cpy := prefetcher.copy()
|
||||||
cpy.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
cpy.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
cpy.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
cpy.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
c := cpy.trie(common.Hash{}, db.originalRoot)
|
c := cpy.trie(common.Hash{}, db.originalRoot)
|
||||||
prefetcher.close()
|
prefetcher.close()
|
||||||
cpy2 := cpy.copy()
|
cpy2 := cpy.copy()
|
||||||
cpy2.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
cpy2.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
d := cpy2.trie(common.Hash{}, db.originalRoot)
|
d := cpy2.trie(common.Hash{}, db.originalRoot)
|
||||||
cpy.close()
|
cpy.close()
|
||||||
cpy2.close()
|
cpy2.close()
|
||||||
@ -72,7 +72,7 @@ func TestUseAfterClose(t *testing.T) {
|
|||||||
db := filledStateDB()
|
db := filledStateDB()
|
||||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||||
skey := common.HexToHash("aaa")
|
skey := common.HexToHash("aaa")
|
||||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||||
prefetcher.close()
|
prefetcher.close()
|
||||||
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||||
@ -88,7 +88,7 @@ func TestCopyClose(t *testing.T) {
|
|||||||
db := filledStateDB()
|
db := filledStateDB()
|
||||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||||
skey := common.HexToHash("aaa")
|
skey := common.HexToHash("aaa")
|
||||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()})
|
||||||
cpy := prefetcher.copy()
|
cpy := prefetcher.copy()
|
||||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||||
b := cpy.trie(common.Hash{}, db.originalRoot)
|
b := cpy.trie(common.Hash{}, db.originalRoot)
|
||||||
|
@ -105,7 +105,7 @@ type odrTrie struct {
|
|||||||
trie *trie.Trie
|
trie *trie.Trie
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *odrTrie) TryGet(key []byte) ([]byte, error) {
|
func (t *odrTrie) TryGetStorage(_ common.Address, key []byte) ([]byte, error) {
|
||||||
key = crypto.Keccak256(key)
|
key = crypto.Keccak256(key)
|
||||||
var res []byte
|
var res []byte
|
||||||
err := t.do(key, func() (err error) {
|
err := t.do(key, func() (err error) {
|
||||||
@ -142,14 +142,14 @@ func (t *odrTrie) TryUpdateAccount(address common.Address, acc *types.StateAccou
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *odrTrie) TryUpdate(key, value []byte) error {
|
func (t *odrTrie) TryUpdateStorage(_ common.Address, key, value []byte) error {
|
||||||
key = crypto.Keccak256(key)
|
key = crypto.Keccak256(key)
|
||||||
return t.do(key, func() error {
|
return t.do(key, func() error {
|
||||||
return t.trie.TryUpdate(key, value)
|
return t.trie.TryUpdate(key, value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *odrTrie) TryDelete(key []byte) error {
|
func (t *odrTrie) TryDeleteStorage(_ common.Address, key []byte) error {
|
||||||
key = crypto.Keccak256(key)
|
key = crypto.Keccak256(key)
|
||||||
return t.do(key, func() error {
|
return t.do(key, func() error {
|
||||||
return t.trie.TryDelete(key)
|
return t.trie.TryDelete(key)
|
||||||
|
@ -75,7 +75,7 @@ func NewStateTrie(id *ID, db *Database) (*StateTrie, error) {
|
|||||||
// Get returns the value for key stored in the trie.
|
// Get returns the value for key stored in the trie.
|
||||||
// The value bytes must not be modified by the caller.
|
// The value bytes must not be modified by the caller.
|
||||||
func (t *StateTrie) Get(key []byte) []byte {
|
func (t *StateTrie) Get(key []byte) []byte {
|
||||||
res, err := t.TryGet(key)
|
res, err := t.TryGetStorage(common.Address{}, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unhandled trie error in StateTrie.Get", "err", err)
|
log.Error("Unhandled trie error in StateTrie.Get", "err", err)
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ func (t *StateTrie) Get(key []byte) []byte {
|
|||||||
// The value bytes must not be modified by the caller.
|
// The value bytes must not be modified by the caller.
|
||||||
// If the specified node is not in the trie, nil will be returned.
|
// If the specified node is not in the trie, nil will be returned.
|
||||||
// If a trie node is not found in the database, a MissingNodeError is returned.
|
// If a trie node is not found in the database, a MissingNodeError is returned.
|
||||||
func (t *StateTrie) TryGet(key []byte) ([]byte, error) {
|
func (t *StateTrie) TryGetStorage(_ common.Address, key []byte) ([]byte, error) {
|
||||||
return t.trie.TryGet(t.hashKey(key))
|
return t.trie.TryGet(t.hashKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ func (t *StateTrie) TryGetNode(path []byte) ([]byte, int, error) {
|
|||||||
// The value bytes must not be modified by the caller while they are
|
// The value bytes must not be modified by the caller while they are
|
||||||
// stored in the trie.
|
// stored in the trie.
|
||||||
func (t *StateTrie) Update(key, value []byte) {
|
func (t *StateTrie) Update(key, value []byte) {
|
||||||
if err := t.TryUpdate(key, value); err != nil {
|
if err := t.TryUpdateStorage(common.Address{}, key, value); err != nil {
|
||||||
log.Error("Unhandled trie error in StateTrie.Update", "err", err)
|
log.Error("Unhandled trie error in StateTrie.Update", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ func (t *StateTrie) Update(key, value []byte) {
|
|||||||
// stored in the trie.
|
// stored in the trie.
|
||||||
//
|
//
|
||||||
// If a node is not found in the database, a MissingNodeError is returned.
|
// If a node is not found in the database, a MissingNodeError is returned.
|
||||||
func (t *StateTrie) TryUpdate(key, value []byte) error {
|
func (t *StateTrie) TryUpdateStorage(_ common.Address, key, value []byte) error {
|
||||||
hk := t.hashKey(key)
|
hk := t.hashKey(key)
|
||||||
err := t.trie.TryUpdate(hk, value)
|
err := t.trie.TryUpdate(hk, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -171,7 +171,7 @@ func (t *StateTrie) TryUpdateAccount(address common.Address, acc *types.StateAcc
|
|||||||
|
|
||||||
// Delete removes any existing value for key from the trie.
|
// Delete removes any existing value for key from the trie.
|
||||||
func (t *StateTrie) Delete(key []byte) {
|
func (t *StateTrie) Delete(key []byte) {
|
||||||
if err := t.TryDelete(key); err != nil {
|
if err := t.TryDeleteStorage(common.Address{}, key); err != nil {
|
||||||
log.Error("Unhandled trie error in StateTrie.Delete", "err", err)
|
log.Error("Unhandled trie error in StateTrie.Delete", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ func (t *StateTrie) Delete(key []byte) {
|
|||||||
// TryDelete removes any existing value for key from the trie.
|
// TryDelete removes any existing value for key from the trie.
|
||||||
// If the specified trie node is not in the trie, nothing will be changed.
|
// If the specified trie node is not in the trie, nothing will be changed.
|
||||||
// If a node is not found in the database, a MissingNodeError is returned.
|
// If a node is not found in the database, a MissingNodeError is returned.
|
||||||
func (t *StateTrie) TryDelete(key []byte) error {
|
func (t *StateTrie) TryDeleteStorage(_ common.Address, key []byte) error {
|
||||||
hk := t.hashKey(key)
|
hk := t.hashKey(key)
|
||||||
delete(t.getSecKeyCache(), string(hk))
|
delete(t.getSecKeyCache(), string(hk))
|
||||||
return t.trie.TryDelete(hk)
|
return t.trie.TryDelete(hk)
|
||||||
|
Loading…
Reference in New Issue
Block a user