Fix checkParentHeadersExist and cover pruning_violation_proof_of_work_and_difficulty.go with tests (#1418)

* Fix checkParentHeadersExist and cover pruning_violation_proof_of_work_and_difficulty.go with tests

* Remove unused variable

* Change consensus violation

* Change condition order

* Get rid of irrelevant error codes in extractRejectCode

* Fix wrong test db names

* Fix checkParentHeadersExist
This commit is contained in:
Ori Newman 2021-01-17 11:27:04 +02:00 committed by GitHub
parent 67be4d82bf
commit dd57e6abe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 357 additions and 400 deletions

View File

@ -1,70 +0,0 @@
package blockvalidator_test
import (
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/mining"
"github.com/kaspanet/kaspad/util/difficulty"
"math"
"math/rand"
"testing"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
)
// TestPOW tests the validation of the block's POW.
func TestPOW(t *testing.T) {
// We set the flag "skip pow" to be false (second argument in the function) for not skipping the check of POW and validate its correctness.
testutils.ForAllNets(t, false, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, false, "TestPOW")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
// Builds and checks block with invalid POW.
invalidBlockWrongPOW, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
invalidBlockWrongPOW = solveBlockWithWrongPOW(invalidBlockWrongPOW)
_, err = tc.ValidateAndInsertBlock(invalidBlockWrongPOW)
if !errors.Is(err, ruleerrors.ErrInvalidPoW) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrInvalidPoW, err)
}
// test on a valid block.
validBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
random := rand.New(rand.NewSource(0))
mining.SolveBlock(validBlock, random)
_, err = tc.ValidateAndInsertBlock(validBlock)
if err != nil {
t.Fatal(err)
}
})
}
// solveBlockWithWrongPOW increments the given block's nonce until it gets wrong POW (for test!).
func solveBlockWithWrongPOW(block *externalapi.DomainBlock) *externalapi.DomainBlock {
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
headerForMining := block.Header.ToMutable()
initialNonce := uint64(0)
for i := initialNonce; i < math.MaxUint64; i++ {
headerForMining.SetNonce(i)
if !pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
block.Header = headerForMining.ToImmutable()
return block
}
}
panic("Failed to solve block! cannot find a invalid POW for the test")
}

View File

