Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blockchain: Optimize stake node pruning. #2943

Merged
merged 3 commits into from May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 21 additions & 2 deletions blockchain/blockindex.go
Expand Up @@ -1069,6 +1069,23 @@ func (bi *blockIndex) maybeUpdateBestHeaderForTip(tip *blockNode) {
}
}

// BestHeader returns the header with the most cumulative work that is NOT known
// to be invalid. This is not necessarily the same as the active best chain,
// especially when block data is not yet known. However, since block nodes are
// only added to the index for block headers that pass all sanity and positional
// checks, which include checking proof of work, it does represent the tip of
// the header chain with the highest known work that has a reasonably high
// chance of becoming the best chain tip and is useful for things such as
// reporting progress and discovering the most suitable blocks to download.
//
// This function is safe for concurrent access.
func (bi *blockIndex) BestHeader() *blockNode {
bi.RLock()
bestHeader := bi.bestHeader
bi.RUnlock()
return bestHeader
}

// MarkBlockFailedValidation marks the passed node as having failed validation
// and then marks all of its descendants (if any) as having a failed ancestor.
//
Expand Down Expand Up @@ -1362,9 +1379,11 @@ func (bi *blockIndex) FindBestChainCandidate() *blockNode {
return bestCandidate
}

// flush writes all of the modified block nodes to the database and clears the
// Flush writes all of the modified block nodes to the database and clears the
// set of modified nodes if it succeeds.
func (bi *blockIndex) flush() error {
//
// This function is safe for concurrent access.
func (bi *blockIndex) Flush() error {
// Nothing to flush if there are no modified nodes.
bi.Lock()
if len(bi.modified) == 0 {
Expand Down
27 changes: 12 additions & 15 deletions blockchain/chain.go
Expand Up @@ -828,14 +828,15 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block,
})
}

// Optimization: Before checkpoints, immediately dump the parent's stake
// node because we no longer need it.
var latestCheckpointHeight int64
if b.latestCheckpoint != nil {
latestCheckpointHeight = b.latestCheckpoint.Height
}
if node.height < latestCheckpointHeight {
parent := b.bestChain.Tip().parent
// Optimization: Immediately prune the parent's stake node when it is no
// longer needed due to being too far behind the best known header.
var pruneHeight int64
bestHeader := b.index.BestHeader()
if bestHeader.height > minMemoryStakeNodes {
pruneHeight = bestHeader.height - minMemoryStakeNodes
}
if node.height < pruneHeight {
parent := node.parent
parent.stakeNode = nil
parent.newTickets = nil
parent.ticketsVoted = nil
Expand Down Expand Up @@ -1531,7 +1532,7 @@ func (b *BlockChain) flushBlockIndex() error {
return err
}
}
return b.index.flush()
return b.index.Flush()
}

// flushBlockIndexWarnOnly attempts to flush any modified block index nodes to
Expand Down Expand Up @@ -1578,9 +1579,7 @@ func (b *BlockChain) maybeUpdateIsCurrent(curBest *blockNode) {

// Not current if the best block is not synced to the header with the
// most cumulative work that is not known to be invalid.
b.index.RLock()
bestHeader := b.index.bestHeader
b.index.RUnlock()
bestHeader := b.index.BestHeader()
syncedToBestHeader := curBest.height == bestHeader.height ||
bestHeader.IsAncestorOf(curBest)
if !syncedToBestHeader {
Expand Down Expand Up @@ -2412,9 +2411,7 @@ func New(ctx context.Context, config *Config) (*BlockChain, error) {
log.Infof("UTXO database version info: version: %d, compression: %d, utxo "+
"set: %d", utxoDbInfo.version, utxoDbInfo.compVer, utxoDbInfo.utxoVer)

b.index.RLock()
bestHdr := b.index.bestHeader
b.index.RUnlock()
bestHdr := b.index.BestHeader()
log.Infof("Best known header: height %d, hash %v", bestHdr.height,
bestHdr.hash)

Expand Down
8 changes: 2 additions & 6 deletions blockchain/chainquery.go
Expand Up @@ -141,9 +141,7 @@ func (b *BlockChain) ChainTips() []ChainTipInfo {
//
// This function is safe for concurrent access.
func (b *BlockChain) BestHeader() (chainhash.Hash, int64) {
b.index.RLock()
header := b.index.bestHeader
b.index.RUnlock()
header := b.index.BestHeader()
return header.hash, header.height
}

Expand Down Expand Up @@ -244,9 +242,7 @@ func (b *BlockChain) PutNextNeededBlocks(out []chainhash.Hash) []chainhash.Hash
//
// This function is safe for concurrent access.
func (b *BlockChain) VerifyProgress() float64 {
b.index.RLock()
bestHdr := b.index.bestHeader
b.index.RUnlock()
bestHdr := b.index.BestHeader()
if bestHdr.height == 0 {
return 0.0
}
Expand Down
4 changes: 1 addition & 3 deletions blockchain/process.go
Expand Up @@ -89,9 +89,7 @@ func (b *BlockChain) isAssumeValidAncestor(node *blockNode) bool {
}

// Return false if the node is not an ancestor of the best header.
b.index.RLock()
bestHeader := b.index.bestHeader
b.index.RUnlock()
bestHeader := b.index.BestHeader()
if !node.IsAncestorOf(bestHeader) {
return false
}
Expand Down