Svarog fb6c9c8f21
Remove virtual diff parents (#1550)
* resolveSingleBlockStatus: If the block being resolved is not going to be the next selectedTip - set it's diffParent to be the old selectedTip

* resolveSingleBlockStatus: If the block being resolved is going to be the next selectedTip - set it as old selectedTip's diffChild

* Remove any mentions of virtualDiffParents

* If block is genesis - don't do all the mumbo-jumbo with oldSelectedTip

* Check an unchecked error

* Write a better log message
2021-02-23 17:19:35 +02:00

219 lines
7.8 KiB
Go

package consensusstatemanager
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
)
func (csm *consensusStateManager) resolveBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveBlockStatus for %s", blockHash))
defer onEnd()
log.Debugf("Getting a list of all blocks in the selected "+
"parent chain of %s that have no yet resolved their status", blockHash)
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(blockHash)
if err != nil {
return 0, err
}
log.Debugf("Got %d unverified blocks in the selected parent "+
"chain of %s: %s", len(unverifiedBlocks), blockHash, unverifiedBlocks)
// If there's no unverified blocks in the given block's chain - this means the given block already has a
// UTXO-verified status, and therefore it should be retrieved from the store and returned
if len(unverifiedBlocks) == 0 {
log.Debugf("There are not unverified blocks in %s's selected parent chain. "+
"This means that the block already has a UTXO-verified status.", blockHash)
status, err := csm.blockStatusStore.Get(csm.databaseContext, blockHash)
if err != nil {
return 0, err
}
log.Debugf("Block %s's status resolved to: %s", blockHash, status)
return status, nil
}
log.Debugf("Finding the status of the selected parent of %s", blockHash)
selectedParentStatus, err := csm.findSelectedParentStatus(unverifiedBlocks)
if err != nil {
return 0, err
}
log.Debugf("The status of the selected parent of %s is: %s", blockHash, selectedParentStatus)
log.Debugf("Resolving the unverified blocks' status in reverse order (past to present)")
var blockStatus externalapi.BlockStatus
for i := len(unverifiedBlocks) - 1; i >= 0; i-- {
unverifiedBlockHash := unverifiedBlocks[i]
if selectedParentStatus == externalapi.StatusDisqualifiedFromChain {
blockStatus = externalapi.StatusDisqualifiedFromChain
} else {
blockStatus, err = csm.resolveSingleBlockStatus(unverifiedBlockHash)
if err != nil {
return 0, err
}
}
csm.blockStatusStore.Stage(unverifiedBlockHash, blockStatus)
selectedParentStatus = blockStatus
log.Debugf("Block %s status resolved to `%s`, finished %d/%d of unverified blocks",
unverifiedBlockHash, blockStatus, len(unverifiedBlocks)-i, len(unverifiedBlocks))
}
return blockStatus, nil
}
// findSelectedParentStatus returns the status of the selectedParent of the last block in the unverifiedBlocks chain
func (csm *consensusStateManager) findSelectedParentStatus(unverifiedBlocks []*externalapi.DomainHash) (
externalapi.BlockStatus, error) {
log.Debugf("findSelectedParentStatus start")
defer log.Debugf("findSelectedParentStatus end")
lastUnverifiedBlock := unverifiedBlocks[len(unverifiedBlocks)-1]
if lastUnverifiedBlock.Equal(csm.genesisHash) {
log.Debugf("the most recent unverified block is the genesis block, "+
"which by definition has status: %s", externalapi.StatusUTXOValid)
return externalapi.StatusUTXOValid, nil
}
lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, lastUnverifiedBlock)
if err != nil {
return 0, err
}
return csm.blockStatusStore.Get(csm.databaseContext, lastUnverifiedBlockGHOSTDAGData.SelectedParent())
}
func (csm *consensusStateManager) getUnverifiedChainBlocks(
blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
log.Debugf("getUnverifiedChainBlocks start for block %s", blockHash)
defer log.Debugf("getUnverifiedChainBlocks end for block %s", blockHash)
var unverifiedBlocks []*externalapi.DomainHash
currentHash := blockHash
for {
log.Debugf("Getting status for block %s", currentHash)
currentBlockStatus, err := csm.blockStatusStore.Get(csm.databaseContext, currentHash)
if err != nil {
return nil, err
}
if currentBlockStatus != externalapi.StatusUTXOPendingVerification {
log.Debugf("Block %s has status %s. Returning all the "+
"unverified blocks prior to it: %s", currentHash, currentBlockStatus, unverifiedBlocks)
return unverifiedBlocks, nil
}
log.Debugf("Block %s is unverified. Adding it to the unverified block collection", currentHash)
unverifiedBlocks = append(unverifiedBlocks, currentHash)
currentBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, currentHash)
if err != nil {
return nil, err
}
if currentBlockGHOSTDAGData.SelectedParent() == nil {
log.Debugf("Genesis block reached. Returning all the "+
"unverified blocks prior to it: %s", unverifiedBlocks)
return unverifiedBlocks, nil
}
currentHash = currentBlockGHOSTDAGData.SelectedParent()
}
}
func (csm *consensusStateManager) resolveSingleBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, fmt.Sprintf("resolveSingleBlockStatus for %s", blockHash))
defer onEnd()
log.Tracef("Calculating pastUTXO and acceptance data and multiset for block %s", blockHash)
pastUTXODiff, acceptanceData, multiset, err := csm.CalculatePastUTXOAndAcceptanceData(blockHash)
if err != nil {
return 0, err
}
log.Tracef("Staging the calculated acceptance data of block %s", blockHash)
csm.acceptanceDataStore.Stage(blockHash, acceptanceData)
block, err := csm.blockStore.Block(csm.databaseContext, blockHash)
if err != nil {
return 0, err
}
log.Tracef("verifying the UTXO of block %s", blockHash)
err = csm.verifyUTXO(block, blockHash, pastUTXODiff, acceptanceData, multiset)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
log.Debugf("UTXO verification for block %s failed: %s", blockHash, err)
return externalapi.StatusDisqualifiedFromChain, nil
}
return 0, err
}
log.Debugf("UTXO verification for block %s passed", blockHash)
log.Tracef("Staging the multiset of block %s", blockHash)
csm.multisetStore.Stage(blockHash, multiset)
if csm.genesisHash.Equal(blockHash) {
log.Tracef("Staging the utxoDiff of genesis")
csm.stageDiff(blockHash, pastUTXODiff, nil)
return externalapi.StatusUTXOValid, nil
}
oldSelectedTip, err := csm.selectedTip()
if err != nil {
return 0, err
}
isNewSelectedTip, err := csm.isNewSelectedTip(blockHash, oldSelectedTip)
if err != nil {
return 0, err
}
oldSelectedTipUTXOSet, err := csm.restorePastUTXO(oldSelectedTip)
if err != nil {
return 0, err
}
if isNewSelectedTip {
log.Debugf("Block %s is the new SelectedTip, therefore setting it as old selectedTip's diffChild", blockHash)
oldSelectedTipUTXOSet, err := pastUTXODiff.DiffFrom(oldSelectedTipUTXOSet.ToImmutable())
if err != nil {
return 0, err
}
csm.stageDiff(oldSelectedTip, oldSelectedTipUTXOSet, blockHash)
log.Tracef("Staging the utxoDiff of block %s", blockHash)
csm.stageDiff(blockHash, pastUTXODiff, nil)
} else {
log.Debugf("Block %s is not the new SelectedTip, therefore setting old selectedTip as it's diffChild", blockHash)
pastUTXODiff, err = oldSelectedTipUTXOSet.DiffFrom(pastUTXODiff)
if err != nil {
return 0, err
}
log.Tracef("Staging the utxoDiff of block %s", blockHash)
csm.stageDiff(blockHash, pastUTXODiff, oldSelectedTip)
}
return externalapi.StatusUTXOValid, nil
}
func (csm *consensusStateManager) isNewSelectedTip(blockHash, oldSelectedTip *externalapi.DomainHash) (bool, error) {
newSelectedTip, err := csm.ghostdagManager.ChooseSelectedParent(blockHash, oldSelectedTip)
if err != nil {
return false, err
}
return blockHash.Equal(newSelectedTip), nil
}
func (csm *consensusStateManager) selectedTip() (*externalapi.DomainHash, error) {
virtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, model.VirtualBlockHash)
if err != nil {
return nil, err
}
return virtualGHOSTDAGData.SelectedParent(), nil
}