mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-22 15:59:26 +00:00
core, miner, trie: relocate witness stats (#34106)
This PR relocates the witness statistics into the witness itself, making it more self-contained.
This commit is contained in:
parent
acdd139717
commit
c3467dd8b5
6 changed files with 63 additions and 50 deletions
|
|
@ -2170,24 +2170,18 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
var (
|
var witness *stateless.Witness
|
||||||
witness *stateless.Witness
|
|
||||||
witnessStats *stateless.WitnessStats
|
|
||||||
)
|
|
||||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
// Generate witnesses either if we're self-testing, or if it's the
|
// Generate witnesses either if we're self-testing, or if it's the
|
||||||
// only block being inserted. A bit crude, but witnesses are huge,
|
// only block being inserted. A bit crude, but witnesses are huge,
|
||||||
// so we refuse to make an entire chain of them.
|
// so we refuse to make an entire chain of them.
|
||||||
if config.StatelessSelfValidation || config.MakeWitness {
|
if config.StatelessSelfValidation || config.MakeWitness {
|
||||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
witness, err = stateless.NewWitness(block.Header(), bc, config.EnableWitnessStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.EnableWitnessStats {
|
|
||||||
witnessStats = stateless.NewWitnessStats()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
statedb.StartPrefetcher("chain", witness, witnessStats)
|
statedb.StartPrefetcher("chain", witness)
|
||||||
defer statedb.StopPrefetcher()
|
defer statedb.StopPrefetcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2306,8 +2300,8 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
||||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
||||||
}
|
}
|
||||||
// Report the collected witness statistics
|
// Report the collected witness statistics
|
||||||
if witnessStats != nil {
|
if witness != nil {
|
||||||
witnessStats.ReportMetrics(block.NumberU64())
|
witness.ReportMetrics(block.NumberU64())
|
||||||
}
|
}
|
||||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||||
stats.TotalTime = elapsed
|
stats.TotalTime = elapsed
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,7 @@ type StateDB struct {
|
||||||
journal *journal
|
journal *journal
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
witnessStats *stateless.WitnessStats
|
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
AccountReads time.Duration
|
AccountReads time.Duration
|
||||||
|
|
@ -201,13 +200,12 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||||
// state trie concurrently while the state is mutated so that when we reach the
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness, witnessStats *stateless.WitnessStats) {
|
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
||||||
// Terminate any previously running prefetcher
|
// Terminate any previously running prefetcher
|
||||||
s.StopPrefetcher()
|
s.StopPrefetcher()
|
||||||
|
|
||||||
// Enable witness collection if requested
|
// Enable witness collection if requested
|
||||||
s.witness = witness
|
s.witness = witness
|
||||||
s.witnessStats = witnessStats
|
|
||||||
|
|
||||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
// rewards are now handled at the consensus layer. Consequently, a block may
|
||||||
|
|
@ -913,7 +911,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
// If witness building is enabled and the state object has a trie,
|
// If witness building is enabled and the state object has a trie,
|
||||||
// gather the witnesses for its specific storage trie
|
// gather the witnesses for its specific storage trie
|
||||||
if s.witness != nil && obj.trie != nil {
|
if s.witness != nil && obj.trie != nil {
|
||||||
s.witness.AddState(obj.trie.Witness())
|
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -930,17 +928,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||||
witness := trie.Witness()
|
s.witness.AddState(trie.Witness(), obj.addrHash())
|
||||||
s.witness.AddState(witness)
|
|
||||||
if s.witnessStats != nil {
|
|
||||||
s.witnessStats.Add(witness, obj.addrHash())
|
|
||||||
}
|
|
||||||
} else if obj.trie != nil {
|
} else if obj.trie != nil {
|
||||||
witness := obj.trie.Witness()
|
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||||
s.witness.AddState(witness)
|
|
||||||
if s.witnessStats != nil {
|
|
||||||
s.witnessStats.Add(witness, obj.addrHash())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pull in only-read and non-destructed trie witnesses
|
// Pull in only-read and non-destructed trie witnesses
|
||||||
|
|
@ -954,17 +944,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||||
witness := trie.Witness()
|
s.witness.AddState(trie.Witness(), obj.addrHash())
|
||||||
s.witness.AddState(witness)
|
|
||||||
if s.witnessStats != nil {
|
|
||||||
s.witnessStats.Add(witness, obj.addrHash())
|
|
||||||
}
|
|
||||||
} else if obj.trie != nil {
|
} else if obj.trie != nil {
|
||||||
witness := obj.trie.Witness()
|
s.witness.AddState(obj.trie.Witness(), obj.addrHash())
|
||||||
s.witness.AddState(witness)
|
|
||||||
if s.witnessStats != nil {
|
|
||||||
s.witnessStats.Add(witness, obj.addrHash())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1037,11 +1019,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
|
|
||||||
// If witness building is enabled, gather the account trie witness
|
// If witness building is enabled, gather the account trie witness
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
witness := s.trie.Witness()
|
s.witness.AddState(s.trie.Witness(), common.Hash{})
|
||||||
s.witness.AddState(witness)
|
|
||||||
if s.witnessStats != nil {
|
|
||||||
s.witnessStats.Add(witness, common.Hash{})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,13 @@ func NewWitnessStats() *WitnessStats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *WitnessStats) copy() *WitnessStats {
|
||||||
|
return &WitnessStats{
|
||||||
|
accountTrie: s.accountTrie.Copy(),
|
||||||
|
storageTrie: s.storageTrie.Copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *WitnessStats) init() {
|
func (s *WitnessStats) init() {
|
||||||
if s.accountTrie == nil {
|
if s.accountTrie == nil {
|
||||||
s.accountTrie = trie.NewLevelStats()
|
s.accountTrie = trie.NewLevelStats()
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,13 @@ type Witness struct {
|
||||||
Codes map[string]struct{} // Set of bytecodes ran or accessed
|
Codes map[string]struct{} // Set of bytecodes ran or accessed
|
||||||
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
|
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
|
||||||
|
|
||||||
chain HeaderReader // Chain reader to convert block hash ops to header proofs
|
chain HeaderReader // Chain reader to convert block hash ops to header proofs
|
||||||
lock sync.Mutex // Lock to allow concurrent state insertions
|
stats *WitnessStats // Optional statistics collector
|
||||||
|
lock sync.Mutex // Lock to allow concurrent state insertions
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWitness creates an empty witness ready for population.
|
// NewWitness creates an empty witness ready for population.
|
||||||
func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
func NewWitness(context *types.Header, chain HeaderReader, enableStats bool) (*Witness, error) {
|
||||||
// When building witnesses, retrieve the parent header, which will *always*
|
// When building witnesses, retrieve the parent header, which will *always*
|
||||||
// be included to act as a trustless pre-root hash container
|
// be included to act as a trustless pre-root hash container
|
||||||
var headers []*types.Header
|
var headers []*types.Header
|
||||||
|
|
@ -59,13 +60,17 @@ func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
||||||
headers = append(headers, parent)
|
headers = append(headers, parent)
|
||||||
}
|
}
|
||||||
// Create the witness with a reconstructed gutted out block
|
// Create the witness with a reconstructed gutted out block
|
||||||
return &Witness{
|
w := &Witness{
|
||||||
context: context,
|
context: context,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
Codes: make(map[string]struct{}),
|
Codes: make(map[string]struct{}),
|
||||||
State: make(map[string]struct{}),
|
State: make(map[string]struct{}),
|
||||||
chain: chain,
|
chain: chain,
|
||||||
}, nil
|
}
|
||||||
|
if enableStats {
|
||||||
|
w.stats = NewWitnessStats()
|
||||||
|
}
|
||||||
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBlockHash adds a "blockhash" to the witness with the designated offset from
|
// AddBlockHash adds a "blockhash" to the witness with the designated offset from
|
||||||
|
|
@ -87,8 +92,11 @@ func (w *Witness) AddCode(code []byte) {
|
||||||
w.Codes[string(code)] = struct{}{}
|
w.Codes[string(code)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddState inserts a batch of MPT trie nodes into the witness.
|
// AddState inserts a batch of MPT trie nodes into the witness. The owner
|
||||||
func (w *Witness) AddState(nodes map[string][]byte) {
|
// identifies which trie the nodes belong to: the zero hash for the account
|
||||||
|
// trie, or the hashed address for a storage trie. This is used for optional
|
||||||
|
// statistics collection.
|
||||||
|
func (w *Witness) AddState(nodes map[string][]byte, owner common.Hash) {
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +106,17 @@ func (w *Witness) AddState(nodes map[string][]byte) {
|
||||||
for _, value := range nodes {
|
for _, value := range nodes {
|
||||||
w.State[string(value)] = struct{}{}
|
w.State[string(value)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
if w.stats != nil {
|
||||||
|
w.stats.Add(nodes, owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportMetrics reports the collected statistics to the global metrics registry.
|
||||||
|
func (w *Witness) ReportMetrics(blockNumber uint64) {
|
||||||
|
if w.stats == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.stats.ReportMetrics(blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Witness) AddKey() {
|
func (w *Witness) AddKey() {
|
||||||
|
|
@ -113,6 +132,9 @@ func (w *Witness) Copy() *Witness {
|
||||||
State: maps.Clone(w.State),
|
State: maps.Clone(w.State),
|
||||||
chain: w.chain,
|
chain: w.chain,
|
||||||
}
|
}
|
||||||
|
if w.stats != nil {
|
||||||
|
cpy.stats = w.stats.copy()
|
||||||
|
}
|
||||||
if w.context != nil {
|
if w.context != nil {
|
||||||
cpy.context = types.CopyHeader(w.context)
|
cpy.context = types.CopyHeader(w.context)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -330,12 +330,12 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
|
||||||
}
|
}
|
||||||
var bundle *stateless.Witness
|
var bundle *stateless.Witness
|
||||||
if witness {
|
if witness {
|
||||||
bundle, err = stateless.NewWitness(header, miner.chain)
|
bundle, err = stateless.NewWitness(header, miner.chain, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.StartPrefetcher("miner", bundle, nil)
|
state.StartPrefetcher("miner", bundle)
|
||||||
// Note the passed coinbase may be different with header.Coinbase.
|
// Note the passed coinbase may be different with header.Coinbase.
|
||||||
return &environment{
|
return &environment{
|
||||||
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,18 @@ func NewLevelStats() *LevelStats {
|
||||||
return &LevelStats{}
|
return &LevelStats{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a deep copy of the statistics.
|
||||||
|
func (s *LevelStats) Copy() *LevelStats {
|
||||||
|
cpy := NewLevelStats()
|
||||||
|
for i := range s.level {
|
||||||
|
cpy.level[i].short.Store(s.level[i].short.Load())
|
||||||
|
cpy.level[i].full.Store(s.level[i].full.Load())
|
||||||
|
cpy.level[i].value.Store(s.level[i].value.Load())
|
||||||
|
cpy.level[i].size.Store(s.level[i].size.Load())
|
||||||
|
}
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
|
||||||
// MaxDepth iterates each level and finds the deepest level with at least one
|
// MaxDepth iterates each level and finds the deepest level with at least one
|
||||||
// trie node.
|
// trie node.
|
||||||
func (s *LevelStats) MaxDepth() int {
|
func (s *LevelStats) MaxDepth() int {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue