Compare commits

...

21 Commits

Author SHA1 Message Date
joeylichang
1c8fb28c99 fix: change version db config 2024-10-15 10:25:10 +08:00
joeylichang
6d41ff8190 chore: delete debug info 2024-09-30 16:49:06 +08:00
joeylichang
2a6bb8720d fix: commit and calc num 2024-09-26 17:07:43 +08:00
joeylichang
53b585e7ab chore: add check for genesis 2024-09-26 16:55:15 +08:00
joeylichang
5018895927 chore: add commit and calc root num log 2024-09-26 16:25:32 +08:00
joeylichang
3dc1030979 chore: add state object commit panic check 2024-09-26 15:39:30 +08:00
will-2012
b8f5b6a135 Merge pull request #2709 from will-2012/update-versadb-config0
chore: update trivals
2024-09-14 16:43:17 +08:00
will@2012
4b17959733 chore: update trivals 2024-09-14 16:42:46 +08:00
will-2012
5bbfa69f35 Merge pull request #2708 from will-2012/update-versadb-config
chore: update versadb default config
2024-09-14 16:37:30 +08:00
will@2012
99678970e1 chore: update versadb default config 2024-09-14 16:34:57 +08:00
joeycli
6d082af0f3 feat: add prefetcher 2024-09-09 14:22:11 +08:00
joeycli
089845c5f3 chore: add statedb get metrices 2024-09-06 14:06:47 +08:00
joeycli
d7e873c28c fix: delete redundancy state close 2024-09-06 09:14:55 +08:00
joeycli
f646d4f1c0 feat: support version and root as key to state
fix: genesis version to -1

fix: copy with version

fix: change version type from uint64 to int64

fix: set genesis for rewind
2024-09-05 14:55:49 +08:00
joeycli
eaddc87def fix: check version and root when rewind
fix: rewind for genesis
2024-09-04 15:49:06 +08:00
joeycli
2c59e15b7a chore: add validate metrics 2024-09-04 10:21:33 +08:00
joeycli
a5e12fe178 feat: support rewind for versa db
fix: rewind ancient store
2024-09-02 17:35:32 +08:00
joeycli
4ed6b8e36f chore: add execute, verify and commit total metrics 2024-09-02 09:12:54 +08:00
joeycli
b76062c354 fix: rewind ancient store 2024-09-01 12:09:00 +08:00
joeycli
b298f4b6e6 chore: add mgasps metrics 2024-08-29 16:40:46 +08:00
joeycli
06366d743d chore: add pprof service 2024-08-29 12:15:39 +08:00
27 changed files with 387 additions and 185 deletions

View File

@@ -361,6 +361,8 @@ func geth(ctx *cli.Context) error {
defer stack.Close() defer stack.Close()
startNode(ctx, stack, backend, false) startNode(ctx, stack, backend, false)
// TODO:: debug code , will be deleted in the future
debug.StartPProf("127.0.0.1:7060", !ctx.IsSet("metrics.addr"))
stack.Wait() stack.Wait()
return nil return nil
} }

View File

@@ -343,7 +343,7 @@ func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block
continue continue
} }
// If we're above the chain head, state availability is a must // If we're above the chain head, state availability is a must
if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) { if !chain.HasBlockAndState(block.Hash(), block.Number().Int64()) {
return blocks[i:] return blocks[i:]
} }
} }

View File

