mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-22 23:07:04 +00:00

* Change DifficultyAdjustmentWindowSize and TimestampDeviationTolerance from uint64 to int * refactor block_heap for readability and usage * Add a new SizedUpHeap * Refactor BlueWindow with the new DAA * Update TestBlueBlockWindow with the new DAA window * Fix review requested changes
207 lines
6.7 KiB
Go
207 lines
6.7 KiB
Go
package difficultymanager_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
func TestDifficulty(t *testing.T) {
|
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
|
if params.DisableDifficultyAdjustment {
|
|
return
|
|
}
|
|
params.K = 1
|
|
params.DifficultyAdjustmentWindowSize = 264
|
|
|
|
factory := consensus.NewFactory()
|
|
tc, teardown, err := factory.NewTestConsensus(params, "TestDifficulty")
|
|
if err != nil {
|
|
t.Fatalf("Error setting up consensus: %+v", err)
|
|
}
|
|
defer teardown()
|
|
|
|
addBlock := func(blockTime int64, parents ...*externalapi.DomainHash) (*externalapi.DomainBlock, *externalapi.DomainHash) {
|
|
bluestParent, err := tc.GHOSTDAGManager().ChooseSelectedParent(parents...)
|
|
if err != nil {
|
|
t.Fatalf("ChooseSelectedParent: %+v", err)
|
|
}
|
|
|
|
if blockTime == 0 {
|
|
header, err := tc.BlockHeaderStore().BlockHeader(tc.DatabaseContext(), bluestParent)
|
|
if err != nil {
|
|
t.Fatalf("BlockHeader: %+v", err)
|
|
}
|
|
|
|
blockTime = header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds()
|
|
}
|
|
|
|
block, _, err := tc.BuildBlockWithParents(parents, nil, nil)
|
|
if err != nil {
|
|
t.Fatalf("BuildBlockWithParents: %+v", err)
|
|
}
|
|
|
|
block.Header.TimeInMilliseconds = blockTime
|
|
err = tc.ValidateAndInsertBlock(block)
|
|
if err != nil {
|
|
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
|
}
|
|
|
|
return block, consensushashing.BlockHash(block)
|
|
}
|
|
|
|
minimumTime := func(parents ...*externalapi.DomainHash) int64 {
|
|
var tempHash externalapi.DomainHash
|
|
tc.BlockRelationStore().StageBlockRelation(&tempHash, &model.BlockRelations{
|
|
Parents: parents,
|
|
Children: nil,
|
|
})
|
|
defer tc.BlockRelationStore().Discard()
|
|
|
|
err = tc.GHOSTDAGManager().GHOSTDAG(&tempHash)
|
|
if err != nil {
|
|
t.Fatalf("GHOSTDAG: %+v", err)
|
|
}
|
|
defer tc.GHOSTDAGDataStore().Discard()
|
|
|
|
pastMedianTime, err := tc.PastMedianTimeManager().PastMedianTime(&tempHash)
|
|
if err != nil {
|
|
t.Fatalf("PastMedianTime: %+v", err)
|
|
}
|
|
|
|
return pastMedianTime + 1
|
|
}
|
|
|
|
addBlockWithMinimumTime := func(parents ...*externalapi.DomainHash) (*externalapi.DomainBlock, *externalapi.DomainHash) {
|
|
minTime := minimumTime(parents...)
|
|
return addBlock(minTime, parents...)
|
|
}
|
|
|
|
tipHash := params.GenesisHash
|
|
tip := params.GenesisBlock
|
|
for i := 0; i < params.DifficultyAdjustmentWindowSize; i++ {
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if tip.Header.Bits != params.GenesisBlock.Header.Bits {
|
|
t.Fatalf("As long as the bluest parent's blue score is less then the difficulty adjustment " +
|
|
"window size, the difficulty should be the same as genesis'")
|
|
}
|
|
}
|
|
for i := 0; i < params.DifficultyAdjustmentWindowSize+100; i++ {
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if tip.Header.Bits != params.GenesisBlock.Header.Bits {
|
|
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
|
|
}
|
|
}
|
|
|
|
blockInThePast, tipHash := addBlockWithMinimumTime(tipHash)
|
|
if blockInThePast.Header.Bits != tip.Header.Bits {
|
|
t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
|
|
}
|
|
tip = blockInThePast
|
|
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if tip.Header.Bits != blockInThePast.Header.Bits {
|
|
t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
|
|
}
|
|
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if compareBits(tip.Header.Bits, blockInThePast.Header.Bits) >= 0 {
|
|
t.Fatalf("tip.bits should be smaller than blockInThePast.bits because blockInThePast increased the " +
|
|
"block rate, so the difficulty should increase as well")
|
|
}
|
|
|
|
var expectedBits uint32
|
|
switch params.Name {
|
|
case "kaspa-testnet", "kaspa-devnet":
|
|
expectedBits = uint32(0x1e7f83df)
|
|
case "kaspa-mainnet":
|
|
expectedBits = uint32(0x207f83df)
|
|
}
|
|
|
|
if tip.Header.Bits != expectedBits {
|
|
t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.Header.Bits)
|
|
}
|
|
|
|
// Increase block rate to increase difficulty
|
|
for i := 0; i < params.DifficultyAdjustmentWindowSize; i++ {
|
|
tip, tipHash = addBlockWithMinimumTime(tipHash)
|
|
tipGHOSTDAGData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), tipHash)
|
|
if err != nil {
|
|
t.Fatalf("GHOSTDAGDataStore: %+v", err)
|
|
}
|
|
|
|
selectedParentHeader, err := tc.BlockHeaderStore().BlockHeader(tc.DatabaseContext(),
|
|
tipGHOSTDAGData.SelectedParent())
|
|
if err != nil {
|
|
t.Fatalf("BlockHeader: %+v", err)
|
|
}
|
|
|
|
if compareBits(tip.Header.Bits, selectedParentHeader.Bits) > 0 {
|
|
t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
|
|
}
|
|
}
|
|
|
|
// Add blocks until difficulty stabilizes
|
|
lastBits := tip.Header.Bits
|
|
sameBitsCount := 0
|
|
for sameBitsCount < params.DifficultyAdjustmentWindowSize+1 {
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if tip.Header.Bits == lastBits {
|
|
sameBitsCount++
|
|
} else {
|
|
lastBits = tip.Header.Bits
|
|
sameBitsCount = 0
|
|
}
|
|
}
|
|
|
|
slowBlockTime := tip.Header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds() + 1000
|
|
slowBlock, tipHash := addBlock(slowBlockTime, tipHash)
|
|
if slowBlock.Header.Bits != tip.Header.Bits {
|
|
t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
|
|
}
|
|
|
|
tip = slowBlock
|
|
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if tip.Header.Bits != slowBlock.Header.Bits {
|
|
t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
|
|
}
|
|
tip, tipHash = addBlock(0, tipHash)
|
|
if compareBits(tip.Header.Bits, slowBlock.Header.Bits) <= 0 {
|
|
t.Fatalf("tip.bits should be smaller than slowBlock.bits because slowBlock decreased the block" +
|
|
" rate, so the difficulty should decrease as well")
|
|
}
|
|
|
|
_, tipHash = addBlock(0, tipHash)
|
|
splitBlockHash := tipHash
|
|
for i := 0; i < 100; i++ {
|
|
_, tipHash = addBlock(0, tipHash)
|
|
}
|
|
blueTipHash := tipHash
|
|
|
|
redChainTipHash := splitBlockHash
|
|
for i := 0; i < 10; i++ {
|
|
_, redChainTipHash = addBlockWithMinimumTime(redChainTipHash)
|
|
}
|
|
tipWithRedPast, _ := addBlock(0, redChainTipHash, blueTipHash)
|
|
tipWithoutRedPast, _ := addBlock(0, blueTipHash)
|
|
if tipWithoutRedPast.Header.Bits != tipWithRedPast.Header.Bits {
|
|
t.Fatalf("tipWithoutRedPast.bits should be the same as tipWithRedPast.bits because red blocks" +
|
|
" shouldn't affect the difficulty")
|
|
}
|
|
})
|
|
}
|
|
|
|
func compareBits(a uint32, b uint32) int {
|
|
aTarget := util.CompactToBig(a)
|
|
bTarget := util.CompactToBig(b)
|
|
return aTarget.Cmp(bTarget)
|
|
}
|