Skip to content

Commit

Permalink
feat: support version and root as key to state
Browse files Browse the repository at this point in the history
fix: genesis version to -1

fix: copy with version

fix: change version type from uint64 to int64
  • Loading branch information
joeylichang committed Sep 5, 2024
1 parent eaddc87 commit ec1d0a4
Show file tree
Hide file tree
Showing 18 changed files with 73 additions and 59 deletions.
2 changes: 1 addition & 1 deletion cmd/utils/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block
continue
}
// 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:]
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func ValidateListsInBody(block *types.Block) error {
// validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// 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
}
if v.bc.isCachedBadBlock(block) {
Expand Down Expand Up @@ -149,7 +149,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return nil
},
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) {
return consensus.ErrUnknownAncestor
}
Expand Down
29 changes: 14 additions & 15 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// if there is no available state, waiting for state sync.
head := bc.CurrentBlock()
if bc.triedb.Scheme() != rawdb.VersionScheme {
if !bc.HasState(head.Root) {
if !bc.HasState(head.Number.Int64(), head.Root) {
if head.Number.Uint64() == 0 {
// The genesis state is missing, which is only possible in the path-based
// scheme. This situation occurs when the initial state sync is not finished
Expand All @@ -447,7 +447,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
}
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
recoverable, _ := bc.triedb.Recoverable(diskRoot)
if !bc.HasState(diskRoot) && !recoverable {
if !bc.HasState(0, diskRoot) && !recoverable {
diskRoot = bc.triedb.Head()
}
}
Expand Down Expand Up @@ -776,8 +776,7 @@ func (bc *BlockChain) loadLastState() error {
archiveVersion, archiveRoot := versa.LatestStoreDiskVersionInfo()
// first start
if archiveVersion == -1 {
archiveVersion = 0
archiveRoot = bc.genesisBlock.Root()
log.Crit("please clear data, resync")
}

if int64(headBlock.NumberU64()) < archiveVersion {
Expand Down Expand Up @@ -989,7 +988,7 @@ func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*typ
}
// If the associated state is not reachable, continue searching
// 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
// block as the new chain head.
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1)
Expand Down Expand Up @@ -1029,7 +1028,7 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ

// noState represents if the target state requested for search
// 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
logged = time.Now() // Timestamp last progress log was printed
Expand All @@ -1050,13 +1049,13 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
// If the root threshold hasn't been crossed but the available
// state is reached, quickly determine if the target state is
// 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
log.Info("Disable the search for unattainable state", "root", root)
}
// Check if the associated state is available or recoverable if
// 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
}
// If pivot block is reached, return the genesis block as the
Expand All @@ -1083,7 +1082,7 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
}
}
// 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 {
log.Crit("Failed to rollback state", "err", err)
}
Expand Down Expand Up @@ -1178,7 +1177,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
// the pivot point. In this scenario, there is no possible recovery
// approach except for rerunning a snap sync. Do nothing here until the
// state syncer picks it up.
if !bc.HasState(newHeadBlock.Root) {
if !bc.HasState(newHeadBlock.Number.Int64(), newHeadBlock.Root) {
if newHeadBlock.Number.Uint64() != 0 {
log.Crit("Chain is stateless at a non-genesis block")
}
Expand Down Expand Up @@ -1290,7 +1289,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
return err
}
}
if !bc.NoTries() && !bc.HasState(root) {
if !bc.NoTries() && !bc.HasState(0, root) {
return fmt.Errorf("non existent state [%x..]", root[:4])
}
// If all checks out, manually set the head block.
Expand Down Expand Up @@ -2347,7 +2346,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
}