@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@@ -40,6 +41,12 @@ func EnableRemoteVerifyManager(remoteValidator *remoteVerifyManager) BlockValida
} }
} }
var (
validateBloomTimer = metrics.NewRegisteredTimer("validate/bloom/time", nil)
validateReceiptTimer = metrics.NewRegisteredTimer("validate/receipt/time", nil)
validateRootTimer = metrics.NewRegisteredTimer("validate/root/time", nil)
)
// BlockValidator is responsible for validating block headers, uncles and // BlockValidator is responsible for validating block headers, uncles and
// processed state. // processed state.
// //
@@ -96,7 +103,7 @@ func ValidateListsInBody(block *types.Block) error {
// validated at this point. // validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error { func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block is already imported. // Check whether the block is already imported.
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { if v.bc.HasBlockAndState(block.Hash(), block.Number().Int64()) {
return ErrKnownBlock return ErrKnownBlock
} }
if v.bc.isCachedBadBlock(block) { if v.bc.isCachedBadBlock(block) {
@@ -142,7 +149,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return nil return nil
}, },
func() error { func() error {
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlockAndState(block.ParentHash(), block.Number().Int64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor
} }
@@ -184,6 +191,10 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
// For valid blocks this should always validate to true. // For valid blocks this should always validate to true.
validateFuns := []func() error{ validateFuns := []func() error{
func() error { func() error {
defer func(start time.Time) {
validateBloomTimer.UpdateSince(start)
}(time.Now())
rbloom := types.CreateBloom(receipts) rbloom := types.CreateBloom(receipts)
if rbloom != header.Bloom { if rbloom != header.Bloom {
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
@@ -191,6 +202,9 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
return nil return nil
}, },
func() error { func() error {
defer func(start time.Time) {
validateReceiptTimer.UpdateSince(start)
}(time.Now())
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil)) receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
if receiptSha != header.ReceiptHash { if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
@@ -209,6 +223,9 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
}) })
} else { } else {
validateFuns = append(validateFuns, func() error { validateFuns = append(validateFuns, func() error {
defer func(start time.Time) {
validateRootTimer.UpdateSince(start)
}(time.Now())
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
} }

View File

@@ -74,6 +74,7 @@ var (
blockInsertMgaspsGauge = metrics.NewRegisteredGauge("chain/insert/mgasps", nil) blockInsertMgaspsGauge = metrics.NewRegisteredGauge("chain/insert/mgasps", nil)
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
mGasPsGauge = metrics.NewRegisteredGauge("chain/process/gas", nil)
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
@@ -91,10 +92,13 @@ var (
triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil) triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil)
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil) blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil) blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil) blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
blockValidationTotalTimer = metrics.NewRegisteredTimer("chain/total/validation", nil)
blockExecutionTotalTimer = metrics.NewRegisteredTimer("chain/total/execution", nil)
blockWriteTotalTimer = metrics.NewRegisteredTimer("chain/total/write", nil)
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
@@ -342,6 +346,15 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// Open trie database with provided config // Open trie database with provided config
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig()) triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig())
if triedb.Scheme() == rawdb.VersionScheme {
vdb := triedb.VersaDB()
ver, root := vdb.LatestStoreDiskVersionInfo()
if ver == -1 {
rawdb.WriteCanonicalHash(db, common.Hash{}, 0)
}
log.Info("version db latest version info", "version", ver, "root", root.String())
}
// Setup the genesis block, commit the provided genesis specification // Setup the genesis block, commit the provided genesis specification
// to database if the genesis block is not present yet, or load the // to database if the genesis block is not present yet, or load the
// stored one from database. // stored one from database.
@@ -424,78 +437,138 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// Make sure the state associated with the block is available, or log out // Make sure the state associated with the block is available, or log out
// if there is no available state, waiting for state sync. // if there is no available state, waiting for state sync.
head := bc.CurrentBlock() head := bc.CurrentBlock()
if !bc.HasState(head.Root) { if bc.triedb.Scheme() != rawdb.VersionScheme {
if head.Number.Uint64() == 0 { if !bc.HasState(head.Number.Int64(), head.Root) {
// The genesis state is missing, which is only possible in the path-based if head.Number.Uint64() == 0 {
// scheme. This situation occurs when the initial state sync is not finished // The genesis state is missing, which is only possible in the path-based
// yet, or the chain head is rewound below the pivot point. In both scenarios, // scheme. This situation occurs when the initial state sync is not finished
// there is no possible recovery approach except for rerunning a snap sync. // yet, or the chain head is rewound below the pivot point. In both scenarios,
// Do nothing here until the state syncer picks it up. // there is no possible recovery approach except for rerunning a snap sync.
log.Info("Genesis state is missing, wait state sync") // Do nothing here until the state syncer picks it up.
} else { log.Info("Genesis state is missing, wait state sync")
// Head state is missing, before the state recovery, find out the
// disk layer point of snapshot(if it's enabled). Make sure the
// rewound point is lower than disk layer.
var diskRoot common.Hash
if bc.cacheConfig.SnapshotLimit > 0 {
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
}
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
recoverable, _ := bc.triedb.Recoverable(diskRoot)
if !bc.HasState(diskRoot) && !recoverable {
diskRoot = bc.triedb.Head()
}
}
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "diskRoot", diskRoot)
snapDisk, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, diskRoot, true)
if err != nil {
return nil, err
}
// Chain rewound, persist old snapshot number to indicate recovery procedure
if snapDisk != 0 {
rawdb.WriteSnapshotRecoveryNumber(bc.db, snapDisk)
}
} else { } else {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash()) // Head state is missing, before the state recovery, find out the
if _, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, common.Hash{}, true); err != nil { // disk layer point of snapshot(if it's enabled). Make sure the
// rewound point is lower than disk layer.
var diskRoot common.Hash
if bc.cacheConfig.SnapshotLimit > 0 {
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
}
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
recoverable, _ := bc.triedb.Recoverable(diskRoot)
if !bc.HasState(0, diskRoot) && !recoverable {
diskRoot = bc.triedb.Head()
}
}
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "diskRoot", diskRoot)
snapDisk, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, diskRoot, true)
if err != nil {
return nil, err
}
// Chain rewound, persist old snapshot number to indicate recovery procedure
if snapDisk != 0 {
rawdb.WriteSnapshotRecoveryNumber(bc.db, snapDisk)
}
} else {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash())
if _, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, common.Hash{}, true); err != nil {
return nil, err
}
}
}
}
} else {
log.Warn("versa db no recovery, rewind in load state")
}
// Ensure that a previous crash in SetHead doesn't leave extra ancients
if bc.triedb.Scheme() != rawdb.VersionScheme {
if frozen, err := bc.db.BlockStore().ItemAmountInAncient(); err == nil && frozen > 0 {
frozen, err = bc.db.BlockStore().Ancients()
if err != nil {
return nil, err
}
var (
needRewind bool
low uint64
)
// The head full block may be rolled back to a very low height due to
// blockchain repair. If the head full block is even lower than the ancient
// chain, truncate the ancient store.
fullBlock := bc.CurrentBlock()
if fullBlock != nil && fullBlock.Hash() != bc.genesisBlock.Hash() && fullBlock.Number.Uint64() < frozen-1 {
needRewind = true
low = fullBlock.Number.Uint64()
}
// In snap sync, it may happen that ancient data has been written to the
// ancient store, but the LastFastBlock has not been updated, truncate the
// extra data here.
snapBlock := bc.CurrentSnapBlock()
if snapBlock != nil && snapBlock.Number.Uint64() < frozen-1 {
needRewind = true
if snapBlock.Number.Uint64() < low || low == 0 {
low = snapBlock.Number.Uint64()
}
}
if needRewind {
log.Error("Truncating ancient chain", "from", bc.CurrentHeader().Number.Uint64(), "to", low)
if err := bc.SetHead(low); err != nil {
return nil, err return nil, err
} }
} }
} }
} }
// Ensure that a previous crash in SetHead doesn't leave extra ancients // Ensure that a previous crash in SetHead doesn't leave extra ancients
if frozen, err := bc.db.BlockStore().ItemAmountInAncient(); err == nil && frozen > 0 { if bc.triedb.Scheme() != rawdb.VersionScheme {
frozen, err = bc.db.BlockStore().Ancients() if frozen, err := bc.db.BlockStore().ItemAmountInAncient(); err == nil && frozen > 0 {
frozen, err = bc.db.BlockStore().Ancients()
if err != nil {
return nil, err
}
var (
needRewind bool
low uint64
)
// The head full block may be rolled back to a very low height due to
// blockchain repair. If the head full block is even lower than the ancient
// chain, truncate the ancient store.
fullBlock := bc.CurrentBlock()
if fullBlock != nil && fullBlock.Hash() != bc.genesisBlock.Hash() && fullBlock.Number.Uint64() < frozen-1 {
needRewind = true
low = fullBlock.Number.Uint64()
}
// In snap sync, it may happen that ancient data has been written to the
// ancient store, but the LastFastBlock has not been updated, truncate the
// extra data here.
snapBlock := bc.CurrentSnapBlock()
if snapBlock != nil && snapBlock.Number.Uint64() < frozen-1 {
needRewind = true
if snapBlock.Number.Uint64() < low || low == 0 {
low = snapBlock.Number.Uint64()
}
}
if needRewind {
log.Error("Truncating ancient chain", "from", bc.CurrentHeader().Number.Uint64(), "to", low)
if err := bc.SetHead(low); err != nil {
return nil, err
}
}
}
} else {
//TODO:: need consider the offline and inline prune block
frozen, err := bc.db.BlockStore().Ancients()
if err != nil {
return nil, err
}
items, err := bc.db.BlockStore().ItemAmountInAncient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var (
needRewind bool
low uint64
)
// The head full block may be rolled back to a very low height due to
// blockchain repair. If the head full block is even lower than the ancient
// chain, truncate the ancient store.
fullBlock := bc.CurrentBlock() fullBlock := bc.CurrentBlock()
if fullBlock != nil && fullBlock.Hash() != bc.genesisBlock.Hash() && fullBlock.Number.Uint64() < frozen-1 { log.Info("version mode rewind ancient store", "target", fullBlock.Number.Uint64(), "old head", frozen, "items", items, "offset", bc.db.BlockStore().AncientOffSet())
needRewind = true if frozen >= fullBlock.Number.Uint64() {
low = fullBlock.Number.Uint64() if _, err = bc.db.BlockStore().TruncateTail(fullBlock.Number.Uint64()); err != nil {
}
// In snap sync, it may happen that ancient data has been written to the
// ancient store, but the LastFastBlock has not been updated, truncate the
// extra data here.
snapBlock := bc.CurrentSnapBlock()
if snapBlock != nil && snapBlock.Number.Uint64() < frozen-1 {
needRewind = true
if snapBlock.Number.Uint64() < low || low == 0 {
low = snapBlock.Number.Uint64()
}
}
if needRewind {
log.Error("Truncating ancient chain", "from", bc.CurrentHeader().Number.Uint64(), "to", low)
if err := bc.SetHead(low); err != nil {
return nil, err return nil, err
} }
} }
@@ -702,20 +775,61 @@ func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 {
// loadLastState loads the last known chain state from the database. This method // loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held. // assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) loadLastState() error {
// Restore the last known head block // TODO:: before versa db support recovery, only rewind
head := rawdb.ReadHeadBlockHash(bc.db) var headBlock *types.Block
if head == (common.Hash{}) { if bc.triedb.Scheme() == rawdb.VersionScheme {
// Corrupt or empty database, init from scratch head := rawdb.ReadHeadBlockHash(bc.db)
log.Warn("Empty database, resetting chain") headBlock = bc.GetBlockByHash(head)
return bc.Reset()
} versa := bc.triedb.VersaDB()
// Make sure the entire head block is available archiveVersion, archiveRoot := versa.LatestStoreDiskVersionInfo()
headBlock := bc.GetBlockByHash(head) // first start
if headBlock == nil { if archiveVersion == -1 {
// Corrupt or empty database, init from scratch archiveVersion = 0
log.Warn("Head block missing, resetting chain", "hash", head) archiveRoot = bc.genesisBlock.Root()
return bc.Reset() }
if int64(headBlock.NumberU64()) < archiveVersion {
log.Crit("versa db disk version large than header block", "head number", headBlock.NumberU64(), "versa archive number", archiveVersion)
}
log.Info("begin rewind versa db head", "target_number", archiveVersion, "target_root", archiveRoot.String(), "head_number", headBlock.NumberU64(), "head_root", headBlock.Root().String())
for {
if int64(headBlock.NumberU64()) == archiveVersion && archiveRoot.Cmp(headBlock.Root()) == 0 {
rawdb.WriteCanonicalHash(bc.db, headBlock.Hash(), headBlock.NumberU64())
rawdb.WriteHeadHeaderHash(bc.db, headBlock.Hash())
rawdb.WriteHeadBlockHash(bc.db, headBlock.Hash())
rawdb.WriteHeadFastBlockHash(bc.db, headBlock.Hash())
log.Info("reset versa db head block", "number", headBlock.NumberU64(), "hash", headBlock.Hash())
break
} else if int64(headBlock.NumberU64()) == archiveVersion {
log.Crit("rewinding meet same number", "target_number", archiveVersion, "target_root", archiveRoot.String(), "head_number", headBlock.NumberU64(), "head_root", headBlock.Root().String())
} else if archiveRoot.Cmp(headBlock.Root()) == 0 {
log.Info("rewinding meet same root", "target_number", archiveVersion, "target_root", archiveRoot.String(), "head_number", headBlock.NumberU64(), "head_root", headBlock.Root().String())
}
log.Info("rewinding", "target_number", archiveVersion, "target_root", archiveRoot.String(), "head_number", headBlock.NumberU64(), "head_root", headBlock.Root().String())
headBlock = rawdb.ReadBlock(bc.db, headBlock.ParentHash(), headBlock.NumberU64()-1)
if headBlock == nil {
panic("versa db rewind head is nil")
}
}
} else {
// Restore the last known head block
head := rawdb.ReadHeadBlockHash(bc.db)
if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch
log.Warn("Empty database, resetting chain")
return bc.Reset()
}
// Make sure the entire head block is available
headBlock = bc.GetBlockByHash(head)
if headBlock == nil {
// Corrupt or empty database, init from scratch
log.Warn("Head block missing, resetting chain", "hash", head)
return bc.Reset()
}
} }
log.Info("load state head block", "number", headBlock.NumberU64())
// Everything seems to be fine, set as the head block // Everything seems to be fine, set as the head block
bc.currentBlock.Store(headBlock.Header()) bc.currentBlock.Store(headBlock.Header())
@@ -884,7 +998,7 @@ func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*typ
} }
// If the associated state is not reachable, continue searching // If the associated state is not reachable, continue searching
// backwards until an available state is found. // backwards until an available state is found.
if !bc.HasState(head.Root) { if !bc.HasState(head.Number.Int64(), head.Root) {
// If the chain is gapped in the middle, return the genesis // If the chain is gapped in the middle, return the genesis
// block as the new chain head. // block as the new chain head.
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1) parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1)
@@ -924,7 +1038,7 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
// noState represents if the target state requested for search // noState represents if the target state requested for search
// is unavailable and impossible to be recovered. // is unavailable and impossible to be recovered.
noState = !bc.HasState(root) && !bc.stateRecoverable(root) noState = !bc.HasState(head.Number.Int64(), root) && !bc.stateRecoverable(root)
start = time.Now() // Timestamp the rewinding is restarted start = time.Now() // Timestamp the rewinding is restarted
logged = time.Now() // Timestamp last progress log was printed logged = time.Now() // Timestamp last progress log was printed
@@ -945,13 +1059,13 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
// If the root threshold hasn't been crossed but the available // If the root threshold hasn't been crossed but the available
// state is reached, quickly determine if the target state is // state is reached, quickly determine if the target state is
// possible to be reached or not. // possible to be reached or not.
if !beyondRoot && noState && bc.HasState(head.Root) { if !beyondRoot && noState && bc.HasState(head.Number.Int64(), head.Root) {
beyondRoot = true beyondRoot = true
log.Info("Disable the search for unattainable state", "root", root) log.Info("Disable the search for unattainable state", "root", root)
} }
// Check if the associated state is available or recoverable if // Check if the associated state is available or recoverable if
// the requested root has already been crossed. // the requested root has already been crossed.
if beyondRoot && (bc.HasState(head.Root) || bc.stateRecoverable(head.Root)) { if beyondRoot && (bc.HasState(head.Number.Int64(), head.Root) || bc.stateRecoverable(head.Root)) {
break break
} }
// If pivot block is reached, return the genesis block as the // If pivot block is reached, return the genesis block as the
@@ -978,7 +1092,7 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
} }
} }
// Recover if the target state if it's not available yet. // Recover if the target state if it's not available yet.
if !bc.HasState(head.Root) { if !bc.HasState(head.Number.Int64(), head.Root) {
if err := bc.triedb.Recover(head.Root); err != nil { if err := bc.triedb.Recover(head.Root); err != nil {
log.Crit("Failed to rollback state", "err", err) log.Crit("Failed to rollback state", "err", err)
} }
@@ -1073,7 +1187,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
// the pivot point. In this scenario, there is no possible recovery // the pivot point. In this scenario, there is no possible recovery
// approach except for rerunning a snap sync. Do nothing here until the // approach except for rerunning a snap sync. Do nothing here until the
// state syncer picks it up. // state syncer picks it up.
if !bc.HasState(newHeadBlock.Root) { if !bc.HasState(newHeadBlock.Number.Int64(), newHeadBlock.Root) {
if newHeadBlock.Number.Uint64() != 0 { if newHeadBlock.Number.Uint64() != 0 {
log.Crit("Chain is stateless at a non-genesis block") log.Crit("Chain is stateless at a non-genesis block")
} }
@@ -1185,7 +1299,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
return err return err
} }
} }
if !bc.NoTries() && !bc.HasState(root) { if !bc.NoTries() && !bc.HasState(0, root) {
return fmt.Errorf("non existent state [%x..]", root[:4]) return fmt.Errorf("non existent state [%x..]", root[:4])
} }
// If all checks out, manually set the head block. // If all checks out, manually set the head block.
@@ -2242,13 +2356,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
} }
if bc.stateCache.Scheme() != rawdb.VersionScheme { bc.stateCache.SetVersion(int64(block.NumberU64()) - 1)
if block.NumberU64() == 2000001 {
log.Crit("exit.... path mode, 200w blocks")
}
}
bc.stateCache.SetVersion(int64(block.NumberU64()))
statedb, err := state.NewWithSharedPool(parent.Root, bc.stateCache, bc.snaps) statedb, err := state.NewWithSharedPool(parent.Root, bc.stateCache, bc.snaps)
if err != nil { if err != nil {
bc.stateCache.Release() bc.stateCache.Release()
@@ -2260,18 +2368,18 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
//statedb.StartPrefetcher("chain") //statedb.StartPrefetcher("chain")
interruptCh := make(chan struct{}) interruptCh := make(chan struct{})
// For diff sync, it may fallback to full sync, so we still do prefetch // For diff sync, it may fallback to full sync, so we still do prefetch
//if len(block.Transactions()) >= prefetchTxNumber { if len(block.Transactions()) >= prefetchTxNumber {
// // do Prefetch in a separate goroutine to avoid blocking the critical path // do Prefetch in a separate goroutine to avoid blocking the critical path
//
// // 1.do state prefetch for snapshot cache //1.do state prefetch for snapshot cache
// throwaway := statedb.CopyDoPrefetch() throwaway := statedb.CopyDoPrefetch()
// go bc.prefetcher.Prefetch(block, throwaway, &bc.vmConfig, interruptCh) go bc.prefetcher.Prefetch(block, throwaway, &bc.vmConfig, interruptCh)
//
// // 2.do trie prefetch for MPT trie node cache // // 2.do trie prefetch for MPT trie node cache
// // it is for the big state trie tree, prefetch based on transaction's From/To address. // // it is for the big state trie tree, prefetch based on transaction's From/To address.
// // trie prefetcher is thread safe now, ok to prefetch in a separate routine // // trie prefetcher is thread safe now, ok to prefetch in a separate routine
// go throwaway.TriePrefetchInAdvance(block, signer) // go throwaway.TriePrefetchInAdvance(block, signer)
//} }
// Process block using the parent state as reference point // Process block using the parent state as reference point
if bc.pipeCommit { if bc.pipeCommit {
@@ -2287,6 +2395,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
statedb.StopPrefetcher() statedb.StopPrefetcher()
return it.index, err return it.index, err
} }
blockExecutionTotalTimer.UpdateSince(pstart)
ptime := time.Since(pstart) ptime := time.Since(pstart)
// Validate the state using the default validator // Validate the state using the default validator
@@ -2297,6 +2407,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
statedb.StopPrefetcher() statedb.StopPrefetcher()
return it.index, err return it.index, err
} }
blockValidationTotalTimer.UpdateSince(vstart)
vtime := time.Since(vstart) vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation proctime := time.Since(start) // processing + validation
@@ -2332,6 +2444,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
return it.index, err return it.index, err
} }
bc.stateCache.Release() bc.stateCache.Release()
blockWriteTotalTimer.UpdateSince(wstart)
bc.cacheReceipts(block.Hash(), receipts, block) bc.cacheReceipts(block.Hash(), receipts, block)
@@ -2511,7 +2624,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
numbers []uint64 numbers []uint64
) )
parent := it.previous() parent := it.previous()
for parent != nil && !bc.HasState(parent.Root) { for parent != nil && !bc.HasState(parent.Number.Int64(), parent.Root) {
if bc.stateRecoverable(parent.Root) { if bc.stateRecoverable(parent.Root) {
if err := bc.triedb.Recover(parent.Root); err != nil { if err := bc.triedb.Recover(parent.Root); err != nil {
return 0, err return 0, err
@@ -2578,7 +2691,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
numbers []uint64 numbers []uint64
parent = block parent = block
) )
for parent != nil && !bc.HasState(parent.Root()) { for parent != nil && !bc.HasState(parent.Number().Int64(), parent.Root()) {
if bc.stateRecoverable(parent.Root()) { if bc.stateRecoverable(parent.Root()) {
if err := bc.triedb.Recover(parent.Root()); err != nil { if err := bc.triedb.Recover(parent.Root()); err != nil {
return common.Hash{}, err return common.Hash{}, err
@@ -2849,7 +2962,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
// Re-execute the reorged chain in case the head state is missing. // Re-execute the reorged chain in case the head state is missing.
if !bc.HasState(head.Root()) { if !bc.HasState(head.Number().Int64(), head.Root()) {
if latestValidHash, err := bc.recoverAncestors(head); err != nil { if latestValidHash, err := bc.recoverAncestors(head); err != nil {
return latestValidHash, err return latestValidHash, err
} }

View File

@@ -64,6 +64,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
"blocks", st.processed, "txs", txs, "blobs", blobs, "mgas", float64(st.usedGas) / 1000000, "blocks", st.processed, "txs", txs, "blobs", blobs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps, "elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
} }
mGasPsGauge.Update(int64(mgasps))
blockInsertMgaspsGauge.Update(int64(mgasps)) blockInsertMgaspsGauge.Update(int64(mgasps))
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute { if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)

View File

@@ -18,6 +18,7 @@ package core
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@@ -338,7 +339,7 @@ func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
} }
// HasState checks if state trie is fully present in the database or not. // HasState checks if state trie is fully present in the database or not.
func (bc *BlockChain) HasState(hash common.Hash) bool { func (bc *BlockChain) HasState(number int64, hash common.Hash) bool {
if bc.NoTries() { if bc.NoTries() {
return bc.snaps != nil && bc.snaps.Snapshot(hash) != nil return bc.snaps != nil && bc.snaps.Snapshot(hash) != nil
} }
@@ -348,18 +349,24 @@ func (bc *BlockChain) HasState(hash common.Hash) bool {
return true return true
} }
} }
return bc.stateCache.HasState(hash) return bc.stateCache.HasState(number, hash)
} }
// HasBlockAndState checks if a block and associated state trie is fully present // HasBlockAndState checks if a block and associated state trie is fully present
// in the database or not, caching it if present. // in the database or not, caching it if present.
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool { func (bc *BlockChain) HasBlockAndState(hash common.Hash, number int64) bool {
// Check first that the block itself is known // Check first that the block itself is known
block := bc.GetBlock(hash, number) var root common.Hash
if block == nil { if number < 0 {
return false root = types.EmptyRootHash
} else {
block := bc.GetBlock(hash, uint64(number))
if block == nil {
return false
}
root = block.Root()
} }
return bc.HasState(block.Root()) return bc.HasState(number, root)
} }
// stateRecoverable checks if the specified state is recoverable. // stateRecoverable checks if the specified state is recoverable.
@@ -390,13 +397,19 @@ func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
// State returns a new mutable state based on the current HEAD block. // State returns a new mutable state based on the current HEAD block.
func (bc *BlockChain) State() (*state.StateDB, error) { func (bc *BlockChain) State() (*state.StateDB, error) {
return bc.StateAt(bc.CurrentBlock().Root) return bc.StateAt(bc.CurrentBlock().Number.Int64(), bc.CurrentBlock().Root)
} }
// StateAt returns a new mutable state based on a particular point in time. // StateAt returns a new mutable state based on a particular point in time.
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { func (bc *BlockChain) StateAt(number int64, root common.Hash) (*state.StateDB, error) {
// new state db with no need commit mode // new state db with no need commit mode
stateDb, err := state.New(root, state.NewDatabaseWithNodeDB(bc.db, bc.triedb, false), bc.snaps) has := bc.HasState(number, root)
if !has {
return nil, fmt.Errorf(fmt.Sprintf("do not has state, verison: %d, root: %s", number, root.String()))
}
sdb := state.NewDatabaseWithNodeDB(bc.db, bc.triedb, false)
sdb.SetVersion(number)
stateDb, err := state.New(root, sdb, bc.snaps)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -158,7 +158,7 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
triedbConfig.NoTries = false triedbConfig.NoTries = false
} }
cachingdb := state.NewDatabaseWithNodeDB(db, triedb, true) cachingdb := state.NewDatabaseWithNodeDB(db, triedb, true)
cachingdb.SetVersion(0) cachingdb.SetVersion(-1)
defer cachingdb.Release() defer cachingdb.Release()
statedb, err := state.New(types.EmptyRootHash, cachingdb, nil) statedb, err := state.New(types.EmptyRootHash, cachingdb, nil)
if err != nil { if err != nil {
@@ -274,6 +274,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
log.Info("genesis block hash", "hash", block.Hash()) log.Info("genesis block hash", "hash", block.Hash())
return genesis.Config, block.Hash(), nil return genesis.Config, block.Hash(), nil
} }
log.Info("init genesis", "stored root", stored.String())
// The genesis block is present(perhaps in ancient database) while the // The genesis block is present(perhaps in ancient database) while the
// state database is not initialized yet. It can happen that the node // state database is not initialized yet. It can happen that the node
// is initialized with an external ancient store. Commit genesis state // is initialized with an external ancient store. Commit genesis state

View File

@@ -60,6 +60,7 @@ func (cv *cachingVersaDB) Copy() Database {
cp.triedb = cv.triedb cp.triedb = cv.triedb
cp.versionDB = cv.versionDB cp.versionDB = cv.versionDB
cp.codeDB = cv.codeDB cp.codeDB = cv.codeDB
cp.version = cv.version
cp.mode = versa.S_RW // it is important cp.mode = versa.S_RW // it is important
// TODO:: maybe add lock for cv.root // TODO:: maybe add lock for cv.root
@@ -109,8 +110,8 @@ func (cv *cachingVersaDB) CopyTrie(tr Trie) Trie {
return nil return nil
} }
func (cv *cachingVersaDB) HasState(root common.Hash) bool { func (cv *cachingVersaDB) HasState(version int64, root common.Hash) bool {
return cv.versionDB.HasState(root) return cv.versionDB.HasState(version, root)
} }
func (cv *cachingVersaDB) OpenTrie(root common.Hash) (Trie, error) { func (cv *cachingVersaDB) OpenTrie(root common.Hash) (Trie, error) {
@@ -214,7 +215,7 @@ func (cv *cachingVersaDB) Flush() error {
} }
func (cv *cachingVersaDB) SetVersion(version int64) { func (cv *cachingVersaDB) SetVersion(version int64) {
cv.version = version - 1 cv.version = version
//cv.debug = NewDebugVersionState(cv.codeDB, cv.versionDB) //cv.debug = NewDebugVersionState(cv.codeDB, cv.versionDB)
//cv.debug.Version = version //cv.debug.Version = version
} }

View File

@@ -87,7 +87,7 @@ type Database interface {
Copy() Database Copy() Database
// HasState returns the state data whether in the triedb. // HasState returns the state data whether in the triedb.
HasState(root common.Hash) bool HasState(version int64, root common.Hash) bool
// HasTreeExpired used for caching versa db, whether the state where the opened tree resides has been closed // HasTreeExpired used for caching versa db, whether the state where the opened tree resides has been closed
HasTreeExpired(tr Trie) bool HasTreeExpired(tr Trie) bool
@@ -402,7 +402,7 @@ func (db *cachingDB) Copy() Database {
return db return db
} }
func (db *cachingDB) HasState(root common.Hash) bool { func (db *cachingDB) HasState(_ int64, root common.Hash) bool {
_, err := db.OpenTrie(root) _, err := db.OpenTrie(root)
return err == nil return err == nil
} }

View File

@@ -34,4 +34,7 @@ var (
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil) slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil) slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil) slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil)
accountIntermediateRootTimer = metrics.NewRegisteredTimer("state/account/intermediate/root/time", nil)
storageIntermediateRootTimer = metrics.NewRegisteredTimer("state/storage/intermediate/root/time", nil)
) )

View File

@@ -235,6 +235,14 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
return common.Hash{} return common.Hash{}
} }
// If no live objects are available, attempt to use snapshots // If no live objects are available, attempt to use snapshots
defer func(start time.Time) {
stateDBGetTimer.UpdateSince(start)
stateDBGetQPS.Mark(1)
stateDBGetStorageTimer.UpdateSince(start)
stateDBGetStorageQPS.Mark(1)
}(time.Now())
var ( var (
enc []byte enc []byte
err error err error
@@ -390,6 +398,10 @@ func (s *stateObject) updateTrie() (Trie, error) {
s.db.setError(err) s.db.setError(err)
return nil, err return nil, err
} }
if len(s.pendingStorage) == 0 {
return s.trie, nil
}
// Insert all the pending storage updates into the trie // Insert all the pending storage updates into the trie
usedStorage := make([][]byte, 0, len(s.pendingStorage)) usedStorage := make([][]byte, 0, len(s.pendingStorage))
dirtyStorage := make(map[common.Hash][]byte) dirtyStorage := make(map[common.Hash][]byte)
@@ -507,6 +519,11 @@ func (s *stateObject) updateRoot() {
// The returned set can be nil if nothing to commit. This function assumes all // The returned set can be nil if nothing to commit. This function assumes all
// storage mutations have already been flushed into trie by updateRoot. // storage mutations have already been flushed into trie by updateRoot.
func (s *stateObject) commit() (*trienode.NodeSet, error) { func (s *stateObject) commit() (*trienode.NodeSet, error) {
if s.IsContractAccount() && s.trie == nil && s.db.db.GetVersion() != 0 {
panic(fmt.Sprintf("not open contract account, owner: %s, r_version: %d, c_version: %d, root: %s",
s.address.String(), s.db.db.GetVersion(), s.version, s.Root().String()))
}
// Short circuit if trie is not even loaded, don't bother with committing anything // Short circuit if trie is not even loaded, don't bother with committing anything
if s.trie == nil { if s.trie == nil {
s.origin = s.data.Copy() s.origin = s.data.Copy()

View File

@@ -54,6 +54,16 @@ type revision struct {
journalIndex int journalIndex int
} }
var (
stateDBGetTimer = metrics.NewRegisteredTimer("statedb/get/time", nil)
stateDBGetQPS = metrics.NewRegisteredMeter("statedb/get/qps", nil)
stateDBGetAccountTimer = metrics.NewRegisteredTimer("statedb/account/get/time", nil)
stateDBGetAccountQPS = metrics.NewRegisteredMeter("statedb/account/get/qps", nil)
stateDBGetStorageTimer = metrics.NewRegisteredTimer("statedb/storage/get/time", nil)
stateDBGetStorageQPS = metrics.NewRegisteredMeter("statedb/storage/get/qps", nil)
)
// StateDB structs within the ethereum protocol are used to store anything // StateDB structs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing // within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve: // nested states. It's the general query interface to retrieve:
@@ -733,6 +743,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if obj := s.stateObjects[addr]; obj != nil { if obj := s.stateObjects[addr]; obj != nil {
return obj return obj
} }
defer func(start time.Time) {
stateDBGetTimer.UpdateSince(start)
stateDBGetQPS.Mark(1)
stateDBGetAccountTimer.UpdateSince(start)
stateDBGetAccountQPS.Mark(1)
}(time.Now())
// If no live objects are available, attempt to use snapshots // If no live objects are available, attempt to use snapshots
var data *types.StateAccount var data *types.StateAccount
if s.snap != nil { if s.snap != nil {
@@ -1177,6 +1195,10 @@ func (s *StateDB) populateSnapStorage(obj *stateObject) bool {
} }
func (s *StateDB) AccountsIntermediateRoot() { func (s *StateDB) AccountsIntermediateRoot() {
defer func(start time.Time) {
storageIntermediateRootTimer.UpdateSince(start)
}(time.Now())
tasks := make(chan func()) tasks := make(chan func())
finishCh := make(chan struct{}) finishCh := make(chan struct{})
defer close(finishCh) defer close(finishCh)
@@ -1204,7 +1226,6 @@ func (s *StateDB) AccountsIntermediateRoot() {
wg.Add(1) wg.Add(1)
tasks <- func() { tasks <- func() {
obj.updateRoot() obj.updateRoot()
// Cache the data until commit. Note, this update mechanism is not symmetric // Cache the data until commit. Note, this update mechanism is not symmetric
// to the deletion, because whereas it is enough to track account updates // to the deletion, because whereas it is enough to track account updates
// at commit time, deletions need tracking at transaction boundary level to // at commit time, deletions need tracking at transaction boundary level to
@@ -1221,6 +1242,9 @@ func (s *StateDB) AccountsIntermediateRoot() {
} }
func (s *StateDB) StateIntermediateRoot() common.Hash { func (s *StateDB) StateIntermediateRoot() common.Hash {
defer func(start time.Time) {
accountIntermediateRootTimer.UpdateSince(start)
}(time.Now())
// If there was a trie prefetcher operating, it gets aborted and irrevocably // If there was a trie prefetcher operating, it gets aborted and irrevocably
// modified after we start retrieving tries. Remove it from the statedb after // modified after we start retrieving tries. Remove it from the statedb after
// this round of use. // this round of use.
@@ -1654,9 +1678,9 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc
if err := s.db.Flush(); err != nil { if err := s.db.Flush(); err != nil {
return err return err
} }
if err := s.db.Release(); err != nil { //if err := s.db.Release(); err != nil {
return err // return err
} //}
s.originalRoot = root s.originalRoot = root
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
s.TrieDBCommits += time.Since(start) s.TrieDBCommits += time.Since(start)

View File

@@ -49,7 +49,6 @@ func NewStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine conse
// the transaction messages using the statedb, but any changes are discarded. The // the transaction messages using the statedb, but any changes are discarded. The
// only goal is to pre-cache transaction signatures and state trie nodes. // only goal is to pre-cache transaction signatures and state trie nodes.
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) { func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg *vm.Config, interruptCh <-chan struct{}) {
panic("prefetcher not support")
var ( var (
header = block.Header() header = block.Header()
signer = types.MakeSigner(p.config, header.Number, header.Time) signer = types.MakeSigner(p.config, header.Number, header.Time)

View File

@@ -366,9 +366,9 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres
// Initialize the state with head block, or fallback to empty one in // Initialize the state with head block, or fallback to empty one in
// case the head state is not available (might occur when node is not // case the head state is not available (might occur when node is not
// fully synced). // fully synced).
state, err := p.chain.StateAt(head.Root) state, err := p.chain.StateAt(head.Number.Int64(), head.Root)
if err != nil { if err != nil {
state, err = p.chain.StateAt(types.EmptyRootHash) state, err = p.chain.StateAt(-1, types.EmptyRootHash)
} }
if err != nil { if err != nil {
return err return err
@@ -793,7 +793,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
resettimeHist.Update(time.Since(start).Nanoseconds()) resettimeHist.Update(time.Since(start).Nanoseconds())
}(time.Now()) }(time.Now())
statedb, err := p.chain.StateAt(newHead.Root) statedb, err := p.chain.StateAt(newHead.Number.Int64(), newHead.Root)
if err != nil { if err != nil {
log.Error("Failed to reset blobpool state", "err", err) log.Error("Failed to reset blobpool state", "err", err)
return return

View File

@@ -40,5 +40,5 @@ type BlockChain interface {
GetBlock(hash common.Hash, number uint64) *types.Block GetBlock(hash common.Hash, number uint64) *types.Block
// StateAt returns a state database for a given root hash (generally the head). // StateAt returns a state database for a given root hash (generally the head).
StateAt(root common.Hash) (*state.StateDB, error) StateAt(number int64, root common.Hash) (*state.StateDB, error)
} }

View File

@@ -120,7 +120,7 @@ type BlockChain interface {
GetBlock(hash common.Hash, number uint64) *types.Block GetBlock(hash common.Hash, number uint64) *types.Block
// StateAt returns a state database for a given root hash (generally the head). // StateAt returns a state database for a given root hash (generally the head).
StateAt(root common.Hash) (*state.StateDB, error) StateAt(number int64, root common.Hash) (*state.StateDB, error)
} }
// Config are the configuration parameters of the transaction pool. // Config are the configuration parameters of the transaction pool.
@@ -311,9 +311,9 @@ func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserve txpool.A
// Initialize the state with head block, or fallback to empty one in // Initialize the state with head block, or fallback to empty one in
// case the head state is not available (might occur when node is not // case the head state is not available (might occur when node is not
// fully synced). // fully synced).
statedb, err := pool.chain.StateAt(head.Root) statedb, err := pool.chain.StateAt(head.Number.Int64(), head.Root)
if err != nil { if err != nil {
statedb, err = pool.chain.StateAt(types.EmptyRootHash) statedb, err = pool.chain.StateAt(-1, types.EmptyRootHash)
} }
if err != nil { if err != nil {
return err return err
@@ -1492,7 +1492,7 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) {
if newHead == nil { if newHead == nil {
newHead = pool.chain.CurrentBlock() // Special case during testing newHead = pool.chain.CurrentBlock() // Special case during testing
} }
statedb, err := pool.chain.StateAt(newHead.Root) statedb, err := pool.chain.StateAt(newHead.Number.Int64(), newHead.Root)
if err != nil { if err != nil {
log.Error("Failed to reset txpool state", "err", err) log.Error("Failed to reset txpool state", "err", err)
return return

View File

@@ -204,7 +204,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
if header == nil { if header == nil {
return nil, nil, errors.New("header not found") return nil, nil, errors.New("header not found")
} }
stateDb, err := b.eth.BlockChain().StateAt(header.Root) stateDb, err := b.eth.BlockChain().StateAt(header.Number.Int64(), header.Root)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -226,7 +226,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
return nil, nil, errors.New("hash is not currently canonical") return nil, nil, errors.New("hash is not currently canonical")
} }
stateDb, err := b.eth.BlockChain().StateAt(header.Root) stateDb, err := b.eth.BlockChain().StateAt(header.Number.Int64(), header.Root)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -81,7 +81,7 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
if header == nil { if header == nil {
return state.Dump{}, fmt.Errorf("block #%d not found", blockNr) return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
} }
stateDb, err := api.eth.BlockChain().StateAt(header.Root) stateDb, err := api.eth.BlockChain().StateAt(header.Number.Int64(), header.Root)
if err != nil { if err != nil {
return state.Dump{}, err return state.Dump{}, err
} }
@@ -167,7 +167,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if header == nil { if header == nil {
return state.Dump{}, fmt.Errorf("block #%d not found", number) return state.Dump{}, fmt.Errorf("block #%d not found", number)
} }
stateDb, err = api.eth.BlockChain().StateAt(header.Root) stateDb, err = api.eth.BlockChain().StateAt(header.Number.Int64(), header.Root)
if err != nil { if err != nil {
return state.Dump{}, err return state.Dump{}, err
} }
@@ -177,7 +177,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if block == nil { if block == nil {
return state.Dump{}, fmt.Errorf("block %s not found", hash.Hex()) return state.Dump{}, fmt.Errorf("block %s not found", hash.Hex())
} }
stateDb, err = api.eth.BlockChain().StateAt(block.Root()) stateDb, err = api.eth.BlockChain().StateAt(block.Number().Int64(), block.Root())
if err != nil { if err != nil {
return state.Dump{}, err return state.Dump{}, err
} }

View File

@@ -220,13 +220,13 @@ func newHandler(config *handlerConfig) (*handler, error) {
} }
h.snapSync.Store(true) h.snapSync.Store(true)
log.Warn("Switch sync mode from full sync to snap sync", "reason", "snap sync incomplete") log.Warn("Switch sync mode from full sync to snap sync", "reason", "snap sync incomplete")
} else if !h.chain.NoTries() && !h.chain.HasState(fullBlock.Root) { } else if !h.chain.NoTries() && !h.chain.HasState(fullBlock.Number.Int64(), fullBlock.Root) {
h.snapSync.Store(true) h.snapSync.Store(true)
log.Warn("Switch sync mode from full sync to snap sync", "reason", "head state missing") log.Warn("Switch sync mode from full sync to snap sync", "reason", "head state missing")
} }
} else { } else {
head := h.chain.CurrentBlock() head := h.chain.CurrentBlock()
if head.Number.Uint64() > 0 && h.chain.HasState(head.Root) { if head.Number.Uint64() > 0 && h.chain.HasState(head.Number.Int64(), head.Root) {
// Print warning log if database is not empty to run snap sync. // Print warning log if database is not empty to run snap sync.
log.Warn("Switch sync mode from snap sync to full sync", "reason", "snap sync complete") log.Warn("Switch sync mode from snap sync to full sync", "reason", "snap sync complete")
} else { } else {

View File

@@ -55,7 +55,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
// The state is available in live database, create a reference // The state is available in live database, create a reference
// on top to prevent garbage collection and return a release // on top to prevent garbage collection and return a release
// function to deref it. // function to deref it.
if statedb, err = eth.blockchain.StateAt(block.Root()); err == nil { if statedb, err = eth.blockchain.StateAt(block.Number().Int64(), block.Root()); err == nil {
eth.blockchain.TrieDB().Reference(block.Root(), common.Hash{}) eth.blockchain.TrieDB().Reference(block.Root(), common.Hash{})
return statedb, func() { return statedb, func() {
eth.blockchain.TrieDB().Dereference(block.Root()) eth.blockchain.TrieDB().Dereference(block.Root())
@@ -185,7 +185,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
func (eth *Ethereum) pathState(block *types.Block) (*state.StateDB, func(), error) { func (eth *Ethereum) pathState(block *types.Block) (*state.StateDB, func(), error) {
// Check if the requested state is available in the live chain. // Check if the requested state is available in the live chain.
statedb, err := eth.blockchain.StateAt(block.Root()) statedb, err := eth.blockchain.StateAt(block.Number().Int64(), block.Root())
if err == nil { if err == nil {
return statedb, noopReleaser, nil return statedb, noopReleaser, nil
} }

View File

@@ -17,6 +17,7 @@
package eth package eth
import ( import (
"fmt"
"math/big" "math/big"
"time" "time"
@@ -191,33 +192,40 @@ func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp {
} }
func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
// If we're in snap sync mode, return that directly
if cs.handler.snapSync.Load() {
block := cs.handler.chain.CurrentSnapBlock()
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
return downloader.SnapSync, td
}
// We are probably in full sync, but we might have rewound to before the
// snap sync pivot, check if we should re-enable snap sync.
head := cs.handler.chain.CurrentBlock() head := cs.handler.chain.CurrentBlock()
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil { if cs.handler.chain.TrieDB().Scheme() != rawdb.VersionScheme {
if head.Number.Uint64() < *pivot { // If we're in snap sync mode, return that directly
if rawdb.ReadAncientType(cs.handler.database) == rawdb.PruneFreezerType { if cs.handler.snapSync.Load() {
log.Crit("Current rewound to before the fast sync pivot, can't enable pruneancient mode", "current block number", head.Number.Uint64(), "pivot", *pivot)
}
block := cs.handler.chain.CurrentSnapBlock() block := cs.handler.chain.CurrentSnapBlock()
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64()) td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
return downloader.SnapSync, td return downloader.SnapSync, td
} }
} // We are probably in full sync, but we might have rewound to before the
// We are in a full sync, but the associated head state is missing. To complete // snap sync pivot, check if we should re-enable snap sync.
// the head state, forcefully rerun the snap sync. Note it doesn't mean the head = cs.handler.chain.CurrentBlock()
// persistent state is corrupted, just mismatch with the head block. if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
if !cs.handler.chain.NoTries() && !cs.handler.chain.HasState(head.Root) { if head.Number.Uint64() < *pivot {
block := cs.handler.chain.CurrentSnapBlock() if rawdb.ReadAncientType(cs.handler.database) == rawdb.PruneFreezerType {
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64()) log.Crit("Current rewound to before the fast sync pivot, can't enable pruneancient mode", "current block number", head.Number.Uint64(), "pivot", *pivot)
log.Info("Reenabled snap sync as chain is stateless") }
return downloader.SnapSync, td block := cs.handler.chain.CurrentSnapBlock()
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
return downloader.SnapSync, td
}
}
// We are in a full sync, but the associated head state is missing. To complete
// the head state, forcefully rerun the snap sync. Note it doesn't mean the
// persistent state is corrupted, just mismatch with the head block.
if !cs.handler.chain.NoTries() && !cs.handler.chain.HasState(head.Number.Int64(), head.Root) {
block := cs.handler.chain.CurrentSnapBlock()
td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64())
log.Info("Reenabled snap sync as chain is stateless")
return downloader.SnapSync, td
}
} else {
if !cs.handler.chain.HasState(head.Number.Int64(), head.Root) {
panic(fmt.Sprintf("version db not support snap sync, version: %d, root: %s", head.Number.Int64(), head.Root))
}
} }
// Nope, we're really full syncing // Nope, we're really full syncing
td := cs.handler.chain.GetTd(head.Hash(), head.Number.Uint64()) td := cs.handler.chain.GetTd(head.Hash(), head.Number.Uint64())

3
go.mod
View File

@@ -42,7 +42,7 @@ require (
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/graph-gophers/graphql-go v1.3.0 github.com/graph-gophers/graphql-go v1.3.0
github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-bexpr v0.1.10
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/hashicorp/golang-lru v1.0.2
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4
github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.3.0 github.com/holiman/uint256 v1.3.0
@@ -252,7 +252,6 @@ require (
github.com/spf13/afero v1.10.0 // indirect github.com/spf13/afero v1.10.0 // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/gjson v1.10.2 // indirect github.com/tidwall/gjson v1.10.2 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect

6
go.sum
View File

@@ -598,8 +598,8 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
@@ -1152,8 +1152,6 @@ github.com/tendermint/iavl v0.12.0 h1:xcaFAr+ycqCj7WN1RzL2EfcBioRDOHcU1oWcg83K02
github.com/tendermint/iavl v0.12.0/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM= github.com/tendermint/iavl v0.12.0/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM=
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo=
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=

View File

@@ -1367,11 +1367,11 @@ func (s *BlockChainAPI) needToReplay(ctx context.Context, block *types.Block, ac
if err != nil { if err != nil {
return false, fmt.Errorf("block not found for block number (%d): %v", block.NumberU64()-1, err) return false, fmt.Errorf("block not found for block number (%d): %v", block.NumberU64()-1, err)
} }
parentState, err := s.b.Chain().StateAt(parent.Root()) parentState, err := s.b.Chain().StateAt(parent.Number().Int64(), parent.Root())
if err != nil { if err != nil {
return false, fmt.Errorf("statedb not found for block number (%d): %v", block.NumberU64()-1, err) return false, fmt.Errorf("statedb not found for block number (%d): %v", block.NumberU64()-1, err)
} }
currentState, err := s.b.Chain().StateAt(block.Root()) currentState, err := s.b.Chain().StateAt(parent.Number().Int64(), block.Root())
if err != nil { if err != nil {
return false, fmt.Errorf("statedb not found for block number (%d): %v", block.NumberU64(), err) return false, fmt.Errorf("statedb not found for block number (%d): %v", block.NumberU64(), err)
} }
@@ -1397,7 +1397,7 @@ func (s *BlockChainAPI) replay(ctx context.Context, block *types.Block, accounts
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("block not found for block number (%d): %v", block.NumberU64()-1, err) return nil, nil, fmt.Errorf("block not found for block number (%d): %v", block.NumberU64()-1, err)
} }
statedb, err := s.b.Chain().StateAt(parent.Root()) statedb, err := s.b.Chain().StateAt(parent.Number().Int64(), parent.Root())
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("state not found for block number (%d): %v", block.NumberU64()-1, err) return nil, nil, fmt.Errorf("state not found for block number (%d): %v", block.NumberU64()-1, err)
} }

View File

@@ -245,7 +245,7 @@ func (miner *Miner) Pending() (*types.Block, *state.StateDB) {
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
stateDb, err := miner.worker.chain.StateAt(block.Root) stateDb, err := miner.worker.chain.StateAt(block.Number.Int64(), block.Root)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }

View File

@@ -690,7 +690,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co
prevEnv *environment) (*environment, error) { prevEnv *environment) (*environment, error) {
// Retrieve the parent state to execute on top and start a prefetcher for // Retrieve the parent state to execute on top and start a prefetcher for
// the miner to speed block sealing up a bit // the miner to speed block sealing up a bit
state, err := w.chain.StateAt(parent.Root) state, err := w.chain.StateAt(parent.Number.Int64(), parent.Root)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -29,11 +29,15 @@ func New(config *Config) *VersionDB {
if config != nil { if config != nil {
path = config.Path path = config.Path
cfg = &versa.VersaDBConfig{ cfg = &versa.VersaDBConfig{
FlushInterval: config.FlushInterval, FlushInterval: 6000,
MaxStatesInMem: config.MaxStatesInMem, MaxStatesInMem: 128,
} }
_ = cfg
} }
db, err := versa.NewVersaDB(path, cfg) db, err := versa.NewVersaDB(path, &versa.VersaDBConfig{
FlushInterval: 6000,
MaxStatesInMem: 128,
})
if err != nil { if err != nil {
log.Crit("failed to new version db", "error", err) log.Crit("failed to new version db", "error", err)
} }
@@ -49,7 +53,8 @@ func (v *VersionDB) Scheme() string {
} }
func (v *VersionDB) Initialized(genesisRoot common.Hash) bool { func (v *VersionDB) Initialized(genesisRoot common.Hash) bool {
return v.db.HasState(genesisRoot) version, _ := v.db.LatestStoreDiskVersionInfo()
return version >= 0
} }
func (v *VersionDB) Size() (common.StorageSize, common.StorageSize, common.StorageSize) { func (v *VersionDB) Size() (common.StorageSize, common.StorageSize, common.StorageSize) {