diff --git a/domain/consensus/constructors.go b/domain/consensus/constructors.go index 29b7a2167..81cab3905 100644 --- a/domain/consensus/constructors.go +++ b/domain/consensus/constructors.go @@ -1,6 +1,17 @@ package consensus -import "github.com/kaspanet/kaspad/domain/consensus/model" +import ( + "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "math/big" + "time" +) // GHOSTDAGManagerConstructor is the function signature for a constructor of a type implementing model.GHOSTDAGManager -type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager, model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager +type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager, + model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager + +// DifficultyManagerConstructor is the function signature for a constructor of a type implementing model.DifficultyManager +type DifficultyManagerConstructor func(model.DBReader, model.GHOSTDAGManager, model.GHOSTDAGDataStore, + model.BlockHeaderStore, model.DAGTopologyManager, model.DAGTraversalManager, *big.Int, int, bool, time.Duration, + *externalapi.DomainHash) model.DifficultyManager diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go index 52c999e4c..1836a3bfb 100644 --- a/domain/consensus/factory.go +++ b/domain/consensus/factory.go @@ -63,19 +63,22 @@ type Factory interface { SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor) SetTestLevelDBCacheSize(cacheSizeMiB int) SetTestPreAllocateCache(preallocateCaches bool) + SetTestDifficultyManager(difficultyConstructor DifficultyManagerConstructor) } type factory struct { - dataDir string - ghostdagConstructor GHOSTDAGManagerConstructor - cacheSizeMiB *int - preallocateCaches *bool + dataDir string + ghostdagConstructor GHOSTDAGManagerConstructor + difficultyConstructor DifficultyManagerConstructor + cacheSizeMiB *int + preallocateCaches *bool } // NewFactory creates a new Consensus factory func NewFactory() Factory { return &factory{ - ghostdagConstructor: ghostdagmanager.New, + ghostdagConstructor: ghostdagmanager.New, + difficultyConstructor: difficultymanager.New, } } @@ -159,7 +162,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat dbManager, pastMedianTimeManager, ghostdagDataStore) - difficultyManager := difficultymanager.New( + difficultyManager := f.difficultyConstructor( dbManager, ghostdagManager, ghostdagDataStore, @@ -466,6 +469,11 @@ func (f *factory) SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerCons f.ghostdagConstructor = ghostdagConstructor } +// SetTestDifficultyManager is a setter for the difficultyManager field on the factory. +func (f *factory) SetTestDifficultyManager(difficultyConstructor DifficultyManagerConstructor) { + f.difficultyConstructor = difficultyConstructor +} + func (f *factory) SetTestLevelDBCacheSize(cacheSizeMiB int) { f.cacheSizeMiB = &cacheSizeMiB } diff --git a/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty_test.go b/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty_test.go index a78d355dc..9a785ed8a 100644 --- a/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty_test.go +++ b/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty_test.go @@ -1,6 +1,7 @@ package blockvalidator_test import ( + "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/pow" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/utils/blockheader" @@ -12,8 +13,8 @@ import ( "math" "math/big" "math/rand" - "testing" + "time" "github.com/kaspanet/kaspad/domain/consensus" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" @@ -241,3 +242,60 @@ func TestCheckPruningPointViolation(t *testing.T) { } }) } + +// TestValidateDifficulty verifies that in case of a block with an unexpected difficulty, +// an appropriate error message (ErrUnexpectedDifficulty) will be returned on the +// function ValidatePruningPointViolationAndProofOfWorkAndDifficulty. The required difficulty is +// "calculated" by the function (dm *mocDifficultyManager) RequiredDifficulty , +// where mocDifficultyManager is special implementation of the type DifficultyManager for this test (defined below). +func TestValidateDifficulty(t *testing.T) { + testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) { + + factory := consensus.NewFactory() + mocDifficulty := &mocDifficultyManager{} + factory.SetTestDifficultyManager(func(model.DBReader, model.GHOSTDAGManager, model.GHOSTDAGDataStore, + model.BlockHeaderStore, model.DAGTopologyManager, model.DAGTraversalManager, *big.Int, int, bool, time.Duration, + *externalapi.DomainHash) model.DifficultyManager { + return mocDifficulty + }) + genesisDifficulty := params.GenesisBlock.Header.Bits() + mocDifficulty.testDifficulty = genesisDifficulty + mocDifficulty.testGenesisBits = genesisDifficulty + tc, teardown, err := factory.NewTestConsensus(params, false, "TestValidateDifficulty") + if err != nil { + t.Fatalf("Error setting up consensus: %+v", err) + } + defer teardown(false) + + emptyCoinbase := externalapi.DomainCoinbaseData{ + ScriptPublicKey: &externalapi.ScriptPublicKey{ + Script: nil, + Version: 0, + }, + } + block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, nil) + if err != nil { + t.Fatalf("TestValidateDifficulty: Failed build block with parents: %v.", err) + } + blockHash := consensushashing.BlockHash(block) + tc.BlockStore().Stage(blockHash, block) + tc.BlockHeaderStore().Stage(blockHash, block.Header) + wrongTestDifficulty := mocDifficulty.testDifficulty + uint32(5) + mocDifficulty.testDifficulty = wrongTestDifficulty + + err = tc.BlockValidator().ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrUnexpectedDifficulty) { + t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrUnexpectedDifficulty, err) + } + }) +} + +type mocDifficultyManager struct { + testDifficulty uint32 + testGenesisBits uint32 +} + +// RequiredDifficulty returns the difficulty required for the test +func (dm *mocDifficultyManager) RequiredDifficulty(*externalapi.DomainHash) (uint32, error) { + return dm.testDifficulty, nil +}