diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go index 78118ae75d..06d1825dcb 100644 --- a/domain/consensus/factory.go +++ b/domain/consensus/factory.go @@ -205,11 +205,15 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas config.SubsidyMergeSetRewardMultiplier, config.CoinbasePayloadScriptPublicKeyMaxLength, config.GenesisHash, + config.FixedSubsidySwitchPruningPointInterval, + config.FixedSubsidySwitchHashRateDifference, dagTraversalManager, ghostdagDataStore, acceptanceDataStore, daaBlocksStore, - blockStore) + blockStore, + pruningStore, + blockHeaderStore) headerTipsManager := headersselectedtipmanager.New(dbManager, dagTopologyManager, dagTraversalManager, ghostdagManager, headersSelectedTipStore, headersSelectedChainStore) genesisHash := config.GenesisHash diff --git a/domain/consensus/model/interface_processes_coinbasemanager.go b/domain/consensus/model/interface_processes_coinbasemanager.go index e8635d997a..42ef4720ca 100644 --- a/domain/consensus/model/interface_processes_coinbasemanager.go +++ b/domain/consensus/model/interface_processes_coinbasemanager.go @@ -6,7 +6,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" // coinbase transactions type CoinbaseManager interface { ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash, - coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) - CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (uint64, error) + coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error) + CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash, blockPruningPoint *externalapi.DomainHash) (uint64, error) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction) (blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error) } diff --git a/domain/consensus/processes/blockbuilder/block_builder.go b/domain/consensus/processes/blockbuilder/block_builder.go index 26fb57cc4f..745bc8809f 100644 --- a/domain/consensus/processes/blockbuilder/block_builder.go +++ b/domain/consensus/processes/blockbuilder/block_builder.go @@ -103,13 +103,17 @@ func (bb *blockBuilder) buildBlock(stagingArea *model.StagingArea, coinbaseData return nil, err } - coinbase, err := bb.newBlockCoinbaseTransaction(stagingArea, coinbaseData) + newBlockPruningPoint, err := bb.newBlockPruningPoint(stagingArea, model.VirtualBlockHash) + if err != nil { + return nil, err + } + coinbase, err := bb.newBlockCoinbaseTransaction(stagingArea, coinbaseData, newBlockPruningPoint) if err != nil { return nil, err } transactionsWithCoinbase := append([]*externalapi.DomainTransaction{coinbase}, transactions...) - header, err := bb.buildHeader(stagingArea, transactionsWithCoinbase) + header, err := bb.buildHeader(stagingArea, transactionsWithCoinbase, newBlockPruningPoint) if err != nil { return nil, err } @@ -171,13 +175,13 @@ func (bb *blockBuilder) validateTransaction( } func (bb *blockBuilder) newBlockCoinbaseTransaction(stagingArea *model.StagingArea, - coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) { + coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error) { - return bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, model.VirtualBlockHash, coinbaseData) + return bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, model.VirtualBlockHash, coinbaseData, blockPruningPoint) } -func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions []*externalapi.DomainTransaction) ( - externalapi.BlockHeader, error) { +func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions []*externalapi.DomainTransaction, + newBlockPruningPoint *externalapi.DomainHash) (externalapi.BlockHeader, error) { parents, err := bb.newBlockParents(stagingArea) if err != nil { @@ -212,10 +216,6 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions if err != nil { return nil, err } - pruningPoint, err := bb.newBlockPruningPoint(stagingArea, model.VirtualBlockHash) - if err != nil { - return nil, err - } return blockheader.NewImmutableBlockHeader( constants.MaxBlockVersion, @@ -229,7 +229,7 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions daaScore, blueScore, blueWork, - pruningPoint, + newBlockPruningPoint, ), nil } diff --git a/domain/consensus/processes/blockbuilder/test_block_builder.go b/domain/consensus/processes/blockbuilder/test_block_builder.go index a798f53dda..4796f9eab9 100644 --- a/domain/consensus/processes/blockbuilder/test_block_builder.go +++ b/domain/consensus/processes/blockbuilder/test_block_builder.go @@ -200,7 +200,11 @@ func (bb *testBlockBuilder) buildBlockWithParents(stagingArea *model.StagingArea bb.acceptanceDataStore.Stage(stagingArea, tempBlockHash, acceptanceData) - coinbase, err := bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, tempBlockHash, coinbaseData) + pruningPoint, err := bb.newBlockPruningPoint(stagingArea, tempBlockHash) + if err != nil { + return nil, nil, err + } + coinbase, err := bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, tempBlockHash, coinbaseData, pruningPoint) if err != nil { return nil, nil, err } diff --git a/domain/consensus/processes/blockvalidator/block_body_in_context.go b/domain/consensus/processes/blockvalidator/block_body_in_context.go index a652c8d870..538051c70a 100644 --- a/domain/consensus/processes/blockvalidator/block_body_in_context.go +++ b/domain/consensus/processes/blockvalidator/block_body_in_context.go @@ -178,7 +178,7 @@ func (v *blockValidator) checkCoinbaseSubsidy( return err } - expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(stagingArea, blockHash) + expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(stagingArea, blockHash, block.Header.PruningPoint()) if err != nil { return err } diff --git a/domain/consensus/processes/coinbasemanager/block_reward_switch.go b/domain/consensus/processes/coinbasemanager/block_reward_switch.go new file mode 100644 index 0000000000..d48dae9f7d --- /dev/null +++ b/domain/consensus/processes/coinbasemanager/block_reward_switch.go @@ -0,0 +1,86 @@ +package coinbasemanager + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "math/big" +) + +func (c *coinbaseManager) isBlockRewardFixed(stagingArea *model.StagingArea, blockPruningPoint *externalapi.DomainHash) (bool, error) { + blockPruningPointIndex, found, err := c.findPruningPointIndex(stagingArea, blockPruningPoint) + if err != nil { + return false, err + } + + // The given `pruningPointBlock` may only not be found under one circumstance: + // we're currently in the process of building the next pruning point. As such, + // we must manually set highIndex to currentIndex + 1 because the next pruning + // point is not yet stored in the database + highPruningPointIndex := blockPruningPointIndex + highPruningPointHash := blockPruningPoint + if !found { + currentPruningPointIndex, err := c.pruningStore.CurrentPruningPointIndex(c.databaseContext, stagingArea) + if err != nil { + return false, err + } + highPruningPointIndex = currentPruningPointIndex + 1 + } + + for { + if highPruningPointIndex <= c.fixedSubsidySwitchPruningPointInterval { + break + } + + lowPruningPointIndex := highPruningPointIndex - c.fixedSubsidySwitchPruningPointInterval + lowPruningPointHash, err := c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, lowPruningPointIndex) + if err != nil { + return false, err + } + + highPruningPointHeader, err := c.blockHeaderStore.BlockHeader(c.databaseContext, stagingArea, highPruningPointHash) + if err != nil { + return false, err + } + lowPruningPointHeader, err := c.blockHeaderStore.BlockHeader(c.databaseContext, stagingArea, lowPruningPointHash) + if err != nil { + return false, err + } + + blueWorkDifference := new(big.Int).Sub(highPruningPointHeader.BlueWork(), lowPruningPointHeader.BlueWork()) + blueScoreDifference := new(big.Int).SetUint64(highPruningPointHeader.BlueScore() - lowPruningPointHeader.BlueScore()) + estimatedAverageHashRate := new(big.Int).Div(blueWorkDifference, blueScoreDifference) + if estimatedAverageHashRate.Cmp(c.fixedSubsidySwitchHashRateDifference) >= 0 { + return true, nil + } + + highPruningPointIndex-- + highPruningPointHash, err = c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, highPruningPointIndex) + if err != nil { + return false, err + } + } + + return false, nil +} + +func (c *coinbaseManager) findPruningPointIndex(stagingArea *model.StagingArea, pruningPointHash *externalapi.DomainHash) (uint64, bool, error) { + currentPruningPointHash, err := c.pruningStore.PruningPoint(c.databaseContext, stagingArea) + if err != nil { + return 0, false, err + } + currentPruningPointIndex, err := c.pruningStore.CurrentPruningPointIndex(c.databaseContext, stagingArea) + if err != nil { + return 0, false, err + } + for !currentPruningPointHash.Equal(pruningPointHash) && currentPruningPointIndex > 0 { + currentPruningPointIndex-- + currentPruningPointHash, err = c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, currentPruningPointIndex) + if err != nil { + return 0, false, err + } + } + if currentPruningPointIndex == 0 && !currentPruningPointHash.Equal(pruningPointHash) { + return 0, false, nil + } + return currentPruningPointIndex, true, nil +} diff --git a/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go b/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go new file mode 100644 index 0000000000..cba6c62bc9 --- /dev/null +++ b/domain/consensus/processes/coinbasemanager/block_reward_switch_test.go @@ -0,0 +1,121 @@ +package coinbasemanager_test + +import ( + "github.com/kaspanet/kaspad/domain/consensus" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" + "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + "github.com/kaspanet/kaspad/domain/consensus/utils/testutils" + "github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper" + "github.com/kaspanet/kaspad/util/difficulty" + "testing" + "time" +) + +func TestBlockRewardSwitch(t *testing.T) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + // Set the pruning depth to 10 + consensusConfig.MergeSetSizeLimit = 1 + consensusConfig.K = 1 + consensusConfig.FinalityDuration = 1 * time.Second + consensusConfig.TargetTimePerBlock = 1 * time.Second + + // Disable difficulty adjustment so that we could reason about blue work + consensusConfig.DisableDifficultyAdjustment = true + + // Disable pruning so that we could have access to all the blocks + consensusConfig.IsArchival = true + + // Set the interval to 10 + consensusConfig.FixedSubsidySwitchPruningPointInterval = 10 + + // Set the hash rate difference such that the switch would trigger exactly + // on the `FixedSubsidySwitchPruningPointInterval + 1`th pruning point + workToAcceptGenesis := difficulty.CalcWork(consensusConfig.GenesisBlock.Header.Bits()) + consensusConfig.FixedSubsidySwitchHashRateDifference = workToAcceptGenesis + + // Set the min, max, and post-switch subsidies to values that would make it + // easy to tell whether the switch happened + consensusConfig.MinSubsidy = 2 * constants.SompiPerKaspa + consensusConfig.MaxSubsidy = 2 * constants.SompiPerKaspa + consensusConfig.SubsidyGenesisReward = 1 * constants.SompiPerKaspa + + factory := consensus.NewFactory() + tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockRewardSwitch") + if err != nil { + t.Fatalf("Error setting up consensus: %+v", err) + } + defer teardown(false) + + // Make the pruning point move FixedSubsidySwitchPruningPointInterval times + tipHash := consensusConfig.GenesisHash + for i := uint64(0); i < consensusConfig.PruningDepth()+consensusConfig.FixedSubsidySwitchPruningPointInterval; i++ { + addedBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + tipHash = addedBlockHash + } + + // We expect to see `FixedSubsidySwitchPruningPointInterval` pruning points + the genesis + pruningPointHeaders, err := tc.PruningPointHeaders() + if err != nil { + t.Fatalf("PruningPointHeaders: %+v", pruningPointHeaders) + } + expectedPruningPointHeaderAmount := consensusConfig.FixedSubsidySwitchPruningPointInterval + 1 + if uint64(len(pruningPointHeaders)) != expectedPruningPointHeaderAmount { + t.Fatalf("Unexpected amount of pruning point headers. "+ + "Want: %d, got: %d", expectedPruningPointHeaderAmount, len(pruningPointHeaders)) + } + + // Make sure that all the headers thus far had a non-fixed subsidies + // Note that we skip the genesis, since that always has the post-switch + // value + for _, pruningPointHeader := range pruningPointHeaders[1:] { + pruningPointHash := consensushashing.HeaderHash(pruningPointHeader) + pruningPoint, err := tc.GetBlock(pruningPointHash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } + pruningPointCoinbase := pruningPoint.Transactions[transactionhelper.CoinbaseTransactionIndex] + _, _, subsidy, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(pruningPointCoinbase) + if err != nil { + t.Fatalf("ExtractCoinbaseDataBlueScoreAndSubsidy: %+v", err) + } + if subsidy != consensusConfig.MinSubsidy { + 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 85af8f62a0..b642b86e37 100644 --- a/domain/consensus/processes/coinbasemanager/coinbasemanager.go +++ b/domain/consensus/processes/coinbasemanager/coinbasemanager.go @@ -21,6 +21,8 @@ type coinbaseManager struct { subsidyMergeSetRewardMultiplier *big.Rat coinbasePayloadScriptPublicKeyMaxLength uint8 genesisHash *externalapi.DomainHash + fixedSubsidySwitchPruningPointInterval uint64 + fixedSubsidySwitchHashRateDifference *big.Int databaseContext model.DBReader dagTraversalManager model.DAGTraversalManager @@ -28,10 +30,12 @@ type coinbaseManager struct { acceptanceDataStore model.AcceptanceDataStore daaBlocksStore model.DAABlocksStore blockStore model.BlockStore + pruningStore model.PruningStore + blockHeaderStore model.BlockHeaderStore } func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, - coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) { + coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error) { ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, true) if !database.IsNotFoundError(err) && err != nil { @@ -79,7 +83,7 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.Staging txOuts = append(txOuts, txOut) } - subsidy, err := c.CalcBlockSubsidy(stagingArea, blockHash) + subsidy, err := c.CalcBlockSubsidy(stagingArea, blockHash, blockPruningPoint) if err != nil { return nil, err } @@ -179,11 +183,21 @@ func acceptanceDataFromArrayToMap(acceptanceData externalapi.AcceptanceData) map // has the expected value. // // Further details: https://hashdag.medium.com/kaspa-launch-plan-9a63f4d754a6 -func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (uint64, error) { +func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea, + blockHash *externalapi.DomainHash, blockPruningPoint *externalapi.DomainHash) (uint64, error) { + if blockHash.Equal(c.genesisHash) { 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 @@ -349,12 +363,16 @@ func New( subsidyMergeSetRewardMultiplier *big.Rat, coinbasePayloadScriptPublicKeyMaxLength uint8, genesisHash *externalapi.DomainHash, + fixedSubsidySwitchPruningPointInterval uint64, + fixedSubsidySwitchHashRateDifference *big.Int, dagTraversalManager model.DAGTraversalManager, ghostdagDataStore model.GHOSTDAGDataStore, acceptanceDataStore model.AcceptanceDataStore, daaBlocksStore model.DAABlocksStore, - blockStore model.BlockStore) model.CoinbaseManager { + blockStore model.BlockStore, + pruningStore model.PruningStore, + blockHeaderStore model.BlockHeaderStore) model.CoinbaseManager { return &coinbaseManager{ databaseContext: databaseContext, @@ -366,11 +384,15 @@ func New( subsidyMergeSetRewardMultiplier: subsidyMergeSetRewardMultiplier, coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength, genesisHash: genesisHash, + fixedSubsidySwitchPruningPointInterval: fixedSubsidySwitchPruningPointInterval, + fixedSubsidySwitchHashRateDifference: fixedSubsidySwitchHashRateDifference, dagTraversalManager: dagTraversalManager, ghostdagDataStore: ghostdagDataStore, acceptanceDataStore: acceptanceDataStore, daaBlocksStore: daaBlocksStore, blockStore: blockStore, + pruningStore: pruningStore, + blockHeaderStore: blockHeaderStore, } } diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go index 490989a1e3..828245fbad 100644 --- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go @@ -162,8 +162,12 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model } log.Tracef("Calculating the expected coinbase transaction for the given coinbase data and block %s", blockHash) + header, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, stagingArea, blockHash) + if err != nil { + return err + } expectedCoinbaseTransaction, err := - csm.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, blockHash, coinbaseData) + csm.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, blockHash, coinbaseData, header.PruningPoint()) if err != nil { return err } diff --git a/domain/dagconfig/consensus_defaults.go b/domain/dagconfig/consensus_defaults.go index 3ca727fc91..e7d7d36015 100644 --- a/domain/dagconfig/consensus_defaults.go +++ b/domain/dagconfig/consensus_defaults.go @@ -46,11 +46,13 @@ const ( // Should be at least an order of magnitude smaller than defaultFinalityDuration/defaultTargetTimePerBlock. // (Higher values make pruning attacks easier by a constant, lower values make merging after a split or a spike // in block take longer) - defaultMergeSetSizeLimit = defaultGHOSTDAGK * 10 - defaultSubsidyGenesisReward = 1 * constants.SompiPerKaspa - defaultMinSubsidy = 1 * constants.SompiPerKaspa - defaultMaxSubsidy = 1000 * constants.SompiPerKaspa - defaultCoinbasePayloadScriptPublicKeyMaxLength = 150 + defaultMergeSetSizeLimit = defaultGHOSTDAGK * 10 + defaultSubsidyGenesisReward = 1 * constants.SompiPerKaspa + defaultMinSubsidy = 1 * constants.SompiPerKaspa + defaultMaxSubsidy = 1000 * constants.SompiPerKaspa + defaultBaseSubsidy = 50 * constants.SompiPerKaspa + defaultFixedSubsidySwitchPruningPointInterval uint64 = 7 + defaultCoinbasePayloadScriptPublicKeyMaxLength = 150 // defaultDifficultyAdjustmentWindowSize is the number of blocks in a block's past used to calculate its difficulty // target. // The DAA should take the median of 2640 blocks, so in order to do that we need 2641 window size. diff --git a/domain/dagconfig/params.go b/domain/dagconfig/params.go index b7198c0f35..49cf0684ce 100644 --- a/domain/dagconfig/params.go +++ b/domain/dagconfig/params.go @@ -179,6 +179,13 @@ type Params struct { // PruningProofM is the 'm' constant in the pruning proof. For more details see: https://github.com/kaspanet/research/issues/3 PruningProofM uint64 + + // BaseSubsidy is the starting subsidy amount for mined blocks. + BaseSubsidy uint64 + + FixedSubsidySwitchPruningPointInterval uint64 + + FixedSubsidySwitchHashRateDifference *big.Int } // NormalizeRPCServerAddress returns addr with the current network default @@ -254,6 +261,8 @@ var MainnetParams = Params{ MergeSetSizeLimit: defaultMergeSetSizeLimit, CoinbasePayloadScriptPublicKeyMaxLength: defaultCoinbasePayloadScriptPublicKeyMaxLength, PruningProofM: defaultPruningProofM, + FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, + FixedSubsidySwitchHashRateDifference: big.NewInt(1_000_000), } // TestnetParams defines the network parameters for the test Kaspa network. @@ -314,6 +323,8 @@ var TestnetParams = Params{ MergeSetSizeLimit: defaultMergeSetSizeLimit, CoinbasePayloadScriptPublicKeyMaxLength: defaultCoinbasePayloadScriptPublicKeyMaxLength, PruningProofM: defaultPruningProofM, + FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, + FixedSubsidySwitchHashRateDifference: big.NewInt(1_000_000), } // SimnetParams defines the network parameters for the simulation test Kaspa @@ -378,6 +389,8 @@ var SimnetParams = Params{ MergeSetSizeLimit: defaultMergeSetSizeLimit, CoinbasePayloadScriptPublicKeyMaxLength: defaultCoinbasePayloadScriptPublicKeyMaxLength, PruningProofM: defaultPruningProofM, + FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, + FixedSubsidySwitchHashRateDifference: big.NewInt(1_000_000), } // DevnetParams defines the network parameters for the development Kaspa network. @@ -438,6 +451,8 @@ var DevnetParams = Params{ MergeSetSizeLimit: defaultMergeSetSizeLimit, CoinbasePayloadScriptPublicKeyMaxLength: defaultCoinbasePayloadScriptPublicKeyMaxLength, PruningProofM: defaultPruningProofM, + FixedSubsidySwitchPruningPointInterval: defaultFixedSubsidySwitchPruningPointInterval, + FixedSubsidySwitchHashRateDifference: big.NewInt(1_000_000), } var (