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
|
||||
/.vim
|
||||
|
||||
#IDE
|
||||
.idea
|
||||
|
||||
# Binaries produced by "make build"
|
||||
/addblock
|
||||
/btcctl
|
||||
|
@ -54,3 +57,4 @@ btcutil/psbt/coverage.txt
|
|||
/gencerts
|
||||
|
||||
.DS_Store
|
||||
.aider*
|
||||
|
|
|
@ -191,12 +191,20 @@ func calcNextRequiredDifficulty(lastNode HeaderCtx, newBlockTime time.Time,
|
|||
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:
|
||||
// currentDifficulty * (adjustedTimespan / targetTimespan)
|
||||
// The result uses integer division which means it will be slightly
|
||||
// rounded down. Bitcoind also uses integer division to calculate this
|
||||
// result.
|
||||
oldTarget := CompactToBig(lastNode.Bits())
|
||||
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
||||
targetTimeSpan := int64(c.ChainParams().TargetTimespan / time.Second)
|
||||
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
||||
|
|
|
@ -220,6 +220,10 @@ const (
|
|||
// current chain tip. This is not a block validation rule, but is required
|
||||
// for block proposals submitted via getblocktemplate RPC.
|
||||
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.
|
||||
|
|
|
@ -102,6 +102,11 @@ type thresholdConditionChecker interface {
|
|||
// not the bit associated with the condition is set, but can be more
|
||||
// complex as needed.
|
||||
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
|
||||
|
@ -279,7 +284,17 @@ func thresholdStateTransition(state ThresholdState, prevNode *blockNode,
|
|||
// threshold states for previous windows are only calculated once.
|
||||
//
|
||||
// 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
|
||||
// defined by definition.
|
||||
confirmationWindow := int32(checker.MinerConfirmationWindow())
|
||||
|
|
|
@ -175,6 +175,10 @@ func (c customDeploymentChecker) Condition(_ *blockNode) (bool, error) {
|
|||
return c.conditionTrue, nil
|
||||
}
|
||||
|
||||
func (c customDeploymentChecker) ForceActive(_ *blockNode) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TestThresholdStateTransition tests that the thresholdStateTransition
|
||||
// properly implements the BIP 009 state machine, along with the speedy trial
|
||||
// augments.
|
||||
|
|
|
@ -46,6 +46,11 @@ const (
|
|||
// coinbaseHeightAllocSize is the amount of bytes that the
|
||||
// ScriptBuilder will allocate when validating the coinbase height.
|
||||
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 (
|
||||
|
@ -684,6 +689,12 @@ func compareScript(height int32, script []byte) error {
|
|||
func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
||||
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
|
||||
if !fastAdd {
|
||||
// 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)
|
||||
return ruleError(ErrTimeTooOld, str)
|
||||
}
|
||||
}
|
||||
|
||||
// The height of this block is one more than the referenced previous
|
||||
// block.
|
||||
blockHeight := prevNode.Height() + 1
|
||||
// Testnet4 only: Check timestamp against prev for
|
||||
// difficulty-adjustment blocks to prevent timewarp attacks.
|
||||
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
|
||||
// has upgraded. These were originally voted on by BIP0034,
|
||||
// BIP0065, and BIP0066.
|
||||
params := c.ChainParams()
|
||||
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
|
||||
header.Version < 3 && blockHeight >= params.BIP0066Height ||
|
||||
header.Version < 4 && blockHeight >= params.BIP0065Height {
|
||||
|
@ -761,6 +780,30 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
|||
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
|
||||
// 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 the CSV soft-fork is now active, then modify the
|
||||
// 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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// test a specific deployment rule. This is required for properly detecting
|
||||
// and activating consensus rule changes.
|
||||
|
@ -207,15 +214,9 @@ func (c deploymentChecker) MinerConfirmationWindow() uint32 {
|
|||
}
|
||||
|
||||
// EligibleToActivate returns true if a custom deployment can transition from
|
||||
// the LockedIn to the Active state. For normal deployments, this always
|
||||
// returns true. However, some deployments add extra rules like a minimum
|
||||
// activation height, which can be abstracted into a generic arbitrary check at
|
||||
// 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.
|
||||
// the LockedIn to the Active state. In addition to the traditional minimum
|
||||
// activation height (MinActivationHeight), an optional AlwaysActiveHeight can
|
||||
// force the deployment to be active after a specified height.
|
||||
func (c deploymentChecker) EligibleToActivate(blkNode *blockNode) bool {
|
||||
// No activation height, so it's always ready to go.
|
||||
if c.deployment.MinActivationHeight == 0 {
|
||||
|
@ -249,6 +250,28 @@ func (c deploymentChecker) Condition(node *blockNode) (bool, error) {
|
|||
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
|
||||
// passed previous block node based on the state of started and locked in
|
||||
// rule change deployments.
|
||||
|
|
|
@ -143,6 +143,77 @@ var testNet3GenesisBlock = wire.MsgBlock{
|
|||
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
|
||||
// simulation test network.
|
||||
var simNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// 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
|
||||
// for validity by checking the encoded bytes and hashes.
|
||||
func TestSimNetGenesisBlock(t *testing.T) {
|
||||
|
@ -268,6 +297,44 @@ var testNet3GenesisBlockBytes = []byte{
|
|||
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
|
||||
// the simulation test network as of protocol version 70002.
|
||||
var simNetGenesisBlockBytes = []byte{
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -78,6 +79,16 @@ type Checkpoint struct {
|
|||
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.
|
||||
type DNSSeed struct {
|
||||
// Host defines the hostname of the seed.
|
||||
|
@ -108,6 +119,11 @@ type ConsensusDeployment struct {
|
|||
// activation. A value of 1815 block denotes a 90% threshold.
|
||||
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
|
||||
// ConsensusDeployment has started or not.
|
||||
DeploymentStarter ConsensusDeploymentStarter
|
||||
|
@ -146,6 +162,10 @@ const (
|
|||
// the deployment of BIPS 340, 341 and 342.
|
||||
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
|
||||
// determine how many defined deployments there currently are.
|
||||
|
||||
|
@ -189,6 +209,10 @@ type Params struct {
|
|||
// regtest like networks.
|
||||
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
|
||||
// BIP became active.
|
||||
BIP0034Height int32
|
||||
|
@ -375,6 +399,16 @@ var MainNetParams = Params{
|
|||
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: 0,
|
||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||
|
@ -486,6 +520,16 @@ var RegressionNetParams = Params{
|
|||
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: 0,
|
||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||
|
@ -620,6 +664,16 @@ var TestNet3Params = Params{
|
|||
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: 0,
|
||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||
|
@ -673,6 +727,131 @@ var TestNet3Params = Params{
|
|||
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
|
||||
// network. This network is similar to the normal test network except it is
|
||||
// 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.
|
||||
},
|
||||
DeploymentTestDummyAlwaysActive: {
|
||||
BitNumber: 29,
|
||||
DeploymentStarter: NewMedianTimeDeploymentStarter(
|
||||
time.Time{}, // Always available for vote
|
||||
),
|
||||
DeploymentEnder: NewMedianTimeDeploymentEnder(
|
||||
time.Time{}, // Never expires
|
||||
),
|
||||
AlwaysActiveHeight: 1,
|
||||
},
|
||||
},
|
||||
|
||||
// Mempool parameters
|
||||
|
@ -861,6 +1050,16 @@ func CustomSignetParams(challenge []byte, dnsSeeds []DNSSeed) Params {
|
|||
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(
|
||||
|
@ -1075,6 +1274,7 @@ func init() {
|
|||
// Register all default networks when the package is initialized.
|
||||
mustRegister(&MainNetParams)
|
||||
mustRegister(&TestNet3Params)
|
||||
mustRegister(&TestNet4Params)
|
||||
mustRegister(&RegressionNetParams)
|
||||
mustRegister(&SimNetParams)
|
||||
}
|
||||
|
|
|
@ -68,6 +68,11 @@ func TestRegister(t *testing.T) {
|
|||
params: &TestNet3Params,
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
{
|
||||
name: "duplicate testnet4",
|
||||
params: &TestNet4Params,
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
{
|
||||
name: "duplicate simnet",
|
||||
params: &SimNetParams,
|
||||
|
@ -83,6 +88,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
|
@ -109,6 +118,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.ScriptHashAddrID,
|
||||
valid: true,
|
||||
|
@ -135,6 +148,10 @@ func TestRegister(t *testing.T) {
|
|||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
|
@ -175,6 +192,11 @@ func TestRegister(t *testing.T) {
|
|||
want: TestNet3Params.HDPublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: TestNet4Params.HDPrivateKeyID[:],
|
||||
want: TestNet4Params.HDPublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: RegressionNetParams.HDPrivateKeyID[:],
|
||||
want: RegressionNetParams.HDPublicKeyID[:],
|
||||
|
@ -217,6 +239,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
|
@ -243,6 +269,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.ScriptHashAddrID,
|
||||
valid: true,
|
||||
|
@ -269,6 +299,10 @@ func TestRegister(t *testing.T) {
|
|||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
|
@ -324,6 +358,11 @@ func TestRegister(t *testing.T) {
|
|||
params: &TestNet3Params,
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
{
|
||||
name: "duplicate testnet4",
|
||||
params: &TestNet4Params,
|
||||
err: ErrDuplicateNet,
|
||||
},
|
||||
{
|
||||
name: "duplicate simnet",
|
||||
params: &SimNetParams,
|
||||
|
@ -344,6 +383,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.PubKeyHashAddrID,
|
||||
valid: true,
|
||||
|
@ -370,6 +413,10 @@ func TestRegister(t *testing.T) {
|
|||
magic: TestNet3Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: TestNet4Params.ScriptHashAddrID,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
magic: RegressionNetParams.ScriptHashAddrID,
|
||||
valid: true,
|
||||
|
@ -396,6 +443,10 @@ func TestRegister(t *testing.T) {
|
|||
prefix: TestNet3Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: TestNet4Params.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
|
||||
valid: true,
|
||||
|
@ -436,6 +487,11 @@ func TestRegister(t *testing.T) {
|
|||
want: TestNet3Params.HDPublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: TestNet4Params.HDPrivateKeyID[:],
|
||||
want: TestNet4Params.HDPublicKeyID[:],
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
priv: RegressionNetParams.HDPrivateKeyID[:],
|
||||
want: RegressionNetParams.HDPublicKeyID[:],
|
||||
|
|
|
@ -31,7 +31,7 @@ var (
|
|||
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.
|
||||
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"`
|
||||
RegressionTest bool `long:"regtest" description:"Use the regression 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"`
|
||||
}
|
||||
|
||||
|
@ -108,6 +109,10 @@ func loadConfig() (*config, []string, error) {
|
|||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet3Params
|
||||
}
|
||||
if cfg.TestNet4 {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet4Params
|
||||
}
|
||||
if cfg.RegressionTest {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.RegressionNetParams
|
||||
|
|
|
@ -106,7 +106,8 @@ type config struct {
|
|||
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
|
||||
SimNet bool `long:"simnet" description:"Connect to the simulation test network"`
|
||||
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"`
|
||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||
Wallet bool `long:"wallet" description:"Connect to wallet"`
|
||||
|
@ -125,6 +126,12 @@ func normalizeAddress(addr string, chain *chaincfg.Params, useWallet bool) (stri
|
|||
} else {
|
||||
defaultPort = "18334"
|
||||
}
|
||||
case &chaincfg.TestNet4Params:
|
||||
if useWallet {
|
||||
defaultPort = "48332"
|
||||
} else {
|
||||
defaultPort = "48334"
|
||||
}
|
||||
case &chaincfg.SimNetParams:
|
||||
if useWallet {
|
||||
defaultPort = "18554"
|
||||
|
@ -272,6 +279,10 @@ func loadConfig() (*config, []string, error) {
|
|||
numNets++
|
||||
network = &chaincfg.TestNet3Params
|
||||
}
|
||||
if cfg.TestNet4 {
|
||||
numNets++
|
||||
network = &chaincfg.TestNet4Params
|
||||
}
|
||||
if cfg.SimNet {
|
||||
numNets++
|
||||
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}"`
|
||||
RegressionTest bool `long:"regtest" description:"Use the regression 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.
|
||||
|
@ -96,6 +97,10 @@ func loadConfig() (*config, []string, error) {
|
|||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet3Params
|
||||
}
|
||||
if cfg.TestNet4 {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet4Params
|
||||
}
|
||||
if cfg.RegressionTest {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.RegressionNetParams
|
||||
|
|
|
@ -171,7 +171,8 @@ type config struct {
|
|||
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"`
|
||||
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."`
|
||||
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"`
|
||||
|
@ -548,6 +549,10 @@ func loadConfig() (*config, []string, error) {
|
|||
numNets++
|
||||
activeNetParams = &testNet3Params
|
||||
}
|
||||
if cfg.TestNet4 {
|
||||
numNets++
|
||||
activeNetParams = &testNet4Params
|
||||
}
|
||||
if cfg.RegressionTest {
|
||||
numNets++
|
||||
activeNetParams = ®ressionNetParams
|
||||
|
|
|
@ -37,6 +37,7 @@ type config struct {
|
|||
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
|
||||
SimNet bool `long:"simnet" description:"Use the simulation 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.
|
||||
|
@ -84,6 +85,10 @@ func setupGlobalConfig() error {
|
|||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet3Params
|
||||
}
|
||||
if cfg.TestNet4 {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.TestNet4Params
|
||||
}
|
||||
if cfg.RegressionTest {
|
||||
numNets++
|
||||
activeNetParams = &chaincfg.RegressionNetParams
|
||||
|
|
1
go.mod
1
go.mod
|
@ -18,6 +18,7 @@ require (
|
|||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/sys v0.19.0
|
||||
pgregory.net/rapid v1.2.0
|
||||
)
|
||||
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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()
|
||||
|
||||
// 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 ***
|
||||
//
|
||||
// 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-min-activation", chaincfg.DeploymentTestDummyMinActivation)
|
||||
testBIP0009(t, "dummy-always-active", chaincfg.DeploymentTestDummyAlwaysActive)
|
||||
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
|
||||
// (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
|
||||
// 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
|
||||
|
@ -41,13 +41,21 @@ var regressionNetParams = params{
|
|||
}
|
||||
|
||||
// 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.
|
||||
var testNet3Params = params{
|
||||
Params: &chaincfg.TestNet3Params,
|
||||
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
|
||||
// (wire.SimNet).
|
||||
var simNetParams = params{
|
||||
|
|
|
@ -1529,6 +1529,8 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error
|
|||
client.chainParams = &chaincfg.MainNetParams
|
||||
case chaincfg.TestNet3Params.Name:
|
||||
client.chainParams = &chaincfg.TestNet3Params
|
||||
case chaincfg.TestNet4Params.Name:
|
||||
client.chainParams = &chaincfg.TestNet4Params
|
||||
case chaincfg.RegressionNetParams.Name:
|
||||
client.chainParams = &chaincfg.RegressionNetParams
|
||||
case chaincfg.SigNetParams.Name:
|
||||
|
|
|
@ -1258,6 +1258,9 @@ func handleGetBlockChainInfo(s *rpcServer, cmd interface{}, closeChan <-chan str
|
|||
case chaincfg.DeploymentTestDummyMinActivation:
|
||||
forkName = "dummy-min-activation"
|
||||
|
||||
case chaincfg.DeploymentTestDummyAlwaysActive:
|
||||
forkName = "dummy-always-active"
|
||||
|
||||
case chaincfg.DeploymentCSV:
|
||||
forkName = "csv"
|
||||
|
||||
|
@ -2358,7 +2361,7 @@ func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in
|
|||
Connections: s.cfg.ConnMgr.ConnectedCount(),
|
||||
Proxy: cfg.Proxy,
|
||||
Difficulty: getDifficultyRatio(best.Bits, s.cfg.ChainParams),
|
||||
TestNet: cfg.TestNet3,
|
||||
TestNet: cfg.TestNet3 || cfg.TestNet4,
|
||||
RelayFee: cfg.minRelayTxFee.ToBTC(),
|
||||
}
|
||||
|
||||
|
@ -2413,7 +2416,7 @@ func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
|||
HashesPerSec: s.cfg.CPUMiner.HashesPerSecond(),
|
||||
NetworkHashPS: networkHashesPerSec,
|
||||
PooledTx: uint64(s.cfg.TxMemPool.Count()),
|
||||
TestNet: cfg.TestNet3,
|
||||
TestNet: cfg.TestNet3 || cfg.TestNet4,
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
|
|
@ -179,6 +179,9 @@ const (
|
|||
// TestNet3 represents the test network (version 3).
|
||||
TestNet3 BitcoinNet = 0x0709110b
|
||||
|
||||
// TestNet4 represents the test network (version 4).
|
||||
TestNet4 BitcoinNet = 0x283f161c
|
||||
|
||||
// SigNet represents the public default SigNet. For custom signets,
|
||||
// see CustomSignetParams.
|
||||
SigNet BitcoinNet = 0x40CF030A
|
||||
|
@ -193,6 +196,7 @@ var bnStrings = map[BitcoinNet]string{
|
|||
MainNet: "MainNet",
|
||||
TestNet: "TestNet",
|
||||
TestNet3: "TestNet3",
|
||||
TestNet4: "TestNet4",
|
||||
SigNet: "SigNet",
|
||||
SimNet: "SimNet",
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ func TestBitcoinNetStringer(t *testing.T) {
|
|||
{MainNet, "MainNet"},
|
||||
{TestNet, "TestNet"},
|
||||
{TestNet3, "TestNet3"},
|
||||
{TestNet4, "TestNet4"},
|
||||
{SigNet, "SigNet"},
|
||||
{SimNet, "SimNet"},
|
||||
{0xffffffff, "Unknown BitcoinNet (4294967295)"},
|
||||
|
|
Loading…
Add table
Reference in a new issue