bc.stateCache.SetVersion(int64(block.NumberU64()))
bc.stateCache.SetVersion(int64(block.NumberU64()) - 1)
statedb, err := state.NewWithSharedPool(parent.Root, bc.stateCache, bc.snaps)
if err != nil {
bc.stateCache.Release()
Expand Down Expand Up @@ -2615,7 +2614,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
numbers []uint64
)
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 err := bc.triedb.Recover(parent.Root); err != nil {
return 0, err
Expand Down Expand Up @@ -2682,7 +2681,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
numbers []uint64
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 err := bc.triedb.Recover(parent.Root()); err != nil {
return common.Hash{}, err
Expand Down Expand Up @@ -2953,7 +2952,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
defer bc.chainmu.Unlock()

// 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 {
return latestValidHash, err
}
Expand Down
33 changes: 23 additions & 10 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package core

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -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.
func (bc *BlockChain) HasState(hash common.Hash) bool {
func (bc *BlockChain) HasState(number int64, hash common.Hash) bool {
if bc.NoTries() {
return bc.snaps != nil && bc.snaps.Snapshot(hash) != nil
}
Expand All @@ -348,18 +349,24 @@ func (bc *BlockChain) HasState(hash common.Hash) bool {
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
// 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
block := bc.GetBlock(hash, number)
if block == nil {
return false
var root common.Hash
if number < 0 {
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.
Expand Down Expand Up @@ -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.
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.
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
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 {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
triedbConfig.NoTries = false
}
cachingdb := state.NewDatabaseWithNodeDB(db, triedb, true)
cachingdb.SetVersion(0)
cachingdb.SetVersion(-1)
defer cachingdb.Release()
statedb, err := state.New(types.EmptyRootHash, cachingdb, nil)
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions core/state/caching_versa_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func (cv *cachingVersaDB) Copy() Database {
cp.triedb = cv.triedb
cp.versionDB = cv.versionDB
cp.codeDB = cv.codeDB
cp.version = cv.version
cp.mode = versa.S_RW // it is important

// TODO:: maybe add lock for cv.root
Expand Down Expand Up @@ -109,8 +110,8 @@ func (cv *cachingVersaDB) CopyTrie(tr Trie) Trie {
return nil
}

func (cv *cachingVersaDB) HasState(root common.Hash) bool {
return cv.versionDB.HasState(root)
func (cv *cachingVersaDB) HasState(version int64, root common.Hash) bool {
return cv.versionDB.HasState(version, root)
}

func (cv *cachingVersaDB) OpenTrie(root common.Hash) (Trie, error) {
Expand Down Expand Up @@ -214,7 +215,7 @@ func (cv *cachingVersaDB) Flush() error {
}

func (cv *cachingVersaDB) SetVersion(version int64) {
cv.version = version - 1
cv.version = version
//cv.debug = NewDebugVersionState(cv.codeDB, cv.versionDB)
//cv.debug.Version = version
}
Expand Down
4 changes: 2 additions & 2 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type Database interface {
Copy() Database

// 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(tr Trie) bool
Expand Down Expand Up @@ -402,7 +402,7 @@ func (db *cachingDB) Copy() Database {
return db
}

func (db *cachingDB) HasState(root common.Hash) bool {
func (db *cachingDB) HasState(_ int64, root common.Hash) bool {
_, err := db.OpenTrie(root)
return err == nil
}
Expand Down
6 changes: 3 additions & 3 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
// case the head state is not available (might occur when node is not
// fully synced).
state, err := p.chain.StateAt(head.Root)
state, err := p.chain.StateAt(head.Number.Int64(), head.Root)
if err != nil {
state, err = p.chain.StateAt(types.EmptyRootHash)
state, err = p.chain.StateAt(-1, types.EmptyRootHash)
}
if err != nil {
return err
Expand Down Expand Up @@ -793,7 +793,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
resettimeHist.Update(time.Since(start).Nanoseconds())
}(time.Now())

statedb, err := p.chain.StateAt(newHead.Root)
statedb, err := p.chain.StateAt(newHead.Number.Int64(), newHead.Root)
if err != nil {
log.Error("Failed to reset blobpool state", "err", err)
return
Expand Down
2 changes: 1 addition & 1 deletion core/txpool/blobpool/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ type BlockChain interface {
GetBlock(hash common.Hash, number uint64) *types.Block

// 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)
}
8 changes: 4 additions & 4 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type BlockChain interface {
GetBlock(hash common.Hash, number uint64) *types.Block

// 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.
Expand Down Expand Up @@ -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
// case the head state is not available (might occur when node is not
// fully synced).
statedb, err := pool.chain.StateAt(head.Root)
statedb, err := pool.chain.StateAt(head.Number.Int64(), head.Root)
if err != nil {
statedb, err = pool.chain.StateAt(types.EmptyRootHash)
statedb, err = pool.chain.StateAt(-1, types.EmptyRootHash)
}
if err != nil {
return err
Expand Down Expand Up @@ -1492,7 +1492,7 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) {
if newHead == nil {
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 {
log.Error("Failed to reset txpool state", "err", err)
return
Expand Down
4 changes: 2 additions & 2 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
if header == nil {
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 {
return nil, nil, err
}
Expand All @@ -226,7 +226,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
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 {
return nil, nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions eth/api_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
if header == nil {
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 {
return state.Dump{}, err
}
Expand Down Expand Up @@ -167,7 +167,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if header == nil {
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 {
return state.Dump{}, err
}
Expand All @@ -177,7 +177,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if block == nil {
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 {
return state.Dump{}, err
}
Expand Down
4 changes: 2 additions & 2 deletions eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ func newHandler(config *handlerConfig) (*handler, error) {
}
h.snapSync.Store(true)
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)
log.Warn("Switch sync mode from full sync to snap sync", "reason", "head state missing")
}
} else {
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.
log.Warn("Switch sync mode from snap sync to full sync", "reason", "snap sync complete")
} else {
Expand Down
Loading

0 comments on commit ec1d0a4

Please sign in to comment.