mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core, light: add NoLock func to avoid double lock
This commit is contained in:
parent
1d7ee903e5
commit
bce581a957
2 changed files with 135 additions and 9 deletions
|
|
@ -329,18 +329,14 @@ func (bc *BlockChain) loadLastState() error {
|
||||||
if head == (common.Hash{}) {
|
if head == (common.Hash{}) {
|
||||||
// Corrupt or empty database, init from scratch
|
// Corrupt or empty database, init from scratch
|
||||||
log.Warn("Empty database, resetting chain")
|
log.Warn("Empty database, resetting chain")
|
||||||
bc.chainmu.Unlock()
|
return bc.resetWithGenesisBlockNoLock(bc.genesisBlock)
|
||||||
defer bc.chainmu.Lock()
|
|
||||||
return bc.Reset()
|
|
||||||
}
|
}
|
||||||
// Make sure the entire head block is available
|
// Make sure the entire head block is available
|
||||||
currentBlock := bc.GetBlockByHash(head)
|
currentBlock := bc.GetBlockByHash(head)
|
||||||
if currentBlock == nil {
|
if currentBlock == nil {
|
||||||
// Corrupt or empty database, init from scratch
|
// Corrupt or empty database, init from scratch
|
||||||
log.Warn("Head block missing, resetting chain", "hash", head)
|
log.Warn("Head block missing, resetting chain", "hash", head)
|
||||||
bc.chainmu.Unlock()
|
return bc.resetWithGenesisBlockNoLock(bc.genesisBlock)
|
||||||
defer bc.chainmu.Lock()
|
|
||||||
return bc.Reset()
|
|
||||||
}
|
}
|
||||||
// Make sure the state associated with the block is available
|
// Make sure the state associated with the block is available
|
||||||
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
|
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
|
||||||
|
|
@ -388,6 +384,85 @@ func (bc *BlockChain) loadLastState() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setHeadNoLock rewinds the local chain to a new head. The callers should hold bc.chainmu.
|
||||||
|
func (bc *BlockChain) setHeadNoLock(head uint64) error {
|
||||||
|
log.Warn("Rewinding blockchain", "target", head)
|
||||||
|
|
||||||
|
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) {
|
||||||
|
// Rewind the block chain, ensuring we don't end up with a stateless head block
|
||||||
|
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() < currentBlock.NumberU64() {
|
||||||
|
newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
|
||||||
|
if newHeadBlock == nil {
|
||||||
|
newHeadBlock = bc.genesisBlock
|
||||||
|
} else {
|
||||||
|
if _, err := state.New(newHeadBlock.Root(), bc.stateCache); err != nil {
|
||||||
|
// Rewound state missing, rolled back to before pivot, reset to genesis
|
||||||
|
newHeadBlock = bc.genesisBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash())
|
||||||
|
|
||||||
|
// Degrade the chain markers if they are explicitly reverted.
|
||||||
|
// In theory we should update all in-memory markers in the
|
||||||
|
// last step, however the direction of SetHead is from high
|
||||||
|
// to low, so it's safe the update in-memory markers directly.
|
||||||
|
bc.currentBlock.Store(newHeadBlock)
|
||||||
|
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind the fast block in a simpleton way to the target head
|
||||||
|
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && header.Number.Uint64() < currentFastBlock.NumberU64() {
|
||||||
|
newHeadFastBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
|
||||||
|
// If either blocks reached nil, reset to the genesis state
|
||||||
|
if newHeadFastBlock == nil {
|
||||||
|
newHeadFastBlock = bc.genesisBlock
|
||||||
|
}
|
||||||
|
rawdb.WriteHeadFastBlockHash(db, newHeadFastBlock.Hash())
|
||||||
|
|
||||||
|
// Degrade the chain markers if they are explicitly reverted.
|
||||||
|
// In theory we should update all in-memory markers in the
|
||||||
|
// last step, however the direction of SetHead is from high
|
||||||
|
// to low, so it's safe the update in-memory markers directly.
|
||||||
|
bc.currentFastBlock.Store(newHeadFastBlock)
|
||||||
|
headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind the header chain, deleting all block bodies until then
|
||||||
|
delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) {
|
||||||
|
// Ignore the error here since light client won't hit this path
|
||||||
|
frozen, _ := bc.db.Ancients()
|
||||||
|
if num+1 <= frozen {
|
||||||
|
// Truncate all relative data(header, total difficulty, body, receipt
|
||||||
|
// and canonical hash) from ancient store.
|
||||||
|
if err := bc.db.TruncateAncients(num + 1); err != nil {
|
||||||
|
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the hash <-> number mapping from the active store.
|
||||||
|
rawdb.DeleteHeaderNumber(db, hash)
|
||||||
|
} else {
|
||||||
|
// Remove relative body and receipts from the active store.
|
||||||
|
// The header, total difficulty and canonical hash will be
|
||||||
|
// removed in the hc.SetHead function.
|
||||||
|
rawdb.DeleteBody(db, hash, num)
|
||||||
|
rawdb.DeleteReceipts(db, hash, num)
|
||||||
|
}
|
||||||
|
// Todo(rjl493456442) txlookup, bloombits, etc
|
||||||
|
}
|
||||||
|
bc.hc.SetHead(head, updateFn, delFn)
|
||||||
|
|
||||||
|
// Clear out any stale content from the caches
|
||||||
|
bc.bodyCache.Purge()
|
||||||
|
bc.bodyRLPCache.Purge()
|
||||||
|
bc.receiptsCache.Purge()
|
||||||
|
bc.blockCache.Purge()
|
||||||
|
bc.txLookupCache.Purge()
|
||||||
|
bc.futureBlocks.Purge()
|
||||||
|
|
||||||
|
return bc.loadLastState()
|
||||||
|
}
|
||||||
|
|
||||||
// SetHead rewinds the local chain to a new head. In the case of headers, everything
|
// SetHead rewinds the local chain to a new head. In the case of headers, everything
|
||||||
// above the new head will be deleted and the new one set. In the case of blocks
|
// above the new head will be deleted and the new one set. In the case of blocks
|
||||||
// though, the head may be further rewound if block bodies are missing (non-archive
|
// though, the head may be further rewound if block bodies are missing (non-archive
|
||||||
|
|
@ -541,6 +616,34 @@ func (bc *BlockChain) Reset() error {
|
||||||
return bc.ResetWithGenesisBlock(bc.genesisBlock)
|
return bc.ResetWithGenesisBlock(bc.genesisBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resetWithGenesisBlockNoLock purges the entire blockchain, restoring it to the
|
||||||
|
// specified genesis state. The callers should hold bc.chainmu.
|
||||||
|
func (bc *BlockChain) resetWithGenesisBlockNoLock(genesis *types.Block) error {
|
||||||
|
// Dump the entire block chain and purge the caches
|
||||||
|
if err := bc.setHeadNoLock(0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the genesis block and reinitialise the chain
|
||||||
|
batch := bc.db.NewBatch()
|
||||||
|
rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||||
|
rawdb.WriteBlock(batch, genesis)
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
log.Crit("Failed to write genesis block", "err", err)
|
||||||
|
}
|
||||||
|
bc.writeHeadBlock(genesis)
|
||||||
|
|
||||||
|
// Last update all in-memory chain markers
|
||||||
|
bc.genesisBlock = genesis
|
||||||
|
bc.currentBlock.Store(bc.genesisBlock)
|
||||||
|
headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
|
||||||
|
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
||||||
|
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||||
|
bc.currentFastBlock.Store(bc.genesisBlock)
|
||||||
|
headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
||||||
// specified genesis state.
|
// specified genesis state.
|
||||||
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,7 @@ func (lc *LightChain) HeaderChain() *core.HeaderChain {
|
||||||
func (lc *LightChain) loadLastState() error {
|
func (lc *LightChain) loadLastState() error {
|
||||||
if head := rawdb.ReadHeadHeaderHash(lc.chainDb); head == (common.Hash{}) {
|
if head := rawdb.ReadHeadHeaderHash(lc.chainDb); head == (common.Hash{}) {
|
||||||
// Corrupt or empty database, init from scratch
|
// Corrupt or empty database, init from scratch
|
||||||
lc.chainmu.Unlock()
|
lc.resetWithGenesisBlockNoLock(lc.genesisBlock)
|
||||||
lc.Reset()
|
|
||||||
lc.chainmu.Lock()
|
|
||||||
} else {
|
} else {
|
||||||
if header := lc.GetHeaderByHash(head); header != nil {
|
if header := lc.GetHeaderByHash(head); header != nil {
|
||||||
lc.hc.SetCurrentHeader(header)
|
lc.hc.SetCurrentHeader(header)
|
||||||
|
|
@ -169,6 +167,12 @@ func (lc *LightChain) loadLastState() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setHeadNoLock rewinds the local chain to a new head. Callers should hold lc.chainmu.
|
||||||
|
func (lc *LightChain) setHeadNoLock(head uint64) error {
|
||||||
|
lc.hc.SetHead(head, nil, nil)
|
||||||
|
return lc.loadLastState()
|
||||||
|
}
|
||||||
|
|
||||||
// SetHead rewinds the local chain to a new head. Everything above the new
|
// SetHead rewinds the local chain to a new head. Everything above the new
|
||||||
// head will be deleted and the new one set.
|
// head will be deleted and the new one set.
|
||||||
func (lc *LightChain) SetHead(head uint64) error {
|
func (lc *LightChain) SetHead(head uint64) error {
|
||||||
|
|
@ -189,6 +193,25 @@ func (lc *LightChain) Reset() {
|
||||||
lc.ResetWithGenesisBlock(lc.genesisBlock)
|
lc.ResetWithGenesisBlock(lc.genesisBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resetWithGenesisBlockNoLock purges the entire blockchain, restoring it to the
|
||||||
|
// specified genesis state. Callers should hold lc.chainmu.
|
||||||
|
func (lc *LightChain) resetWithGenesisBlockNoLock(genesis *types.Block) {
|
||||||
|
// Dump the entire block chain and purge the caches
|
||||||
|
lc.setHeadNoLock(0)
|
||||||
|
|
||||||
|
// Prepare the genesis block and reinitialise the chain
|
||||||
|
batch := lc.chainDb.NewBatch()
|
||||||
|
rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||||
|
rawdb.WriteBlock(batch, genesis)
|
||||||
|
rawdb.WriteHeadHeaderHash(batch, genesis.Hash())
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
log.Crit("Failed to reset genesis block", "err", err)
|
||||||
|
}
|
||||||
|
lc.genesisBlock = genesis
|
||||||
|
lc.hc.SetGenesis(lc.genesisBlock.Header())
|
||||||
|
lc.hc.SetCurrentHeader(lc.genesisBlock.Header())
|
||||||
|
}
|
||||||
|
|
||||||
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
||||||
// specified genesis state.
|
// specified genesis state.
|
||||||
func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
|
func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue