diff --git a/app/app.go b/app/app.go index 221ddb14c..ee64ac0db 100644 --- a/app/app.go +++ b/app/app.go @@ -20,7 +20,10 @@ import ( "github.com/kaspanet/kaspad/version" ) -const leveldbCacheSizeMiB = 256 +const ( + leveldbCacheSizeMiB = 256 + defaultDataDirname = "datadir" +) var desiredLimits = &limits.DesiredLimits{ FileLimitWant: 2048, @@ -159,7 +162,7 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error { // dbPath returns the path to the block database given a database type. func databasePath(cfg *config.Config) string { - return filepath.Join(cfg.AppDir, "data") + return filepath.Join(cfg.AppDir, defaultDataDirname) } func removeDatabase(cfg *config.Config) error { diff --git a/app/appmessage/protocol.go b/app/appmessage/protocol.go index dc80eb4ba..7c15973b4 100644 --- a/app/appmessage/protocol.go +++ b/app/appmessage/protocol.go @@ -12,7 +12,7 @@ import ( const ( // ProtocolVersion is the latest protocol version this package supports. - ProtocolVersion uint32 = 1 + ProtocolVersion uint32 = 2 // DefaultServices describes the default services that are supported by // the server. diff --git a/app/protocol/protocol.go b/app/protocol/protocol.go index bc3ef5961..d5b6dfbba 100644 --- a/app/protocol/protocol.go +++ b/app/protocol/protocol.go @@ -102,7 +102,7 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection, outgoingRoute *routerpkg.Route) { if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) { - if !m.context.Config().DisableBanning && protocolErr.ShouldBan { + if m.context.Config().EnableBanning && protocolErr.ShouldBan { log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause) err := m.context.ConnectionManager().Ban(netConnection) diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go index 1509969af..a09796fab 100644 --- a/domain/consensus/factory.go +++ b/domain/consensus/factory.go @@ -159,7 +159,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas dagTraversalManager := dagTraversalManagers[0] // Processes - parentsManager := parentssanager.New(config.GenesisHash, config.HardForkOmitGenesisFromParentsDAAScore) + parentsManager := parentssanager.New(config.GenesisHash) blockParentBuilder := blockparentbuilder.New( dbManager, blockHeaderStore, @@ -168,7 +168,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas reachabilityDataStore, pruningStore, - config.HardForkOmitGenesisFromParentsDAAScore, config.GenesisHash, ) pastMedianTimeManager := f.pastMedianTimeConsructor( diff --git a/domain/consensus/finality_test.go b/domain/consensus/finality_test.go index dda3d1f7e..120721473 100644 --- a/domain/consensus/finality_test.go +++ b/domain/consensus/finality_test.go @@ -16,8 +16,8 @@ import ( func TestFinality(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - // Set finalityInterval to 50 blocks, so that test runs quickly - consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock + // Set finalityInterval to 20 blocks, so that test runs quickly + consensusConfig.FinalityDuration = 20 * consensusConfig.TargetTimePerBlock factory := consensus.NewFactory() consensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestFinality") @@ -180,7 +180,8 @@ func TestFinality(t *testing.T) { func TestBoundedMergeDepth(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { // Set finalityInterval to 50 blocks, so that test runs quickly - consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock + consensusConfig.K = 5 + consensusConfig.FinalityDuration = 7 * consensusConfig.TargetTimePerBlock finalityInterval := int(consensusConfig.FinalityDepth()) if int(consensusConfig.K) >= finalityInterval { diff --git a/domain/consensus/processes/blockparentbuilder/blockparentbuilder.go b/domain/consensus/processes/blockparentbuilder/blockparentbuilder.go index d12fd41a7..ffd3e4d34 100644 --- a/domain/consensus/processes/blockparentbuilder/blockparentbuilder.go +++ b/domain/consensus/processes/blockparentbuilder/blockparentbuilder.go @@ -16,8 +16,7 @@ type blockParentBuilder struct { reachabilityDataStore model.ReachabilityDataStore pruningStore model.PruningStore - hardForkOmitGenesisFromParentsDAAScore uint64 - genesisHash *externalapi.DomainHash + genesisHash *externalapi.DomainHash } // New creates a new instance of a BlockParentBuilder @@ -30,7 +29,6 @@ func New( reachabilityDataStore model.ReachabilityDataStore, pruningStore model.PruningStore, - hardForkOmitGenesisFromParentsDAAScore uint64, genesisHash *externalapi.DomainHash, ) model.BlockParentBuilder { return &blockParentBuilder{ @@ -39,10 +37,9 @@ func New( dagTopologyManager: dagTopologyManager, parentsManager: parentsManager, - reachabilityDataStore: reachabilityDataStore, - pruningStore: pruningStore, - hardForkOmitGenesisFromParentsDAAScore: hardForkOmitGenesisFromParentsDAAScore, - genesisHash: genesisHash, + reachabilityDataStore: reachabilityDataStore, + pruningStore: pruningStore, + genesisHash: genesisHash, } } @@ -217,8 +214,10 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea, parents := make([]externalapi.BlockLevelParents, 0, len(candidatesByLevelToReferenceBlocksMap)) for blockLevel := 0; blockLevel < len(candidatesByLevelToReferenceBlocksMap); blockLevel++ { - if _, ok := candidatesByLevelToReferenceBlocksMap[blockLevel][*bpb.genesisHash]; daaScore >= bpb.hardForkOmitGenesisFromParentsDAAScore && ok && len(candidatesByLevelToReferenceBlocksMap[blockLevel]) == 1 { - break + if blockLevel > 0 { + if _, ok := candidatesByLevelToReferenceBlocksMap[blockLevel][*bpb.genesisHash]; ok && len(candidatesByLevelToReferenceBlocksMap[blockLevel]) == 1 { + break + } } levelBlocks := make(externalapi.BlockLevelParents, 0, len(candidatesByLevelToReferenceBlocksMap[blockLevel])) diff --git a/domain/consensus/processes/blockprocessor/resources/utxos.gz b/domain/consensus/processes/blockprocessor/resources/utxos.gz new file mode 100644 index 000000000..0eb4996d4 Binary files /dev/null and b/domain/consensus/processes/blockprocessor/resources/utxos.gz differ diff --git a/domain/consensus/processes/blockprocessor/validate_and_insert_block.go b/domain/consensus/processes/blockprocessor/validate_and_insert_block.go index c1a9a0b7b..d5ec112d5 100644 --- a/domain/consensus/processes/blockprocessor/validate_and_insert_block.go +++ b/domain/consensus/processes/blockprocessor/validate_and_insert_block.go @@ -1,12 +1,20 @@ package blockprocessor import ( + "bytes" + "compress/gzip" + "github.com/kaspanet/go-muhash" + + // we need to embed the utxoset of mainnet genesis here + _ "embed" "fmt" + "github.com/kaspanet/kaspad/domain/consensus/utils/multiset" + "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" "github.com/kaspanet/kaspad/infrastructure/db/database" - - "github.com/kaspanet/kaspad/util/staging" - "github.com/kaspanet/kaspad/util/difficulty" + "github.com/kaspanet/kaspad/util/staging" + "io" + "sync" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" @@ -16,6 +24,9 @@ import ( "github.com/pkg/errors" ) +//go:embed resources/utxos.gz +var utxoDumpFile []byte + func (bp *blockProcessor) setBlockStatusAfterBlockValidation( stagingArea *model.StagingArea, block *externalapi.DomainBlock, isPruningPoint bool) error { @@ -128,6 +139,11 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea, } } + err = bp.ifGenesisSetUtxoSet(block) + if err != nil { + return nil, err + } + var selectedParentChainChanges *externalapi.SelectedChainPath var virtualUTXODiff externalapi.UTXODiff var reversalData *model.UTXODiffReversalData @@ -215,6 +231,93 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea, }, nil } +var mainnetGenesisUTXOSet externalapi.UTXODiff +var mainnetGenesisMultiSet model.Multiset +var mainnetGenesisOnce sync.Once +var mainnetGenesisErr error + +func deserializeMainnetUTXOSet() (externalapi.UTXODiff, model.Multiset, error) { + mainnetGenesisOnce.Do(func() { + toAdd := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry) + mainnetGenesisMultiSet = multiset.New() + file, err := gzip.NewReader(bytes.NewReader(utxoDumpFile)) + if err != nil { + mainnetGenesisErr = err + return + } + for i := 0; ; i++ { + size := make([]byte, 1) + _, err = io.ReadFull(file, size) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + mainnetGenesisErr = err + return + } + + serializedUTXO := make([]byte, size[0]) + _, err = io.ReadFull(file, serializedUTXO) + if err != nil { + mainnetGenesisErr = err + return + } + + mainnetGenesisMultiSet.Add(serializedUTXO) + + entry, outpoint, err := utxo.DeserializeUTXO(serializedUTXO) + if err != nil { + mainnetGenesisErr = err + return + } + toAdd[*outpoint] = entry + } + mainnetGenesisUTXOSet, mainnetGenesisErr = utxo.NewUTXODiffFromCollections(utxo.NewUTXOCollection(toAdd), utxo.NewUTXOCollection(make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry))) + }) + return mainnetGenesisUTXOSet, mainnetGenesisMultiSet, mainnetGenesisErr +} + +func (bp *blockProcessor) ifGenesisSetUtxoSet(block *externalapi.DomainBlock) error { + isGenesis := len(block.Header.DirectParents()) == 0 + if !isGenesis { + return nil + } + blockHash := consensushashing.BlockHash(block) + if !block.Header.UTXOCommitment().Equal(externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray())) { + log.Infof("Loading checkpoint UTXO set") + diff, utxoSetMultiset, err := deserializeMainnetUTXOSet() + if err != nil { + return err + } + log.Infof("Finished loading checkpoint UTXO set") + utxoSetHash := utxoSetMultiset.Hash() + if !utxoSetHash.Equal(block.Header.UTXOCommitment()) { + return errors.New("Invalid UTXO set dump") + } + + area := model.NewStagingArea() + bp.consensusStateStore.StageVirtualUTXODiff(area, diff) + bp.utxoDiffStore.Stage(area, blockHash, diff, nil) + // commit the multiset of genesis + bp.multisetStore.Stage(area, blockHash, utxoSetMultiset) + err = staging.CommitAllChanges(bp.databaseContext, area) + if err != nil { + return err + } + } else { + // if it's genesis but has an empty muhash then commit an empty multiset and an empty diff + area := model.NewStagingArea() + bp.consensusStateStore.StageVirtualUTXODiff(area, utxo.NewUTXODiff()) + bp.utxoDiffStore.Stage(area, blockHash, utxo.NewUTXODiff(), nil) + bp.multisetStore.Stage(area, blockHash, multiset.New()) + err := staging.CommitAllChanges(bp.databaseContext, area) + if err != nil { + return err + } + } + return nil +} + func isHeaderOnlyBlock(block *externalapi.DomainBlock) bool { return len(block.Transactions) == 0 } diff --git a/domain/consensus/processes/blockprocessor/validate_and_insert_imported_pruning_point_test.go b/domain/consensus/processes/blockprocessor/validate_and_insert_imported_pruning_point_test.go index b2fbde847..9ccbd12f3 100644 --- a/domain/consensus/processes/blockprocessor/validate_and_insert_imported_pruning_point_test.go +++ b/domain/consensus/processes/blockprocessor/validate_and_insert_imported_pruning_point_test.go @@ -140,10 +140,21 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) { } } - pruningPointUTXOs, err := tcSyncer.GetPruningPointUTXOs(pruningPoint, nil, 1000) - if err != nil { - t.Fatalf("GetPruningPointUTXOs: %+v", err) + var fromOutpoint *externalapi.DomainOutpoint + var pruningPointUTXOs []*externalapi.OutpointAndUTXOEntryPair + const step = 100_000 + for { + outpointAndUTXOEntryPairs, err := tcSyncer.GetPruningPointUTXOs(pruningPoint, fromOutpoint, step) + if err != nil { + t.Fatalf("GetPruningPointUTXOs: %+v", err) + } + fromOutpoint = outpointAndUTXOEntryPairs[len(outpointAndUTXOEntryPairs)-1].Outpoint + pruningPointUTXOs = append(pruningPointUTXOs, outpointAndUTXOEntryPairs...) + if len(outpointAndUTXOEntryPairs) < step { + break + } } + err = synceeStaging.AppendImportedPruningPointUTXOs(pruningPointUTXOs) if err != nil { t.Fatalf("AppendImportedPruningPointUTXOs: %+v", err) @@ -507,7 +518,7 @@ func TestGetPruningPointUTXOs(t *testing.T) { // Get pruning point UTXOs in a loop var allOutpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair - step := 100 + const step = 100_000 var fromOutpoint *externalapi.DomainOutpoint for { outpointAndUTXOEntryPairs, err := testConsensus.GetPruningPointUTXOs(pruningPoint, fromOutpoint, step) @@ -522,11 +533,17 @@ func TestGetPruningPointUTXOs(t *testing.T) { } } + const mainnetUTXOSize = 1232643 + expected := len(outputs) + 1 + if consensusConfig.Name == "kaspa-mainnet" { + expected += mainnetUTXOSize + } + // Make sure the length of the UTXOs is exactly spendingTransaction.Outputs + 1 coinbase // output (includingBlock's coinbase) - if len(allOutpointAndUTXOEntryPairs) != len(outputs)+1 { + if len(allOutpointAndUTXOEntryPairs) != expected { t.Fatalf("Returned an unexpected amount of UTXOs. "+ - "Want: %d, got: %d", len(outputs)+2, len(allOutpointAndUTXOEntryPairs)) + "Want: %d, got: %d", expected, len(allOutpointAndUTXOEntryPairs)) } // Make sure all spendingTransaction.Outputs are in the returned UTXOs diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go index 7eb94f508..97916e27d 100644 --- a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go +++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go @@ -4,6 +4,8 @@ import ( "bytes" "math" "math/big" + "reflect" + "runtime" "testing" "github.com/kaspanet/kaspad/domain/consensus" @@ -21,6 +23,31 @@ import ( "github.com/pkg/errors" ) +func TestBlockValidator_ValidateBodyInIsolation(t *testing.T) { + tests := []func(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config){ + CheckBlockSanity, + CheckBlockHashMerkleRoot, + BlockMass, + CheckBlockDuplicateTransactions, + CheckBlockContainsOnlyOneCoinbase, + CheckBlockDoubleSpends, + CheckFirstBlockTransactionIsCoinbase, + } + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestChainedTransactions") + if err != nil { + t.Fatalf("Error setting up consensus: %+v", err) + } + defer teardown(false) + for _, test := range tests { + testName := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name() + t.Run(testName, func(t *testing.T) { + test(t, tc, consensusConfig) + }) + } + }) +} + func TestChainedTransactions(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { consensusConfig.BlockCoinbaseMaturity = 0 @@ -89,47 +116,39 @@ func TestChainedTransactions(t *testing.T) { }) } -// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works +// CheckBlockSanity tests the CheckBlockSanity function to ensure it works // as expected. -func TestCheckBlockSanity(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockSanity") - if err != nil { - t.Fatalf("Error setting up consensus: %+v", err) - } - defer teardown(false) - blockHash := consensushashing.BlockHash(&exampleValidBlock) - if len(exampleValidBlock.Transactions) < 3 { - t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions)) - } +func CheckBlockSanity(t *testing.T, tc testapi.TestConsensus, _ *consensus.Config) { + blockHash := consensushashing.BlockHash(&exampleValidBlock) + if len(exampleValidBlock.Transactions) < 3 { + t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions)) + } - stagingArea := model.NewStagingArea() + stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, &exampleValidBlock) + tc.BlockStore().Stage(stagingArea, blockHash, &exampleValidBlock) - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err != nil { - t.Fatalf("Failed validating block in isolation: %v", err) - } + err := tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err != nil { + t.Fatalf("Failed validating block in isolation: %v", err) + } - // Test with block with wrong transactions sorting order - blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder) - tc.BlockStore().Stage(stagingArea, blockHash, &blockWithWrongTxOrder) - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) { - t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err) - } + // Test with block with wrong transactions sorting order + blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder) + tc.BlockStore().Stage(stagingArea, blockHash, &blockWithWrongTxOrder) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) { + t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err) + } - // Test a block with invalid parents order - // We no longer require blocks to have ordered parents - blockHash = consensushashing.BlockHash(&unOrderedParentsBlock) - tc.BlockStore().Stage(stagingArea, blockHash, &unOrderedParentsBlock) - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err != nil { - t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err) - } - }) + // Test a block with invalid parents order + // We no longer require blocks to have ordered parents + blockHash = consensushashing.BlockHash(&unOrderedParentsBlock) + tc.BlockStore().Stage(stagingArea, blockHash, &unOrderedParentsBlock) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err != nil { + t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err) + } } var unOrderedParentsBlock = externalapi.DomainBlock{ @@ -1025,59 +1044,41 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{ }, } -func TestCheckBlockHashMerkleRoot(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockHashMerkleRoot") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) +func CheckBlockHashMerkleRoot(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) + if err != nil { + t.Fatalf("BuildBlockWithParents: %+v", err) + } + blockWithInvalidMerkleRoot := block.Clone() + blockWithInvalidMerkleRoot.Transactions[0].Version += 1 - block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) - if err != nil { - t.Fatalf("BuildBlockWithParents: %+v", err) - } - blockWithInvalidMerkleRoot := block.Clone() - blockWithInvalidMerkleRoot.Transactions[0].Version += 1 + _, err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true) + if !errors.Is(err, ruleerrors.ErrBadMerkleRoot) { + t.Fatalf("Unexpected error: %+v", err) + } - _, err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true) - if !errors.Is(err, ruleerrors.ErrBadMerkleRoot) { - t.Fatalf("Unexpected error: %+v", err) - } - - // Check that a block with invalid merkle root is not marked as invalid - // and can be re-added with the right transactions. - _, err = tc.ValidateAndInsertBlock(block, true) - if err != nil { - t.Fatalf("ValidateAndInsertBlock: %+v", err) - } - }) + // Check that a block with invalid merkle root is not marked as invalid + // and can be re-added with the right transactions. + _, err = tc.ValidateAndInsertBlock(block, true) + if err != nil { + t.Fatalf("ValidateAndInsertBlock: %+v", err) + } } -func TestBlockMass(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockMass") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) +func BlockMass(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := initBlockWithInvalidBlockMass(consensusConfig, tc) + if err != nil { + t.Fatalf("Error BuildBlockWithParents : %+v", err) + } + blockHash := consensushashing.BlockHash(block) + stagingArea := model.NewStagingArea() + tc.BlockStore().Stage(stagingArea, blockHash, block) - block, _, err := initBlockWithInvalidBlockMass(consensusConfig, tc) - if err != nil { - t.Fatalf("Error BuildBlockWithParents : %+v", err) - } - blockHash := consensushashing.BlockHash(block) - stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, block) - - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err == nil || !errors.Is(err, ruleerrors.ErrBlockMassTooHigh) { - t.Fatalf("ValidateBodyInIsolationTest: TestBlockMass:"+ - " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrBlockMassTooHigh, err) - } - }) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrBlockMassTooHigh) { + t.Fatalf("ValidateBodyInIsolationTest: TestBlockMass:"+ + " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrBlockMassTooHigh, err) + } } func initBlockWithInvalidBlockMass(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) { @@ -1113,30 +1114,20 @@ func initBlockWithInvalidBlockMass(consensusConfig *consensus.Config, tc testapi return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx}) } -func TestCheckBlockDuplicateTransactions(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { +func CheckBlockDuplicateTransactions(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := initBlockWithDuplicateTransaction(consensusConfig, tc) + if err != nil { + t.Fatalf("Error BuildBlockWithParents : %+v", err) + } + blockHash := consensushashing.BlockHash(block) + stagingArea := model.NewStagingArea() + tc.BlockStore().Stage(stagingArea, blockHash, block) - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDuplicateTransactions") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) - - block, _, err := initBlockWithDuplicateTransaction(consensusConfig, tc) - if err != nil { - t.Fatalf("Error BuildBlockWithParents : %+v", err) - } - blockHash := consensushashing.BlockHash(block) - stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, block) - - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateTx) { - t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDuplicateTransactions:"+ - " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDuplicateTx, err) - } - }) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateTx) { + t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDuplicateTransactions:"+ + " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDuplicateTx, err) + } } func initBlockWithDuplicateTransaction(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) { @@ -1170,30 +1161,20 @@ func initBlockWithDuplicateTransaction(consensusConfig *consensus.Config, tc tes return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx, tx}) } -func TestCheckBlockContainsOnlyOneCoinbase(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { +func CheckBlockContainsOnlyOneCoinbase(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := initBlockWithMoreThanOneCoinbase(consensusConfig, tc) + if err != nil { + t.Fatalf("Error BuildBlockWithParents : %+v", err) + } + blockHash := consensushashing.BlockHash(block) + stagingArea := model.NewStagingArea() + tc.BlockStore().Stage(stagingArea, blockHash, block) - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockContainsOnlyOneCoinbase") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) - - block, _, err := initBlockWithMoreThanOneCoinbase(consensusConfig, tc) - if err != nil { - t.Fatalf("Error BuildBlockWithParents : %+v", err) - } - blockHash := consensushashing.BlockHash(block) - stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, block) - - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err == nil || !errors.Is(err, ruleerrors.ErrMultipleCoinbases) { - t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockContainsOnlyOneCoinbase:"+ - " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrMultipleCoinbases, err) - } - }) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrMultipleCoinbases) { + t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockContainsOnlyOneCoinbase:"+ + " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrMultipleCoinbases, err) + } } func initBlockWithMoreThanOneCoinbase(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) { @@ -1227,30 +1208,20 @@ func initBlockWithMoreThanOneCoinbase(consensusConfig *consensus.Config, tc test return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx}) } -func TestCheckBlockDoubleSpends(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { +func CheckBlockDoubleSpends(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := initBlockWithDoubleSpends(consensusConfig, tc) + if err != nil { + t.Fatalf("Error BuildBlockWithParents : %+v", err) + } + blockHash := consensushashing.BlockHash(block) + stagingArea := model.NewStagingArea() + tc.BlockStore().Stage(stagingArea, blockHash, block) - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDoubleSpends") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) - - block, _, err := initBlockWithDoubleSpends(consensusConfig, tc) - if err != nil { - t.Fatalf("Error BuildBlockWithParents : %+v", err) - } - blockHash := consensushashing.BlockHash(block) - stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, block) - - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err == nil || !errors.Is(err, ruleerrors.ErrDoubleSpendInSameBlock) { - t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDoubleSpends:"+ - " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDoubleSpendInSameBlock, err) - } - }) + err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrDoubleSpendInSameBlock) { + t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDoubleSpends:"+ + " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDoubleSpendInSameBlock, err) + } } func initBlockWithDoubleSpends(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) { @@ -1303,27 +1274,18 @@ func initBlockWithDoubleSpends(consensusConfig *consensus.Config, tc testapi.Tes &emptyCoinbase, []*externalapi.DomainTransaction{tx, txSameOutpoint}) } -func TestCheckFirstBlockTransactionIsCoinbase(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { +func CheckFirstBlockTransactionIsCoinbase(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckFirstBlockTransactionIsCoinbase") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) + block := initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig) + blockHash := consensushashing.BlockHash(block) + stagingArea := model.NewStagingArea() + tc.BlockStore().Stage(stagingArea, blockHash, block) - block := initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig) - blockHash := consensushashing.BlockHash(block) - stagingArea := model.NewStagingArea() - tc.BlockStore().Stage(stagingArea, blockHash, block) - - err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) - if err == nil || !errors.Is(err, ruleerrors.ErrFirstTxNotCoinbase) { - t.Fatalf("ValidateBodyInIsolationTest: TestCheckFirstBlockTransactionIsCoinbase:"+ - " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrFirstTxNotCoinbase, err) - } - }) + err := tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash) + if err == nil || !errors.Is(err, ruleerrors.ErrFirstTxNotCoinbase) { + t.Fatalf("ValidateBodyInIsolationTest: TestCheckFirstBlockTransactionIsCoinbase:"+ + " Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrFirstTxNotCoinbase, err) + } } func initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig *consensus.Config) *externalapi.DomainBlock { diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context_test.go b/domain/consensus/processes/blockvalidator/block_header_in_context_test.go index d55bd313b..aec6d16bd 100644 --- a/domain/consensus/processes/blockvalidator/block_header_in_context_test.go +++ b/domain/consensus/processes/blockvalidator/block_header_in_context_test.go @@ -67,8 +67,8 @@ func TestValidateMedianTime(t *testing.T) { blockTime := tip.Header.TimeInMilliseconds() - for i := 0; i < 100; i++ { - blockTime += 1000 + for i := 0; i < 10; i++ { + blockTime += 100 _, tipHash = addBlock(blockTime, []*externalapi.DomainHash{tipHash}, nil) } @@ -163,7 +163,7 @@ func TestCheckParentsIncest(t *testing.T) { func TestCheckMergeSizeLimit(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - consensusConfig.MergeSetSizeLimit = 2 * uint64(consensusConfig.K) + consensusConfig.MergeSetSizeLimit = 5 factory := consensus.NewFactory() tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckMergeSizeLimit") if err != nil { diff --git a/domain/consensus/processes/blockvalidator/block_header_in_isolation_test.go b/domain/consensus/processes/blockvalidator/block_header_in_isolation_test.go index cf275737f..39cb66d9c 100644 --- a/domain/consensus/processes/blockvalidator/block_header_in_isolation_test.go +++ b/domain/consensus/processes/blockvalidator/block_header_in_isolation_test.go @@ -1,6 +1,9 @@ package blockvalidator_test import ( + "github.com/kaspanet/kaspad/domain/consensus/model/testapi" + "reflect" + "runtime" "testing" "github.com/kaspanet/kaspad/domain/consensus" @@ -13,73 +16,74 @@ import ( "github.com/pkg/errors" ) -func TestCheckParentsLimit(t *testing.T) { +func TestBlockValidator_ValidateHeaderInIsolation(t *testing.T) { + tests := []func(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config){ + CheckParentsLimit, + CheckBlockVersion, + CheckBlockTimestampInIsolation, + } testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsLimit") + tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestBlockValidator_ValidateHeaderInIsolation") if err != nil { t.Fatalf("Error setting up consensus: %+v", err) } defer teardown(false) - - for i := externalapi.KType(0); i < consensusConfig.MaxBlockParents+1; i++ { - _, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - } - - tips, err := tc.Tips() - if err != nil { - t.Fatalf("Tips: %+v", err) - } - - _, _, err = tc.AddBlock(tips, nil, nil) - if !errors.Is(err, ruleerrors.ErrTooManyParents) { - t.Fatalf("Unexpected error: %+v", err) + for _, test := range tests { + testName := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name() + t.Run(testName, func(t *testing.T) { + test(t, tc, consensusConfig) + }) } }) } -func TestCheckBlockVersion(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - factory := consensus.NewFactory() - - tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockVersion") +func CheckParentsLimit(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + for i := externalapi.KType(0); i < consensusConfig.MaxBlockParents+1; i++ { + _, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) if err != nil { - t.Fatalf("Error setting up consensus: %+v", err) + t.Fatalf("AddBlock: %+v", err) } - defer teardown(false) + } - block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) - if err != nil { - t.Fatalf("BuildBlockWithParents: %+v", err) - } + tips, err := tc.Tips() + if err != nil { + t.Fatalf("Tips: %+v", err) + } - block.Header = blockheader.NewImmutableBlockHeader( - constants.MaxBlockVersion+1, - block.Header.Parents(), - block.Header.HashMerkleRoot(), - block.Header.AcceptedIDMerkleRoot(), - block.Header.UTXOCommitment(), - block.Header.TimeInMilliseconds(), - block.Header.Bits(), - block.Header.Nonce(), - block.Header.DAAScore(), - block.Header.BlueScore(), - block.Header.BlueWork(), - block.Header.PruningPoint(), - ) - - _, err = tc.ValidateAndInsertBlock(block, true) - if !errors.Is(err, ruleerrors.ErrBlockVersionIsUnknown) { - t.Fatalf("Unexpected error: %+v", err) - } - }) + _, _, err = tc.AddBlock(tips, nil, nil) + if !errors.Is(err, ruleerrors.ErrTooManyParents) { + t.Fatalf("Unexpected error: %+v", err) + } } -func TestCheckBlockTimestampInIsolation(t *testing.T) { +func CheckBlockVersion(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) { + block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil) + if err != nil { + t.Fatalf("BuildBlockWithParents: %+v", err) + } + + block.Header = blockheader.NewImmutableBlockHeader( + constants.MaxBlockVersion+1, + block.Header.Parents(), + block.Header.HashMerkleRoot(), + block.Header.AcceptedIDMerkleRoot(), + block.Header.UTXOCommitment(), + block.Header.TimeInMilliseconds(), + block.Header.Bits(), + block.Header.Nonce(), + block.Header.DAAScore(), + block.Header.BlueScore(), + block.Header.BlueWork(), + block.Header.PruningPoint(), + ) + + _, err = tc.ValidateAndInsertBlock(block, true) + if !errors.Is(err, ruleerrors.ErrBlockVersionIsUnknown) { + t.Fatalf("Unexpected error: %+v", err) + } +} + +func CheckBlockTimestampInIsolation(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { factory := consensus.NewFactory() diff --git a/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty.go b/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty.go index 142aa3e09..27b87d748 100644 --- a/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty.go +++ b/domain/consensus/processes/blockvalidator/pruning_violation_proof_of_work_and_difficulty.go @@ -49,9 +49,11 @@ func (v *blockValidator) ValidatePruningPointViolationAndProofOfWorkAndDifficult } } - err = v.checkProofOfWork(header) - if err != nil { - return err + if !blockHash.Equal(v.genesisHash) { + err = v.checkProofOfWork(header) + if err != nil { + return err + } } err = v.validateDifficulty(stagingArea, blockHash, isBlockWithTrustedData) 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 83b525bb4..e50a6539a 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 @@ -101,10 +101,13 @@ func TestPOW(t *testing.T) { t.Fatal(err) } random := rand.New(rand.NewSource(0)) - mining.SolveBlock(validBlock, random) - _, err = tc.ValidateAndInsertBlock(validBlock, true) - if err != nil { - t.Fatal(err) + // Difficulty is too high on mainnet to actually mine. + if consensusConfig.Name != "kaspa-mainnet" { + mining.SolveBlock(validBlock, random) + _, err = tc.ValidateAndInsertBlock(validBlock, true) + if err != nil { + t.Fatal(err) + } } }) } @@ -296,7 +299,7 @@ func TestCheckPruningPointViolation(t *testing.T) { func TestValidateDifficulty(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { factory := consensus.NewFactory() - mocDifficulty := &mocDifficultyManager{} + mocDifficulty := &mocDifficultyManager{genesisDaaScore: consensusConfig.GenesisBlock.Header.DAAScore()} factory.SetTestDifficultyManager(func(_ model.DBReader, _ model.GHOSTDAGManager, _ model.GHOSTDAGDataStore, _ model.BlockHeaderStore, daaBlocksStore model.DAABlocksStore, _ model.DAGTopologyManager, _ model.DAGTraversalManager, _ *big.Int, _ int, _ bool, _ time.Duration, @@ -342,6 +345,7 @@ type mocDifficultyManager struct { testDifficulty uint32 testGenesisBits uint32 daaBlocksStore model.DAABlocksStore + genesisDaaScore uint64 } // RequiredDifficulty returns the difficulty required for the test @@ -352,7 +356,7 @@ func (dm *mocDifficultyManager) RequiredDifficulty(*model.StagingArea, *external // StageDAADataAndReturnRequiredDifficulty returns the difficulty required for the test func (dm *mocDifficultyManager) StageDAADataAndReturnRequiredDifficulty(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (uint32, error) { // Populate daaBlocksStore with fake values - dm.daaBlocksStore.StageDAAScore(stagingArea, blockHash, 0) + dm.daaBlocksStore.StageDAAScore(stagingArea, blockHash, dm.genesisDaaScore) dm.daaBlocksStore.StageBlockDAAAddedBlocks(stagingArea, blockHash, nil) return dm.testDifficulty, nil diff --git a/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go b/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go index d82975131..93a6e7d9e 100644 --- a/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go +++ b/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go @@ -86,36 +86,5 @@ func TestBlockRewardSwitch(t *testing.T) { t.Fatalf("Subsidy has unexpected value. Want: %d, got: %d", consensusConfig.MinSubsidy, subsidy) } } - - // Add another block. We expect it to be another pruning point - lastPruningPointHash, _, err := tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - // Make sure that another pruning point had been added - pruningPointHeaders, err = tc.PruningPointHeaders() - if err != nil { - t.Fatalf("PruningPointHeaders: %+v", pruningPointHeaders) - } - expectedPruningPointHeaderAmount = expectedPruningPointHeaderAmount + 1 - if uint64(len(pruningPointHeaders)) != expectedPruningPointHeaderAmount { - t.Fatalf("Unexpected amount of pruning point headers. "+ - "Want: %d, got: %d", expectedPruningPointHeaderAmount, len(pruningPointHeaders)) - } - - // Make sure that the last pruning point has a post-switch subsidy - lastPruningPoint, err := tc.GetBlock(lastPruningPointHash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } - lastPruningPointCoinbase := lastPruningPoint.Transactions[transactionhelper.CoinbaseTransactionIndex] - _, _, subsidy, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(lastPruningPointCoinbase) - if err != nil { - t.Fatalf("ExtractCoinbaseDataBlueScoreAndSubsidy: %+v", err) - } - if subsidy != consensusConfig.SubsidyGenesisReward { - t.Fatalf("Subsidy has unexpected value. Want: %d, got: %d", consensusConfig.SubsidyGenesisReward, subsidy) - } }) } diff --git a/domain/consensus/processes/coinbasemanager/coinbasemanager.go b/domain/consensus/processes/coinbasemanager/coinbasemanager.go index e0b035c99..0573ca34a 100644 --- a/domain/consensus/processes/coinbasemanager/coinbasemanager.go +++ b/domain/consensus/processes/coinbasemanager/coinbasemanager.go @@ -2,6 +2,8 @@ package coinbasemanager import ( "encoding/binary" + "math/big" + "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/constants" @@ -10,7 +12,6 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper" "github.com/kaspanet/kaspad/infrastructure/db/database" "github.com/pkg/errors" - "math/big" ) type coinbaseManager struct { @@ -190,51 +191,7 @@ func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea, return c.subsidyGenesisReward, nil } - isBlockRewardFixed, err := c.isBlockRewardFixed(stagingArea, blockPruningPoint) - if err != nil { - return 0, err - } - if isBlockRewardFixed { - return c.subsidyGenesisReward, nil - } - - averagePastSubsidy, err := c.calculateAveragePastSubsidy(stagingArea, blockHash) - if err != nil { - return 0, err - } - mergeSetSubsidySum, err := c.calculateMergeSetSubsidySum(stagingArea, blockHash) - if err != nil { - return 0, err - } - subsidyRandomVariable, err := c.calculateSubsidyRandomVariable(stagingArea, blockHash) - if err != nil { - return 0, err - } - - pastSubsidy := new(big.Rat).Mul(averagePastSubsidy, c.subsidyPastRewardMultiplier) - mergeSetSubsidy := new(big.Rat).Mul(mergeSetSubsidySum, c.subsidyMergeSetRewardMultiplier) - - // In order to avoid unsupported negative exponents in powInt64, flip - // the numerator and the denominator manually - subsidyRandom := new(big.Rat) - if subsidyRandomVariable >= 0 { - subsidyRandom = subsidyRandom.SetInt64(1 << subsidyRandomVariable) - } else { - subsidyRandom = subsidyRandom.SetFrac64(1, 1<<(-subsidyRandomVariable)) - } - - blockSubsidyBigRat := new(big.Rat).Add(mergeSetSubsidy, new(big.Rat).Mul(pastSubsidy, subsidyRandom)) - blockSubsidyBigInt := new(big.Int).Div(blockSubsidyBigRat.Num(), blockSubsidyBigRat.Denom()) - blockSubsidyUint64 := blockSubsidyBigInt.Uint64() - - clampedBlockSubsidy := blockSubsidyUint64 - if clampedBlockSubsidy < c.minSubsidy { - clampedBlockSubsidy = c.minSubsidy - } else if clampedBlockSubsidy > c.maxSubsidy { - clampedBlockSubsidy = c.maxSubsidy - } - - return clampedBlockSubsidy, nil + return c.maxSubsidy, nil } func (c *coinbaseManager) calculateAveragePastSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*big.Rat, error) { diff --git a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual_test.go b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual_test.go index 51a02c997..7222cee7c 100644 --- a/domain/consensus/processes/consensusstatemanager/add_block_to_virtual_test.go +++ b/domain/consensus/processes/consensusstatemanager/add_block_to_virtual_test.go @@ -75,7 +75,7 @@ func TestVirtualDiff(t *testing.T) { blockB.Transactions[0].Outputs[0].Value, blockB.Transactions[0].Outputs[0].ScriptPublicKey, true, - 2, //Expected virtual DAA score + consensusConfig.GenesisBlock.Header.DAAScore()+2, //Expected virtual DAA score )) { t.Fatalf("Unexpected entry %s", entry) } diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go index 2479828e2..793ebd4f3 100644 --- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go @@ -2,7 +2,6 @@ package consensusstatemanager import ( "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" - "github.com/kaspanet/kaspad/domain/consensus/utils/multiset" "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" "github.com/kaspanet/kaspad/infrastructure/logger" "github.com/pkg/errors" @@ -23,8 +22,16 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea if blockHash.Equal(csm.genesisHash) { log.Debugf("Block %s is the genesis. By definition, "+ - "it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash) - return utxo.NewUTXODiff(), externalapi.AcceptanceData{}, multiset.New(), nil + "it has a predefined UTXO diff, empty acceptance data, and a predefined multiset", blockHash) + multiset, err := csm.multisetStore.Get(csm.databaseContext, stagingArea, blockHash) + if err != nil { + return nil, nil, nil, err + } + utxoDiff, err := csm.utxoDiffStore.UTXODiff(csm.databaseContext, stagingArea, blockHash) + if err != nil { + return nil, nil, nil, err + } + return utxoDiff, externalapi.AcceptanceData{}, multiset, nil } blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, blockHash, false) diff --git a/domain/consensus/processes/consensusstatemanager/multisets.go b/domain/consensus/processes/consensusstatemanager/multisets.go index 558c1a0e8..0c6a64324 100644 --- a/domain/consensus/processes/consensusstatemanager/multisets.go +++ b/domain/consensus/processes/consensusstatemanager/multisets.go @@ -4,7 +4,6 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" - "github.com/kaspanet/kaspad/domain/consensus/utils/multiset" "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" ) @@ -19,8 +18,8 @@ func (csm *consensusStateManager) calculateMultiset(stagingArea *model.StagingAr if blockHash.Equal(csm.genesisHash) { log.Debugf("Selected parent is nil, which could only happen for the genesis. " + - "The genesis, by definition, has an empty multiset") - return multiset.New(), nil + "The genesis has a predefined multiset") + return csm.multisetStore.Get(csm.databaseContext, stagingArea, blockHash) } ms, err := csm.multisetStore.Get(csm.databaseContext, stagingArea, blockGHOSTDAGData.SelectedParent()) diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go index 93bfa4e3a..a896b7907 100644 --- a/domain/consensus/processes/consensusstatemanager/resolve_block_status.go +++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status.go @@ -3,8 +3,6 @@ package consensusstatemanager import ( "fmt" - "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" - "github.com/kaspanet/kaspad/util/staging" "github.com/kaspanet/kaspad/domain/consensus/model" @@ -129,7 +127,11 @@ func (csm *consensusStateManager) selectedParentInfo( 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 lastUnverifiedBlock, externalapi.StatusUTXOValid, utxo.NewUTXODiff(), nil + utxoDiff, err := csm.utxoDiffStore.UTXODiff(csm.databaseContext, stagingArea, lastUnverifiedBlock) + if err != nil { + return nil, 0, nil, err + } + return lastUnverifiedBlock, externalapi.StatusUTXOValid, utxoDiff, nil } lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, lastUnverifiedBlock, false) if err != nil { diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go index d7d916a42..1614ff49a 100644 --- a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go +++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go @@ -285,7 +285,7 @@ func TestTransactionAcceptance(t *testing.T) { if err != nil { t.Fatalf("Error getting blockF: %+v", err) } - updatedDAAScoreVirtualBlock := 26 + updatedDAAScoreVirtualBlock := consensusConfig.GenesisBlock.Header.DAAScore() + 26 //We expect the second transaction in the "blue block" (blueChildOfRedBlock) to be accepted because the merge set is ordered topologically //and the red block is ordered topologically before the "blue block" so the input is known in the UTXOSet. expectedAcceptanceData := externalapi.AcceptanceData{ diff --git a/domain/consensus/processes/dagtraversalmanager/window_test.go b/domain/consensus/processes/dagtraversalmanager/window_test.go index e76fcc9c0..15e8552db 100644 --- a/domain/consensus/processes/dagtraversalmanager/window_test.go +++ b/domain/consensus/processes/dagtraversalmanager/window_test.go @@ -40,12 +40,12 @@ func TestBlockWindow(t *testing.T) { { parents: []string{"C", "D"}, id: "E", - expectedWindow: []string{"C", "D", "B"}, + expectedWindow: []string{"D", "C", "B"}, }, { parents: []string{"C", "D"}, id: "F", - expectedWindow: []string{"C", "D", "B"}, + expectedWindow: []string{"D", "C", "B"}, }, { parents: []string{"A"}, @@ -60,37 +60,38 @@ func TestBlockWindow(t *testing.T) { { parents: []string{"H", "F"}, id: "I", - expectedWindow: []string{"F", "H", "C", "D", "B", "G"}, + expectedWindow: []string{"F", "D", "H", "C", "G", "B"}, }, { parents: []string{"I"}, id: "J", - expectedWindow: []string{"I", "F", "H", "C", "D", "B", "G"}, + expectedWindow: []string{"I", "F", "D", "H", "C", "G", "B"}, }, + // { parents: []string{"J"}, id: "K", - expectedWindow: []string{"J", "I", "F", "H", "C", "D", "B", "G"}, + expectedWindow: []string{"J", "I", "F", "D", "H", "C", "G", "B"}, }, { parents: []string{"K"}, id: "L", - expectedWindow: []string{"K", "J", "I", "F", "H", "C", "D", "B", "G"}, + expectedWindow: []string{"K", "J", "I", "F", "D", "H", "C", "G", "B"}, }, { parents: []string{"L"}, id: "M", - expectedWindow: []string{"L", "K", "J", "I", "F", "H", "C", "D", "B", "G"}, + expectedWindow: []string{"L", "K", "J", "I", "F", "D", "H", "C", "G", "B"}, }, { parents: []string{"M"}, id: "N", - expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "C", "D", "B"}, + expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "H", "C", "G"}, }, { parents: []string{"N"}, id: "O", - expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"}, + expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"}, }, }, dagconfig.TestnetParams.Name: { @@ -132,37 +133,37 @@ func TestBlockWindow(t *testing.T) { { parents: []string{"H", "F"}, id: "I", - expectedWindow: []string{"F", "H", "C", "D", "G", "B"}, + expectedWindow: []string{"F", "C", "D", "H", "B", "G"}, }, { parents: []string{"I"}, id: "J", - expectedWindow: []string{"I", "F", "H", "C", "D", "G", "B"}, + expectedWindow: []string{"I", "F", "C", "D", "H", "B", "G"}, }, { parents: []string{"J"}, id: "K", - expectedWindow: []string{"J", "I", "F", "H", "C", "D", "G", "B"}, + expectedWindow: []string{"J", "I", "F", "C", "D", "H", "B", "G"}, }, { parents: []string{"K"}, id: "L", - expectedWindow: []string{"K", "J", "I", "F", "H", "C", "D", "G", "B"}, + expectedWindow: []string{"K", "J", "I", "F", "C", "D", "H", "B", "G"}, }, { parents: []string{"L"}, id: "M", - expectedWindow: []string{"L", "K", "J", "I", "F", "H", "C", "D", "G", "B"}, + expectedWindow: []string{"L", "K", "J", "I", "F", "C", "D", "H", "B", "G"}, }, { parents: []string{"M"}, id: "N", - expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "C", "D", "G"}, + expectedWindow: []string{"M", "L", "K", "J", "I", "F", "C", "D", "H", "B"}, }, { parents: []string{"N"}, id: "O", - expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"}, + expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "C", "D", "H"}, }, }, dagconfig.DevnetParams.Name: { @@ -204,37 +205,37 @@ func TestBlockWindow(t *testing.T) { { parents: []string{"H", "F"}, id: "I", - expectedWindow: []string{"F", "D", "C", "H", "B", "G"}, + expectedWindow: []string{"F", "D", "H", "C", "B", "G"}, }, { parents: []string{"I"}, id: "J", - expectedWindow: []string{"I", "F", "D", "C", "H", "B", "G"}, + expectedWindow: []string{"I", "F", "D", "H", "C", "B", "G"}, }, { parents: []string{"J"}, id: "K", - expectedWindow: []string{"J", "I", "F", "D", "C", "H", "B", "G"}, + expectedWindow: []string{"J", "I", "F", "D", "H", "C", "B", "G"}, }, { parents: []string{"K"}, id: "L", - expectedWindow: []string{"K", "J", "I", "F", "D", "C", "H", "B", "G"}, + expectedWindow: []string{"K", "J", "I", "F", "D", "H", "C", "B", "G"}, }, { parents: []string{"L"}, id: "M", - expectedWindow: []string{"L", "K", "J", "I", "F", "D", "C", "H", "B", "G"}, + expectedWindow: []string{"L", "K", "J", "I", "F", "D", "H", "C", "B", "G"}, }, { parents: []string{"M"}, id: "N", - expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "C", "H", "B"}, + expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "H", "C", "B"}, }, { parents: []string{"N"}, id: "O", - expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "C", "H"}, + expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"}, }, }, dagconfig.SimnetParams.Name: { @@ -276,37 +277,37 @@ func TestBlockWindow(t *testing.T) { { parents: []string{"H", "F"}, id: "I", - expectedWindow: []string{"F", "D", "H", "C", "G", "B"}, + expectedWindow: []string{"F", "H", "D", "C", "B", "G"}, }, { parents: []string{"I"}, id: "J", - expectedWindow: []string{"I", "F", "D", "H", "C", "G", "B"}, + expectedWindow: []string{"I", "F", "H", "D", "C", "B", "G"}, }, { parents: []string{"J"}, id: "K", - expectedWindow: []string{"J", "I", "F", "D", "H", "C", "G", "B"}, + expectedWindow: []string{"J", "I", "F", "H", "D", "C", "B", "G"}, }, { parents: []string{"K"}, id: "L", - expectedWindow: []string{"K", "J", "I", "F", "D", "H", "C", "G", "B"}, + expectedWindow: []string{"K", "J", "I", "F", "H", "D", "C", "B", "G"}, }, { parents: []string{"L"}, id: "M", - expectedWindow: []string{"L", "K", "J", "I", "F", "D", "H", "C", "G", "B"}, + expectedWindow: []string{"L", "K", "J", "I", "F", "H", "D", "C", "B", "G"}, }, { parents: []string{"M"}, id: "N", - expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "H", "C", "G"}, + expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "D", "C", "B"}, }, { parents: []string{"N"}, id: "O", - expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"}, + expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "D", "C"}, }, }, } diff --git a/domain/consensus/processes/difficultymanager/difficultymanager.go b/domain/consensus/processes/difficultymanager/difficultymanager.go index db20698fa..3f7805f6d 100644 --- a/domain/consensus/processes/difficultymanager/difficultymanager.go +++ b/domain/consensus/processes/difficultymanager/difficultymanager.go @@ -105,10 +105,14 @@ func (dm *difficultyManager) requiredDifficultyFromTargetsWindow(targetsWindow b return dm.genesisBits, nil } + // in the past this was < 2 as the comment explains, we changed it to under the window size to + // make the hashrate(which is ~1.5GH/s) constant in the first 2641 blocks so that we won't have a lot of tips + // We need at least 2 blocks to get a timestamp interval // We could instead clamp the timestamp difference to `targetTimePerBlock`, // but then everything will cancel out and we'll get the target from the last block, which will be the same as genesis. - if len(targetsWindow) < 2 { + // We add 64 as a safety margin + if len(targetsWindow) < 2 || len(targetsWindow) < dm.difficultyAdjustmentWindowSize + 64 { return dm.genesisBits, nil } windowMinTimestamp, windowMaxTimeStamp, windowsMinIndex, _ := targetsWindow.minMaxTimestamps() @@ -157,7 +161,11 @@ func (dm *difficultyManager) calculateDaaScoreAndAddedBlocks(stagingArea *model. isBlockWithTrustedData bool) (uint64, []*externalapi.DomainHash, error) { if blockHash.Equal(dm.genesisHash) { - return 0, nil, nil + genesisHeader, err := dm.headerStore.BlockHeader(dm.databaseContext, stagingArea, dm.genesisHash) + if err != nil { + return 0, nil, err + } + return genesisHeader.DAAScore(), nil, nil } ghostdagData, err := dm.ghostdagStore.Get(dm.databaseContext, stagingArea, blockHash, false) diff --git a/domain/consensus/processes/difficultymanager/difficultymanager_test.go b/domain/consensus/processes/difficultymanager/difficultymanager_test.go index 5da6e3297..03293bed2 100644 --- a/domain/consensus/processes/difficultymanager/difficultymanager_test.go +++ b/domain/consensus/processes/difficultymanager/difficultymanager_test.go @@ -19,12 +19,6 @@ import ( func TestDifficulty(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - // Mainnet's genesis is too new, so if we'll build on it we'll get to the future very quickly. - // TODO: Once it gets older, we should unskip this test. - if consensusConfig.Name == "kaspa-mainnet" { - return - } - if consensusConfig.DisableDifficultyAdjustment { return } @@ -37,7 +31,7 @@ func TestDifficulty(t *testing.T) { } consensusConfig.K = 1 - consensusConfig.DifficultyAdjustmentWindowSize = 265 + consensusConfig.DifficultyAdjustmentWindowSize = 140 factory := consensus.NewFactory() tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestDifficulty") @@ -114,7 +108,7 @@ func TestDifficulty(t *testing.T) { "window size, the difficulty should be the same as genesis'") } } - for i := 0; i < consensusConfig.DifficultyAdjustmentWindowSize+100; i++ { + for i := 0; i < consensusConfig.DifficultyAdjustmentWindowSize+10; i++ { tip, tipHash = addBlock(0, tipHash) if tip.Header.Bits() != consensusConfig.GenesisBlock.Header.Bits() { t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change") @@ -136,9 +130,9 @@ func TestDifficulty(t *testing.T) { var expectedBits uint32 switch consensusConfig.Name { case dagconfig.TestnetParams.Name, dagconfig.DevnetParams.Name: - expectedBits = uint32(0x1e7f83df) + expectedBits = uint32(0x1e7f1441) case dagconfig.MainnetParams.Name: - expectedBits = uint32(0x1e7f83df) + expectedBits = uint32(0x1d02c50f) } if tip.Header.Bits() != expectedBits { @@ -237,7 +231,7 @@ func TestDifficulty(t *testing.T) { func TestDAAScore(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - consensusConfig.DifficultyAdjustmentWindowSize = 265 + consensusConfig.DifficultyAdjustmentWindowSize = 86 stagingArea := model.NewStagingArea() @@ -269,9 +263,9 @@ func TestDAAScore(t *testing.T) { t.Fatalf("DAAScore: %+v", err) } - blockBlueScore3ExpectedDAAScore := uint64(2) + blockBlueScore3ExpectedDAAScore := uint64(2) + consensusConfig.GenesisBlock.Header.DAAScore() if blockBlueScore3DAAScore != blockBlueScore3ExpectedDAAScore { - t.Fatalf("DAA score is expected to be %d but got %d", blockBlueScore3ExpectedDAAScore, blockBlueScore3ExpectedDAAScore) + t.Fatalf("DAA score is expected to be %d but got %d", blockBlueScore3ExpectedDAAScore, blockBlueScore3DAAScore) } tipDAAScore := blockBlueScore3ExpectedDAAScore diff --git a/domain/consensus/processes/parentsmanager/parentsmanager.go b/domain/consensus/processes/parentsmanager/parentsmanager.go index bec3d648c..512e17241 100644 --- a/domain/consensus/processes/parentsmanager/parentsmanager.go +++ b/domain/consensus/processes/parentsmanager/parentsmanager.go @@ -7,15 +7,13 @@ import ( ) type parentsManager struct { - hardForkOmitGenesisFromParentsDAAScore uint64 - genesisHash *externalapi.DomainHash + genesisHash *externalapi.DomainHash } // New instantiates a new ParentsManager -func New(genesisHash *externalapi.DomainHash, hardForkOmitGenesisFromParentsDAAScore uint64) model.ParentsManager { +func New(genesisHash *externalapi.DomainHash) model.ParentsManager { return &parentsManager{ - genesisHash: genesisHash, - hardForkOmitGenesisFromParentsDAAScore: hardForkOmitGenesisFromParentsDAAScore, + genesisHash: genesisHash, } } @@ -25,7 +23,7 @@ func (pm *parentsManager) ParentsAtLevel(blockHeader externalapi.BlockHeader, le parentsAtLevel = blockHeader.Parents()[level] } - if len(parentsAtLevel) == 0 && len(blockHeader.DirectParents()) > 0 && blockHeader.DAAScore() >= pm.hardForkOmitGenesisFromParentsDAAScore { + if len(parentsAtLevel) == 0 && len(blockHeader.DirectParents()) > 0 { return externalapi.BlockLevelParents{pm.genesisHash} } @@ -33,11 +31,7 @@ func (pm *parentsManager) ParentsAtLevel(blockHeader externalapi.BlockHeader, le } func (pm *parentsManager) Parents(blockHeader externalapi.BlockHeader) []externalapi.BlockLevelParents { - numParents := len(blockHeader.Parents()) - if blockHeader.DAAScore() >= pm.hardForkOmitGenesisFromParentsDAAScore { - numParents = constants.MaxBlockLevel + 1 - } - + numParents := constants.MaxBlockLevel + 1 parents := make([]externalapi.BlockLevelParents, numParents) for i := 0; i < numParents; i++ { parents[i] = pm.ParentsAtLevel(blockHeader, i) diff --git a/domain/consensus/processes/pruningmanager/pruning_test.go b/domain/consensus/processes/pruningmanager/pruning_test.go index 83fba86d8..bcd3b188d 100644 --- a/domain/consensus/processes/pruningmanager/pruning_test.go +++ b/domain/consensus/processes/pruningmanager/pruning_test.go @@ -36,14 +36,16 @@ func TestPruning(t *testing.T) { dagconfig.SimnetParams.Name: "1582", }, "dag-for-test-pruning.json": { - dagconfig.MainnetParams.Name: "502", + dagconfig.MainnetParams.Name: "503", dagconfig.TestnetParams.Name: "502", dagconfig.DevnetParams.Name: "502", - dagconfig.SimnetParams.Name: "503", + dagconfig.SimnetParams.Name: "502", }, } testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + // Improve the performance of the test a little + consensusConfig.DisableDifficultyAdjustment = true err := filepath.Walk("./testdata", func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -71,6 +73,7 @@ func TestPruning(t *testing.T) { consensusConfig.DifficultyAdjustmentWindowSize = 400 factory := consensus.NewFactory() + factory.SetTestLevelDBCacheSize(128) tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestPruning") if err != nil { t.Fatalf("Error setting up consensus: %+v", err) diff --git a/domain/consensus/processes/pruningmanager/pruningmanager.go b/domain/consensus/processes/pruningmanager/pruningmanager.go index 5b27685cc..7e182deb2 100644 --- a/domain/consensus/processes/pruningmanager/pruningmanager.go +++ b/domain/consensus/processes/pruningmanager/pruningmanager.go @@ -675,7 +675,19 @@ func (pm *pruningManager) calculateDiffBetweenPreviousAndCurrentPruningPoints(st onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.calculateDiffBetweenPreviousAndCurrentPruningPoints") defer onEnd() if currentPruningHash.Equal(pm.genesisHash) { - return utxo.NewUTXODiff(), nil + iter, err := pm.consensusStateManager.RestorePastUTXOSetIterator(stagingArea, currentPruningHash) + if err != nil { + return nil, err + } + set := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry) + for ok := iter.First(); ok; ok = iter.Next() { + outpoint, entry, err := iter.Get() + if err != nil { + return nil, err + } + set[*outpoint] = entry + } + return utxo.NewUTXODiffFromCollections(utxo.NewUTXOCollection(set), utxo.NewUTXOCollection(make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry))) } pruningPointIndex, err := pm.pruningStore.CurrentPruningPointIndex(pm.databaseContext, stagingArea) diff --git a/domain/consensus/timelock_CLTV_test.go b/domain/consensus/timelock_CLTV_test.go index 94a6779a7..323f9776c 100644 --- a/domain/consensus/timelock_CLTV_test.go +++ b/domain/consensus/timelock_CLTV_test.go @@ -49,7 +49,7 @@ func TestCheckLockTimeVerifyConditionedByDAAScore(t *testing.T) { } fees := uint64(1) //Create a CLTV script: - targetDAAScore := uint64(30) + targetDAAScore := consensusConfig.GenesisBlock.Header.DAAScore() + uint64(30) redeemScriptCLTV, err := createScriptCLTV(targetDAAScore) if err != nil { t.Fatalf("Failed to create a script using createScriptCLTV: %v", err) @@ -156,7 +156,7 @@ func TestCheckLockTimeVerifyConditionedByDAAScoreWithWrongLockTime(t *testing.T) } fees := uint64(1) //Create a CLTV script: - targetDAAScore := uint64(30) + targetDAAScore := consensusConfig.GenesisBlock.Header.DAAScore() + uint64(30) redeemScriptCLTV, err := createScriptCLTV(targetDAAScore) if err != nil { t.Fatalf("Failed to create a script using createScriptCLTV: %v", err) diff --git a/domain/consensus/utils/constants/constants.go b/domain/consensus/utils/constants/constants.go index 13090b0b8..219df23a4 100644 --- a/domain/consensus/utils/constants/constants.go +++ b/domain/consensus/utils/constants/constants.go @@ -37,5 +37,7 @@ const ( LockTimeThreshold = 5e11 // Tue Nov 5 00:53:20 1985 UTC // MaxBlockLevel is the maximum possible block level. - MaxBlockLevel = 255 + // This is technically 255, but we clamped it at 256 - block level of mainnet genesis + // This means that any block that has a level lower or equal to genesis will be level 0. + MaxBlockLevel = 225 ) diff --git a/domain/consensus/utils/pow/pow.go b/domain/consensus/utils/pow/pow.go index 8009c9f52..051e98400 100644 --- a/domain/consensus/utils/pow/pow.go +++ b/domain/consensus/utils/pow/pow.go @@ -104,9 +104,10 @@ func BlockLevel(header externalapi.BlockHeader) int { } proofOfWorkValue := NewState(header.ToMutable()).CalculateProofOfWorkValue() - for blockLevel := 0; ; blockLevel++ { - if blockLevel == constants.MaxBlockLevel || proofOfWorkValue.Bit(blockLevel+1) != 0 { - return blockLevel - } + level := constants.MaxBlockLevel - proofOfWorkValue.BitLen() + // If the block has a level lower than genesis make it zero. + if level < 0 { + level = 0 } + return level } diff --git a/domain/dagconfig/consensus_defaults.go b/domain/dagconfig/consensus_defaults.go index 2c70a676b..1f25f09b4 100644 --- a/domain/dagconfig/consensus_defaults.go +++ b/domain/dagconfig/consensus_defaults.go @@ -22,7 +22,7 @@ import ( // const ( - defaultMaxCoinbasePayloadLength = 172 + defaultMaxCoinbasePayloadLength = 204 // defaultMaxBlockMass is a bound on the mass of a block, larger values increase the bound d // on the round trip time of a block, which affects the other parameters as described below defaultMaxBlockMass = 500_000 @@ -49,7 +49,7 @@ const ( defaultMergeSetSizeLimit = defaultGHOSTDAGK * 10 defaultSubsidyGenesisReward = 1 * constants.SompiPerKaspa defaultMinSubsidy = 1 * constants.SompiPerKaspa - defaultMaxSubsidy = 1000 * constants.SompiPerKaspa + defaultMaxSubsidy = 500 * constants.SompiPerKaspa defaultBaseSubsidy = 50 * constants.SompiPerKaspa defaultFixedSubsidySwitchPruningPointInterval uint64 = 7 defaultCoinbasePayloadScriptPublicKeyMaxLength = 150 diff --git a/domain/dagconfig/genesis.go b/domain/dagconfig/genesis.go index 1c419e60c..3abfe9411 100644 --- a/domain/dagconfig/genesis.go +++ b/domain/dagconfig/genesis.go @@ -36,10 +36,14 @@ var genesisTxPayload = []byte{ 0x20, 0xd7, 0x90, 0xd7, 0x9c, 0xd7, 0x94, 0xd7, 0x9b, 0xd7, 0x9d, 0x20, 0xd7, 0xaa, 0xd7, 0xa2, 0xd7, 0x91, 0xd7, 0x93, 0xd7, 0x95, 0xd7, 0x9f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bitcoin block hash 00000000000000000001733c62adb19f1b77fa0735d0e11f25af36fc9ca908a5 - 0x00, 0x01, 0x73, 0x3c, 0x62, 0xad, 0xb1, 0x9f, - 0x1b, 0x77, 0xfa, 0x07, 0x35, 0xd0, 0xe1, 0x1f, - 0x25, 0xaf, 0x36, 0xfc, 0x9c, 0xa9, 0x08, 0xa5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bitcoin block hash 0000000000000000000b1f8e1c17b0133d439174e52efbb0c41c3583a8aa66b0 + 0x00, 0x0b, 0x1f, 0x8e, 0x1c, 0x17, 0xb0, 0x13, + 0x3d, 0x43, 0x91, 0x74 ,0xe5, 0x2e, 0xfb, 0xb0, + 0xc4, 0x1c, 0x35, 0x83, 0xa8, 0xaa, 0x66, 0xb0, + 0x0f, 0xca, 0x37, 0xca, 0x66, 0x7c, 0x2d, 0x55, // Checkpoint block hash 0fca37ca667c2d550a6c4416dad9717e50927128c424fa4edbebc436ab13aeef + 0x0a, 0x6c, 0x44, 0x16, 0xda, 0xd9, 0x71, 0x7e, + 0x50, 0x92, 0x71, 0x28, 0xc4, 0x24, 0xfa, 0x4e, + 0xdb, 0xeb, 0xc4, 0x36, 0xab, 0x13, 0xae, 0xef, } // genesisCoinbaseTx is the coinbase transaction for the genesis blocks for @@ -50,19 +54,13 @@ var genesisCoinbaseTx = transactionhelper.NewSubnetworkTransaction(0, []*externa // genesisHash is the hash of the first block in the block DAG for the main // network (genesis block). var genesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ - 0xca, 0xeb, 0x97, 0x96, 0x0a, 0x16, 0x0c, 0x21, - 0x1a, 0x6b, 0x21, 0x96, 0xbd, 0x78, 0x39, 0x9f, - 0xd4, 0xc4, 0xcc, 0x5b, 0x50, 0x9f, 0x55, 0xc1, - 0x2c, 0x8a, 0x7d, 0x81, 0x5f, 0x75, 0x36, 0xea, + 0x58, 0xc2, 0xd4, 0x19, 0x9e, 0x21, 0xf9, 0x10, 0xd1, 0x57, 0x1d, 0x11, 0x49, 0x69, 0xce, 0xce, 0xf4, 0x8f, 0x9, 0xf9, 0x34, 0xd4, 0x2c, 0xcb, 0x6a, 0x28, 0x1a, 0x15, 0x86, 0x8f, 0x29, 0x99, }) // genesisMerkleRoot is the hash of the first transaction in the genesis block // for the main network. var genesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ - 0xca, 0xed, 0xaf, 0x7d, 0x4a, 0x08, 0xbb, 0xe8, - 0x90, 0x11, 0x64, 0x0c, 0x48, 0x41, 0xb6, 0x6d, - 0x5b, 0xba, 0x67, 0xd7, 0x28, 0x8c, 0xe6, 0xd6, - 0x72, 0x28, 0xdb, 0x00, 0x09, 0x66, 0xe9, 0x74, + 0x8e, 0xc8, 0x98, 0x56, 0x8c, 0x68, 0x1, 0xd1, 0x3d, 0xf4, 0xee, 0x6e, 0x2a, 0x1b, 0x54, 0xb7, 0xe6, 0x23, 0x6f, 0x67, 0x1f, 0x20, 0x95, 0x4f, 0x5, 0x30, 0x64, 0x10, 0x51, 0x8e, 0xeb, 0x32, }) // genesisBlock defines the genesis block of the block DAG which serves as the @@ -73,11 +71,13 @@ var genesisBlock = externalapi.DomainBlock{ []externalapi.BlockLevelParents{}, genesisMerkleRoot, &externalapi.DomainHash{}, - externalapi.NewDomainHashFromByteArray(muhash.EmptyMuHashHash.AsArray()), - 0x17cfb020c02, - 0x1e7fffff, + externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ + 0x71, 0x0f, 0x27, 0xdf, 0x42, 0x3e, 0x63, 0xaa, 0x6c, 0xdb, 0x72, 0xb8, 0x9e, 0xa5, 0xa0, 0x6c, 0xff, 0xa3, 0x99, 0xd6, 0x6f, 0x16, 0x77, 0x04, 0x45, 0x5b, 0x5a, 0xf5, 0x9d, 0xef, 0x8e, 0x20, + }), + 1637609671037, + 486722099, 0x3392c, - 0, + 1312860, // Checkpoint DAA score 0, big.NewInt(0), &externalapi.DomainHash{}, diff --git a/domain/dagconfig/params.go b/domain/dagconfig/params.go index 8288e9205..3da90f9c1 100644 --- a/domain/dagconfig/params.go +++ b/domain/dagconfig/params.go @@ -186,8 +186,6 @@ type Params struct { FixedSubsidySwitchPruningPointInterval uint64 FixedSubsidySwitchHashRateThreshold *big.Int - - HardForkOmitGenesisFromParentsDAAScore uint64 } // NormalizeRPCServerAddress returns addr with the current network default @@ -213,7 +211,15 @@ var MainnetParams = Params{ Net: appmessage.Mainnet, RPCPort: "16110", DefaultPort: "16111", - DNSSeeds: []string{"mainnet-dnsseed.daglabs-dev.com"}, + DNSSeeds: []string{ + "mainnet-dnsseed.daglabs-dev.com", + // This DNS seeder is run by Denis Mashkevich + "mainnet-dnsseed-1.kaspanet.org", + // This DNS seeder is run by Denis Mashkevich + "mainnet-dnsseed-2.kaspanet.org", + // This DNS seeder is run by Elichai Turkel + "kaspa.turkel.in", + }, // DAG parameters GenesisBlock: &genesisBlock, @@ -266,7 +272,6 @@ var MainnetParams = Params{ PruningProofM: defaultPruningProofM, FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, FixedSubsidySwitchHashRateThreshold: big.NewInt(150_000_000_000), - HardForkOmitGenesisFromParentsDAAScore: 1320000, } // TestnetParams defines the network parameters for the test Kaspa network. @@ -329,7 +334,6 @@ var TestnetParams = Params{ PruningProofM: defaultPruningProofM, FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, FixedSubsidySwitchHashRateThreshold: big.NewInt(150_000_000_000), - HardForkOmitGenesisFromParentsDAAScore: 2e6, } // SimnetParams defines the network parameters for the simulation test Kaspa @@ -396,7 +400,6 @@ var SimnetParams = Params{ PruningProofM: defaultPruningProofM, FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, FixedSubsidySwitchHashRateThreshold: big.NewInt(150_000_000_000), - HardForkOmitGenesisFromParentsDAAScore: 5, } // DevnetParams defines the network parameters for the development Kaspa network. @@ -459,7 +462,6 @@ var DevnetParams = Params{ PruningProofM: defaultPruningProofM, FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, FixedSubsidySwitchHashRateThreshold: big.NewInt(150_000_000_000), - HardForkOmitGenesisFromParentsDAAScore: 3000, } var ( diff --git a/domain/miningmanager/miningmanager_test.go b/domain/miningmanager/miningmanager_test.go index 064fbc195..81ed47699 100644 --- a/domain/miningmanager/miningmanager_test.go +++ b/domain/miningmanager/miningmanager_test.go @@ -40,7 +40,7 @@ func TestValidateAndInsertTransaction(t *testing.T) { miningManager := miningFactory.NewMiningManager(consensusReference, &consensusConfig.Params, mempool.DefaultConfig(&consensusConfig.Params)) transactionsToInsert := make([]*externalapi.DomainTransaction, 10) for i := range transactionsToInsert { - transactionsToInsert[i] = createTransactionWithUTXOEntry(t, i) + transactionsToInsert[i] = createTransactionWithUTXOEntry(t, i, 0) _, err = miningManager.ValidateAndInsertTransaction(transactionsToInsert[i], false, true) if err != nil { t.Fatalf("ValidateAndInsertTransaction: %v", err) @@ -89,7 +89,7 @@ func TestImmatureSpend(t *testing.T) { tcAsConsensusPointer := &tcAsConsensus consensusReference := consensusreference.NewConsensusReference(&tcAsConsensusPointer) miningManager := miningFactory.NewMiningManager(consensusReference, &consensusConfig.Params, mempool.DefaultConfig(&consensusConfig.Params)) - tx := createTransactionWithUTXOEntry(t, 0) + tx := createTransactionWithUTXOEntry(t, 0, consensusConfig.GenesisBlock.Header.DAAScore()) _, err = miningManager.ValidateAndInsertTransaction(tx, false, false) txRuleError := &mempool.TxRuleError{} if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool.RejectImmatureSpend { @@ -119,7 +119,7 @@ func TestInsertDoubleTransactionsToMempool(t *testing.T) { tcAsConsensusPointer := &tcAsConsensus consensusReference := consensusreference.NewConsensusReference(&tcAsConsensusPointer) miningManager := miningFactory.NewMiningManager(consensusReference, &consensusConfig.Params, mempool.DefaultConfig(&consensusConfig.Params)) - transaction := createTransactionWithUTXOEntry(t, 0) + transaction := createTransactionWithUTXOEntry(t, 0, 0) _, err = miningManager.ValidateAndInsertTransaction(transaction, false, true) if err != nil { t.Fatalf("ValidateAndInsertTransaction: %v", err) @@ -186,7 +186,7 @@ func TestHandleNewBlockTransactions(t *testing.T) { miningManager := miningFactory.NewMiningManager(consensusReference, &consensusConfig.Params, mempool.DefaultConfig(&consensusConfig.Params)) transactionsToInsert := make([]*externalapi.DomainTransaction, 10) for i := range transactionsToInsert { - transaction := createTransactionWithUTXOEntry(t, i) + transaction := createTransactionWithUTXOEntry(t, i, 0) transactionsToInsert[i] = transaction _, err = miningManager.ValidateAndInsertTransaction(transaction, false, true) if err != nil { @@ -253,12 +253,12 @@ func TestDoubleSpendWithBlock(t *testing.T) { tcAsConsensusPointer := &tcAsConsensus consensusReference := consensusreference.NewConsensusReference(&tcAsConsensusPointer) miningManager := miningFactory.NewMiningManager(consensusReference, &consensusConfig.Params, mempool.DefaultConfig(&consensusConfig.Params)) - transactionInTheMempool := createTransactionWithUTXOEntry(t, 0) + transactionInTheMempool := createTransactionWithUTXOEntry(t, 0, 0) _, err = miningManager.ValidateAndInsertTransaction(transactionInTheMempool, false, true) if err != nil { t.Fatalf("ValidateAndInsertTransaction: %v", err) } - doubleSpendTransactionInTheBlock := createTransactionWithUTXOEntry(t, 0) + doubleSpendTransactionInTheBlock := createTransactionWithUTXOEntry(t, 0, 0) doubleSpendTransactionInTheBlock.Inputs[0].PreviousOutpoint = transactionInTheMempool.Inputs[0].PreviousOutpoint blockTransactions := []*externalapi.DomainTransaction{nil, doubleSpendTransactionInTheBlock} _, err = miningManager.HandleNewBlockTransactions(blockTransactions) @@ -571,7 +571,7 @@ func TestRevalidateHighPriorityTransactions(t *testing.T) { }) } -func createTransactionWithUTXOEntry(t *testing.T, i int) *externalapi.DomainTransaction { +func createTransactionWithUTXOEntry(t *testing.T, i int, daaScore uint64) *externalapi.DomainTransaction { prevOutTxID := externalapi.DomainTransactionID{} prevOutPoint := externalapi.DomainOutpoint{TransactionID: prevOutTxID, Index: uint32(i)} scriptPublicKey, redeemScript := testutils.OpTrueScript() @@ -587,7 +587,7 @@ func createTransactionWithUTXOEntry(t *testing.T, i int) *externalapi.DomainTran 100000000, // 1 KAS scriptPublicKey, true, - uint64(0)), + daaScore), } txOut := externalapi.DomainTransactionOutput{ Value: 10000, diff --git a/infrastructure/config/config.go b/infrastructure/config/config.go index cb6365889..f76347504 100644 --- a/infrastructure/config/config.go +++ b/infrastructure/config/config.go @@ -29,7 +29,6 @@ import ( const ( defaultConfigFilename = "kaspad.conf" - defaultDataDirname = "data" defaultLogLevel = "info" defaultLogDirname = "logs" defaultLogFilename = "kaspad.log" @@ -86,7 +85,7 @@ type Flags struct { Listeners []string `long:"listen" description:"Add an interface/port to listen for connections (default all interfaces port: 16111, testnet: 16211)"` TargetOutboundPeers int `long:"outpeers" description:"Target number of outbound peers"` MaxInboundPeers int `long:"maxinpeers" description:"Max number of inbound peers"` - DisableBanning bool `long:"nobanning" description:"Disable banning of misbehaving peers"` + EnableBanning bool `long:"enablebanning" description:"Enable banning of misbehaving peers"` BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` Whitelists []string `long:"whitelist" description:"Add an IP network or IP that will not be banned. (eg. 192.168.1.0/24 or ::1)"` diff --git a/util/difficulty/difficulty_test.go b/util/difficulty/difficulty_test.go index 6843440dc..51bed746d 100644 --- a/util/difficulty/difficulty_test.go +++ b/util/difficulty/difficulty_test.go @@ -14,7 +14,7 @@ import ( func TestGetHashrateString(t *testing.T) { var results = map[string]string{ - dagconfig.MainnetParams.Name: "131.07 KH/s", + dagconfig.MainnetParams.Name: "1.53 GH/s", dagconfig.TestnetParams.Name: "131.07 KH/s", dagconfig.DevnetParams.Name: "131.07 KH/s", dagconfig.SimnetParams.Name: "2.00 KH/s", diff --git a/version/version.go b/version/version.go index 2f71d32ff..501630ef2 100644 --- a/version/version.go +++ b/version/version.go @@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs const ( appMajor uint = 0 appMinor uint = 11 - appPatch uint = 4 + appPatch uint = 5 ) // appBuild is defined as a variable so it can be overridden during the build