mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 13:00:10 +00:00

* [NOD-1098] Change timestamps to be millisecond precision * [NOD-1098] Change lock times to use milliseconds * [NOD-1098] Use milliseconds precision everywhere * [NOD-1098] Implement type mstime.Time * [NOD-1098] Fix block 100000 timestamp * [NOD-1098] Change orphan child to be one millisecond delay after its parent * [NOD-1098] Remove test that checks if header timestamps have the right precision, and instead add tests for mstime, and fix genesis for testnet and devnet * [NOD-1098] Fix comment * [NOD-1098] Fix comment * [NOD-1098] Fix testnet genesis * [NOD-1098] Rename UnixMilli->UnixMilliseconds
221 lines
6.5 KiB
Go
221 lines
6.5 KiB
Go
// Copyright (c) 2014-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockdag
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/dagconfig"
|
|
"github.com/kaspanet/kaspad/util/mstime"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
// TestBigToCompact ensures BigToCompact converts big integers to the expected
|
|
// compact representation.
|
|
func TestBigToCompact(t *testing.T) {
|
|
tests := []struct {
|
|
in int64
|
|
out uint32
|
|
}{
|
|
{0, 0},
|
|
{-1, 25231360},
|
|
}
|
|
|
|
for x, test := range tests {
|
|
n := big.NewInt(test.in)
|
|
r := util.BigToCompact(n)
|
|
if r != test.out {
|
|
t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n",
|
|
x, r, test.out)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCompactToBig ensures CompactToBig converts numbers using the compact
|
|
// representation to the expected big intergers.
|
|
func TestCompactToBig(t *testing.T) {
|
|
tests := []struct {
|
|
in uint32
|
|
out int64
|
|
}{
|
|
{10000000, 0},
|
|
}
|
|
|
|
for x, test := range tests {
|
|
n := util.CompactToBig(test.in)
|
|
want := big.NewInt(test.out)
|
|
if n.Cmp(want) != 0 {
|
|
t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n",
|
|
x, n.Int64(), want.Int64())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCalcWork ensures CalcWork calculates the expected work value from values
|
|
// in compact representation.
|
|
func TestCalcWork(t *testing.T) {
|
|
tests := []struct {
|
|
in uint32
|
|
out int64
|
|
}{
|
|
{10000000, 0},
|
|
}
|
|
|
|
for x, test := range tests {
|
|
bits := uint32(test.in)
|
|
|
|
r := util.CalcWork(bits)
|
|
if r.Int64() != test.out {
|
|
t.Errorf("TestCalcWork test #%d failed: got %v want %d\n",
|
|
x, r.Int64(), test.out)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDifficulty(t *testing.T) {
|
|
params := dagconfig.SimnetParams
|
|
params.K = 1
|
|
params.DifficultyAdjustmentWindowSize = 264
|
|
dag, teardownFunc, err := DAGSetup("TestDifficulty", true, Config{
|
|
DAGParams: ¶ms,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to setup DAG instance: %v", err)
|
|
}
|
|
defer teardownFunc()
|
|
|
|
zeroTime := mstime.Time{}
|
|
addNode := func(parents blockSet, blockTime mstime.Time) *blockNode {
|
|
bluestParent := parents.bluest()
|
|
if blockTime.IsZero() {
|
|
blockTime = bluestParent.time()
|
|
blockTime = blockTime.Add(params.TargetTimePerBlock)
|
|
}
|
|
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error in PrepareBlockForTest: %s", err)
|
|
}
|
|
block.Header.Timestamp = blockTime
|
|
block.Header.Bits = dag.requiredDifficulty(bluestParent, blockTime)
|
|
|
|
isOrphan, isDelayed, err := dag.ProcessBlock(util.NewBlock(block), BFNoPoWCheck)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error in ProcessBlock: %s", err)
|
|
}
|
|
if isDelayed {
|
|
t.Fatalf("block is too far in the future")
|
|
}
|
|
if isOrphan {
|
|
t.Fatalf("block was unexpectedly orphan")
|
|
}
|
|
node, ok := dag.index.LookupNode(block.BlockHash())
|
|
if !ok {
|
|
t.Fatalf("block %s does not exist in the DAG", block.BlockHash())
|
|
}
|
|
return node
|
|
}
|
|
tip := dag.genesis
|
|
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize; i++ {
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if tip.bits != dag.genesis.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 := uint64(0); i < dag.difficultyAdjustmentWindowSize+100; i++ {
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if tip.bits != dag.genesis.bits {
|
|
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
|
|
}
|
|
}
|
|
nodeInThePast := addNode(blockSetFromSlice(tip), tip.PastMedianTime(dag))
|
|
if nodeInThePast.bits != tip.bits {
|
|
t.Fatalf("The difficulty should only change when nodeInThePast is in the past of a block bluest parent")
|
|
}
|
|
tip = nodeInThePast
|
|
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if tip.bits != nodeInThePast.bits {
|
|
t.Fatalf("The difficulty should only change when nodeInThePast is in the past of a block bluest parent")
|
|
}
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if compareBits(tip.bits, nodeInThePast.bits) >= 0 {
|
|
t.Fatalf("tip.bits should be smaller than nodeInThePast.bits because nodeInThePast increased the " +
|
|
"block rate, so the difficulty should increase as well")
|
|
}
|
|
expectedBits := uint32(0x207f83df)
|
|
if tip.bits != expectedBits {
|
|
t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.bits)
|
|
}
|
|
|
|
// Increase block rate to increase difficulty
|
|
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize; i++ {
|
|
tip = addNode(blockSetFromSlice(tip), tip.PastMedianTime(dag))
|
|
if compareBits(tip.bits, tip.parents.bluest().bits) > 0 {
|
|
t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
|
|
}
|
|
}
|
|
|
|
// Add blocks until difficulty stabilizes
|
|
lastBits := tip.bits
|
|
sameBitsCount := uint64(0)
|
|
for sameBitsCount < dag.difficultyAdjustmentWindowSize+1 {
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if tip.bits == lastBits {
|
|
sameBitsCount++
|
|
} else {
|
|
lastBits = tip.bits
|
|
sameBitsCount = 0
|
|
}
|
|
}
|
|
slowBlockTime := tip.time()
|
|
slowBlockTime = slowBlockTime.Add(params.TargetTimePerBlock + time.Second)
|
|
slowNode := addNode(blockSetFromSlice(tip), slowBlockTime)
|
|
if slowNode.bits != tip.bits {
|
|
t.Fatalf("The difficulty should only change when slowNode is in the past of a block bluest parent")
|
|
}
|
|
|
|
tip = slowNode
|
|
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if tip.bits != slowNode.bits {
|
|
t.Fatalf("The difficulty should only change when slowNode is in the past of a block bluest parent")
|
|
}
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
if compareBits(tip.bits, slowNode.bits) <= 0 {
|
|
t.Fatalf("tip.bits should be smaller than slowNode.bits because slowNode decreased the block" +
|
|
" rate, so the difficulty should decrease as well")
|
|
}
|
|
|
|
splitNode := addNode(blockSetFromSlice(tip), zeroTime)
|
|
tip = splitNode
|
|
for i := 0; i < 100; i++ {
|
|
tip = addNode(blockSetFromSlice(tip), zeroTime)
|
|
}
|
|
blueTip := tip
|
|
|
|
redChainTip := splitNode
|
|
for i := 0; i < 10; i++ {
|
|
redChainTip = addNode(blockSetFromSlice(redChainTip), redChainTip.PastMedianTime(dag))
|
|
}
|
|
tipWithRedPast := addNode(blockSetFromSlice(redChainTip, blueTip), zeroTime)
|
|
tipWithoutRedPast := addNode(blockSetFromSlice(blueTip), zeroTime)
|
|
if tipWithoutRedPast.bits != tipWithRedPast.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)
|
|
}
|