Pruning fixes (#1243)

* Pruning related fixes

* Rename setBlockStatus->setBlockStatusAfterBlockValidation

* Rename StatusValid->StatusUTXOValid

* Add comment

* Fix typo

* Rename hasValidatedOnlyHeader->hasValidatedHeader

* Rename checkBlockBodiesExist->checkParentBlockBodiesExist

* Add comments and logs

* Adding logs

* Add logs and assert

* Add comment

* Fix typo

* Fix log
This commit is contained in:
Ori Newman 2020-12-17 17:57:51 +02:00 committed by Mike Zak
parent 6b1e691a57
commit bd5f4e8c6a
16 changed files with 243 additions and 129 deletions

View File

@ -62,8 +62,8 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
if !info.Exists {
t.Fatal("The block is missing")
}
if info.BlockStatus != externalapi.StatusValid {
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusValid, info.BlockStatus)
if info.BlockStatus != externalapi.StatusUTXOValid {
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusUTXOValid, info.BlockStatus)
}
})

View File

@ -55,9 +55,9 @@ func TestFinality(t *testing.T) {
if err != nil {
t.Fatalf("TestFinality: Block #%d failed to get info: %+v", i, err)
}
if blockInfo.BlockStatus != externalapi.StatusValid {
if blockInfo.BlockStatus != externalapi.StatusUTXOValid {
t.Fatalf("Block #%d in main chain expected to have status '%s', but got '%s'",
i, externalapi.StatusValid, blockInfo.BlockStatus)
i, externalapi.StatusUTXOValid, blockInfo.BlockStatus)
}
}
@ -99,9 +99,9 @@ func TestFinality(t *testing.T) {
} else if !blockInfo.Exists {
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
}
if blockInfo.BlockStatus != externalapi.StatusValid {
if blockInfo.BlockStatus != externalapi.StatusUTXOValid {
t.Fatalf("TestFinality: Overtaking block in side-chain expected to have status '%s', but got '%s'",
externalapi.StatusValid, blockInfo.BlockStatus)
externalapi.StatusUTXOValid, blockInfo.BlockStatus)
}
selectedTip, err := consensus.GetVirtualSelectedParent()
if err != nil {
@ -387,14 +387,14 @@ func TestBoundedMergeDepth(t *testing.T) {
}
// Lets validate the status of all the interesting blocks
if getStatus(consensusReal, pointAtBlueKosherizing) != externalapi.StatusValid {
t.Fatalf("TestBoundedMergeDepth: pointAtBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, pointAtBlueKosherizing))
if getStatus(consensusReal, pointAtBlueKosherizing) != externalapi.StatusUTXOValid {
t.Fatalf("TestBoundedMergeDepth: pointAtBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusUTXOValid, getStatus(consensusReal, pointAtBlueKosherizing))
}
if getStatus(consensusReal, pointAtRedKosherizing) != externalapi.StatusInvalid {
t.Fatalf("TestBoundedMergeDepth: pointAtRedKosherizing expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, pointAtRedKosherizing))
}
if getStatus(consensusReal, transitiveBlueKosherizing) != externalapi.StatusValid {
t.Fatalf("TestBoundedMergeDepth: transitiveBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, transitiveBlueKosherizing))
if getStatus(consensusReal, transitiveBlueKosherizing) != externalapi.StatusUTXOValid {
t.Fatalf("TestBoundedMergeDepth: transitiveBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusUTXOValid, getStatus(consensusReal, transitiveBlueKosherizing))
}
if getStatus(consensusReal, mergeDepthViolatingBlockBottom) != externalapi.StatusInvalid {
t.Fatalf("TestBoundedMergeDepth: mergeDepthViolatingBlockBottom expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, mergeDepthViolatingBlockBottom))
@ -412,8 +412,8 @@ func TestBoundedMergeDepth(t *testing.T) {
}
}
for i, b := range selectedChain {
if getStatus(consensusReal, b) != externalapi.StatusValid {
t.Fatalf("selectedChain[%d] expected status '%s' but got '%s'", i, externalapi.StatusValid, getStatus(consensusReal, b))
if getStatus(consensusReal, b) != externalapi.StatusUTXOValid {
t.Fatalf("selectedChain[%d] expected status '%s' but got '%s'", i, externalapi.StatusUTXOValid, getStatus(consensusReal, b))
}
}
})

View File

@ -12,8 +12,8 @@ const (
// StatusInvalid indicates that the block is invalid.
StatusInvalid BlockStatus = iota
// StatusValid indicates that the block has been fully validated.
StatusValid
// StatusUTXOValid indicates the block is valid from any UTXO related aspects and has passed all the other validations as well.
StatusUTXOValid
// StatusUTXOPendingVerification indicates that the block is pending verification against its past UTXO-Set, either
// because it was not yet verified since the block was never in the selected parent chain, or if the
@ -29,7 +29,7 @@ const (
var blockStatusStrings = map[BlockStatus]string{
StatusInvalid: "Invalid",
StatusValid: "Valid",
StatusUTXOValid: "Valid",
StatusUTXOPendingVerification: "UTXOPendingVerification",
StatusDisqualifiedFromChain: "DisqualifiedFromChain",
StatusHeaderOnly: "HeaderOnly",

View File

@ -10,6 +10,6 @@ type BlockValidator interface {
ValidateHeaderInIsolation(blockHash *externalapi.DomainHash) error
ValidateBodyInIsolation(blockHash *externalapi.DomainHash) error
ValidateHeaderInContext(blockHash *externalapi.DomainHash) error
ValidateBodyInContext(blockHash *externalapi.DomainHash) error
ValidateBodyInContext(blockHash *externalapi.DomainHash, isPruningPoint bool) error
ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash *externalapi.DomainHash) error
}

View File

@ -126,7 +126,7 @@ func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock)
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertBlock")
defer onEnd()
return bp.validateAndInsertBlock(block)
return bp.validateAndInsertBlock(block, false)
}
func (bp *blockProcessor) ValidateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {

View File

@ -11,19 +11,59 @@ import (
"github.com/pkg/errors"
)
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock) (*externalapi.BlockInsertionResult, error) {
func (bp *blockProcessor) setBlockStatusAfterBlockValidation(block *externalapi.DomainBlock, isPruningPoint bool) error {
blockHash := consensushashing.BlockHash(block)
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, blockHash)
if err != nil {
return err
}
if exists {
status, err := bp.blockStatusStore.Get(bp.databaseContext, blockHash)
if err != nil {
return err
}
if status == externalapi.StatusUTXOValid {
// A block cannot have status StatusUTXOValid just after finishing bp.validateBlock, because
// if it's the case it should have been rejected as duplicate block.
// The only exception is the pruning point because its status is manually set before inserting
// the block.
if !isPruningPoint {
return errors.Errorf("block %s that is not the pruning point is not expected to be valid "+
"before adding to to the consensus state manager", blockHash)
}
log.Tracef("Block %s is the pruning point and has status %s, so leaving its status untouched",
blockHash, status)
return nil
}
}
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
if isHeaderOnlyBlock {
log.Tracef("Block %s is a header-only block so setting its status as %s",
blockHash, externalapi.StatusHeaderOnly)
bp.blockStatusStore.Stage(blockHash, externalapi.StatusHeaderOnly)
} else {
log.Tracef("Block %s has body so setting its status as %s",
blockHash, externalapi.StatusUTXOPendingVerification)
bp.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
}
return nil
}
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock, isPruningPoint bool) (*externalapi.BlockInsertionResult, error) {
blockHash := consensushashing.HeaderHash(block.Header)
err := bp.validateBlock(block)
err := bp.validateBlock(block, isPruningPoint)
if err != nil {
bp.discardAllChanges()
return nil, err
}
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
if isHeaderOnlyBlock {
bp.blockStatusStore.Stage(blockHash, externalapi.StatusHeaderOnly)
} else {
bp.blockStatusStore.Stage(blockHash, externalapi.StatusUTXOPendingVerification)
err = bp.setBlockStatusAfterBlockValidation(block, isPruningPoint)
if err != nil {
return nil, err
}
// Block validations passed, save whatever DAG data was
@ -34,8 +74,8 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
}
var oldHeadersSelectedTip *externalapi.DomainHash
isGenesis := *blockHash != *bp.genesisHash
if isGenesis {
isGenesis := *blockHash == *bp.genesisHash
if !isGenesis {
var err error
oldHeadersSelectedTip, err = bp.headersSelectedTipStore.HeadersSelectedTip(bp.databaseContext)
if err != nil {
@ -49,15 +89,21 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
}
var selectedParentChainChanges *externalapi.SelectedParentChainChanges
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
if !isHeaderOnlyBlock {
// Attempt to add the block to the virtual
selectedParentChainChanges, err = bp.consensusStateManager.AddBlock(blockHash)
if err != nil {
return nil, err
// There's no need to update the consensus state manager when
// processing the pruning point since it was already handled
// in consensusStateManager.UpdatePruningPoint
if !isPruningPoint {
// Attempt to add the block to the virtual
selectedParentChainChanges, err = bp.consensusStateManager.AddBlock(blockHash)
if err != nil {
return nil, err
}
}
}
if isGenesis {
if !isGenesis {
err := bp.updateReachabilityReindexRoot(oldHeadersSelectedTip)
if err != nil {
return nil, err
@ -137,14 +183,22 @@ func (bp *blockProcessor) checkBlockStatus(block *externalapi.DomainBlock) error
return errors.Wrapf(ruleerrors.ErrKnownInvalid, "block %s is a known invalid block", hash)
}
isBlockBodyAfterBlockHeader := !isHeaderOnlyBlock && status == externalapi.StatusHeaderOnly
if !isBlockBodyAfterBlockHeader {
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
}
isDuplicateHeader := isHeaderOnlyBlock && status == externalapi.StatusHeaderOnly
if isDuplicateHeader {
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
if !isHeaderOnlyBlock {
hasBlock, err := bp.blockStore.HasBlock(bp.databaseContext, hash)
if err != nil {
return err
}
if hasBlock {
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
}
} else {
hasHeader, err := bp.blockHeaderStore.HasBlockHeader(bp.databaseContext, hash)
if err != nil {
return err
}
if hasHeader {
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s header already exists", hash)
}
}
return nil
@ -153,12 +207,12 @@ func (bp *blockProcessor) checkBlockStatus(block *externalapi.DomainBlock) error
func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock) error {
blockHash := consensushashing.BlockHash(block)
hasValidatedOnlyHeader, err := bp.hasValidatedOnlyHeader(blockHash)
hasValidatedHeader, err := bp.hasValidatedHeader(blockHash)
if err != nil {
return err
}
if hasValidatedOnlyHeader {
if hasValidatedHeader {
log.Debugf("Block %s header was already validated, so skip the rest of validatePreProofOfWork", blockHash)
return nil
}
@ -170,7 +224,7 @@ func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock)
return nil
}
func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock) error {
func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock, isPruningPoint bool) error {
blockHash := consensushashing.BlockHash(block)
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
@ -182,7 +236,7 @@ func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock
}
}
hasValidatedHeader, err := bp.hasValidatedOnlyHeader(blockHash)
hasValidatedHeader, err := bp.hasValidatedHeader(blockHash)
if err != nil {
return err
}
@ -195,7 +249,7 @@ func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock
}
if !isHeaderOnlyBlock {
err = bp.blockValidator.ValidateBodyInContext(blockHash)
err = bp.blockValidator.ValidateBodyInContext(blockHash, isPruningPoint)
if err != nil {
return err
}
@ -206,7 +260,10 @@ func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock
return nil
}
func (bp *blockProcessor) hasValidatedOnlyHeader(blockHash *externalapi.DomainHash) (bool, error) {
// hasValidatedHeader returns whether the block header was validated. It returns
// true in any case the block header was validated, whether it was validated as a
// header-only block or as a block with body.
func (bp *blockProcessor) hasValidatedHeader(blockHash *externalapi.DomainHash) (bool, error) {
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, blockHash)
if err != nil {
return false, err
@ -221,7 +278,7 @@ func (bp *blockProcessor) hasValidatedOnlyHeader(blockHash *externalapi.DomainHa
return false, err
}
return status == externalapi.StatusHeaderOnly, nil
return status != externalapi.StatusInvalid, nil
}
func (bp *blockProcessor) discardAllChanges() {

View File

@ -24,16 +24,27 @@ func (bp *blockProcessor) validateAndInsertPruningPoint(newPruningPoint *externa
// We have to validate the pruning point block before we set the new pruning point in consensusStateManager.
log.Infof("Validating the new pruning point %s", newPruningPointHash)
err = bp.validateBlockAndDiscardChanges(newPruningPoint)
err = bp.validateBlockAndDiscardChanges(newPruningPoint, true)
if err != nil {
return err
}
log.Infof("Updating consensus state manager according to the new pruning point %s", newPruningPointHash)
err = bp.consensusStateManager.UpdatePruningPoint(newPruningPoint, serializedUTXOSet)
if err != nil {
return err
}
_, err = bp.ValidateAndInsertBlock(newPruningPoint)
return err
log.Infof("Inserting the new pruning point %s", newPruningPointHash)
_, err = bp.validateAndInsertBlock(newPruningPoint, true)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
// This should never happen because we already validated the block with bp.validateBlockAndDiscardChanges.
// We use Errorf so it won't be identified later on to be a rule error and will eventually cause
// the program to panic.
return errors.Errorf("validateAndInsertBlock returned unexpected rule error while processing "+
"the pruning point: %+v", err)
}
}
return nil
}

View File

@ -7,12 +7,12 @@ import (
"github.com/pkg/errors"
)
func (bp *blockProcessor) validateBlockAndDiscardChanges(block *externalapi.DomainBlock) error {
func (bp *blockProcessor) validateBlockAndDiscardChanges(block *externalapi.DomainBlock, isPruningPoint bool) error {
defer bp.discardAllChanges()
return bp.validateBlock(block)
return bp.validateBlock(block, isPruningPoint)
}
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, isPruningPoint bool) error {
blockHash := consensushashing.HeaderHash(block.Header)
log.Debugf("Validating block %s", blockHash)
@ -21,7 +21,7 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
return err
}
hasValidatedHeader, err := bp.hasValidatedOnlyHeader(blockHash)
hasValidatedHeader, err := bp.hasValidatedHeader(blockHash)
if err != nil {
return err
}
@ -50,15 +50,19 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
// If in-context validations fail, discard all changes and store the
// block with StatusInvalid.
err = bp.validatePostProofOfWork(block)
err = bp.validatePostProofOfWork(block, isPruningPoint)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
bp.discardAllChanges()
hash := consensushashing.BlockHash(block)
bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
commitErr := bp.commitAllChanges()
if commitErr != nil {
return commitErr
// If we got ErrMissingParents the block shouldn't be considered as invalid
// because it could be added later on when its parents are present.
if !errors.As(err, &ruleerrors.ErrMissingParents{}) {
hash := consensushashing.BlockHash(block)
bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
commitErr := bp.commitAllChanges()
if commitErr != nil {
return commitErr
}
}
}
return err

View File

@ -13,11 +13,69 @@ import (
// ValidateBodyInContext validates block bodies in the context of the current
// consensus state
func (v *blockValidator) ValidateBodyInContext(blockHash *externalapi.DomainHash) error {
func (v *blockValidator) ValidateBodyInContext(blockHash *externalapi.DomainHash, isPruningPoint bool) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateBodyInContext")
defer onEnd()
return v.checkBlockTransactionsFinalized(blockHash)
err := v.checkBlockTransactionsFinalized(blockHash)
if err != nil {
return err
}
if !isPruningPoint {
err := v.checkParentBlockBodiesExist(blockHash)
if err != nil {
return err
}
}
return nil
}
func (v *blockValidator) checkParentBlockBodiesExist(blockHash *externalapi.DomainHash) error {
missingParentHashes := []*externalapi.DomainHash{}
header, err := v.blockHeaderStore.BlockHeader(v.databaseContext, blockHash)
if err != nil {
return err
}
for _, parent := range header.ParentHashes {
hasBlock, err := v.blockStore.HasBlock(v.databaseContext, parent)
if err != nil {
return err
}
if !hasBlock {
pruningPoint, err := v.pruningStore.PruningPoint(v.databaseContext)
if err != nil {
return err
}
isInPastOfPruningPoint, err := v.dagTopologyManager.IsAncestorOf(parent, pruningPoint)
if err != nil {
return err
}
// If a block parent is in the past of the pruning point
// it means its body will never be used, so it's ok if
// it's missing.
// This will usually happen during IBD when getting the blocks
// in the pruning point anticone.
if isInPastOfPruningPoint {
log.Debugf("Block %s parent %s is missing a body, but is in the past of the pruning point",
blockHash, parent)
continue
}
log.Debugf("Block %s parent %s is missing a body", blockHash, parent)
missingParentHashes = append(missingParentHashes, parent)
}
}
if len(missingParentHashes) > 0 {
return ruleerrors.NewErrMissingParents(missingParentHashes)
}
return nil
}
func (v *blockValidator) checkBlockTransactionsFinalized(blockHash *externalapi.DomainHash) error {

View File

@ -19,7 +19,7 @@ func (v *blockValidator) ValidatePruningPointViolationAndProofOfWorkAndDifficult
return err
}
err = v.checkParentsExist(blockHash, header)
err = v.checkParentHeadersExist(header)
if err != nil {
return err
}
@ -103,14 +103,8 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
return nil
}
func (v *blockValidator) checkParentsExist(blockHash *externalapi.DomainHash, header *externalapi.DomainBlockHeader) error {
func (v *blockValidator) checkParentHeadersExist(header *externalapi.DomainBlockHeader) error {
missingParentHashes := []*externalapi.DomainHash{}
hasBlockBody, err := v.blockStore.HasBlock(v.databaseContext, blockHash)
if err != nil {
return err
}
for _, parent := range header.ParentHashes {
parentHeaderExists, err := v.blockHeaderStore.HasBlockHeader(v.databaseContext, parent)
if err != nil {
@ -129,26 +123,6 @@ func (v *blockValidator) checkParentsExist(blockHash *externalapi.DomainHash, he
if parentStatus == externalapi.StatusInvalid {
return errors.Wrapf(ruleerrors.ErrInvalidAncestorBlock, "parent %s is invalid", parent)
}
if hasBlockBody {
if parentStatus == externalapi.StatusHeaderOnly {
pruningPoint, err := v.pruningStore.PruningPoint(v.databaseContext)
if err != nil {
return err
}
isInPastOfPruningPoint, err := v.dagTopologyManager.IsAncestorOf(parent, pruningPoint)
if err != nil {
return err
}
if isInPastOfPruningPoint {
continue
}
missingParentHashes = append(missingParentHashes, parent)
}
}
}
if len(missingParentHashes) > 0 {

View File

@ -5,7 +5,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// AddBlockToVirtual submits the given block to be added to the
// AddBlock submits the given block to be added to the
// current virtual. This process may result in a new virtual block
// getting created
func (csm *consensusStateManager) AddBlock(blockHash *externalapi.DomainHash) (*externalapi.SelectedParentChainChanges, error) {

View File

@ -256,10 +256,10 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *external
if err != nil {
return nil, err
}
if blockStatus != externalapi.StatusValid {
if blockStatus != externalapi.StatusUTXOValid {
return nil, errors.Errorf(
"block %s, has status '%s', and therefore can't restore it's UTXO set. Only blocks with status '%s' can be restored.",
blockHash, blockStatus, externalapi.StatusValid)
blockHash, blockStatus, externalapi.StatusUTXOValid)
}
log.Tracef("RestorePastUTXOSetIterator start for block %s", blockHash)

View File

@ -88,7 +88,7 @@ func (csm *consensusStateManager) selectVirtualSelectedParent(
if err != nil {
return nil, err
}
if selectedParentCandidateStatus == externalapi.StatusValid {
if selectedParentCandidateStatus == externalapi.StatusUTXOValid {
log.Tracef("Block %s is valid. Returning it as the selected parent", selectedParentCandidate)
return selectedParentCandidate, nil
}

View File

@ -72,8 +72,8 @@ func (csm *consensusStateManager) findSelectedParentStatus(unverifiedBlocks []*e
lastUnverifiedBlock := unverifiedBlocks[len(unverifiedBlocks)-1]
if *lastUnverifiedBlock == *csm.genesisHash {
log.Tracef("the most recent unverified block is the genesis block, "+
"which by definition has status: %s", externalapi.StatusValid)
return externalapi.StatusValid, nil
"which by definition has status: %s", externalapi.StatusUTXOValid)
return externalapi.StatusUTXOValid, nil
}
lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, lastUnverifiedBlock)
if err != nil {
@ -164,7 +164,7 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(blockHash *externalap
return 0, err
}
return externalapi.StatusValid, nil
return externalapi.StatusUTXOValid, nil
}
func (csm *consensusStateManager) removeAncestorsFromVirtualDiffParentsAndAssignDiffChild(

View File

@ -70,8 +70,8 @@ func TestDoubleSpends(t *testing.T) {
if err != nil {
t.Fatalf("Error getting status of goodBlock1: %+v", err)
}
if goodBlock1Status != externalapi.StatusValid {
t.Fatalf("GoodBlock1 status expected to be '%s', but is '%s'", externalapi.StatusValid, goodBlock1Status)
if goodBlock1Status != externalapi.StatusUTXOValid {
t.Fatalf("GoodBlock1 status expected to be '%s', but is '%s'", externalapi.StatusUTXOValid, goodBlock1Status)
}
// To check that a block containing the same transaction already in it's past is disqualified:
@ -145,8 +145,8 @@ func TestDoubleSpends(t *testing.T) {
if err != nil {
t.Fatalf("Error getting status of goodBlock: %+v", err)
}
if goodBlock2Status != externalapi.StatusValid {
t.Fatalf("GoodBlock2 status expected to be '%s', but is '%s'", externalapi.StatusValid, goodBlock2Status)
if goodBlock2Status != externalapi.StatusUTXOValid {
t.Fatalf("GoodBlock2 status expected to be '%s', but is '%s'", externalapi.StatusUTXOValid, goodBlock2Status)
}
})
}
@ -167,7 +167,7 @@ func TestResolveBlockStatusSanity(t *testing.T) {
if err != nil {
t.Fatalf("error getting genesis status: %s", err)
}
if genesisStatus != externalapi.StatusValid {
if genesisStatus != externalapi.StatusUTXOValid {
t.Fatalf("genesis is unexpectedly non-valid. Its status is: %s", genesisStatus)
}
@ -185,7 +185,7 @@ func TestResolveBlockStatusSanity(t *testing.T) {
if err != nil {
t.Fatalf("error getting block %d (%s) status: %s", i, addedBlockHash, err)
}
if blockStatus != externalapi.StatusValid {
if blockStatus != externalapi.StatusUTXOValid {
t.Fatalf("block %d (%s) is unexpectedly non-valid. Its status is: %s", i, addedBlockHash, blockStatus)
}
currentHash = addedBlockHash
@ -224,13 +224,13 @@ func TestResolveBlockStatusSanity(t *testing.T) {
allHashes = append(allHashes, addedBlockHash)
}
// Make sure that all the blocks in the DAG now have StatusValid
// Make sure that all the blocks in the DAG now have StatusUTXOValid
for _, hash := range allHashes {
blockStatus, err := consensus.BlockStatusStore().Get(consensus.DatabaseContext(), hash)
if err != nil {
t.Fatalf("error getting block %s status: %s", hash, err)
}
if blockStatus != externalapi.StatusValid {
if blockStatus != externalapi.StatusUTXOValid {
t.Fatalf("block %s is unexpectedly non-valid. Its status is: %s", hash, blockStatus)
}
}

View File

@ -54,59 +54,69 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
if err != nil {
return err
}
log.Tracef("Calculated multiset for given UTXO set: %s", utxoSetMultiSet.Hash())
log.Debugf("Calculated multiset for given UTXO set: %s", utxoSetMultiSet.Hash())
newPruningPointHeader, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, newPruningPointHash)
if err != nil {
return err
}
log.Tracef("The UTXO commitment of the pruning point: %s",
log.Debugf("The UTXO commitment of the pruning point: %s",
newPruningPointHeader.UTXOCommitment)
if newPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() {
return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+
"point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
}
log.Tracef("The new pruning point UTXO commitment validation passed")
log.Debugf("The new pruning point UTXO commitment validation passed")
log.Tracef("Staging the parent hashes for pruning point as the DAG tips")
csm.consensusStateStore.StageTips(newPruningPointHeader.ParentHashes)
newTips := []*externalapi.DomainHash{newPruningPointHash}
log.Tracef("Setting the parent hashes for the header tips pruning point as the virtual parents")
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, newPruningPointHeader.ParentHashes)
if err != nil {
return err
}
log.Tracef("Staging the virtual UTXO set")
err = csm.consensusStateStore.StageVirtualUTXOSet(protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet))
if err != nil {
return err
}
// Before we manually mark the new pruning point as valid, we validate that all of its transactions are valid
// against the provided UTXO set.
err = csm.validateBlockTransactionsAgainstPastUTXO(newPruningPoint, utxo.NewUTXODiff())
log.Debugf("Staging the the pruning point as the only DAG tip")
csm.consensusStateStore.StageTips(newTips)
log.Debugf("Setting the pruning point as the only virtual parent")
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, newTips)
if err != nil {
return err
}
log.Debugf("Calculating GHOSTDAG for the new virtual")
err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
if err != nil {
return err
}
log.Tracef("Updating the header tips pruning point diff parents with an empty UTXO diff")
err = csm.updateVirtualDiffParents(utxo.NewUTXODiff())
log.Debugf("Staging the virtual UTXO set")
err = csm.consensusStateStore.StageVirtualUTXOSet(protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet))
if err != nil {
return err
}
log.Tracef("Staging the new pruning point and its UTXO set")
log.Debugf("Deleting all the existing virtual diff parents")
csm.consensusStateStore.StageVirtualDiffParents(nil)
log.Debugf("Updating the new pruning point to be the new virtual diff parent with an empty diff")
err = csm.stageDiff(newPruningPointHash, utxo.NewUTXODiff(), nil)
if err != nil {
return err
}
log.Debugf("Staging the new pruning point and its UTXO set")
csm.pruningStore.Stage(newPruningPointHash, serializedUTXOSet)
log.Tracef("Staging the new pruning point as %s", externalapi.StatusValid)
csm.blockStatusStore.Stage(newPruningPointHash, externalapi.StatusValid)
// Before we manually mark the new pruning point as valid, we validate that all of its transactions are valid
// against the provided UTXO set.
log.Debugf("Validating that the pruning point is UTXO valid")
err = csm.validateBlockTransactionsAgainstPastUTXO(newPruningPoint, utxo.NewUTXODiff())
if err != nil {
return err
}
log.Debugf("Staging the new pruning point as %s", externalapi.StatusUTXOValid)
csm.blockStatusStore.Stage(newPruningPointHash, externalapi.StatusUTXOValid)
log.Debugf("Staging the new pruning point multiset")
csm.multisetStore.Stage(newPruningPointHash, utxoSetMultiSet)
return nil
}