mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-04 05:58:40 +00:00
Because the UBT doesn't differentiate slots from accounts, the content of the tree can not be exported as a `GenesisAlloc`, which means that `evm t8n` can not intergrate it. We have tried integrating the new format into execution-specs, but this is very hard to maintain because the team doesn't see it as a priority and their own repository is seeing a lot of churn. This PR adds the ability to capture the structure of what is being inserted in the tree, so that the information isn't lost and it can be dumped in the t8n context. --------- Co-authored-by: felipe <fselmo2@gmail.com>
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
// Copyright 2026 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package bintrie
|
|
|
|
import (
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
)
|
|
|
|
// Recorder maintains the inverse of the binary-trie key transform: it captures
|
|
// every mutation applied to a BinaryTrie keyed by the original address (and,
|
|
// for storage, the original slot key) so the post-state can be rendered as a
|
|
// types.GenesisAlloc.
|
|
type Recorder struct {
|
|
accounts map[common.Address]*types.Account
|
|
}
|
|
|
|
// NewRecorder returns an empty Recorder.
|
|
func NewRecorder() *Recorder {
|
|
return &Recorder{accounts: make(map[common.Address]*types.Account)}
|
|
}
|
|
|
|
// entry returns the existing account entry, or creates a fresh one.
|
|
func (r *Recorder) entry(addr common.Address) *types.Account {
|
|
if acc, ok := r.accounts[addr]; ok {
|
|
return acc
|
|
}
|
|
acc := &types.Account{}
|
|
r.accounts[addr] = acc
|
|
return acc
|
|
}
|
|
|
|
// RecordAccount upserts the nonce and balance for addr. Existing storage and
|
|
// code on the entry are preserved.
|
|
func (r *Recorder) RecordAccount(addr common.Address, acc *types.StateAccount) {
|
|
e := r.entry(addr)
|
|
e.Nonce = acc.Nonce
|
|
if acc.Balance != nil {
|
|
e.Balance = acc.Balance.ToBig()
|
|
} else {
|
|
e.Balance = nil
|
|
}
|
|
}
|
|
|
|
// RecordStorage records a storage write. A zero value removes the slot.
|
|
func (r *Recorder) RecordStorage(addr common.Address, key, value []byte) {
|
|
k := bytesToHash(key)
|
|
v := bytesToHash(value)
|
|
e := r.entry(addr)
|
|
if (v == common.Hash{}) {
|
|
if e.Storage != nil {
|
|
delete(e.Storage, k)
|
|
if len(e.Storage) == 0 {
|
|
e.Storage = nil
|
|
}
|
|
}
|
|
return
|
|
}
|
|
if e.Storage == nil {
|
|
e.Storage = make(map[common.Hash]common.Hash)
|
|
}
|
|
e.Storage[k] = v
|
|
}
|
|
|
|
// RecordCode records the contract code for addr. Empty code clears the field.
|
|
func (r *Recorder) RecordCode(addr common.Address, code []byte) {
|
|
e := r.entry(addr)
|
|
if len(code) == 0 {
|
|
e.Code = nil
|
|
return
|
|
}
|
|
e.Code = common.CopyBytes(code)
|
|
}
|
|
|
|
// RecordDeleteAccount drops addr entirely from the recorded set.
|
|
func (r *Recorder) RecordDeleteAccount(addr common.Address) {
|
|
delete(r.accounts, addr)
|
|
}
|
|
|
|
// RecordDeleteStorage clears a single storage slot for addr.
|
|
func (r *Recorder) RecordDeleteStorage(addr common.Address, key []byte) {
|
|
r.RecordStorage(addr, key, nil)
|
|
}
|
|
|
|
// Alloc returns the recorded post-state as a types.GenesisAlloc. The returned
|
|
// map shares storage with the recorder; callers must not mutate it concurrently
|
|
// with further Record calls.
|
|
func (r *Recorder) Alloc() types.GenesisAlloc {
|
|
out := make(types.GenesisAlloc, len(r.accounts))
|
|
for addr, a := range r.accounts {
|
|
out[addr] = *a
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Has reports whether addr has been recorded.
|
|
func (r *Recorder) Has(addr common.Address) bool {
|
|
_, ok := r.accounts[addr]
|
|
return ok
|
|
}
|
|
|
|
// bytesToHash left-pads short slices into a common.Hash, matching the
|
|
// normalization performed by BinaryTrie.UpdateStorage on values.
|
|
func bytesToHash(b []byte) common.Hash {
|
|
var h common.Hash
|
|
if len(b) >= common.HashLength {
|
|
copy(h[:], b[:common.HashLength])
|
|
} else {
|
|
copy(h[common.HashLength-len(b):], b)
|
|
}
|
|
return h
|
|
}
|