@ -5,6 +5,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
@ -83,13 +84,13 @@ func (v *blockValidator) checkProofOfWork(header externalapi.BlockHeader) error
// The target difficulty must be larger than zero.
target := difficulty.CompactToBig(header.Bits())
if target.Sign() <= 0 {
return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block target difficulty of %064x is too low",
return errors.Wrapf(ruleerrors.ErrNegativeTarget, "block target difficulty of %064x is too low",
target)
}
// The target difficulty must be less than the maximum allowed.
if target.Cmp(v.powMax) > 0 {
return errors.Wrapf(ruleerrors.ErrUnexpectedDifficulty, "block target difficulty of %064x is "+
return errors.Wrapf(ruleerrors.ErrTargetTooHigh, "block target difficulty of %064x is "+
"higher than max of %064x", target, v.powMax)
}
@ -111,18 +112,18 @@ func (v *blockValidator) checkParentHeadersExist(header externalapi.BlockHeader)
return err
}
if !parentHeaderExists {
parentStatus, err := v.blockStatusStore.Get(v.databaseContext, parent)
if err != nil {
if !database.IsNotFoundError(err) {
return err
}
} else if parentStatus == externalapi.StatusInvalid {
return errors.Wrapf(ruleerrors.ErrInvalidAncestorBlock, "parent %s is invalid", parent)
}
missingParentHashes = append(missingParentHashes, parent)
continue
}
parentStatus, err := v.blockStatusStore.Get(v.databaseContext, parent)
if err != nil {
return err
}
if parentStatus == externalapi.StatusInvalid {
return errors.Wrapf(ruleerrors.ErrInvalidAncestorBlock, "parent %s is invalid", parent)
}
}
if len(missingParentHashes) > 0 {

View File

@ -0,0 +1,243 @@
package blockvalidator_test
import (
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
"github.com/kaspanet/kaspad/domain/consensus/utils/mining"
"github.com/kaspanet/kaspad/util/difficulty"
"math"
"math/big"
"math/rand"
"testing"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
)
// TestPOW tests the validation of the block's POW.
func TestPOW(t *testing.T) {
// We set the flag "skip pow" to be false (second argument in the function) for not skipping the check of POW and validate its correctness.
testutils.ForAllNets(t, false, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, false, "TestPOW")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
// Builds and checks block with invalid POW.
invalidBlockWrongPOW, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
invalidBlockWrongPOW = solveBlockWithWrongPOW(invalidBlockWrongPOW)
_, err = tc.ValidateAndInsertBlock(invalidBlockWrongPOW)
if !errors.Is(err, ruleerrors.ErrInvalidPoW) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrInvalidPoW, err)
}
abovePowMaxBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
abovePowMaxTarget := big.NewInt(0).Add(big.NewInt(1), params.PowMax)
abovePowMaxBlock.Header = blockheader.NewImmutableBlockHeader(
abovePowMaxBlock.Header.Version(),
abovePowMaxBlock.Header.ParentHashes(),
abovePowMaxBlock.Header.HashMerkleRoot(),
abovePowMaxBlock.Header.AcceptedIDMerkleRoot(),
abovePowMaxBlock.Header.UTXOCommitment(),
abovePowMaxBlock.Header.TimeInMilliseconds(),
difficulty.BigToCompact(abovePowMaxTarget),
abovePowMaxBlock.Header.Nonce(),
)
_, err = tc.ValidateAndInsertBlock(abovePowMaxBlock)
if !errors.Is(err, ruleerrors.ErrTargetTooHigh) {
t.Fatalf("Unexpected error: %+v", err)
}
negativeTargetBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
negativeTargetBlock.Header = blockheader.NewImmutableBlockHeader(
negativeTargetBlock.Header.Version(),
negativeTargetBlock.Header.ParentHashes(),
negativeTargetBlock.Header.HashMerkleRoot(),
negativeTargetBlock.Header.AcceptedIDMerkleRoot(),
negativeTargetBlock.Header.UTXOCommitment(),
negativeTargetBlock.Header.TimeInMilliseconds(),
0x00800000,
negativeTargetBlock.Header.Nonce(),
)
_, err = tc.ValidateAndInsertBlock(negativeTargetBlock)
if !errors.Is(err, ruleerrors.ErrNegativeTarget) {
t.Fatalf("Unexpected error: %+v", err)
}
// test on a valid block.
validBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
random := rand.New(rand.NewSource(0))
mining.SolveBlock(validBlock, random)
_, err = tc.ValidateAndInsertBlock(validBlock)
if err != nil {
t.Fatal(err)
}
})
}
// solveBlockWithWrongPOW increments the given block's nonce until it gets wrong POW (for test!).
func solveBlockWithWrongPOW(block *externalapi.DomainBlock) *externalapi.DomainBlock {
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
headerForMining := block.Header.ToMutable()
initialNonce := uint64(0)
for i := initialNonce; i < math.MaxUint64; i++ {
headerForMining.SetNonce(i)
if !pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
block.Header = headerForMining.ToImmutable()
return block
}
}
panic("Failed to solve block! cannot find a invalid POW for the test")
}
func TestCheckParentHeadersExist(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentHeadersExist")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
orphanBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
parentHash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{}) // Non existing parent hash
orphanBlock.Header = blockheader.NewImmutableBlockHeader(
orphanBlock.Header.Version(),
[]*externalapi.DomainHash{
parentHash,
},
orphanBlock.Header.HashMerkleRoot(),
orphanBlock.Header.AcceptedIDMerkleRoot(),
orphanBlock.Header.UTXOCommitment(),
orphanBlock.Header.TimeInMilliseconds(),
orphanBlock.Header.Bits(),
orphanBlock.Header.Nonce(),
)
_, err = tc.ValidateAndInsertBlock(orphanBlock)
errMissingParents := &ruleerrors.ErrMissingParents{}
if !errors.As(err, errMissingParents) {
t.Fatalf("Unexpected error: %+v", err)
}
if !externalapi.HashesEqual(errMissingParents.MissingParentHashes, []*externalapi.DomainHash{parentHash}) {
t.Fatalf("unexpected missing parents %s", errMissingParents.MissingParentHashes)
}
invalidBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
invalidBlock.Transactions[0].Version = constants.MaxTransactionVersion + 1 // This should invalidate the block
invalidBlock.Header = blockheader.NewImmutableBlockHeader(
invalidBlock.Header.Version(),
invalidBlock.Header.ParentHashes(),
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions),
orphanBlock.Header.AcceptedIDMerkleRoot(),
orphanBlock.Header.UTXOCommitment(),
orphanBlock.Header.TimeInMilliseconds(),
orphanBlock.Header.Bits(),
orphanBlock.Header.Nonce(),
)
_, err = tc.ValidateAndInsertBlock(invalidBlock)
if !errors.Is(err, ruleerrors.ErrTransactionVersionIsUnknown) {
t.Fatalf("Unexpected error: %+v", err)
}
invalidBlockHash := consensushashing.BlockHash(invalidBlock)
invalidBlockChild, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
invalidBlockChild.Header = blockheader.NewImmutableBlockHeader(
invalidBlockChild.Header.Version(),
[]*externalapi.DomainHash{invalidBlockHash},
invalidBlockChild.Header.HashMerkleRoot(),
invalidBlockChild.Header.AcceptedIDMerkleRoot(),
invalidBlockChild.Header.UTXOCommitment(),
invalidBlockChild.Header.TimeInMilliseconds(),
invalidBlockChild.Header.Bits(),
invalidBlockChild.Header.Nonce(),
)
_, err = tc.ValidateAndInsertBlock(invalidBlockChild)
if !errors.Is(err, ruleerrors.ErrInvalidAncestorBlock) {
t.Fatalf("Unexpected error: %+v", err)
}
})
}
func TestCheckPruningPointViolation(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
// This is done to reduce the pruning depth to 6 blocks
params.FinalityDuration = 2 * params.TargetTimePerBlock
params.K = 0
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckPruningPointViolation")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
// Add blocks until the pruning point changes
tipHash := params.GenesisHash
for {
tipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
if err != nil {
t.Fatalf("AddBlock: %+v", err)
}
pruningPoint, err := tc.PruningPoint()
if err != nil {
t.Fatalf("PruningPoint: %+v", err)
}
if !pruningPoint.Equal(params.GenesisHash) {
break
}
}
_, _, err = tc.AddUTXOInvalidBlock([]*externalapi.DomainHash{params.GenesisHash})
if !errors.Is(err, ruleerrors.ErrPruningPointViolation) {
t.Fatalf("Unexpected error: %+v", err)
}
})
}

