mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-13 11:35:52 +01:00
Merge 987745ed25
into cba88226f4
This commit is contained in:
commit
d307d3a150
25 changed files with 921 additions and 26 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -46,6 +46,9 @@ btcutil/psbt/coverage.txt
|
||||||
*.swo
|
*.swo
|
||||||
/.vim
|
/.vim
|
||||||
|
|
||||||
|
#IDE
|
||||||
|
.idea
|
||||||
|
|
||||||
# Binaries produced by "make build"
|
# Binaries produced by "make build"
|
||||||
/addblock
|
/addblock
|
||||||
/btcctl
|
/btcctl
|
||||||
|
@ -54,3 +57,4 @@ btcutil/psbt/coverage.txt
|
||||||
/gencerts
|
/gencerts
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.aider*
|
||||||
|
|
|
@ -191,12 +191,20 @@ func calcNextRequiredDifficulty(lastNode HeaderCtx, newBlockTime time.Time,
|
||||||
adjustedTimespan = c.MaxRetargetTimespan()
|
adjustedTimespan = c.MaxRetargetTimespan()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special difficulty rule for Testnet4
|
||||||
|
oldTarget := CompactToBig(lastNode.Bits())
|
||||||
|
if c.ChainParams().EnforceBIP94 {
|
||||||
|
// Here we use the first block of the difficulty period. This way
|
||||||
|
// the real difficulty is always preserved in the first block as
|
||||||
|
// it is not allowed to use the min-difficulty exception.
|
||||||
|
oldTarget = CompactToBig(firstNode.Bits())
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate new target difficulty as:
|
// Calculate new target difficulty as:
|
||||||
// currentDifficulty * (adjustedTimespan / targetTimespan)
|
// currentDifficulty * (adjustedTimespan / targetTimespan)
|
||||||
// The result uses integer division which means it will be slightly
|
// The result uses integer division which means it will be slightly
|
||||||
// rounded down. Bitcoind also uses integer division to calculate this
|
// rounded down. Bitcoind also uses integer division to calculate this
|
||||||
// result.
|
// result.
|
||||||
oldTarget := CompactToBig(lastNode.Bits())
|
|
||||||
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
||||||
targetTimeSpan := int64(c.ChainParams().TargetTimespan / time.Second)
|
targetTimeSpan := int64(c.ChainParams().TargetTimespan / time.Second)
|
||||||
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
||||||
|
|
|
@ -220,6 +220,10 @@ const (
|
||||||
// current chain tip. This is not a block validation rule, but is required
|
// current chain tip. This is not a block validation rule, but is required
|
||||||
// for block proposals submitted via getblocktemplate RPC.
|
// for block proposals submitted via getblocktemplate RPC.
|
||||||
ErrPrevBlockNotBest
|
ErrPrevBlockNotBest
|
||||||
|
|
||||||
|
// ErrTimewarpAttack indicates a timewarp attack i.e.
|
||||||
|
// when block's timestamp is too early on diff adjustment block.
|
||||||
|
ErrTimewarpAttack
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
|
|
|
@ -102,6 +102,11 @@ type thresholdConditionChecker interface {
|
||||||
// not the bit associated with the condition is set, but can be more
|
// not the bit associated with the condition is set, but can be more
|
||||||
// complex as needed.
|
// complex as needed.
|
||||||
Condition(*blockNode) (bool, error)
|
Condition(*blockNode) (bool, error)
|
||||||
|
|
||||||
|
// ForceActive returns if the deployment should be forced to transition
|
||||||
|
// to the active state. This is useful on certain testnet, where we
|
||||||
|
// we'd like for a deployment to always be active.
|
||||||
|
ForceActive(*blockNode) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// thresholdStateCache provides a type to cache the threshold states of each
|
// thresholdStateCache provides a type to cache the threshold states of each
|
||||||
|
@ -279,7 +284,17 @@ func thresholdStateTransition(state ThresholdState, prevNode *blockNode,
|
||||||
// threshold states for previous windows are only calculated once.
|
// threshold states for previous windows are only calculated once.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
// This function MUST be called with the chain state lock held (for writes).
|
||||||
func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
|
func (b *BlockChain) thresholdState(prevNode *blockNode,
|
||||||
|
checker thresholdConditionChecker,
|
||||||
|
cache *thresholdStateCache) (ThresholdState, error) {
|
||||||
|
|
||||||
|
// If the deployment has a nonzero AlwaysActiveHeight and the next
|
||||||
|
// block’s height is at or above that threshold, then force the state
|
||||||
|
// to Active.
|
||||||
|
if checker.ForceActive(prevNode) {
|
||||||
|
return ThresholdActive, nil
|
||||||
|
}
|
||||||
|
|
||||||
// The threshold state for the window that contains the genesis block is
|
// The threshold state for the window that contains the genesis block is
|
||||||
// defined by definition.
|
// defined by definition.
|
||||||
confirmationWindow := int32(checker.MinerConfirmationWindow())
|
confirmationWindow := int32(checker.MinerConfirmationWindow())
|
||||||
|
|
|
@ -175,6 +175,10 @@ func (c customDeploymentChecker) Condition(_ *blockNode) (bool, error) {
|
||||||
return c.conditionTrue, nil
|
return c.conditionTrue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c customDeploymentChecker) ForceActive(_ *blockNode) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// TestThresholdStateTransition tests that the thresholdStateTransition
|
// TestThresholdStateTransition tests that the thresholdStateTransition
|
||||||
// properly implements the BIP 009 state machine, along with the speedy trial
|
// properly implements the BIP 009 state machine, along with the speedy trial
|
||||||
// augments.
|
// augments.
|
||||||
|
|
|
@ -46,6 +46,11 @@ const (
|
||||||
// coinbaseHeightAllocSize is the amount of bytes that the
|
// coinbaseHeightAllocSize is the amount of bytes that the
|
||||||
// ScriptBuilder will allocate when validating the coinbase height.
|
// ScriptBuilder will allocate when validating the coinbase height.
|
||||||
coinbaseHeightAllocSize = 5
|
coinbaseHeightAllocSize = 5
|
||||||
|
|
||||||
|
// maxTimeWarp is a maximum number of seconds that the timestamp of the first
|
||||||
|
// block of a difficulty adjustment period is allowed to
|
||||||
|
// be earlier than the last block of the previous period (BIP94).
|
||||||
|
maxTimeWarp = 600 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -684,6 +689,12 @@ func compareScript(height int32, script []byte) error {
|
||||||
func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
||||||
flags BehaviorFlags, c ChainCtx, skipCheckpoint bool) error {
|
flags BehaviorFlags, c ChainCtx, skipCheckpoint bool) error {
|
||||||
|
|
||||||
|
// The height of this block is one more than the referenced previous
|
||||||
|
// block.
|
||||||
|
blockHeight := prevNode.Height() + 1
|
||||||
|
|
||||||
|
params := c.ChainParams()
|
||||||
|
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||||
if !fastAdd {
|
if !fastAdd {
|
||||||
// Ensure the difficulty specified in the block header matches
|
// Ensure the difficulty specified in the block header matches
|
||||||
|
@ -710,16 +721,24 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
||||||
str = fmt.Sprintf(str, header.Timestamp, medianTime)
|
str = fmt.Sprintf(str, header.Timestamp, medianTime)
|
||||||
return ruleError(ErrTimeTooOld, str)
|
return ruleError(ErrTimeTooOld, str)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// The height of this block is one more than the referenced previous
|
// Testnet4 only: Check timestamp against prev for
|
||||||
// block.
|
// difficulty-adjustment blocks to prevent timewarp attacks.
|
||||||
blockHeight := prevNode.Height() + 1
|
if params.EnforceBIP94 {
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
blockHeight, c.BlocksPerRetarget(),
|
||||||
|
header.Timestamp,
|
||||||
|
time.Unix(prevNode.Timestamp(), 0),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reject outdated block versions once a majority of the network
|
// Reject outdated block versions once a majority of the network
|
||||||
// has upgraded. These were originally voted on by BIP0034,
|
// has upgraded. These were originally voted on by BIP0034,
|
||||||
// BIP0065, and BIP0066.
|
// BIP0065, and BIP0066.
|
||||||
params := c.ChainParams()
|
|
||||||
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
|
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
|
||||||
header.Version < 3 && blockHeight >= params.BIP0066Height ||
|
header.Version < 3 && blockHeight >= params.BIP0066Height ||
|
||||||
header.Version < 4 && blockHeight >= params.BIP0065Height {
|
header.Version < 4 && blockHeight >= params.BIP0065Height {
|
||||||
|
@ -761,6 +780,30 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assertNoTimeWarp checks the timestamp of the block against the previous
|
||||||
|
// block's timestamp for the first block of each difficulty adjustment interval
|
||||||
|
// to prevent timewarp attacks. This is defined in BIP-0094.
|
||||||
|
func assertNoTimeWarp(blockHeight, blocksPerReTarget int32, headerTimestamp,
|
||||||
|
prevBlockTimestamp time.Time) error {
|
||||||
|
|
||||||
|
// If this isn't the first block of the difficulty adjustment interval,
|
||||||
|
// then we can exit early.
|
||||||
|
if blockHeight%blocksPerReTarget != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check timestamp for the first block of each difficulty adjustment
|
||||||
|
// interval, except the genesis block.
|
||||||
|
if headerTimestamp.Before(prevBlockTimestamp.Add(-maxTimeWarp)) {
|
||||||
|
str := "block's timestamp %v is too early on diff adjustment " +
|
||||||
|
"block %v"
|
||||||
|
str = fmt.Sprintf(str, headerTimestamp, prevBlockTimestamp)
|
||||||
|
return ruleError(ErrTimewarpAttack, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// checkBlockContext performs several validation checks on the block which depend
|
// checkBlockContext performs several validation checks on the block which depend
|
||||||
// on its position within the block chain.
|
// on its position within the block chain.
|
||||||
//
|
//
|
||||||
|
@ -1230,7 +1273,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi
|
||||||
if csvState == ThresholdActive {
|
if csvState == ThresholdActive {
|
||||||
// If the CSV soft-fork is now active, then modify the
|
// If the CSV soft-fork is now active, then modify the
|
||||||
// scriptFlags to ensure that the CSV op code is properly
|
// scriptFlags to ensure that the CSV op code is properly
|
||||||
// validated during the script checks bleow.
|
// validated during the script checks below.
|
||||||
scriptFlags |= txscript.ScriptVerifyCheckSequenceVerify
|
scriptFlags |= txscript.ScriptVerifyCheckSequenceVerify
|
||||||
|
|
||||||
// We obtain the MTP of the *previous* block in order to
|
// We obtain the MTP of the *previous* block in order to
|
||||||
|
|
339
blockchain/validate_rapid_test.go
Normal file
339
blockchain/validate_rapid_test.go
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"pgregory.net/rapid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAssertNoTimeWarpProperties uses property-based testing to verify that
|
||||||
|
// the assertNoTimeWarp function correctly implements the BIP-94 rule. This
|
||||||
|
// helps catch edge cases that might be missed with regular unit tests.
|
||||||
|
func TestAssertNoTimeWarpProperties(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Define constant for blocks per retarget (similar to Bitcoin's 2016).
|
||||||
|
const blocksPerRetarget = 2016
|
||||||
|
|
||||||
|
// Rapid test that only the retarget blocks are checked.
|
||||||
|
t.Run("only_checks_retarget_blocks", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate block height that is not a retarget block.
|
||||||
|
height := rapid.Int32Range(
|
||||||
|
1, 1000000,
|
||||||
|
).Filter(func(h int32) bool {
|
||||||
|
return h%blocksPerRetarget != 0
|
||||||
|
}).Draw(t, "height")
|
||||||
|
|
||||||
|
// Even with an "extreme" time warp, the function should return
|
||||||
|
// nil because it only applies the check to retarget blocks.
|
||||||
|
// Define headerTime as the Unix epoch start.
|
||||||
|
headerTime := time.Unix(0, 0)
|
||||||
|
|
||||||
|
// Define prevBlockTime as the current time (creating an
|
||||||
|
// extreme gap).
|
||||||
|
prevBlockTime := time.Now()
|
||||||
|
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(
|
||||||
|
t, err, "expected nil error for non-retarget block "+
|
||||||
|
"but got: %v.", err,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Rapid test that retarget blocks with acceptable timestamps pass
|
||||||
|
// validation.
|
||||||
|
t.Run("valid_timestamps_pass", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate block height that is a retarget block
|
||||||
|
height := rapid.Int32Range(blocksPerRetarget, 1000000).
|
||||||
|
Filter(func(h int32) bool {
|
||||||
|
return h%blocksPerRetarget == 0
|
||||||
|
}).Draw(t, "height")
|
||||||
|
|
||||||
|
// Generate a previous block timestamp.
|
||||||
|
prevTimeUnix := rapid.Int64Range(
|
||||||
|
1000000, 2000000000,
|
||||||
|
).Draw(t, "prev_time")
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// Generate a header timestamp that is not more than
|
||||||
|
// maxTimeWarp earlier than the previous block timestamp.
|
||||||
|
minValidHeaderTime := prevBlockTime.Add(
|
||||||
|
-maxTimeWarp,
|
||||||
|
).Add(time.Second)
|
||||||
|
|
||||||
|
// Generate any valid header time between the minimum valid
|
||||||
|
// time and prevBlockTime to ensure it passes the time warp
|
||||||
|
// check.
|
||||||
|
minTimeUnix := minValidHeaderTime.Unix()
|
||||||
|
maxTimeUnix := prevBlockTime.Unix()
|
||||||
|
|
||||||
|
// Ensure min is always less than max.
|
||||||
|
if minTimeUnix >= maxTimeUnix {
|
||||||
|
// If a valid range cannot be generated, use the
|
||||||
|
// previous block time which is guaranteed to pass the
|
||||||
|
// test.
|
||||||
|
headerTime := prevBlockTime
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(t, err, "expected valid timestamps to "+
|
||||||
|
"pass but got: %v.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headerTimeUnix := rapid.Int64Range(
|
||||||
|
minTimeUnix, maxTimeUnix,
|
||||||
|
).Draw(t, "header_time_unix")
|
||||||
|
headerTime := time.Unix(headerTimeUnix, 0)
|
||||||
|
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(t, err, "expected valid timestamps to pass but "+
|
||||||
|
"got: %v.")
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Rapid test that retarget blocks with invalid timestamps fail
|
||||||
|
t.Run("invalid_timestamps_fail", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// validation.
|
||||||
|
// Generate block height that is a retarget block.
|
||||||
|
height := rapid.Int32Range(blocksPerRetarget, 1000000).
|
||||||
|
Filter(func(h int32) bool {
|
||||||
|
return h%blocksPerRetarget == 0
|
||||||
|
}).Draw(t, "height")
|
||||||
|
|
||||||
|
// Generate a previous block timestamp.
|
||||||
|
prevTimeUnix := rapid.Int64Range(
|
||||||
|
1000000, 2000000000,
|
||||||
|
).Draw(t, "prev_time")
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// Invalid header timestamp: more than maxTimeWarp earlier than
|
||||||
|
// prevBlockTime Ensure we generate a time that is definitely
|
||||||
|
// beyond the maxTimeWarp (which is 600 seconds) by using at
|
||||||
|
// least 601 seconds.
|
||||||
|
invalidDelta := time.Duration(
|
||||||
|
-rapid.Int64Range(601, 86400).Draw(t, "invalid_delta"),
|
||||||
|
) * time.Second
|
||||||
|
headerTime := prevBlockTime.Add(invalidDelta)
|
||||||
|
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.Error(t, err, "expected error for time-warped header but got nil.")
|
||||||
|
|
||||||
|
// Verify the correct error type is returned.
|
||||||
|
require.IsType(
|
||||||
|
t, RuleError{}, err, "expected RuleError but got: %T.", err,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify it's the expected ErrTimewarpAttack error.
|
||||||
|
ruleErr, ok := err.(RuleError)
|
||||||
|
require.True(t, ok, "expected RuleError but got: %T.", err)
|
||||||
|
require.Equal(
|
||||||
|
t, ErrTimewarpAttack, ruleErr.ErrorCode, "expected "+
|
||||||
|
"ErrTimewarpAttack but got: %v.", ruleErr.ErrorCode,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Test the edge case right at the boundary of maxTimeWarp.
|
||||||
|
t.Run("boundary_timestamps", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate block height that is a retarget block.
|
||||||
|
height := rapid.Int32Range(blocksPerRetarget, 1000000).
|
||||||
|
Filter(func(h int32) bool {
|
||||||
|
return h%blocksPerRetarget == 0
|
||||||
|
}).Draw(t, "height")
|
||||||
|
|
||||||
|
// Generate a previous block timestamp with enough padding
|
||||||
|
// to avoid time.Time precision issues.
|
||||||
|
prevTimeUnix := rapid.Int64Range(
|
||||||
|
1000000, 2000000000,
|
||||||
|
).Draw(t, "prev_time")
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// Test exact boundary: headerTime is exactly maxTimeWarp earlier.
|
||||||
|
headerTime := prevBlockTime.Add(-maxTimeWarp)
|
||||||
|
|
||||||
|
// Check the actual implementation (looking at
|
||||||
|
// validate.go:797-798) The comparison is
|
||||||
|
// "headerTimestamp.Before(prevBlockTimestamp.Add(-maxTimeWarp))"
|
||||||
|
// This means at exact boundary (headerTime ==
|
||||||
|
// prevBlockTime.Add(-maxTimeWarp)) it should NOT fail, since
|
||||||
|
// Before() is strict < not <=.
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(
|
||||||
|
t, err, "expected no error at exact boundary but "+
|
||||||
|
"got: %v.",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test 1 nanosecond BEYOND the boundary (which should fail).
|
||||||
|
headerTime = prevBlockTime.Add(-maxTimeWarp).Add(
|
||||||
|
-time.Nanosecond,
|
||||||
|
)
|
||||||
|
|
||||||
|
// This should fail as it is just beyond the maxTimeWarp limit.
|
||||||
|
err = assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.Error(
|
||||||
|
t, err, "expected error just beyond boundary but "+
|
||||||
|
"got nil.",
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAssertNoTimeWarpInvariants uses property-based testing to verify the
|
||||||
|
// invariants of the assertNoTimeWarp function regardless of inputs.
|
||||||
|
func TestAssertNoTimeWarpInvariants(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Invariant: The function should never panic regardless of input.
|
||||||
|
t.Run("never_panics", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate any possible inputs
|
||||||
|
height := rapid.Int32().Draw(t, "height")
|
||||||
|
blocksPerRetarget := rapid.Int32Range(
|
||||||
|
1, 10000,
|
||||||
|
).Draw(t, "blocks_per_retarget")
|
||||||
|
headerTimeUnix := rapid.Int64().Draw(t, "header_time")
|
||||||
|
prevTimeUnix := rapid.Int64().Draw(t, "prev_time")
|
||||||
|
|
||||||
|
headerTime := time.Unix(headerTimeUnix, 0)
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// The function should never panic regardless of input
|
||||||
|
_ = assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Invariant: For non-retarget blocks, the function always returns nil.
|
||||||
|
// nolint:lll.
|
||||||
|
t.Run("non_retarget_blocks_return_nil", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate height and blocksPerRetarget such that height is
|
||||||
|
// not a multiple of blocksPerRetarget.
|
||||||
|
blocksPerRetarget := rapid.Int32Range(2, 10000).Draw(
|
||||||
|
t, "blocks_per_retarget",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure height is not a multiple of blocksPerRetarget.
|
||||||
|
remainders := rapid.Int32Range(1, blocksPerRetarget-1).Draw(
|
||||||
|
t, "remainder",
|
||||||
|
)
|
||||||
|
height := rapid.Int32Range(0, 1000000).Draw(
|
||||||
|
t, "base",
|
||||||
|
)*blocksPerRetarget + remainders
|
||||||
|
|
||||||
|
// Generate any timestamps, even invalid ones.
|
||||||
|
headerTime := time.Unix(rapid.Int64().Draw(t, "header_time"), 0)
|
||||||
|
prevBlockTime := time.Unix(
|
||||||
|
rapid.Int64().Draw(t, "prev_time"), 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
// For non-retarget blocks, should always return nil.
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(
|
||||||
|
t, err, "expected nil for non-retarget block "+
|
||||||
|
"(height=%d, blocks_per_retarget=%d) but "+
|
||||||
|
"got: %v.", height, blocksPerRetarget, err,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAssertNoTimeWarpSecurity tests the security properties of the
|
||||||
|
// assertNoTimeWarp function. This verifies that the function properly prevents
|
||||||
|
// "time warp" attacks where miners might attempt to manipulate timestamps for
|
||||||
|
// difficulty adjustment blocks.
|
||||||
|
func TestAssertNoTimeWarpSecurity(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const blocksPerRetarget = 2016
|
||||||
|
|
||||||
|
// Test that all difficulty adjustment blocks are protected from timewarp.
|
||||||
|
t.Run("all_retarget_blocks_protected", rapid.MakeCheck(func(t *rapid.T) { //nolint:lll
|
||||||
|
// Generate any retarget block height (multiples of
|
||||||
|
// blocksPerRetarget).
|
||||||
|
multiplier := rapid.Int32Range(1, 1000).Draw(t, "multiplier")
|
||||||
|
height := multiplier * blocksPerRetarget
|
||||||
|
|
||||||
|
// Generate a reasonable previous block timestamp.
|
||||||
|
prevTimeUnix := rapid.Int64Range(
|
||||||
|
1000000, 2000000000,
|
||||||
|
).Draw(t, "prev_time")
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// Generate a test header timestamp that's significantly before
|
||||||
|
// the previous timestamp This should always be rejected for
|
||||||
|
// retarget blocks.
|
||||||
|
timeDiff := rapid.Int64Range(
|
||||||
|
int64(maxTimeWarp+time.Second),
|
||||||
|
int64(maxTimeWarp+time.Hour*24*7),
|
||||||
|
).Draw(t, "warp_amount")
|
||||||
|
invalidDelta := time.Duration(-timeDiff)
|
||||||
|
headerTime := prevBlockTime.Add(invalidDelta)
|
||||||
|
|
||||||
|
// This should always fail with ErrTimewarpAttack for any retarget block.
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.Error(
|
||||||
|
t, err, "security vulnerability: Time warp attack not "+
|
||||||
|
"detected for height %d.", height,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify it's the expected error type.
|
||||||
|
ruleErr, ok := err.(RuleError)
|
||||||
|
require.True(t, ok, "expected RuleError but got: %T.", err)
|
||||||
|
require.Equal(
|
||||||
|
t, ErrTimewarpAttack, ruleErr.ErrorCode,
|
||||||
|
"expected ErrTimewarpAttack but got: %v.",
|
||||||
|
ruleErr.ErrorCode,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Test that non-adjustment blocks are not subject to the same check.
|
||||||
|
// nolint:lll.
|
||||||
|
t.Run("non_retarget_blocks_not_affected", rapid.MakeCheck(func(t *rapid.T) {
|
||||||
|
// Generate any non-retarget block height.
|
||||||
|
baseHeight := rapid.Int32Range(0, 1000).Draw(
|
||||||
|
t, "base_height",
|
||||||
|
) * blocksPerRetarget
|
||||||
|
offset := rapid.Int32Range(1, blocksPerRetarget-1).Draw(
|
||||||
|
t, "offset",
|
||||||
|
)
|
||||||
|
height := baseHeight + offset
|
||||||
|
|
||||||
|
// Generate a reasonable previous block timestamp.
|
||||||
|
prevTimeUnix := rapid.Int64Range(1000000, 2000000000).Draw(
|
||||||
|
t, "prev_time",
|
||||||
|
)
|
||||||
|
prevBlockTime := time.Unix(prevTimeUnix, 0)
|
||||||
|
|
||||||
|
// Generate a test header timestamp that's significantly before
|
||||||
|
// the previous timestamp. Even though this would be rejected
|
||||||
|
// for retarget blocks, it shouldn't matter here.
|
||||||
|
timeDiff := rapid.Int64Range(
|
||||||
|
int64(maxTimeWarp+time.Second),
|
||||||
|
int64(maxTimeWarp+time.Hour*24*7),
|
||||||
|
).Draw(t, "warp_amount")
|
||||||
|
invalidDelta := time.Duration(-timeDiff)
|
||||||
|
headerTime := prevBlockTime.Add(invalidDelta)
|
||||||
|
|
||||||
|
// This should NOT fail for non-retarget blocks, even with
|
||||||
|
// extreme timewarp.
|
||||||
|
err := assertNoTimeWarp(
|
||||||
|
height, blocksPerRetarget, headerTime, prevBlockTime,
|
||||||
|
)
|
||||||
|
require.NoError(
|
||||||
|
t, err, "non-retarget blocks should not be affected "+
|
||||||
|
"by time warp check, but got: %v.", err,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
|
@ -134,6 +134,13 @@ func (c bitConditionChecker) IsSpeedy() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForceActive returns if the deployment should be forced to transition to the
|
||||||
|
// active state. This is useful on certain testnet, where we we'd like for a
|
||||||
|
// deployment to always be active.
|
||||||
|
func (c bitConditionChecker) ForceActive(node *blockNode) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// deploymentChecker provides a thresholdConditionChecker which can be used to
|
// deploymentChecker provides a thresholdConditionChecker which can be used to
|
||||||
// test a specific deployment rule. This is required for properly detecting
|
// test a specific deployment rule. This is required for properly detecting
|
||||||
// and activating consensus rule changes.
|
// and activating consensus rule changes.
|
||||||
|
@ -207,15 +214,9 @@ func (c deploymentChecker) MinerConfirmationWindow() uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EligibleToActivate returns true if a custom deployment can transition from
|
// EligibleToActivate returns true if a custom deployment can transition from
|
||||||
// the LockedIn to the Active state. For normal deployments, this always
|
// the LockedIn to the Active state. In addition to the traditional minimum
|
||||||
// returns true. However, some deployments add extra rules like a minimum
|
// activation height (MinActivationHeight), an optional AlwaysActiveHeight can
|
||||||
// activation height, which can be abstracted into a generic arbitrary check at
|
// force the deployment to be active after a specified height.
|
||||||
// the final state via this method.
|
|
||||||
//
|
|
||||||
// This implementation always returns true, unless a minimum activation height
|
|
||||||
// is specified.
|
|
||||||
//
|
|
||||||
// This is part of the thresholdConditionChecker interface implementation.
|
|
||||||
func (c deploymentChecker) EligibleToActivate(blkNode *blockNode) bool {
|
func (c deploymentChecker) EligibleToActivate(blkNode *blockNode) bool {
|
||||||
// No activation height, so it's always ready to go.
|
// No activation height, so it's always ready to go.
|
||||||
if c.deployment.MinActivationHeight == 0 {
|
if c.deployment.MinActivationHeight == 0 {
|
||||||
|
@ -249,6 +250,28 @@ func (c deploymentChecker) Condition(node *blockNode) (bool, error) {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForceActive returns if the deployment should be forced to transition to the
|
||||||
|
// active state. This is useful on certain testnet, where we we'd like for a
|
||||||
|
// deployment to always be active.
|
||||||
|
func (c deploymentChecker) ForceActive(node *blockNode) bool {
|
||||||
|
if node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the deployment has a nonzero AlwaysActiveHeight and the next
|
||||||
|
// block’s height is at or above that threshold, then force the state
|
||||||
|
// to Active.
|
||||||
|
effectiveHeight := c.deployment.EffectiveAlwaysActiveHeight()
|
||||||
|
if uint32(node.height)+1 >= effectiveHeight {
|
||||||
|
log.Debugf("Force activating deployment: next block "+
|
||||||
|
"height %d >= EffectiveAlwaysActiveHeight %d",
|
||||||
|
uint32(node.height)+1, effectiveHeight)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// calcNextBlockVersion calculates the expected version of the block after the
|
// calcNextBlockVersion calculates the expected version of the block after the
|
||||||
// passed previous block node based on the state of started and locked in
|
// passed previous block node based on the state of started and locked in
|
||||||
// rule change deployments.
|
// rule change deployments.
|
||||||
|
|
|
@ -143,6 +143,77 @@ var testNet3GenesisBlock = wire.MsgBlock{
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testNet4GenesisTx is the transaction for the genesis blocks for test network (version 4).
|
||||||
|
var testNet4GenesisTx = wire.MsgTx{
|
||||||
|
Version: 1,
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
{
|
||||||
|
PreviousOutPoint: wire.OutPoint{
|
||||||
|
Hash: chainhash.Hash{},
|
||||||
|
Index: 0xffffffff,
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{
|
||||||
|
// Message: `03/May/2024 000000000000000000001ebd58c244970b3aa9d783bb001011fbe8ea8e98e00e`
|
||||||
|
0x4, 0xff, 0xff, 0x0, 0x1d, 0x1, 0x4, 0x4c,
|
||||||
|
0x4c, 0x30, 0x33, 0x2f, 0x4d, 0x61, 0x79, 0x2f,
|
||||||
|
0x32, 0x30, 0x32, 0x34, 0x20, 0x30, 0x30, 0x30,
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||||
|
0x30, 0x31, 0x65, 0x62, 0x64, 0x35, 0x38, 0x63,
|
||||||
|
0x32, 0x34, 0x34, 0x39, 0x37, 0x30, 0x62, 0x33,
|
||||||
|
0x61, 0x61, 0x39, 0x64, 0x37, 0x38, 0x33, 0x62,
|
||||||
|
0x62, 0x30, 0x30, 0x31, 0x30, 0x31, 0x31, 0x66,
|
||||||
|
0x62, 0x65, 0x38, 0x65, 0x61, 0x38, 0x65, 0x39,
|
||||||
|
0x38, 0x65, 0x30, 0x30, 0x65},
|
||||||
|
Sequence: 0xffffffff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 0x12a05f200,
|
||||||
|
PkScript: []byte{
|
||||||
|
0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0xac},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LockTime: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// testNet4GenesisHash is the hash of the first block in the block chain for the
|
||||||
|
// test network (version 4).
|
||||||
|
var testNet4GenesisHash = chainhash.Hash([chainhash.HashSize]byte{
|
||||||
|
0x43, 0xf0, 0x8b, 0xda, 0xb0, 0x50, 0xe3, 0x5b,
|
||||||
|
0x56, 0x7c, 0x86, 0x4b, 0x91, 0xf4, 0x7f, 0x50,
|
||||||
|
0xae, 0x72, 0x5a, 0xe2, 0xde, 0x53, 0xbc, 0xfb,
|
||||||
|
0xba, 0xf2, 0x84, 0xda, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
|
||||||
|
// testNet4GenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||||
|
// block for the test network (version 4). It is the same as the merkle root
|
||||||
|
// for the main network.
|
||||||
|
var testNet4GenesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
|
0x4e, 0x7b, 0x2b, 0x91, 0x28, 0xfe, 0x02, 0x91,
|
||||||
|
0xdb, 0x06, 0x93, 0xaf, 0x2a, 0xe4, 0x18, 0xb7,
|
||||||
|
0x67, 0xe6, 0x57, 0xcd, 0x40, 0x7e, 0x80, 0xcb,
|
||||||
|
0x14, 0x34, 0x22, 0x1e, 0xae, 0xa7, 0xa0, 0x7a,
|
||||||
|
})
|
||||||
|
|
||||||
|
// testNet4GenesisBlock defines the genesis block of the block chain which
|
||||||
|
// serves as the public transaction ledger for the test network (version 3).
|
||||||
|
var testNet4GenesisBlock = wire.MsgBlock{
|
||||||
|
Header: wire.BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
MerkleRoot: testNet4GenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
||||||
|
Timestamp: time.Unix(1714777860, 0), // 2024-05-03 23:11:00 +0000 UTC
|
||||||
|
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
||||||
|
Nonce: 0x17780cbb, // 393743547
|
||||||
|
},
|
||||||
|
Transactions: []*wire.MsgTx{&testNet4GenesisTx},
|
||||||
|
}
|
||||||
|
|
||||||
// simNetGenesisHash is the hash of the first block in the block chain for the
|
// simNetGenesisHash is the hash of the first block in the block chain for the
|
||||||
// simulation test network.
|
// simulation test network.
|
||||||
var simNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
var simNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestGenesisBlock tests the genesis block of the main network for validity by
|
// TestGenesisBlock tests the genesis block of the main network for validity by
|
||||||
|
@ -91,6 +92,34 @@ func TestTestNet3GenesisBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTestNet4GenesisBlock tests the genesis block of the test network (version
|
||||||
|
// 4) for validity by checking the encoded bytes and hashes.
|
||||||
|
func TestTestNet4GenesisBlock(t *testing.T) {
|
||||||
|
// Encode the genesis block to raw bytes.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := TestNet4Params.GenesisBlock.Serialize(&buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Ensure the encoded block matches the expected bytes.
|
||||||
|
if !bytes.Equal(buf.Bytes(), testNet4GenesisBlockBytes) {
|
||||||
|
t.Fatalf("TestTestNet4GenesisBlock: Genesis block does not "+
|
||||||
|
"appear valid - got %v, want %v",
|
||||||
|
spew.Sdump(buf.Bytes()),
|
||||||
|
spew.Sdump(testNet4GenesisBlockBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check hash of the block against expected hash.
|
||||||
|
hash := TestNet4Params.GenesisBlock.BlockHash()
|
||||||
|
if !TestNet4Params.GenesisHash.IsEqual(&hash) {
|
||||||
|
t.Fatalf("TestTestNet4GenesisBlock: Genesis block hash does "+
|
||||||
|
"not appear valid - got %v, want %v", spew.Sdump(hash),
|
||||||
|
spew.Sdump(TestNet4Params.GenesisHash))
|
||||||
|
}
|
||||||
|
expectedHash := "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be3" +
|
||||||
|
"50b0da8bf043"
|
||||||
|
require.Equal(t, expectedHash, hash.String())
|
||||||
|
}
|
||||||
|
|
||||||
// TestSimNetGenesisBlock tests the genesis block of the simulation test network
|
// TestSimNetGenesisBlock tests the genesis block of the simulation test network
|
||||||
// for validity by checking the encoded bytes and hashes.
|
// for validity by checking the encoded bytes and hashes.
|
||||||
func TestSimNetGenesisBlock(t *testing.T) {
|
func TestSimNetGenesisBlock(t *testing.T) {
|
||||||
|
@ -268,6 +297,44 @@ var testNet3GenesisBlockBytes = []byte{
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testNet4GenesisBlockBytes are the wire encoded bytes for the genesis block of
|
||||||
|
// the test network (version 4)
|
||||||
|
var testNet4GenesisBlockBytes = []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x4e, 0x7b, 0x2b, 0x91, /* |....N{+.| */
|
||||||
|
0x28, 0xfe, 0x02, 0x91, 0xdb, 0x06, 0x93, 0xaf, /* |(.......| */
|
||||||
|
0x2a, 0xe4, 0x18, 0xb7, 0x67, 0xe6, 0x57, 0xcd, /* |*...g.W.| */
|
||||||
|
0x40, 0x7e, 0x80, 0xcb, 0x14, 0x34, 0x22, 0x1e, /* |@~...4".| */
|
||||||
|
0xae, 0xa7, 0xa0, 0x7a, 0x04, 0x6f, 0x35, 0x66, /* |...z.o5f| */
|
||||||
|
0xff, 0xff, 0x00, 0x1d, 0xbb, 0x0c, 0x78, 0x17, /* |......x.| */
|
||||||
|
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, /* |........| */
|
||||||
|
0xff, 0xff, 0x55, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..U.....| */
|
||||||
|
0x01, 0x04, 0x4c, 0x4c, 0x30, 0x33, 0x2f, 0x4d, /* |..LL03/M| */
|
||||||
|
0x61, 0x79, 0x2f, 0x32, 0x30, 0x32, 0x34, 0x20, /* |ay/2024 | */
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* |00000000| */
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* |00000000| */
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x31, 0x65, 0x62, 0x64, /* |00001ebd| */
|
||||||
|
0x35, 0x38, 0x63, 0x32, 0x34, 0x34, 0x39, 0x37, /* |58c24497| */
|
||||||
|
0x30, 0x62, 0x33, 0x61, 0x61, 0x39, 0x64, 0x37, /* |0b3aa9d7| */
|
||||||
|
0x38, 0x33, 0x62, 0x62, 0x30, 0x30, 0x31, 0x30, /* |83bb0010| */
|
||||||
|
0x31, 0x31, 0x66, 0x62, 0x65, 0x38, 0x65, 0x61, /* |11fbe8ea| */
|
||||||
|
0x38, 0x65, 0x39, 0x38, 0x65, 0x30, 0x30, 0x65, /* |8e98e00e| */
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */
|
||||||
|
0x2a, 0x01, 0x00, 0x00, 0x00, 0x23, 0x21, 0x00, /* |*....#!.| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */
|
||||||
|
0xac, 0x00, 0x00, 0x00, 0x00, /* |..... | */
|
||||||
|
}
|
||||||
|
|
||||||
// simNetGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
// simNetGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
||||||
// the simulation test network as of protocol version 70002.
|
// the simulation test network as of protocol version 70002.
|
||||||
var simNetGenesisBlockBytes = []byte{
|
var simNetGenesisBlockBytes = []byte{
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -78,6 +79,16 @@ type Checkpoint struct {
|
||||||
Hash *chainhash.Hash
|
Hash *chainhash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EffectiveAlwaysActiveHeight returns the effective activation height for the
|
||||||
|
// deployment. If AlwaysActiveHeight is unset (i.e. zero), it returns
|
||||||
|
// the maximum uint32 value to indicate that it does not force activation.
|
||||||
|
func (d *ConsensusDeployment) EffectiveAlwaysActiveHeight() uint32 {
|
||||||
|
if d.AlwaysActiveHeight == 0 {
|
||||||
|
return math.MaxUint32
|
||||||
|
}
|
||||||
|
return d.AlwaysActiveHeight
|
||||||
|
}
|
||||||
|
|
||||||
// DNSSeed identifies a DNS seed.
|
// DNSSeed identifies a DNS seed.
|
||||||
type DNSSeed struct {
|
type DNSSeed struct {
|
||||||
// Host defines the hostname of the seed.
|
// Host defines the hostname of the seed.
|
||||||
|
@ -108,6 +119,11 @@ type ConsensusDeployment struct {
|
||||||
// activation. A value of 1815 block denotes a 90% threshold.
|
// activation. A value of 1815 block denotes a 90% threshold.
|
||||||
CustomActivationThreshold uint32
|
CustomActivationThreshold uint32
|
||||||
|
|
||||||
|
// AlwaysActiveHeight defines an optional block threshold at which the
|
||||||
|
// deployment is forced to be active. If unset (0), it defaults to
|
||||||
|
// math.MaxUint32, meaning the deployment does not force activation.
|
||||||
|
AlwaysActiveHeight uint32
|
||||||
|
|
||||||
// DeploymentStarter is used to determine if the given
|
// DeploymentStarter is used to determine if the given
|
||||||
// ConsensusDeployment has started or not.
|
// ConsensusDeployment has started or not.
|
||||||
DeploymentStarter ConsensusDeploymentStarter
|
DeploymentStarter ConsensusDeploymentStarter
|
||||||
|
@ -146,6 +162,10 @@ const (
|
||||||
// the deployment of BIPS 340, 341 and 342.
|
// the deployment of BIPS 340, 341 and 342.
|
||||||
DeploymentTaproot
|
DeploymentTaproot
|
||||||
|
|
||||||
|
// DeploymentTestDummyAlwaysActive is a dummy deployment that is meant
|
||||||
|
// to always be active.
|
||||||
|
DeploymentTestDummyAlwaysActive
|
||||||
|
|
||||||
// NOTE: DefinedDeployments must always come last since it is used to
|
// NOTE: DefinedDeployments must always come last since it is used to
|
||||||
// determine how many defined deployments there currently are.
|
// determine how many defined deployments there currently are.
|
||||||
|
|
||||||
|
@ -189,6 +209,10 @@ type Params struct {
|
||||||
// regtest like networks.
|
// regtest like networks.
|
||||||
PoWNoRetargeting bool
|
PoWNoRetargeting bool
|
||||||
|
|
||||||
|
// EnforceBIP94 enforces timewarp attack mitigation and on testnet4
|
||||||
|
// this also enforces the block storm mitigation.
|
||||||
|
EnforceBIP94 bool
|
||||||
|
|
||||||
// These fields define the block heights at which the specified softfork
|
// These fields define the block heights at which the specified softfork
|
||||||
// BIP became active.
|
// BIP became active.
|
||||||
BIP0034Height int32
|
BIP0034Height int32
|
||||||
|
@ -375,6 +399,16 @@ var MainNetParams = Params{
|
||||||
time.Time{}, // Never expires
|
time.Time{}, // Never expires
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 30,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
DeploymentCSV: {
|
DeploymentCSV: {
|
||||||
BitNumber: 0,
|
BitNumber: 0,
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
@ -486,6 +520,16 @@ var RegressionNetParams = Params{
|
||||||
time.Time{}, // Never expires
|
time.Time{}, // Never expires
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 30,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
DeploymentCSV: {
|
DeploymentCSV: {
|
||||||
BitNumber: 0,
|
BitNumber: 0,
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
@ -620,6 +664,16 @@ var TestNet3Params = Params{
|
||||||
time.Time{}, // Never expires
|
time.Time{}, // Never expires
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 30,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
DeploymentCSV: {
|
DeploymentCSV: {
|
||||||
BitNumber: 0,
|
BitNumber: 0,
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
@ -673,6 +727,131 @@ var TestNet3Params = Params{
|
||||||
HDCoinType: 1,
|
HDCoinType: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestNet4Params defines the network parameters for the test Bitcoin network
|
||||||
|
// (version 4).
|
||||||
|
var TestNet4Params = Params{
|
||||||
|
Name: "testnet4",
|
||||||
|
Net: wire.TestNet4,
|
||||||
|
DefaultPort: "48333",
|
||||||
|
DNSSeeds: []DNSSeed{
|
||||||
|
{"seed.testnet4.bitcoin.sprovoost.nl", true},
|
||||||
|
{"seed.testnet4.wiz.biz", true},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Chain parameters
|
||||||
|
GenesisBlock: &testNet4GenesisBlock,
|
||||||
|
GenesisHash: &testNet4GenesisHash,
|
||||||
|
PowLimit: testNet3PowLimit,
|
||||||
|
PowLimitBits: 0x1d00ffff,
|
||||||
|
EnforceBIP94: true,
|
||||||
|
BIP0034Height: 1,
|
||||||
|
BIP0065Height: 1,
|
||||||
|
BIP0066Height: 1,
|
||||||
|
CoinbaseMaturity: 100,
|
||||||
|
SubsidyReductionInterval: 210000,
|
||||||
|
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
||||||
|
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
||||||
|
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
||||||
|
ReduceMinDifficulty: true,
|
||||||
|
MinDiffReductionTime: time.Minute * 20, // TargetTimePerBlock * 2
|
||||||
|
GenerateSupported: false,
|
||||||
|
|
||||||
|
// Checkpoints ordered from oldest to newest.
|
||||||
|
Checkpoints: []Checkpoint{},
|
||||||
|
|
||||||
|
// Consensus rule change deployments.
|
||||||
|
//
|
||||||
|
// The miner confirmation window is defined as:
|
||||||
|
// target proof of work timespan / target proof of work spacing
|
||||||
|
RuleChangeActivationThreshold: 1512, // 75% of MinerConfirmationWindow
|
||||||
|
MinerConfirmationWindow: 2016,
|
||||||
|
Deployments: [DefinedDeployments]ConsensusDeployment{
|
||||||
|
DeploymentTestDummy: {
|
||||||
|
BitNumber: 28,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Unix(1199145601, 0), // January 1, 2008 UTC
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Unix(1230767999, 0), // December 31, 2008 UTC
|
||||||
|
),
|
||||||
|
},
|
||||||
|
DeploymentTestDummyMinActivation: {
|
||||||
|
BitNumber: 22,
|
||||||
|
CustomActivationThreshold: 1815, // Only needs 90% hash rate.
|
||||||
|
MinActivationHeight: 10_0000, // Can only activate after height 10k.
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 30,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
|
DeploymentCSV: {
|
||||||
|
BitNumber: 29,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
|
DeploymentSegwit: {
|
||||||
|
BitNumber: 29,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
|
DeploymentTaproot: {
|
||||||
|
BitNumber: 2,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
MinActivationHeight: 0,
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Mempool parameters
|
||||||
|
RelayNonStdTxs: true,
|
||||||
|
|
||||||
|
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
||||||
|
// BIP 173.
|
||||||
|
Bech32HRPSegwit: "tb", // always tb for test net
|
||||||
|
|
||||||
|
// Address encoding magics
|
||||||
|
PubKeyHashAddrID: 0x6f, // starts with m or n
|
||||||
|
ScriptHashAddrID: 0xc4, // starts with 2
|
||||||
|
WitnessPubKeyHashAddrID: 0x03, // starts with QW
|
||||||
|
WitnessScriptHashAddrID: 0x28, // starts with T7n
|
||||||
|
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||||
|
|
||||||
|
// BIP32 hierarchical deterministic extended key magics
|
||||||
|
HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
|
||||||
|
HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub
|
||||||
|
|
||||||
|
// BIP44 coin type used in the hierarchical deterministic path for
|
||||||
|
// address generation.
|
||||||
|
HDCoinType: 1,
|
||||||
|
}
|
||||||
|
|
||||||
// SimNetParams defines the network parameters for the simulation test Bitcoin
|
// SimNetParams defines the network parameters for the simulation test Bitcoin
|
||||||
// network. This network is similar to the normal test network except it is
|
// network. This network is similar to the normal test network except it is
|
||||||
// intended for private use within a group of individuals doing simulation
|
// intended for private use within a group of individuals doing simulation
|
||||||
|
@ -761,6 +940,16 @@ var SimNetParams = Params{
|
||||||
),
|
),
|
||||||
CustomActivationThreshold: 75, // Only needs 75% hash rate.
|
CustomActivationThreshold: 75, // Only needs 75% hash rate.
|
||||||
},
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 29,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mempool parameters
|
// Mempool parameters
|
||||||
|
@ -861,6 +1050,16 @@ func CustomSignetParams(challenge []byte, dnsSeeds []DNSSeed) Params {
|
||||||
time.Time{}, // Never expires
|
time.Time{}, // Never expires
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
DeploymentTestDummyAlwaysActive: {
|
||||||
|
BitNumber: 30,
|
||||||
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
time.Time{}, // Always available for vote
|
||||||
|
),
|
||||||
|
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||||
|
time.Time{}, // Never expires
|
||||||
|
),
|
||||||
|
AlwaysActiveHeight: 1,
|
||||||
|
},
|
||||||
DeploymentCSV: {
|
DeploymentCSV: {
|
||||||
BitNumber: 29,
|
BitNumber: 29,
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||||
|
@ -1075,6 +1274,7 @@ func init() {
|
||||||
// Register all default networks when the package is initialized.
|
// Register all default networks when the package is initialized.
|
||||||
mustRegister(&MainNetParams)
|
mustRegister(&MainNetParams)
|
||||||
mustRegister(&TestNet3Params)
|
mustRegister(&TestNet3Params)
|
||||||
|
mustRegister(&TestNet4Params)
|
||||||
mustRegister(&RegressionNetParams)
|
mustRegister(&RegressionNetParams)
|
||||||
mustRegister(&SimNetParams)
|
mustRegister(&SimNetParams)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,11 @@ func TestRegister(t *testing.T) {
|
||||||
params: &TestNet3Params,
|
params: &TestNet3Params,
|
||||||
err: ErrDuplicateNet,
|
err: ErrDuplicateNet,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate testnet4",
|
||||||
|
params: &TestNet4Params,
|
||||||
|
err: ErrDuplicateNet,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "duplicate simnet",
|
name: "duplicate simnet",
|
||||||
params: &SimNetParams,
|
params: &SimNetParams,
|
||||||
|
@ -83,6 +88,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.PubKeyHashAddrID,
|
magic: TestNet3Params.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.PubKeyHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -109,6 +118,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.ScriptHashAddrID,
|
magic: TestNet3Params.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.ScriptHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.ScriptHashAddrID,
|
magic: RegressionNetParams.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -135,6 +148,10 @@ func TestRegister(t *testing.T) {
|
||||||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -175,6 +192,11 @@ func TestRegister(t *testing.T) {
|
||||||
want: TestNet3Params.HDPublicKeyID[:],
|
want: TestNet3Params.HDPublicKeyID[:],
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
priv: TestNet4Params.HDPrivateKeyID[:],
|
||||||
|
want: TestNet4Params.HDPublicKeyID[:],
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
priv: RegressionNetParams.HDPrivateKeyID[:],
|
priv: RegressionNetParams.HDPrivateKeyID[:],
|
||||||
want: RegressionNetParams.HDPublicKeyID[:],
|
want: RegressionNetParams.HDPublicKeyID[:],
|
||||||
|
@ -217,6 +239,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.PubKeyHashAddrID,
|
magic: TestNet3Params.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.PubKeyHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -243,6 +269,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.ScriptHashAddrID,
|
magic: TestNet3Params.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.ScriptHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.ScriptHashAddrID,
|
magic: RegressionNetParams.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -269,6 +299,10 @@ func TestRegister(t *testing.T) {
|
||||||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -324,6 +358,11 @@ func TestRegister(t *testing.T) {
|
||||||
params: &TestNet3Params,
|
params: &TestNet3Params,
|
||||||
err: ErrDuplicateNet,
|
err: ErrDuplicateNet,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate testnet4",
|
||||||
|
params: &TestNet4Params,
|
||||||
|
err: ErrDuplicateNet,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "duplicate simnet",
|
name: "duplicate simnet",
|
||||||
params: &SimNetParams,
|
params: &SimNetParams,
|
||||||
|
@ -344,6 +383,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.PubKeyHashAddrID,
|
magic: TestNet3Params.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.PubKeyHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -370,6 +413,10 @@ func TestRegister(t *testing.T) {
|
||||||
magic: TestNet3Params.ScriptHashAddrID,
|
magic: TestNet3Params.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
magic: TestNet4Params.ScriptHashAddrID,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
magic: RegressionNetParams.ScriptHashAddrID,
|
magic: RegressionNetParams.ScriptHashAddrID,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -396,6 +443,10 @@ func TestRegister(t *testing.T) {
|
||||||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||||
valid: true,
|
valid: true,
|
||||||
|
@ -436,6 +487,11 @@ func TestRegister(t *testing.T) {
|
||||||
want: TestNet3Params.HDPublicKeyID[:],
|
want: TestNet3Params.HDPublicKeyID[:],
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
priv: TestNet4Params.HDPrivateKeyID[:],
|
||||||
|
want: TestNet4Params.HDPublicKeyID[:],
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
priv: RegressionNetParams.HDPrivateKeyID[:],
|
priv: RegressionNetParams.HDPrivateKeyID[:],
|
||||||
want: RegressionNetParams.HDPublicKeyID[:],
|
want: RegressionNetParams.HDPublicKeyID[:],
|
||||||
|
|
|
@ -31,7 +31,7 @@ var (
|
||||||
activeNetParams = &chaincfg.MainNetParams
|
activeNetParams = &chaincfg.MainNetParams
|
||||||
)
|
)
|
||||||
|
|
||||||
// config defines the configuration options for findcheckpoint.
|
// config defines the configuration options for addblock.
|
||||||
//
|
//
|
||||||
// See loadConfig for details on the configuration load process.
|
// See loadConfig for details on the configuration load process.
|
||||||
type config struct {
|
type config struct {
|
||||||
|
@ -42,7 +42,8 @@ type config struct {
|
||||||
Progress int `short:"p" long:"progress" description:"Show a progress message each time this number of seconds have passed -- Use 0 to disable progress announcements"`
|
Progress int `short:"p" long:"progress" description:"Show a progress message each time this number of seconds have passed -- Use 0 to disable progress announcements"`
|
||||||
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
||||||
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
||||||
TestNet3 bool `long:"testnet" description:"Use the test network"`
|
TestNet3 bool `long:"testnet" description:"Use the test network (version 3)"`
|
||||||
|
TestNet4 bool `long:"testnet4" description:"Use the test network (version 4)"`
|
||||||
TxIndex bool `long:"txindex" description:"Build a full hash-based transaction index which makes all transactions available via the getrawtransaction RPC"`
|
TxIndex bool `long:"txindex" description:"Build a full hash-based transaction index which makes all transactions available via the getrawtransaction RPC"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +109,10 @@ func loadConfig() (*config, []string, error) {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.TestNet3Params
|
activeNetParams = &chaincfg.TestNet3Params
|
||||||
}
|
}
|
||||||
|
if cfg.TestNet4 {
|
||||||
|
numNets++
|
||||||
|
activeNetParams = &chaincfg.TestNet4Params
|
||||||
|
}
|
||||||
if cfg.RegressionTest {
|
if cfg.RegressionTest {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.RegressionNetParams
|
activeNetParams = &chaincfg.RegressionNetParams
|
||||||
|
|
|
@ -106,7 +106,8 @@ type config struct {
|
||||||
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
|
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
|
||||||
SimNet bool `long:"simnet" description:"Connect to the simulation test network"`
|
SimNet bool `long:"simnet" description:"Connect to the simulation test network"`
|
||||||
TLSSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"`
|
TLSSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"`
|
||||||
TestNet3 bool `long:"testnet" description:"Connect to testnet"`
|
TestNet3 bool `long:"testnet" description:"Connect to testnet (version 3)"`
|
||||||
|
TestNet4 bool `long:"testnet4" description:"Connect to testnet (version 4)"`
|
||||||
SigNet bool `long:"signet" description:"Connect to signet"`
|
SigNet bool `long:"signet" description:"Connect to signet"`
|
||||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||||
Wallet bool `long:"wallet" description:"Connect to wallet"`
|
Wallet bool `long:"wallet" description:"Connect to wallet"`
|
||||||
|
@ -125,6 +126,12 @@ func normalizeAddress(addr string, chain *chaincfg.Params, useWallet bool) (stri
|
||||||
} else {
|
} else {
|
||||||
defaultPort = "18334"
|
defaultPort = "18334"
|
||||||
}
|
}
|
||||||
|
case &chaincfg.TestNet4Params:
|
||||||
|
if useWallet {
|
||||||
|
defaultPort = "48332"
|
||||||
|
} else {
|
||||||
|
defaultPort = "48334"
|
||||||
|
}
|
||||||
case &chaincfg.SimNetParams:
|
case &chaincfg.SimNetParams:
|
||||||
if useWallet {
|
if useWallet {
|
||||||
defaultPort = "18554"
|
defaultPort = "18554"
|
||||||
|
@ -272,6 +279,10 @@ func loadConfig() (*config, []string, error) {
|
||||||
numNets++
|
numNets++
|
||||||
network = &chaincfg.TestNet3Params
|
network = &chaincfg.TestNet3Params
|
||||||
}
|
}
|
||||||
|
if cfg.TestNet4 {
|
||||||
|
numNets++
|
||||||
|
network = &chaincfg.TestNet4Params
|
||||||
|
}
|
||||||
if cfg.SimNet {
|
if cfg.SimNet {
|
||||||
numNets++
|
numNets++
|
||||||
network = &chaincfg.SimNetParams
|
network = &chaincfg.SimNetParams
|
||||||
|
|
|
@ -42,7 +42,8 @@ type config struct {
|
||||||
NumCandidates int `short:"n" long:"numcandidates" description:"Max num of checkpoint candidates to show {1-20}"`
|
NumCandidates int `short:"n" long:"numcandidates" description:"Max num of checkpoint candidates to show {1-20}"`
|
||||||
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
||||||
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
||||||
TestNet3 bool `long:"testnet" description:"Use the test network"`
|
TestNet3 bool `long:"testnet" description:"Use the test network (version 3)"`
|
||||||
|
TestNet4 bool `long:"testnet4" description:"Use the test network (version 4)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// validDbType returns whether or not dbType is a supported database type.
|
// validDbType returns whether or not dbType is a supported database type.
|
||||||
|
@ -96,6 +97,10 @@ func loadConfig() (*config, []string, error) {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.TestNet3Params
|
activeNetParams = &chaincfg.TestNet3Params
|
||||||
}
|
}
|
||||||
|
if cfg.TestNet4 {
|
||||||
|
numNets++
|
||||||
|
activeNetParams = &chaincfg.TestNet4Params
|
||||||
|
}
|
||||||
if cfg.RegressionTest {
|
if cfg.RegressionTest {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.RegressionNetParams
|
activeNetParams = &chaincfg.RegressionNetParams
|
||||||
|
|
|
@ -171,7 +171,8 @@ type config struct {
|
||||||
SigNet bool `long:"signet" description:"Use the signet test network"`
|
SigNet bool `long:"signet" description:"Use the signet test network"`
|
||||||
SigNetChallenge string `long:"signetchallenge" description:"Connect to a custom signet network defined by this challenge instead of using the global default signet test network -- Can be specified multiple times"`
|
SigNetChallenge string `long:"signetchallenge" description:"Connect to a custom signet network defined by this challenge instead of using the global default signet test network -- Can be specified multiple times"`
|
||||||
SigNetSeedNode []string `long:"signetseednode" description:"Specify a seed node for the signet network instead of using the global default signet network seed nodes"`
|
SigNetSeedNode []string `long:"signetseednode" description:"Specify a seed node for the signet network instead of using the global default signet network seed nodes"`
|
||||||
TestNet3 bool `long:"testnet" description:"Use the test network"`
|
TestNet3 bool `long:"testnet" description:"Use the test network (version 3)"`
|
||||||
|
TestNet4 bool `long:"testnet4" description:"Use the test network (version 4)"`
|
||||||
TorIsolation bool `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
|
TorIsolation bool `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
|
||||||
TrickleInterval time.Duration `long:"trickleinterval" description:"Minimum time between attempts to send new inventory to a connected peer"`
|
TrickleInterval time.Duration `long:"trickleinterval" description:"Minimum time between attempts to send new inventory to a connected peer"`
|
||||||
UtxoCacheMaxSizeMiB uint `long:"utxocachemaxsize" description:"The maximum size in MiB of the UTXO cache"`
|
UtxoCacheMaxSizeMiB uint `long:"utxocachemaxsize" description:"The maximum size in MiB of the UTXO cache"`
|
||||||
|
@ -548,6 +549,10 @@ func loadConfig() (*config, []string, error) {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &testNet3Params
|
activeNetParams = &testNet3Params
|
||||||
}
|
}
|
||||||
|
if cfg.TestNet4 {
|
||||||
|
numNets++
|
||||||
|
activeNetParams = &testNet4Params
|
||||||
|
}
|
||||||
if cfg.RegressionTest {
|
if cfg.RegressionTest {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = ®ressionNetParams
|
activeNetParams = ®ressionNetParams
|
||||||
|
|
|
@ -37,6 +37,7 @@ type config struct {
|
||||||
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
||||||
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
||||||
TestNet3 bool `long:"testnet" description:"Use the test network"`
|
TestNet3 bool `long:"testnet" description:"Use the test network"`
|
||||||
|
TestNet4 bool `long:"testnet4" description:"Use the test network (version 4)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileExists reports whether the named file or directory exists.
|
// fileExists reports whether the named file or directory exists.
|
||||||
|
@ -84,6 +85,10 @@ func setupGlobalConfig() error {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.TestNet3Params
|
activeNetParams = &chaincfg.TestNet3Params
|
||||||
}
|
}
|
||||||
|
if cfg.TestNet4 {
|
||||||
|
numNets++
|
||||||
|
activeNetParams = &chaincfg.TestNet4Params
|
||||||
|
}
|
||||||
if cfg.RegressionTest {
|
if cfg.RegressionTest {
|
||||||
numNets++
|
numNets++
|
||||||
activeNetParams = &chaincfg.RegressionNetParams
|
activeNetParams = &chaincfg.RegressionNetParams
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -18,6 +18,7 @@ require (
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.22.0
|
||||||
golang.org/x/sys v0.19.0
|
golang.org/x/sys v0.19.0
|
||||||
|
pgregory.net/rapid v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -139,3 +139,5 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||||
|
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||||
|
|
|
@ -139,6 +139,14 @@ func testBIP0009(t *testing.T, forkKey string, deploymentID uint32) {
|
||||||
}
|
}
|
||||||
defer r.TearDown()
|
defer r.TearDown()
|
||||||
|
|
||||||
|
// If the deployment is meant to be always active, then it should be
|
||||||
|
// active from the very first block.
|
||||||
|
if deploymentID == chaincfg.DeploymentTestDummyAlwaysActive {
|
||||||
|
assertChainHeight(r, t, 0)
|
||||||
|
assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdActive)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// *** ThresholdDefined ***
|
// *** ThresholdDefined ***
|
||||||
//
|
//
|
||||||
// Assert the chain height is the expected value and the soft fork
|
// Assert the chain height is the expected value and the soft fork
|
||||||
|
@ -340,6 +348,7 @@ func TestBIP0009(t *testing.T) {
|
||||||
|
|
||||||
testBIP0009(t, "dummy", chaincfg.DeploymentTestDummy)
|
testBIP0009(t, "dummy", chaincfg.DeploymentTestDummy)
|
||||||
testBIP0009(t, "dummy-min-activation", chaincfg.DeploymentTestDummyMinActivation)
|
testBIP0009(t, "dummy-min-activation", chaincfg.DeploymentTestDummyMinActivation)
|
||||||
|
testBIP0009(t, "dummy-always-active", chaincfg.DeploymentTestDummyAlwaysActive)
|
||||||
testBIP0009(t, "segwit", chaincfg.DeploymentSegwit)
|
testBIP0009(t, "segwit", chaincfg.DeploymentSegwit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
params.go
12
params.go
|
@ -21,7 +21,7 @@ type params struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mainNetParams contains parameters specific to the main network
|
// mainNetParams contains parameters specific to the main network
|
||||||
// (wire.MainNet). NOTE: The RPC port is intentionally different than the
|
// (wire.MainNet). NOTE: The RPC port is intentionally different from the
|
||||||
// reference implementation because btcd does not handle wallet requests. The
|
// reference implementation because btcd does not handle wallet requests. The
|
||||||
// separate wallet process listens on the well-known port and forwards requests
|
// separate wallet process listens on the well-known port and forwards requests
|
||||||
// it does not handle on to btcd. This approach allows the wallet process
|
// it does not handle on to btcd. This approach allows the wallet process
|
||||||
|
@ -41,13 +41,21 @@ var regressionNetParams = params{
|
||||||
}
|
}
|
||||||
|
|
||||||
// testNet3Params contains parameters specific to the test network (version 3)
|
// testNet3Params contains parameters specific to the test network (version 3)
|
||||||
// (wire.TestNet3). NOTE: The RPC port is intentionally different than the
|
// (wire.TestNet3). NOTE: The RPC port is intentionally different from the
|
||||||
// reference implementation - see the mainNetParams comment for details.
|
// reference implementation - see the mainNetParams comment for details.
|
||||||
var testNet3Params = params{
|
var testNet3Params = params{
|
||||||
Params: &chaincfg.TestNet3Params,
|
Params: &chaincfg.TestNet3Params,
|
||||||
rpcPort: "18334",
|
rpcPort: "18334",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testNet4Params contains parameters specific to the test network (version 4)
|
||||||
|
// (wire.TestNet4). NOTE: The RPC port is intentionally different from the
|
||||||
|
// reference implementation - see the mainNetParams comment for details.
|
||||||
|
var testNet4Params = params{
|
||||||
|
Params: &chaincfg.TestNet4Params,
|
||||||
|
rpcPort: "48334",
|
||||||
|
}
|
||||||
|
|
||||||
// simNetParams contains parameters specific to the simulation test network
|
// simNetParams contains parameters specific to the simulation test network
|
||||||
// (wire.SimNet).
|
// (wire.SimNet).
|
||||||
var simNetParams = params{
|
var simNetParams = params{
|
||||||
|
|
|
@ -1529,6 +1529,8 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error
|
||||||
client.chainParams = &chaincfg.MainNetParams
|
client.chainParams = &chaincfg.MainNetParams
|
||||||
case chaincfg.TestNet3Params.Name:
|
case chaincfg.TestNet3Params.Name:
|
||||||
client.chainParams = &chaincfg.TestNet3Params
|
client.chainParams = &chaincfg.TestNet3Params
|
||||||
|
case chaincfg.TestNet4Params.Name:
|
||||||
|
client.chainParams = &chaincfg.TestNet4Params
|
||||||
case chaincfg.RegressionNetParams.Name:
|
case chaincfg.RegressionNetParams.Name:
|
||||||
client.chainParams = &chaincfg.RegressionNetParams
|
client.chainParams = &chaincfg.RegressionNetParams
|
||||||
case chaincfg.SigNetParams.Name:
|
case chaincfg.SigNetParams.Name:
|
||||||
|
|
|
@ -1258,6 +1258,9 @@ func handleGetBlockChainInfo(s *rpcServer, cmd interface{}, closeChan <-chan str
|
||||||
case chaincfg.DeploymentTestDummyMinActivation:
|
case chaincfg.DeploymentTestDummyMinActivation:
|
||||||
forkName = "dummy-min-activation"
|
forkName = "dummy-min-activation"
|
||||||
|
|
||||||
|
case chaincfg.DeploymentTestDummyAlwaysActive:
|
||||||
|
forkName = "dummy-always-active"
|
||||||
|
|
||||||
case chaincfg.DeploymentCSV:
|
case chaincfg.DeploymentCSV:
|
||||||
forkName = "csv"
|
forkName = "csv"
|
||||||
|
|
||||||
|
@ -2358,7 +2361,7 @@ func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in
|
||||||
Connections: s.cfg.ConnMgr.ConnectedCount(),
|
Connections: s.cfg.ConnMgr.ConnectedCount(),
|
||||||
Proxy: cfg.Proxy,
|
Proxy: cfg.Proxy,
|
||||||
Difficulty: getDifficultyRatio(best.Bits, s.cfg.ChainParams),
|
Difficulty: getDifficultyRatio(best.Bits, s.cfg.ChainParams),
|
||||||
TestNet: cfg.TestNet3,
|
TestNet: cfg.TestNet3 || cfg.TestNet4,
|
||||||
RelayFee: cfg.minRelayTxFee.ToBTC(),
|
RelayFee: cfg.minRelayTxFee.ToBTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2413,7 +2416,7 @@ func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
||||||
HashesPerSec: s.cfg.CPUMiner.HashesPerSecond(),
|
HashesPerSec: s.cfg.CPUMiner.HashesPerSecond(),
|
||||||
NetworkHashPS: networkHashesPerSec,
|
NetworkHashPS: networkHashesPerSec,
|
||||||
PooledTx: uint64(s.cfg.TxMemPool.Count()),
|
PooledTx: uint64(s.cfg.TxMemPool.Count()),
|
||||||
TestNet: cfg.TestNet3,
|
TestNet: cfg.TestNet3 || cfg.TestNet4,
|
||||||
}
|
}
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,9 @@ const (
|
||||||
// TestNet3 represents the test network (version 3).
|
// TestNet3 represents the test network (version 3).
|
||||||
TestNet3 BitcoinNet = 0x0709110b
|
TestNet3 BitcoinNet = 0x0709110b
|
||||||
|
|
||||||
|
// TestNet4 represents the test network (version 4).
|
||||||
|
TestNet4 BitcoinNet = 0x283f161c
|
||||||
|
|
||||||
// SigNet represents the public default SigNet. For custom signets,
|
// SigNet represents the public default SigNet. For custom signets,
|
||||||
// see CustomSignetParams.
|
// see CustomSignetParams.
|
||||||
SigNet BitcoinNet = 0x40CF030A
|
SigNet BitcoinNet = 0x40CF030A
|
||||||
|
@ -193,6 +196,7 @@ var bnStrings = map[BitcoinNet]string{
|
||||||
MainNet: "MainNet",
|
MainNet: "MainNet",
|
||||||
TestNet: "TestNet",
|
TestNet: "TestNet",
|
||||||
TestNet3: "TestNet3",
|
TestNet3: "TestNet3",
|
||||||
|
TestNet4: "TestNet4",
|
||||||
SigNet: "SigNet",
|
SigNet: "SigNet",
|
||||||
SimNet: "SimNet",
|
SimNet: "SimNet",
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ func TestBitcoinNetStringer(t *testing.T) {
|
||||||
{MainNet, "MainNet"},
|
{MainNet, "MainNet"},
|
||||||
{TestNet, "TestNet"},
|
{TestNet, "TestNet"},
|
||||||
{TestNet3, "TestNet3"},
|
{TestNet3, "TestNet3"},
|
||||||
|
{TestNet4, "TestNet4"},
|
||||||
{SigNet, "SigNet"},
|
{SigNet, "SigNet"},
|
||||||
{SimNet, "SimNet"},
|
{SimNet, "SimNet"},
|
||||||
{0xffffffff, "Unknown BitcoinNet (4294967295)"},
|
{0xffffffff, "Unknown BitcoinNet (4294967295)"},
|
||||||
|
|
Loading…
Add table
Reference in a new issue