Compare commits

..

21 Commits

Author SHA1 Message Date
VM
d572c77e4c fix: fillTransactions func add time metrics 2024-05-29 15:46:25 +08:00
will@2012
565085959b perf: add more layer tree metrics 2024-05-28 20:43:42 +08:00
will@2012
b7b1b0c001 fix: add temp debug code 2024-05-28 18:52:02 +08:00
will@2012
08702d3380 perf: disable tx indexer 2024-05-21 15:37:20 +08:00
will@2012
91e3a3ea1f perf: add more log 2024-05-20 23:02:40 +08:00
will@2012
73477bd0fc perf: enable tx indexer 2024-05-20 21:10:14 +08:00
will@2012
8749c8e8ce perf: add more metrics/log to perf pebble 2024-05-20 20:40:51 +08:00
will@2012
79fe2899c7 chore: add more metrics to perf pbss 2024-05-20 17:35:29 +08:00
Nathan
f45305b1ad cmd/utils: add a flag to change breathe block interval for testing (#2472) 2024-05-17 16:18:29 +08:00
Eric
d16532d678 internal/ethapi: add optional parameter for blobSidecars (#2468) 2024-05-16 19:07:14 +08:00
buddho
c577ce3720 Merge pull request #2460 from bnb-chain/develop
merge some PRs for v1.4.7(2nd)
2024-05-14 19:59:57 +08:00
Nathan
f2ec3cc6a5 eth/handler: check blobs before broadcast blocks (#2450) 2024-05-13 17:21:45 +08:00
Martin HS
0a2e1282d2 core/rawdb: add sanity-limit to header accessor (#29534) 2024-05-13 17:21:45 +08:00
Nathan
adb5e8fe86 eth/filters: enforce topic-limit early on filter criterias (#29535) (#2448)
This PR adds a limit of 1000 to the "inner" topics in a filter-criteria

Co-authored-by: Martin HS <martin@swende.se>
2024-05-13 17:21:45 +08:00
zzzckck
4ab1c865b2 Merge pull request #2441 from bnb-chain/develop
Draft release v1.4.7
2024-05-10 13:10:56 +08:00
zzzckck
43b2ffa63b Merge pull request #2427 from bnb-chain/develop
Draft release v1.4.6
2024-04-29 14:07:29 +08:00
zzzckck
a05724588f Merge pull request #2388 from bnb-chain/develop
merge develop to master for several 4844 bug fix.
2024-04-10 21:33:55 +08:00
zzzckck
060e5c6b34 Merge pull request #2380 from bnb-chain/develop
Draft release v1.4.5
2024-04-09 11:05:30 +08:00
zzzckck
46df9b4dcb Merge pull request #2366 from bnb-chain/develop
Draft release v1.4.4-beta
2024-04-02 19:24:18 +08:00
zzzckck
7f3f72ed41 Merge pull request #2320 from bnb-chain/develop
Draft release v1.4.3-alpha
2024-03-21 20:47:44 +08:00
zzzckck
cbff31944b Merge pull request #2274 from bnb-chain/develop
draft release v1.4.2-alpha
2024-03-12 14:57:28 +08:00
31 changed files with 456 additions and 403 deletions

View File

@@ -280,6 +280,7 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) {
cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name)
}
cfg.Metrics.EnabledExpensive = true
if ctx.IsSet(utils.MetricsHTTPFlag.Name) {
cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name)
}

View File

@@ -106,12 +106,12 @@ Remove blockchain and state databases`,
dbInspectTrieCmd = &cli.Command{
Action: inspectTrie,
Name: "inspect-trie",
ArgsUsage: "<blocknum> <jobnum> <topn>",
ArgsUsage: "<blocknum> <jobnum>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.SyncModeFlag,
},
Usage: "Inspect the MPT tree of the account and contract. 'blocknum' can be latest/snapshot/number. 'topn' means output the top N storage tries info ranked by the total number of TrieNodes",
Usage: "Inspect the MPT tree of the account and contract.",
Description: `This commands iterates the entrie WorldState.`,
}
dbCheckStateContentCmd = &cli.Command{
@@ -386,7 +386,6 @@ func inspectTrie(ctx *cli.Context) error {
blockNumber uint64
trieRootHash common.Hash
jobnum uint64
topN uint64
)
stack, _ := makeConfigNode(ctx)
@@ -412,25 +411,12 @@ func inspectTrie(ctx *cli.Context) error {
if ctx.NArg() == 1 {
jobnum = 1000
topN = 10
} else if ctx.NArg() == 2 {
var err error
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
if err != nil {
return fmt.Errorf("failed to Parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
}
topN = 10
} else {
var err error
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
if err != nil {
return fmt.Errorf("failed to Parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
}
topN, err = strconv.ParseUint(ctx.Args().Get(2), 10, 64)
if err != nil {
return fmt.Errorf("failed to Parse topn, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
}
}
if blockNumber != math.MaxUint64 {
@@ -451,7 +437,6 @@ func inspectTrie(ctx *cli.Context) error {
if dbScheme == rawdb.PathScheme {
config = &triedb.Config{
PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly),
Cache: 0,
}
} else if dbScheme == rawdb.HashScheme {
config = triedb.HashDefaults
@@ -463,7 +448,7 @@ func inspectTrie(ctx *cli.Context) error {
fmt.Printf("fail to new trie tree, err: %v, rootHash: %v\n", err, trieRootHash.String())
return err
}
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum, int(topN))
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum)
if err != nil {
return err
}

View File

@@ -1,51 +0,0 @@
import { ethers } from "ethers";
import program from "commander";
// depends on ethjs v6.11.0+ for 4844, https://github.com/ethers-io/ethers.js/releases/tag/v6.11.0
// BSC testnet enabled 4844 on block: 39539137
// Usage:
// nvm use 20
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137 --endNum 40345994
program.option("--rpc <Rpc>", "Rpc Server URL");
program.option("--startNum <Num>", "start block", 0);
program.option("--endNum <Num>", "end block", 0);
program.parse(process.argv);
const provider = new ethers.JsonRpcProvider(program.rpc);
const main = async () => {
var startBlock = parseInt(program.startNum)
var endBlock = parseInt(program.endNum)
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
return
}
// if --endNum is not specified, set it to the latest block number.
if (endBlock == 0) {
endBlock = await provider.getBlockNumber();
}
if (startBlock > endBlock) {
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
return
}
for (let i = startBlock; i <= endBlock; i++) {
let blockData = await provider.getBlock(i);
console.log("startBlock:",startBlock, "endBlock:", endBlock, "curBlock", i, "blobGasUsed", blockData.blobGasUsed);
if (blockData.blobGasUsed == 0) {
continue
}
for (let txIndex = 0; txIndex<= blockData.transactions.length - 1; txIndex++) {
let txHash = blockData.transactions[txIndex]
let txData = await provider.getTransaction(txHash);
if (txData.type == 3) {
console.log("BlobTx in block:",i, " txIndex:", txIndex, " txHash:", txHash);
}
}
}
};
main().then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -1,49 +0,0 @@
import { ethers } from "ethers";
import program from "commander";
// Usage:
// node faucet_request.js --rpc localhost:8545 --startNum 39539137
// node faucet_request.js --rpc localhost:8545 --startNum 39539137 --endNum 40345994
// node faucet_request.js --rpc https://data-seed-prebsc-1-s1.bnbchain.org:8545 --startNum 39539137 --endNum 40345994
program.option("--rpc <Rpc>", "Rpc Server URL");
program.option("--startNum <Num>", "start block", 0);
program.option("--endNum <Num>", "end block", 0);
program.parse(process.argv);
const provider = new ethers.JsonRpcProvider(program.rpc);
const main = async () => {
var startBlock = parseInt(program.startNum)
var endBlock = parseInt(program.endNum)
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
return
}
// if --endNum is not specified, set it to the latest block number.
if (endBlock == 0) {
endBlock = await provider.getBlockNumber();
}
if (startBlock > endBlock) {
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
return
}
let startBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", startBlock)
let endBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", endBlock)
const faucetAmount = BigInt(0.3 * 10**18); // Convert 0.3 ether to wei as a BigInt
const numFaucetRequest = (startBalance - endBalance) / faucetAmount;
// Convert BigInt to ether
const startBalanceEth = Number(startBalance) / 10**18;
const endBalanceEth = Number(endBalance) / 10**18;
console.log(`Start Balance: ${startBalanceEth} ETH`);
console.log(`End Balance: ${endBalanceEth} ETH`);
console.log("successful faucet request: ",numFaucetRequest);
};
main().then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -66,31 +66,6 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
return validator
}
// ValidateListsInBody validates that UncleHash, WithdrawalsHash, and WithdrawalsHash correspond to the lists in the block body, respectively.
func ValidateListsInBody(block *types.Block) error {
header := block.Header()
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
}
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
}
// Withdrawals are present after the Shanghai fork.
if header.WithdrawalsHash != nil {
// Withdrawals list must be present in body after Shanghai.
if block.Withdrawals() == nil {
return errors.New("missing withdrawals in block body")
}
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
}
} else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars
// Withdrawals are not allowed prior to shanghai fork
return errors.New("withdrawals present in block body")
}
return nil
}
// ValidateBody validates the given block's uncles and verifies the block
// header's transaction and uncle roots. The headers are assumed to be already
// validated at this point.
@@ -108,12 +83,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
return err
}
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
}
validateFuns := []func() error{
func() error {
return ValidateListsInBody(block)
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
}
return nil
},
func() error {
// Withdrawals are present after the Shanghai fork.
if header.WithdrawalsHash != nil {
// Withdrawals list must be present in body after Shanghai.
if block.Withdrawals() == nil {
return errors.New("missing withdrawals in block body")
}
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
}
} else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars
// Withdrawals are not allowed prior to shanghai fork
return errors.New("withdrawals present in block body")
}
// Blob transactions may be present after the Cancun fork.
var blobs int
for i, tx := range block.Transactions() {

View File

@@ -441,6 +441,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
diskRoot = bc.triedb.Head()
}
}
diskRoot = common.HexToHash("0x59d2a69ad465dbadf78f99635af9ed8125636cbdedc50bda9668ab2ac677b17a")
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "diskRoot", diskRoot)
@@ -576,7 +577,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
}
// Start tx indexer if it's enabled.
if txLookupLimit != nil {
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
// bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
}
return bc, nil
}
@@ -2267,20 +2268,16 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
bc.cacheBlock(block.Hash(), block)
// Update the metrics touched during block processing and validation
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
blockExecutionTimer.Update(ptime) // The time spent on EVM processing
blockValidationTimer.Update(vtime) // The time spent on block validation
// Write the block to the chain and get the status.
var (
@@ -2305,7 +2302,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
blockWriteTimer.UpdateSince(wstart)
blockInsertTimer.UpdateSince(start)
// Report the import stats before returning the various results

View File

@@ -511,12 +511,3 @@ func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscr
func (bc *BlockChain) SubscribeFinalizedHeaderEvent(ch chan<- FinalizedHeaderEvent) event.Subscription {
return bc.scope.Track(bc.finalizedHeaderFeed.Subscribe(ch))
}
// AncientTail retrieves the tail the ancients blocks
func (bc *BlockChain) AncientTail() (uint64, error) {
tail, err := bc.db.Tail()
if err != nil {
return 0, err
}
return tail, nil
}

View File

@@ -18,10 +18,12 @@ package rawdb
import (
"encoding/binary"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
// ReadSnapshotDisabled retrieves if the snapshot maintenance is disabled.
@@ -74,6 +76,10 @@ func DeleteSnapshotRoot(db ethdb.KeyValueWriter) {
// ReadAccountSnapshot retrieves the snapshot entry of an account trie leaf.
func ReadAccountSnapshot(db ethdb.KeyValueReader, hash common.Hash) []byte {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { rawdbGetAccountSnapNodeTimer.UpdateSince(start) }()
}
data, _ := db.Get(accountSnapshotKey(hash))
return data
}
@@ -94,6 +100,10 @@ func DeleteAccountSnapshot(db ethdb.KeyValueWriter, hash common.Hash) {
// ReadStorageSnapshot retrieves the snapshot entry of an storage trie leaf.
func ReadStorageSnapshot(db ethdb.KeyValueReader, accountHash, storageHash common.Hash) []byte {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { rawdbGetStorageSnapNodeTimer.UpdateSince(start) }()
}
data, _ := db.Get(storageSnapshotKey(accountHash, storageHash))
return data
}

View File

@@ -19,11 +19,13 @@ package rawdb
import (
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"golang.org/x/crypto/sha3"
)
@@ -68,6 +70,10 @@ func (h *hasher) release() {
// ReadAccountTrieNode retrieves the account trie node and the associated node
// hash with the specified node path.
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { rawdbGetAccountTrieNodeTimer.UpdateSince(start) }()
}
data, err := db.Get(accountTrieNodeKey(path))
if err != nil {
return nil, common.Hash{}
@@ -116,6 +122,10 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) {
// ReadStorageTrieNode retrieves the storage trie node and the associated node
// hash with the specified node path.
func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { rawdbGetStorageTrieNodeTimer.UpdateSince(start) }()
}
data, err := db.Get(storageTrieNodeKey(accountHash, path))
if err != nil {
return nil, common.Hash{}
@@ -218,7 +228,22 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte {
switch scheme {
case HashScheme:
return ReadLegacyTrieNode(db, hash)
var (
blob []byte
start time.Time
)
start = time.Now()
blob = ReadLegacyTrieNode(db, hash)
if owner == (common.Hash{}) {
if metrics.EnabledExpensive {
rawdbGetAccountTrieNodeTimer.UpdateSince(start)
}
} else {
if metrics.EnabledExpensive {
rawdbGetStorageTrieNodeTimer.UpdateSince(start)
}
}
return blob
case PathScheme:
var (
blob []byte

View File

@@ -239,7 +239,7 @@ func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
// - if maxBytes is not specified, 'count' items will be returned if they are present.
func (f *Freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
if table := f.tables[kind]; table != nil {
return table.RetrieveItems(start-f.offset, count, maxBytes)
return table.RetrieveItems(start, count, maxBytes)
}
return nil, errUnknownTable
}
@@ -252,7 +252,7 @@ func (f *Freezer) Ancients() (uint64, error) {
func (f *Freezer) TableAncients(kind string) (uint64, error) {
f.writeLock.RLock()
defer f.writeLock.RUnlock()
return f.tables[kind].items.Load() + f.offset, nil
return f.tables[kind].items.Load(), nil
}
// ItemAmountInAncient returns the actual length of current ancientDB.

10
core/rawdb/metrics.go Normal file
View File

@@ -0,0 +1,10 @@
package rawdb
import "github.com/ethereum/go-ethereum/metrics"
var (
rawdbGetAccountTrieNodeTimer = metrics.NewRegisteredTimer("rawdb/get/account/trienode/time", nil)
rawdbGetStorageTrieNodeTimer = metrics.NewRegisteredTimer("rawdb/get/storage/trienode/time", nil)
rawdbGetAccountSnapNodeTimer = metrics.NewRegisteredTimer("rawdb/get/account/snapnode/time", nil)
rawdbGetStorageSnapNodeTimer = metrics.NewRegisteredTimer("rawdb/get/storage/snapnode/time", nil)
)

View File

@@ -236,7 +236,7 @@ func New(config Config, diskdb ethdb.KeyValueStore, triedb *triedb.Database, roo
snap.layers[head.Root()] = head
head = head.Parent()
}
log.Info("Snapshot loaded", "diskRoot", snap.diskRoot(), "root", root)
log.Info("Snapshot loaded", "diskRoot", snap.diskRoot(), "root", root, "snapshot_cache_size", common.StorageSize(config.CacheSize)*1024*1024)
return snap, nil
}

View File

@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -29,9 +30,14 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
)
var (
processTxTimer = metrics.NewRegisteredTimer("process/tx/time", nil)
)
// StateProcessor is a basic Processor, which takes care of transitioning
// state from one point to another.
//
@@ -104,6 +110,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
systemTxs := make([]*types.Transaction, 0, 2)
for i, tx := range block.Transactions() {
if metrics.EnabledExpensive {
start := time.Now()
defer processTxTimer.UpdateSince(start)
}
if isPoSA {
if isSystemTx, err := posa.IsSystemTransaction(tx, block.Header()); err != nil {
bloomProcessors.Close()

View File

@@ -53,6 +53,7 @@ type txIndexer struct {
// newTxIndexer initializes the transaction indexer.
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
limit = 0
indexer := &txIndexer{
limit: limit,
db: chain.db,

View File

@@ -161,11 +161,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// Optimize memory distribution by reallocating surplus allowance from the
// dirty cache to the clean cache.
if config.StateScheme == rawdb.PathScheme && config.TrieDirtyCache > pathdb.MaxDirtyBufferSize/1024/1024 {
config.TrieCleanCache += config.TrieDirtyCache - pathdb.MaxDirtyBufferSize/1024/1024
config.TrieDirtyCache = pathdb.MaxDirtyBufferSize / 1024 / 1024
log.Info("Capped dirty cache size", "provided", common.StorageSize(config.TrieDirtyCache)*1024*1024, "adjusted", common.StorageSize(pathdb.MaxDirtyBufferSize))
log.Info("Clean cache size", "provided", common.StorageSize(config.TrieCleanCache)*1024*1024)
config.TrieDirtyCache = pathdb.MaxDirtyBufferSize / 1024 / 1024
}
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
log.Info("Allocated trie memory caches", "schema", config.StateScheme, "trie_clean_cache", common.StorageSize(config.TrieCleanCache)*1024*1024, "trie_dirty_cache", common.StorageSize(config.TrieDirtyCache)*1024*1024, "snapshot_cache", common.StorageSize(config.SnapshotCache)*1024*1024)
// Try to recover offline state pruning only in hash-based.
if config.StateScheme == rawdb.HashScheme {

View File

@@ -209,9 +209,6 @@ type BlockChain interface {
// UpdateChasingHead update remote best chain head, used by DA check now.
UpdateChasingHead(head *types.Header)
// AncientTail retrieves the tail the ancients blocks
AncientTail() (uint64, error)
}
type DownloadOption func(downloader *Downloader) *Downloader
@@ -800,11 +797,6 @@ func (d *Downloader) findAncestor(p *peerConnection, localHeight uint64, remoteH
// We're above the max reorg threshold, find the earliest fork point
floor = int64(localHeight - maxForkAncestry)
}
// if we have pruned too much history, reset the floor
if tail, err := d.blockchain.AncientTail(); err == nil && tail > uint64(floor) {
floor = int64(tail)
}
// If we're doing a light sync, ensure the floor doesn't go below the CHT, as
// all headers before that point will be missing.
if mode == LightSync {

View File

@@ -320,22 +320,26 @@ func newHandler(config *handlerConfig) (*handler, error) {
}
broadcastBlockWithCheck := func(block *types.Block, propagate bool) {
// All the block fetcher activities should be disabled
// after the transition. Print the warning log.
if h.merger.PoSFinalized() {
log.Warn("Unexpected validation activity", "hash", block.Hash(), "number", block.Number())
return
}
// Reject all the PoS style headers in the first place. No matter
// the chain has finished the transition or not, the PoS headers
// should only come from the trusted consensus layer instead of
// p2p network.
if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok {
if beacon.IsPoSHeader(block.Header()) {
log.Warn("unexpected post-merge header")
return
}
}
if propagate {
checkErrs := make(chan error, 2)
go func() {
checkErrs <- core.ValidateListsInBody(block)
}()
go func() {
checkErrs <- core.IsDataAvailable(h.chain, block)
}()
for i := 0; i < cap(checkErrs); i++ {
err := <-checkErrs
if err != nil {
log.Error("Propagating invalid block", "number", block.Number(), "hash", block.Hash(), "err", err)
return
}
if err := core.IsDataAvailable(h.chain, block); err != nil {
log.Error("Propagating block with invalid sidecars", "number", block.Number(), "hash", block.Hash(), "err", err)
return
}
}
h.BroadcastBlock(block, propagate)

View File

@@ -131,9 +131,9 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb
}
// BlobSidecars return the Sidecars of a given block number or hash.
func (ec *Client) BlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, fullBlob bool) ([]*types.BlobTxSidecar, error) {
func (ec *Client) BlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.BlobTxSidecar, error) {
var r []*types.BlobTxSidecar
err := ec.c.CallContext(ctx, &r, "eth_getBlobSidecars", blockNrOrHash.String(), fullBlob)
err := ec.c.CallContext(ctx, &r, "eth_getBlobSidecars", blockNrOrHash.String(), true)
if err == nil && r == nil {
return nil, ethereum.NotFound
}
@@ -141,9 +141,9 @@ func (ec *Client) BlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumbe
}
// BlobSidecarByTxHash return a sidecar of a given blob transaction
func (ec *Client) BlobSidecarByTxHash(ctx context.Context, hash common.Hash, fullBlob bool) (*types.BlobTxSidecar, error) {
func (ec *Client) BlobSidecarByTxHash(ctx context.Context, hash common.Hash) (*types.BlobTxSidecar, error) {
var r *types.BlobTxSidecar
err := ec.c.CallContext(ctx, &r, "eth_getBlockSidecarByTxHash", hash, fullBlob)
err := ec.c.CallContext(ctx, &r, "eth_getBlockSidecarByTxHash", hash, true)
if err == nil && r == nil {
return nil, ethereum.NotFound
}

View File

@@ -111,12 +111,18 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
func NewCustom(file string, namespace string, customize func(options *opt.Options)) (*Database, error) {
options := configureOptions(customize)
logger := log.New("database", file)
usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2
logCtx := []interface{}{"cache", common.StorageSize(usedCache), "handles", options.GetOpenFilesCacheCapacity()}
// usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2
logCtx := []interface{}{"handles", options.GetOpenFilesCacheCapacity()}
if options.ReadOnly {
logCtx = append(logCtx, "readonly", "true")
}
logger.Info("Allocated cache and file handles", logCtx...)
if options.BlockCacheCapacity != 0 {
logCtx = append(logCtx, "block_cache_size", common.StorageSize(options.BlockCacheCapacity))
}
if options.WriteBuffer != 0 {
logCtx = append(logCtx, "memory_table_size", common.StorageSize(options.WriteBuffer))
}
logger.Info("Level db Allocated cache and file handles", logCtx...)
// Open the db and recover any potential corruptions
db, err := leveldb.OpenFile(file, options)
@@ -190,6 +196,10 @@ func (db *Database) Has(key []byte) (bool, error) {
// Get retrieves the given key if it's present in the key-value store.
func (db *Database) Get(key []byte) ([]byte, error) {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbGetTimer.UpdateSince(start) }()
}
dat, err := db.db.Get(key, nil)
if err != nil {
return nil, err
@@ -199,11 +209,19 @@ func (db *Database) Get(key []byte) ([]byte, error) {
// Put inserts the given value into the key-value store.
func (db *Database) Put(key []byte, value []byte) error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbPutTimer.UpdateSince(start) }()
}
return db.db.Put(key, value, nil)
}
// Delete removes the key from the key-value store.
func (db *Database) Delete(key []byte) error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbDeleteTimer.UpdateSince(start) }()
}
return db.db.Delete(key, nil)
}
@@ -301,6 +319,8 @@ func (db *Database) meter(refresh time.Duration, namespace string) {
merr = err
continue
}
fmt.Printf("loop print level db stats db_metrics=\n%v\n", stats)
db.log.Info("loop print level db stats", "stats", stats)
// Iterate over all the leveldbTable rows, and accumulate the entries
for j := 0; j < len(compactions[i%2]); j++ {
compactions[i%2][j] = 0
@@ -414,6 +434,10 @@ func (b *batch) ValueSize() int {
// Write flushes any accumulated data to disk.
func (b *batch) Write() error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbBatchWriteTimer.UpdateSince(start) }()
}
return b.db.Write(b.b, nil)
}

11
ethdb/metrics.go Normal file
View File

@@ -0,0 +1,11 @@
package ethdb
import "github.com/ethereum/go-ethereum/metrics"
var (
EthdbGetTimer = metrics.NewRegisteredTimer("ethdb/get/time", nil)
EthdbInnerGetTimer = metrics.NewRegisteredTimer("ethdb/inner/get/time", nil)
EthdbPutTimer = metrics.NewRegisteredTimer("ethdb/put/time", nil)
EthdbDeleteTimer = metrics.NewRegisteredTimer("ethdb/delete/time", nil)
EthdbBatchWriteTimer = metrics.NewRegisteredTimer("ethdb/batch/write/time", nil)
)

View File

@@ -183,8 +183,8 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e
memTableSize = maxMemTableSize - 1
}
logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024),
"handles", handles, "memory table", common.StorageSize(memTableSize))
logger.Info("Pebble db Allocated cache and file handles", "handles", handles, "block_cache_size", common.StorageSize(cache*1024*1024),
"memory_table_size", common.StorageSize(memTableSize))
db := &Database{
fn: file,
@@ -309,23 +309,69 @@ func (d *Database) Has(key []byte) (bool, error) {
// Get retrieves the given key if it's present in the key-value store.
func (d *Database) Get(key []byte) ([]byte, error) {
var (
step1Start time.Time
step1End time.Time
step2Start time.Time
step2End time.Time
step3Start time.Time
step3End time.Time
step4Start time.Time
step4End time.Time
keyLen int
valueLen int
)
if metrics.EnabledExpensive {
start := time.Now()
defer func() {
ethdb.EthdbGetTimer.UpdateSince(start)
if time.Now().Sub(start) > 100*time.Millisecond {
d.log.Error("perf pebble read",
"key", key,
"key_len", keyLen,
"value_len", valueLen,
"step1", common.PrettyDuration(step1End.Sub(step1Start)),
"step2", common.PrettyDuration(step2End.Sub(step2Start)),
"step3", common.PrettyDuration(step3End.Sub(step3Start)),
"step4", common.PrettyDuration(step4End.Sub(step4Start)))
}
}()
}
keyLen = len(key)
step1Start = time.Now()
d.quitLock.RLock()
step1End = time.Now()
defer d.quitLock.RUnlock()
if d.closed {
return nil, pebble.ErrClosed
}
step2Start = time.Now()
innerStart := time.Now()
dat, closer, err := d.db.Get(key)
valueLen = len(dat)
ethdb.EthdbInnerGetTimer.UpdateSince(innerStart)
step2End = time.Now()
if err != nil {
return nil, err
}
step3Start = time.Now()
ret := make([]byte, len(dat))
copy(ret, dat)
step3End = time.Now()
step4Start = time.Now()
closer.Close()
step4End = time.Now()
return ret, nil
}
// Put inserts the given value into the key-value store.
func (d *Database) Put(key []byte, value []byte) error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbPutTimer.UpdateSince(start) }()
}
d.quitLock.RLock()
defer d.quitLock.RUnlock()
if d.closed {
@@ -336,6 +382,10 @@ func (d *Database) Put(key []byte, value []byte) error {
// Delete removes the key from the key-value store.
func (d *Database) Delete(key []byte) error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbDeleteTimer.UpdateSince(start) }()
}
d.quitLock.RLock()
defer d.quitLock.RUnlock()
if d.closed {
@@ -494,6 +544,9 @@ func (d *Database) meter(refresh time.Duration, namespace string) {
nonLevel0CompCount = int64(d.nonLevel0Comp.Load())
level0CompCount = int64(d.level0Comp.Load())
)
fmt.Printf("loop print pebble db stats db_metrics=\n%v\n", stats)
d.log.Info("loop print pebble db stats", "comp_time", compTime, "write_delay_count", writeDelayCount, "write_delay_time",
writeDelayTime, "non_level0_comp_count", nonLevel0CompCount, "level0_comp_count", level0CompCount)
writeDelayTimes[i%2] = writeDelayTime
writeDelayCounts[i%2] = writeDelayCount
compTimes[i%2] = compTime
@@ -599,6 +652,10 @@ func (b *batch) ValueSize() int {
// Write flushes any accumulated data to disk.
func (b *batch) Write() error {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { ethdb.EthdbBatchWriteTimer.UpdateSince(start) }()
}
b.db.quitLock.RLock()
defer b.db.quitLock.RUnlock()
if b.db.closed {

View File

@@ -1010,7 +1010,11 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.
return result, nil
}
func (s *BlockChainAPI) GetBlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, fullBlob bool) ([]map[string]interface{}, error) {
func (s *BlockChainAPI) GetBlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, fullBlob *bool) ([]map[string]interface{}, error) {
showBlob := true
if fullBlob != nil {
showBlob = *fullBlob
}
header, err := s.b.HeaderByNumberOrHash(ctx, blockNrOrHash)
if header == nil || err != nil {
// When the block doesn't exist, the RPC method should return JSON null
@@ -1023,12 +1027,16 @@ func (s *BlockChainAPI) GetBlobSidecars(ctx context.Context, blockNrOrHash rpc.B
}
result := make([]map[string]interface{}, len(blobSidecars))
for i, sidecar := range blobSidecars {
result[i] = marshalBlobSidecar(sidecar, fullBlob)
result[i] = marshalBlobSidecar(sidecar, showBlob)
}
return result, nil
}
func (s *BlockChainAPI) GetBlobSidecarByTxHash(ctx context.Context, hash common.Hash, fullBlob bool) (map[string]interface{}, error) {
func (s *BlockChainAPI) GetBlobSidecarByTxHash(ctx context.Context, hash common.Hash, fullBlob *bool) (map[string]interface{}, error) {
showBlob := true
if fullBlob != nil {
showBlob = *fullBlob
}
txTarget, blockHash, _, Index := rawdb.ReadTransaction(s.b.ChainDb(), hash)
if txTarget == nil {
return nil, nil
@@ -1045,7 +1053,7 @@ func (s *BlockChainAPI) GetBlobSidecarByTxHash(ctx context.Context, hash common.
}
for _, sidecar := range blobSidecars {
if sidecar.TxIndex == Index {
return marshalBlobSidecar(sidecar, fullBlob), nil
return marshalBlobSidecar(sidecar, showBlob), nil
}
}

View File

@@ -2204,7 +2204,7 @@ func TestRPCGetBlobSidecars(t *testing.T) {
result interface{}
err error
)
result, err = api.GetBlobSidecars(context.Background(), tt.test, tt.fullBlob)
result, err = api.GetBlobSidecars(context.Background(), tt.test, &tt.fullBlob)
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
@@ -2254,7 +2254,7 @@ func TestGetBlobSidecarByTxHash(t *testing.T) {
fullBlob: true,
file: "block-with-blobSidecars",
},
// 4. block show part blobs
// 5. block show part blobs
{
test: txHashs[6],
fullBlob: false,
@@ -2267,7 +2267,7 @@ func TestGetBlobSidecarByTxHash(t *testing.T) {
result interface{}
err error
)
result, err = api.GetBlobSidecarByTxHash(context.Background(), tt.test, tt.fullBlob)
result, err = api.GetBlobSidecarByTxHash(context.Background(), tt.test, &tt.fullBlob)
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue

View File

@@ -37,8 +37,8 @@ type Config struct {
// DefaultConfig is the default config for metrics used in go-ethereum.
var DefaultConfig = Config{
Enabled: false,
EnabledExpensive: false,
Enabled: true,
EnabledExpensive: true,
HTTP: "127.0.0.1",
Port: 6060,
EnableInfluxDB: false,

View File

@@ -22,12 +22,12 @@ import (
//
// This global kill-switch helps quantify the observer effect and makes
// for less cluttered pprof profiles.
var Enabled = false
var Enabled = true
// EnabledExpensive is a soft-flag meant for external packages to check if costly
// metrics gathering is allowed or not. The goal is to separate standard metrics
// for health monitoring and debug metrics that might impact runtime performance.
var EnabledExpensive = false
var EnabledExpensive = true
// enablerFlags is the CLI flag names to use to enable metrics collections.
var enablerFlags = []string{"metrics"}

View File

@@ -70,8 +70,10 @@ const (
)
var (
writeBlockTimer = metrics.NewRegisteredTimer("worker/writeblock", nil)
finalizeBlockTimer = metrics.NewRegisteredTimer("worker/finalizeblock", nil)
writeBlockTimer = metrics.NewRegisteredTimer("worker/writeblock", nil)
finalizeBlockTimer = metrics.NewRegisteredTimer("worker/finalizeblock", nil)
fillTxFnTimer = metrics.NewRegisteredTimer("worker/filltransactions/all", nil)
fillTxFnPartialTimer = metrics.NewRegisteredTimer("worker/filltransactions/partial", nil)
errBlockInterruptedByNewHead = errors.New("new head arrived while building block")
errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
@@ -1056,6 +1058,8 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
// into the given sealing block. The transaction selection and ordering strategy can
// be customized with the plugin in the future.
func (w *worker) fillTransactions(interruptCh chan int32, env *environment, stopTimer *time.Timer, bidTxs mapset.Set[common.Hash]) (err error) {
start := time.Now()
w.mu.RLock()
tip := w.tip
w.mu.RUnlock()
@@ -1110,6 +1114,7 @@ func (w *worker) fillTransactions(interruptCh chan int32, env *environment, stop
localBlobTxs[account] = txs
}
}
fillTxFnPartialTimer.UpdateSince(start)
// Fill the block with all available pending transactions.
// we will abort when:
@@ -1134,6 +1139,7 @@ func (w *worker) fillTransactions(interruptCh chan int32, env *environment, stop
return err
}
}
fillTxFnTimer.UpdateSince(start)
return nil
}

View File

@@ -4,15 +4,17 @@ import (
"bytes"
"errors"
"fmt"
"math/big"
"os"
"runtime"
"strings"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
@@ -24,113 +26,63 @@ import (
"golang.org/x/sync/semaphore"
)
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
type Database interface {
database.Database
Scheme() string
Cap(limit common.StorageSize) error
DiskDB() ethdb.Database
}
const TopN = 3
type Inspector struct {
trie *Trie // traverse trie
db Database
stateRootHash common.Hash
blockNum uint64
blocknum uint64
root node // root of triedb
totalNum uint64
wg sync.WaitGroup
statLock sync.RWMutex
result map[string]*TrieTreeStat
sem *semaphore.Weighted
eoaAccountNums uint64
wg sync.WaitGroup
results stat
topN int
totalAccountNum atomic.Uint64
totalStorageNum atomic.Uint64
lastTime mclock.AbsTime
}
type stat struct {
lock sync.RWMutex
account *trieStat
storageTopN []*trieStat
storageTopNTotal []uint64
storageTotal nodeStat
storageTrieNum uint64
type TrieTreeStat struct {
is_account_trie bool
theNodeStatByLevel [15]NodeStat
totalNodeStat NodeStat
}
type trieStat struct {
owner common.Hash
totalNodeStat nodeStat
nodeStatByLevel [16]nodeStat
type NodeStat struct {
ShortNodeCnt uint64
FullNodeCnt uint64
ValueNodeCnt uint64
}
type nodeStat struct {
ShortNodeCnt atomic.Uint64
FullNodeCnt atomic.Uint64
ValueNodeCnt atomic.Uint64
}
func (ns *nodeStat) IsEmpty() bool {
if ns.FullNodeCnt.Load() == 0 && ns.ShortNodeCnt.Load() == 0 && ns.ValueNodeCnt.Load() == 0 {
return true
}
return false
}
func (s *stat) add(ts *trieStat, topN int) {
s.lock.Lock()
defer s.lock.Unlock()
if ts.owner == (common.Hash{}) {
s.account = ts
return
}
total := ts.totalNodeStat.ValueNodeCnt.Load() + ts.totalNodeStat.FullNodeCnt.Load() + ts.totalNodeStat.ShortNodeCnt.Load()
if len(s.storageTopNTotal) == 0 || total > s.storageTopNTotal[len(s.storageTopNTotal)-1] {
var (
i int
t uint64
)
for i, t = range s.storageTopNTotal {
if total < t {
continue
}
break
}
s.storageTopNTotal = append(s.storageTopNTotal[:i], append([]uint64{total}, s.storageTopNTotal[i:]...)...)
s.storageTopN = append(s.storageTopN[:i], append([]*trieStat{ts}, s.storageTopN[i:]...)...)
if len(s.storageTopN) > topN {
s.storageTopNTotal = s.storageTopNTotal[:topN]
s.storageTopN = s.storageTopN[:topN]
}
}
s.storageTotal.ShortNodeCnt.Add(ts.totalNodeStat.ShortNodeCnt.Load())
s.storageTotal.ValueNodeCnt.Add(ts.totalNodeStat.ValueNodeCnt.Load())
s.storageTotal.FullNodeCnt.Add(ts.totalNodeStat.FullNodeCnt.Load())
s.storageTrieNum++
}
func (trieStat *trieStat) add(theNode node, height int) {
func (trieStat *TrieTreeStat) AtomicAdd(theNode node, height uint32) {
switch (theNode).(type) {
case *shortNode:
trieStat.totalNodeStat.ShortNodeCnt.Add(1)
trieStat.nodeStatByLevel[height].ShortNodeCnt.Add(1)
atomic.AddUint64(&trieStat.totalNodeStat.ShortNodeCnt, 1)
atomic.AddUint64(&(trieStat.theNodeStatByLevel[height].ShortNodeCnt), 1)
case *fullNode:
trieStat.totalNodeStat.FullNodeCnt.Add(1)
trieStat.nodeStatByLevel[height].FullNodeCnt.Add(1)
atomic.AddUint64(&trieStat.totalNodeStat.FullNodeCnt, 1)
atomic.AddUint64(&trieStat.theNodeStatByLevel[height].FullNodeCnt, 1)
case valueNode:
trieStat.totalNodeStat.ValueNodeCnt.Add(1)
trieStat.nodeStatByLevel[height].ValueNodeCnt.Add(1)
atomic.AddUint64(&trieStat.totalNodeStat.ValueNodeCnt, 1)
atomic.AddUint64(&((trieStat.theNodeStatByLevel[height]).ValueNodeCnt), 1)
default:
panic(errors.New("Invalid node type to statistics"))
}
}
func (trieStat *trieStat) Display(ownerAddress string, treeType string) string {
sw := new(strings.Builder)
table := tablewriter.NewWriter(sw)
func (trieStat *TrieTreeStat) Display(ownerAddress string, treeType string) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"-", "Level", "ShortNodeCnt", "FullNodeCnt", "ValueNodeCnt"})
if ownerAddress == "" {
table.SetCaption(true, fmt.Sprintf("%v", treeType))
@@ -138,27 +90,38 @@ func (trieStat *trieStat) Display(ownerAddress string, treeType string) string {
table.SetCaption(true, fmt.Sprintf("%v-%v", treeType, ownerAddress))
}
table.SetAlignment(1)
for i := range trieStat.nodeStatByLevel {
if trieStat.nodeStatByLevel[i].IsEmpty() {
continue
for i := 0; i < len(trieStat.theNodeStatByLevel); i++ {
nodeStat := trieStat.theNodeStatByLevel[i]
if nodeStat.FullNodeCnt == 0 && nodeStat.ShortNodeCnt == 0 && nodeStat.ValueNodeCnt == 0 {
break
}
table.AppendBulk([][]string{
{"-", fmt.Sprintf("%d", i),
fmt.Sprintf("%d", trieStat.nodeStatByLevel[i].ShortNodeCnt.Load()),
fmt.Sprintf("%d", trieStat.nodeStatByLevel[i].FullNodeCnt.Load()),
fmt.Sprintf("%d", trieStat.nodeStatByLevel[i].ValueNodeCnt.Load())},
{"-", strconv.Itoa(i), nodeStat.ShortNodeCount(), nodeStat.FullNodeCount(), nodeStat.ValueNodeCount()},
})
}
table.AppendBulk([][]string{
{"Total", "-", fmt.Sprintf("%d", trieStat.totalNodeStat.ShortNodeCnt.Load()), fmt.Sprintf("%d", trieStat.totalNodeStat.FullNodeCnt.Load()), fmt.Sprintf("%d", trieStat.totalNodeStat.ValueNodeCnt.Load())},
{"Total", "-", trieStat.totalNodeStat.ShortNodeCount(), trieStat.totalNodeStat.FullNodeCount(), trieStat.totalNodeStat.ValueNodeCount()},
})
table.Render()
return sw.String()
}
func Uint64ToString(cnt uint64) string {
return fmt.Sprintf("%v", cnt)
}
func (nodeStat *NodeStat) ShortNodeCount() string {
return Uint64ToString(nodeStat.ShortNodeCnt)
}
func (nodeStat *NodeStat) FullNodeCount() string {
return Uint64ToString(nodeStat.FullNodeCnt)
}
func (nodeStat *NodeStat) ValueNodeCount() string {
return Uint64ToString(nodeStat.ValueNodeCnt)
}
// NewInspector return a inspector obj
func NewInspector(tr *Trie, db Database, stateRootHash common.Hash, blockNum uint64, jobNum uint64, topN int) (*Inspector, error) {
func NewInspector(tr *Trie, db Database, stateRootHash common.Hash, blocknum uint64, jobnum uint64) (*Inspector, error) {
if tr == nil {
return nil, errors.New("trie is nil")
}
@@ -168,20 +131,15 @@ func NewInspector(tr *Trie, db Database, stateRootHash common.Hash, blockNum uin
}
ins := &Inspector{
trie: tr,
db: db,
stateRootHash: stateRootHash,
blockNum: blockNum,
root: tr.root,
results: stat{},
topN: topN,
totalAccountNum: atomic.Uint64{},
totalStorageNum: atomic.Uint64{},
lastTime: mclock.Now(),
sem: semaphore.NewWeighted(int64(jobNum)),
wg: sync.WaitGroup{},
trie: tr,
db: db,
stateRootHash: stateRootHash,
blocknum: blocknum,
root: tr.root,
result: make(map[string]*TrieTreeStat),
totalNum: (uint64)(0),
wg: sync.WaitGroup{},
sem: semaphore.NewWeighted(int64(jobnum)),
eoaAccountNums: 0,
}
@@ -189,123 +147,155 @@ func NewInspector(tr *Trie, db Database, stateRootHash common.Hash, blockNum uin
}
// Run statistics, external call
func (s *Inspector) Run() {
ticker := time.NewTicker(30 * time.Second)
go func() {
defer ticker.Stop()
for range ticker.C {
if s.db.Scheme() == rawdb.HashScheme {
s.db.Cap(DEFAULT_TRIEDBCACHE_SIZE)
}
runtime.GC()
}
}()
log.Info("Find Account Trie Tree", "rootHash: ", s.trie.Hash().String(), "BlockNum: ", s.blockNum)
ts := &trieStat{
owner: common.Hash{},
func (inspect *Inspector) Run() {
accountTrieStat := &TrieTreeStat{
is_account_trie: true,
}
s.traversal(s.trie, ts, s.root, 0, []byte{})
s.results.add(ts, s.topN)
s.wg.Wait()
if inspect.db.Scheme() == rawdb.HashScheme {
ticker := time.NewTicker(30 * time.Second)
go func() {
defer ticker.Stop()
for range ticker.C {
inspect.db.Cap(DEFAULT_TRIEDBCACHE_SIZE)
}
}()
}
if _, ok := inspect.result[""]; !ok {
inspect.result[""] = accountTrieStat
}
log.Info("Find Account Trie Tree", "rootHash: ", inspect.trie.Hash().String(), "BlockNum: ", inspect.blocknum)
inspect.ConcurrentTraversal(inspect.trie, accountTrieStat, inspect.root, 0, []byte{})
inspect.wg.Wait()
}
func (s *Inspector) traversal(trie *Trie, ts *trieStat, n node, height int, path []byte) {
func (inspect *Inspector) SubConcurrentTraversal(theTrie *Trie, theTrieTreeStat *TrieTreeStat, theNode node, height uint32, path []byte) {
inspect.ConcurrentTraversal(theTrie, theTrieTreeStat, theNode, height, path)
inspect.wg.Done()
}
func (inspect *Inspector) ConcurrentTraversal(theTrie *Trie, theTrieTreeStat *TrieTreeStat, theNode node, height uint32, path []byte) {
// print process progress
total_num := atomic.AddUint64(&inspect.totalNum, 1)
if total_num%100000 == 0 {
fmt.Printf("Complete progress: %v, go routines Num: %v\n", total_num, runtime.NumGoroutine())
}
// nil node
if n == nil {
if theNode == nil {
return
}
ts.add(n, height)
switch current := (n).(type) {
switch current := (theNode).(type) {
case *shortNode:
s.traversal(trie, ts, current.Val, height, append(path, current.Key...))
inspect.ConcurrentTraversal(theTrie, theTrieTreeStat, current.Val, height, append(path, current.Key...))
case *fullNode:
for idx, child := range current.Children {
if child == nil {
continue
}
p := common.CopyBytes(append(path, byte(idx)))
s.traversal(trie, ts, child, height+1, p)
childPath := append(path, byte(idx))
if inspect.sem.TryAcquire(1) {
inspect.wg.Add(1)
dst := make([]byte, len(childPath))
copy(dst, childPath)
go inspect.SubConcurrentTraversal(theTrie, theTrieTreeStat, child, height+1, dst)
} else {
inspect.ConcurrentTraversal(theTrie, theTrieTreeStat, child, height+1, childPath)
}
}
case hashNode:
tn, err := trie.resloveWithoutTrack(current, path)
n, err := theTrie.resloveWithoutTrack(current, path)
if err != nil {
fmt.Printf("Resolve HashNode error: %v, TrieRoot: %v, Height: %v, Path: %v\n", err, trie.Hash().String(), height+1, path)
fmt.Printf("Resolve HashNode error: %v, TrieRoot: %v, Height: %v, Path: %v\n", err, theTrie.Hash().String(), height+1, path)
return
}
s.PrintProgress(trie)
s.traversal(trie, ts, tn, height, path)
inspect.ConcurrentTraversal(theTrie, theTrieTreeStat, n, height, path)
return
case valueNode:
if !hasTerm(path) {
break
}
var account types.StateAccount
var account Account
if err := rlp.Decode(bytes.NewReader(current), &account); err != nil {
break
}
if common.BytesToHash(account.CodeHash) == types.EmptyCodeHash {
s.eoaAccountNums++
inspect.eoaAccountNums++
}
if account.Root == (common.Hash{}) || account.Root == types.EmptyRootHash {
break
}
ownerAddress := common.BytesToHash(hexToCompact(path))
contractTrie, err := New(StorageTrieID(s.stateRootHash, ownerAddress, account.Root), s.db)
contractTrie, err := New(StorageTrieID(inspect.stateRootHash, ownerAddress, account.Root), inspect.db)
if err != nil {
panic(err)
fmt.Printf("New contract trie node: %v, error: %v, Height: %v, Path: %v\n", theNode, err, height, path)
break
}
contractTrie.tracer.reset()
if s.sem.TryAcquire(1) {
s.wg.Add(1)
go func() {
t := &trieStat{
owner: ownerAddress,
}
s.traversal(contractTrie, t, contractTrie.root, 0, []byte{})
s.results.add(t, s.topN)
s.sem.Release(1)
s.wg.Done()
}()
} else {
t := &trieStat{
owner: ownerAddress,
}
s.traversal(contractTrie, t, contractTrie.root, 0, []byte{})
s.results.add(t, s.topN)
trieStat := &TrieTreeStat{
is_account_trie: false,
}
inspect.statLock.Lock()
if _, ok := inspect.result[ownerAddress.String()]; !ok {
inspect.result[ownerAddress.String()] = trieStat
}
inspect.statLock.Unlock()
// log.Info("Find Contract Trie Tree, rootHash: ", contractTrie.Hash().String(), "")
inspect.wg.Add(1)
go inspect.SubConcurrentTraversal(contractTrie, trieStat, contractTrie.root, 0, []byte{})
default:
panic(errors.New("invalid node type to traverse"))
panic(errors.New("Invalid node type to traverse."))
}
theTrieTreeStat.AtomicAdd(theNode, height)
}
func (s *Inspector) PrintProgress(t *Trie) {
var (
elapsed = mclock.Now().Sub(s.lastTime)
)
if t.owner == (common.Hash{}) {
s.totalAccountNum.Add(1)
} else {
s.totalStorageNum.Add(1)
}
if elapsed > 4*time.Second {
log.Info("traversal progress", "TotalAccountNum", s.totalAccountNum.Load(), "TotalStorageNum", s.totalStorageNum.Load(), "Goroutine", runtime.NumGoroutine())
s.lastTime = mclock.Now()
}
}
func (s *Inspector) DisplayResult() {
func (inspect *Inspector) DisplayResult() {
// display root hash
fmt.Println(s.results.account.Display("", "AccountTrie"))
fmt.Println("EOA accounts num: ", s.eoaAccountNums)
if _, ok := inspect.result[""]; !ok {
log.Info("Display result error", "missing account trie")
return
}
inspect.result[""].Display("", "AccountTrie")
type SortedTrie struct {
totalNum uint64
ownerAddress string
}
// display contract trie
for _, st := range s.results.storageTopN {
fmt.Println(st.Display(st.owner.String(), "StorageTrie"))
var sortedTriesByNums []SortedTrie
var totalContactsNodeStat NodeStat
var contractTrieCnt uint64 = 0
for ownerAddress, stat := range inspect.result {
if ownerAddress == "" {
continue
}
contractTrieCnt++
totalContactsNodeStat.ShortNodeCnt += stat.totalNodeStat.ShortNodeCnt
totalContactsNodeStat.FullNodeCnt += stat.totalNodeStat.FullNodeCnt
totalContactsNodeStat.ValueNodeCnt += stat.totalNodeStat.ValueNodeCnt
totalNodeCnt := stat.totalNodeStat.ShortNodeCnt + stat.totalNodeStat.ValueNodeCnt + stat.totalNodeStat.FullNodeCnt
sortedTriesByNums = append(sortedTriesByNums, SortedTrie{totalNum: totalNodeCnt, ownerAddress: ownerAddress})
}
sort.Slice(sortedTriesByNums, func(i, j int) bool {
return sortedTriesByNums[i].totalNum > sortedTriesByNums[j].totalNum
})
fmt.Println("EOA accounts num: ", inspect.eoaAccountNums)
// only display top 5
for i, t := range sortedTriesByNums {
if i > 5 {
break
}
if stat, ok := inspect.result[t.ownerAddress]; !ok {
log.Error("Storage trie stat not found", "ownerAddress", t.ownerAddress)
} else {
stat.Display(t.ownerAddress, "ContractTrie")
}
}
fmt.Printf("Contract Trie, total trie num: %v, ShortNodeCnt: %v, FullNodeCnt: %v, ValueNodeCnt: %v\n",
s.results.storageTrieNum, s.results.storageTotal.ShortNodeCnt.Load(), s.results.storageTotal.FullNodeCnt.Load(), s.results.storageTotal.ValueNodeCnt.Load())
contractTrieCnt, totalContactsNodeStat.ShortNodeCnt, totalContactsNodeStat.FullNodeCnt, totalContactsNodeStat.ValueNodeCnt)
}

View File

@@ -21,14 +21,22 @@ import (
"bytes"
"errors"
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb/database"
)
var (
trieGetTimer = metrics.NewRegisteredTimer("trie/get/time", nil)
trieReaderGetTimer = metrics.NewRegisteredTimer("trie/reader/get/time", nil)
trieReaderTotalTimer = metrics.NewRegisteredTimer("trie/reader/total/time", nil)
)
// Trie is a Merkle Patricia Trie. Use New to create a trie that sits on
// top of a database. Whenever trie performs a commit operation, the generated
// nodes will be gathered and returned in a set. Once the trie is committed,
@@ -146,6 +154,10 @@ func (t *Trie) Get(key []byte) ([]byte, error) {
if t.committed {
return nil, ErrCommitted
}
if metrics.EnabledExpensive {
start := time.Now()
defer func() { trieGetTimer.UpdateSince(start) }()
}
value, newroot, didResolve, err := t.get(t.root, keybytesToHex(key), 0)
if err == nil && didResolve {
t.root = newroot
@@ -178,7 +190,11 @@ func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode no
}
return value, n, didResolve, err
case hashNode:
start := time.Now()
child, err := t.resolveAndTrack(n, key[:pos])
if metrics.EnabledExpensive {
trieReaderGetTimer.UpdateSince(start)
}
if err != nil {
return nil, n, true, err
}
@@ -586,6 +602,10 @@ func (t *Trie) resolve(n node, prefix []byte) (node, error) {
// node's original value. The rlp-encoded blob is preferred to be loaded from
// database because it's easy to decode node while complex to encode node to blob.
func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
if metrics.EnabledExpensive {
start := time.Now()
defer func() { trieReaderTotalTimer.UpdateSince(start) }()
}
blob, err := t.reader.node(prefix, common.BytesToHash(n))
if err != nil {
return nil, err

View File

@@ -19,6 +19,7 @@ package pathdb
import (
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
@@ -104,6 +105,8 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
dl.lock.RLock()
defer dl.lock.RUnlock()
start := time.Now()
// If the trie node is known locally, return it
subset, ok := dl.nodes[owner]
if ok {
@@ -114,14 +117,18 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
if n.Hash != hash {
dirtyFalseMeter.Mark(1)
log.Error("Unexpected trie node in diff layer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
pathGetDiffLayerTimer.UpdateSince(start)
return nil, newUnexpectedNodeError("diff", hash, n.Hash, owner, path, n.Blob)
}
dirtyHitMeter.Mark(1)
dirtyNodeHitDepthHist.Update(int64(depth))
dirtyReadMeter.Mark(int64(len(n.Blob)))
pathGetDiffLayerTimer.UpdateSince(start)
return n.Blob, nil
}
}
pathGetDiffLayerTimer.UpdateSince(start)
// Trie node unknown to this layer, resolve from parent
if diff, ok := dl.parent.(*diffLayer); ok {
return diff.node(owner, path, hash, depth+1)

View File

@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"sync"
"time"
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
@@ -27,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate"
"golang.org/x/crypto/sha3"
@@ -155,6 +157,10 @@ func (dl *diskLayer) markStale() {
func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
dl.lock.RLock()
defer dl.lock.RUnlock()
if metrics.EnabledExpensive {
start := time.Now()
defer func() { pathGetDiskLayerTimer.UpdateSince(start) }()
}
if dl.stale {
return nil, errSnapshotStale

View File

@@ -47,4 +47,7 @@ var (
historyBuildTimeMeter = metrics.NewRegisteredTimer("pathdb/history/time", nil)
historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil)
historyIndexBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/index", nil)
pathGetDiffLayerTimer = metrics.NewRegisteredTimer("pathdb/get/difflayer/time", nil)
pathGetDiskLayerTimer = metrics.NewRegisteredTimer("pathdb/get/disklayer/time", nil)
)