View File

@ -29,16 +29,20 @@ var (
// ErrNoParents indicates that the block is missing parents
ErrNoParents = newRuleError("ErrNoParents")
// ErrDifficultyTooLow indicates the difficulty for the block is lower
// than the difficulty required.
ErrDifficultyTooLow = newRuleError("ErrDifficultyTooLow")
// ErrUnexpectedDifficulty indicates specified bits do not align with
// the expected value either because it doesn't match the calculated
// valued based on difficulty regarted rules or it is out of the valid
// range.
// valued based on difficulty regarted rules.
ErrUnexpectedDifficulty = newRuleError("ErrUnexpectedDifficulty")
// ErrTargetTooHigh indicates specified bits do not align with
// the expected value either because it is above the valid
// range.
ErrTargetTooHigh = newRuleError("ErrTargetTooHigh")
// ErrUnexpectedDifficulty indicates specified bits do not align with
// the expected value either because it is negative.
ErrNegativeTarget = newRuleError("ErrNegativeTarget")
// ErrInvalidPoW indicates that the block proof-of-work is invalid.
ErrInvalidPoW = newRuleError("ErrInvalidPoW")

View File

@ -1,153 +0,0 @@
package math
import (
"math/big"
)
var (
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
// log2FloorMasks defines the masks to use when quickly calculating
// floor(log2(x)) in a constant log2(64) = 6 steps, where x is a uint64, using
// shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 5..0.
log2FloorMasks = []uint64{0xffffffff00000000, 0xffff0000, 0xff00, 0xf0, 0xc, 0x2}
)
// FastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps.
func FastLog2Floor(n uint64) uint8 {
rv := uint8(0)
exponent := uint8(32)
for i := 0; i < 6; i++ {
if n&log2FloorMasks[i] != 0 {
rv += exponent
n >>= exponent
}
exponent >>= 1
}
return rv
}
// CompactToBig converts a compact representation of a whole number N to an
// unsigned 32-bit number. The representation is similar to IEEE754 floating
// point numbers.
//
// Like IEEE754 floating point, there are three basic components: the sign,
// the exponent, and the mantissa. They are broken out as follows:
//
// * the most significant 8 bits represent the unsigned base 256 exponent
// * bit 23 (the 24th bit) represents the sign bit
// * the least significant 23 bits represent the mantissa
//
// -------------------------------------------------
// | Exponent | Sign | Mantissa |
// -------------------------------------------------
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
// -------------------------------------------------
//
// The formula to calculate N is:
// N = (-1^sign) * mantissa * 256^(exponent-3)
func CompactToBig(compact uint32) *big.Int {
destination := big.NewInt(0)
CompactToBigWithDestination(compact, destination)
return destination
}
// CompactToBigWithDestination is a version of CompactToBig that
// takes a destination parameter. This is useful for saving memory,
// as then the destination big.Int can be reused.
// See CompactToBig for further details.
func CompactToBigWithDestination(compact uint32, destination *big.Int) {
// Extract the mantissa, sign bit, and exponent.
mantissa := compact & 0x007fffff
isNegative := compact&0x00800000 != 0
exponent := uint(compact >> 24)
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes to represent the full 256-bit number. So,
// treat the exponent as the number of bytes and shift the mantissa
// right or left accordingly. This is equivalent to:
// N = mantissa * 256^(exponent-3)
if exponent <= 3 {
mantissa >>= 8 * (3 - exponent)
destination.SetInt64(int64(mantissa))
} else {
destination.SetInt64(int64(mantissa))
destination.Lsh(destination, 8*(exponent-3))
}
// Make it negative if the sign bit is set.
if isNegative {
destination.Neg(destination)
}
}
// BigToCompact converts a whole number N to a compact representation using
// an unsigned 32-bit number. The compact representation only provides 23 bits
// of precision, so values larger than (2^23 - 1) only encode the most
// significant digits of the number. See CompactToBig for details.
func BigToCompact(n *big.Int) uint32 {
// No need to do any work if it's zero.
if n.Sign() == 0 {
return 0
}
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes. So, shift the number right or left
// accordingly. This is equivalent to:
// mantissa = mantissa / 256^(exponent-3)
var mantissa uint32
exponent := uint(len(n.Bytes()))
if exponent <= 3 {
mantissa = uint32(n.Bits()[0])
mantissa <<= 8 * (3 - exponent)
} else {
// Use a copy to avoid modifying the caller's original number.
tn := new(big.Int).Set(n)
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
}
// When the mantissa already has the sign bit set, the number is too
// large to fit into the available 23-bits, so divide the number by 256
// and increment the exponent accordingly.
if mantissa&0x00800000 != 0 {
mantissa >>= 8
exponent++
}
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
// int and return it.
compact := uint32(exponent<<24) | mantissa
if n.Sign() < 0 {
compact |= 0x00800000
}
return compact
}
// CalcWork calculates a work value from difficulty bits. Kaspa increases
// the difficulty for generating a block by decreasing the value which the
// generated hash must be less than. This difficulty target is stored in each
// block header using a compact representation as described in the documentation
// for CompactToBig. Since a lower target difficulty value equates to higher
// actual difficulty, the work value which will be accumulated must be the
// inverse of the difficulty. Also, in order to avoid potential division by
// zero and really small floating point numbers, the result adds 1 to the
// denominator and multiplies the numerator by 2^256.
func CalcWork(bits uint32) *big.Int {
// Return a work value of zero if the passed difficulty bits represent
// a negative number. Note this should not happen in practice with valid
// blocks, but an invalid block could trigger it.
difficultyNum := CompactToBig(bits)
if difficultyNum.Sign() <= 0 {
return big.NewInt(0)
}
// (1 << 256) / (difficultyNum + 1)
denominator := new(big.Int).Add(difficultyNum, bigOne)
return new(big.Int).Div(oneLsh256, denominator)
}

View File

@ -1,123 +0,0 @@
package math
import (
"fmt"
"math"
"math/big"
"testing"
)
func TestFastLog2Floor(t *testing.T) {
tests := []struct {
n uint64
expectedResult uint8
}{
{1, 0},
{2, 1},
{3, 1},
{4, 2},
{5, 2},
{16, 4},
{31, 4},
{1684234, 20},
{4294967295, 31}, // math.MaxUint32 (2^32 - 1)
{4294967296, 32}, // 2^32
{4294967297, 32}, // 2^32 + 1
{4611686018427387904, 62},
{9223372036854775808, 63}, // 2^63
{18446744073709551615, 63}, // math.MaxUint64 (2^64 - 1).
}
for _, test := range tests {
actualResult := FastLog2Floor(test.n)
if test.expectedResult != actualResult {
t.Errorf("TestFastLog2Floor: %d: expected result: %d but got: %d", test.n, test.expectedResult, actualResult)
}
}
}
// TestBigToCompact ensures BigToCompact converts big integers to the expected
// compact representation.
func TestBigToCompact(t *testing.T) {
tests := []struct {
in string
out uint32
}{
{"0000000000000000000000000000000000000000000000000000000000000000", 0},
{"-1", 25231360},
{"9223372036854775807", 142606335},
{"922337203685477580712312312123487", 237861256},
{"128", 0x02008000},
}
for x, test := range tests {
n := new(big.Int)
n.SetString(test.in, 10)
r := 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 integers.
func TestCompactToBig(t *testing.T) {
tests := []struct {
before uint32
intHex string
after uint32
}{
{math.MaxUint32, "-7fffff000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000", math.MaxUint32},
{0x00000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x0989680, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x87fffff, "0000000000000000000000000000000000000000000000007fffff0000000000", 0x87fffff},
{0x1810000, "-000000000000000000000000000000000000000000000000000000000000001", 0x1810000},
{0xe2d7988, "0000000000000000000000000000000000002d79880000000000000000000000", 0xe2d7988},
{0x00123456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01003456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x02000056, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x03000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x04000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x00923456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01803456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x02800056, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x03800000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x04800000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01123456, "0000000000000000000000000000000000000000000000000000000000000012", 0x01120000},
{0x02008000, "0000000000000000000000000000000000000000000000000000000000000080", 0x02008000},
{0x01fedcba, "-00000000000000000000000000000000000000000000000000000000000007e", 0x01fe0000},
{0x02123456, "0000000000000000000000000000000000000000000000000000000000001234", 0x02123400},
{0x03123456, "0000000000000000000000000000000000000000000000000000000000123456", 0x03123456},
{0x04123456, "0000000000000000000000000000000000000000000000000000000012345600", 0x04123456},
{0x04923456, "-000000000000000000000000000000000000000000000000000000012345600", 0x04923456},
{0x05009234, "0000000000000000000000000000000000000000000000000000000092340000", 0x05009234},
{0x20123456, "1234560000000000000000000000000000000000000000000000000000000000", 0x20123456},
{0xff123456, "123456000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 0xff123456},
}
for i, test := range tests {
n := CompactToBig(test.before)
convertBack := BigToCompact(n)
got := fmt.Sprintf("%064x", n)
if got != test.intHex {
t.Errorf("TestCompactToBig test #%d failed: got %s want %s, input: 0x%08x",
i, got, test.intHex, test.before)
}
if convertBack != test.after {
t.Errorf("TestCompactToBig test #%d failed: got: 0x%08x want 0x%08x input: 0x%08x", i, convertBack, test.after, test.before)
}
}
}

View File

@ -2,17 +2,17 @@ package mining
import (
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/util/difficulty"
"math"
"math/rand"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
utilsMath "github.com/kaspanet/kaspad/domain/consensus/utils/math"
"github.com/pkg/errors"
)
// SolveBlock increments the given block's nonce until it matches the difficulty requirements in its bits field
func SolveBlock(block *externalapi.DomainBlock, rd *rand.Rand) {
targetDifficulty := utilsMath.CompactToBig(block.Header.Bits())
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
headerForMining := block.Header.ToMutable()
for i := rd.Uint64(); i < math.MaxUint64; i++ {
headerForMining.SetNonce(i)

View File

@ -7,8 +7,6 @@ package mempool
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors"
)
@ -115,41 +113,10 @@ func extractRejectCode(err error) (RejectCode, bool) {
err = ruleErr.Err
}
var dagRuleErr ruleerrors.RuleError
if errors.As(err, &dagRuleErr) {
// Convert the DAG error to a reject code.
var code RejectCode
switch dagRuleErr {
// Rejected due to duplicate.
case ruleerrors.ErrDuplicateBlock:
code = RejectDuplicate
// Rejected due to obsolete version.
case ruleerrors.ErrBlockVersionTooOld:
code = RejectObsolete
// Rejected due to being earlier than the last finality point.
case ruleerrors.ErrFinalityPointTimeTooOld:
code = RejectFinality
case ruleerrors.ErrDifficultyTooLow:
code = RejectDifficulty
// Everything else is due to the block or transaction being invalid.
default:
code = RejectInvalid
}
return code, true
}
var trErr TxRuleError
if errors.As(err, &trErr) {
return trErr.RejectCode, true
}
if err == nil {
return RejectInvalid, false
}
return RejectInvalid, false
}

View File

@ -5,8 +5,8 @@ import (
"fmt"
"github.com/jessevdk/go-flags"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/utils/math"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
"math/big"
"os"
@ -165,7 +165,7 @@ func (networkFlags *NetworkFlags) overrideDAGParams() error {
return errors.Errorf("couldn't convert %s to big int", *config.PowMax)
}
genesisTarget := math.CompactToBig(networkFlags.ActiveNetParams.GenesisBlock.Header.Bits())
genesisTarget := difficulty.CompactToBig(networkFlags.ActiveNetParams.GenesisBlock.Header.Bits())
if powMax.Cmp(genesisTarget) > 0 {
return errors.Errorf("powMax (%s) is smaller than genesis's target (%s)", powMax.Text(16),
genesisTarget.Text(16))

View File

@ -1,9 +1,12 @@
package difficulty_test
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util/difficulty"
"math"
"math/big"
"testing"
)
@ -22,3 +25,88 @@ func TestGetHashrateString(t *testing.T) {
}
})
}
// TestBigToCompact ensures BigToCompact converts big integers to the expected
// compact representation.
func TestBigToCompact(t *testing.T) {
tests := []struct {
in string
out uint32
}{
{"0000000000000000000000000000000000000000000000000000000000000000", 0},
{"-1", 25231360},
{"9223372036854775807", 142606335},
{"922337203685477580712312312123487", 237861256},
{"128", 0x02008000},
}
for x, test := range tests {
n := new(big.Int)
n.SetString(test.in, 10)
r := difficulty.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 integers.
func TestCompactToBig(t *testing.T) {
tests := []struct {
before uint32
intHex string
after uint32
}{
{math.MaxUint32, "-7fffff000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000", math.MaxUint32},
{0x00000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x0989680, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x87fffff, "0000000000000000000000000000000000000000000000007fffff0000000000", 0x87fffff},
{0x1810000, "-000000000000000000000000000000000000000000000000000000000000001", 0x1810000},
{0xe2d7988, "0000000000000000000000000000000000002d79880000000000000000000000", 0xe2d7988},
{0x00123456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01003456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x02000056, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x03000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x04000000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x00923456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01803456, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x02800056, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x03800000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x04800000, "0000000000000000000000000000000000000000000000000000000000000000", 0x00000000},
{0x01123456, "0000000000000000000000000000000000000000000000000000000000000012", 0x01120000},
{0x02008000, "0000000000000000000000000000000000000000000000000000000000000080", 0x02008000},
{0x01fedcba, "-00000000000000000000000000000000000000000000000000000000000007e", 0x01fe0000},
{0x02123456, "0000000000000000000000000000000000000000000000000000000000001234", 0x02123400},
{0x03123456, "0000000000000000000000000000000000000000000000000000000000123456", 0x03123456},
{0x04123456, "0000000000000000000000000000000000000000000000000000000012345600", 0x04123456},
{0x04923456, "-000000000000000000000000000000000000000000000000000000012345600", 0x04923456},
{0x05009234, "0000000000000000000000000000000000000000000000000000000092340000", 0x05009234},
{0x20123456, "1234560000000000000000000000000000000000000000000000000000000000", 0x20123456},
{0xff123456, "123456000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 0xff123456},
}
for i, test := range tests {
n := difficulty.CompactToBig(test.before)
convertBack := difficulty.BigToCompact(n)
got := fmt.Sprintf("%064x", n)
if got != test.intHex {
t.Errorf("TestCompactToBig test #%d failed: got %s want %s, input: 0x%08x",
i, got, test.intHex, test.before)
}
if convertBack != test.after {
t.Errorf("TestCompactToBig test #%d failed: got: 0x%08x want 0x%08x input: 0x%08x", i, convertBack, test.after, test.before)
}
}
}