mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
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:
parent
6b1e691a57
commit
bd5f4e8c6a
@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user