From 730a170b10efd368b509fae183c4b192767a8cd3 Mon Sep 17 00:00:00 2001 From: Oleg Bondar Date: Sat, 9 Nov 2024 22:24:42 +0000 Subject: [PATCH] multi: add testnet4 support Adds support of the Bitcoin testnet version 4 chain. Reference: https://bips.dev/94/ --- .gitignore | 2 + blockchain/difficulty.go | 12 +++- blockchain/error.go | 2 + blockchain/validate.go | 42 ++++++++++---- btcd.go | 5 ++ chaincfg/genesis.go | 71 +++++++++++++++++++++++ chaincfg/genesis_test.go | 67 ++++++++++++++++++++++ chaincfg/params.go | 89 +++++++++++++++++++++++++++++ chaincfg/register_test.go | 56 ++++++++++++++++++ cmd/addblock/config.go | 10 +++- cmd/btcctl/config.go | 13 ++++- cmd/findcheckpoint/config.go | 7 ++- config.go | 7 ++- database/cmd/dbtool/globalconfig.go | 5 ++ params.go | 10 +++- peer/peer.go | 1 + rpcclient/infrastructure.go | 2 + rpcserver.go | 4 +- wire/protocol.go | 4 ++ wire/protocol_test.go | 1 + 20 files changed, 390 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 1e5a40ca27..bedae6c325 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ btcutil/psbt/coverage.txt *.swo /.vim +.idea + # Binaries produced by "make build" /addblock /btcctl diff --git a/blockchain/difficulty.go b/blockchain/difficulty.go index b1e39b9d62..5dcc55667e 100644 --- a/blockchain/difficulty.go +++ b/blockchain/difficulty.go @@ -191,12 +191,22 @@ func calcNextRequiredDifficulty(lastNode HeaderCtx, newBlockTime time.Time, adjustedTimespan = c.MaxRetargetTimespan() } + var oldTarget *big.Int + // Special difficulty rule for Testnet4 + 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()) + } else { + oldTarget = CompactToBig(lastNode.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)) diff --git a/blockchain/error.go b/blockchain/error.go index dc40222235..723cabb0bf 100644 --- a/blockchain/error.go +++ b/blockchain/error.go @@ -220,6 +220,8 @@ const ( // current chain tip. This is not a block validation rule, but is required // for block proposals submitted via getblocktemplate RPC. ErrPrevBlockNotBest + + ErrTimewarpAttack ) // Map of ErrorCode values back to their constant names for pretty printing. diff --git a/blockchain/validate.go b/blockchain/validate.go index 5e24405ef9..e11f4efd3e 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -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 ( @@ -85,7 +90,7 @@ func ShouldHaveSerializedBlockHeight(header *wire.BlockHeader) bool { // IsCoinBaseTx determines whether or not a transaction is a coinbase. A coinbase // is a special transaction created by miners that has no inputs. This is -// represented in the block chain by a transaction with a single input that has +// represented in the blockchain by a transaction with a single input that has // a previous output transaction index set to the maximum value along with a // zero hash. // @@ -109,7 +114,7 @@ func IsCoinBaseTx(msgTx *wire.MsgTx) bool { // IsCoinBase determines whether or not a transaction is a coinbase. A coinbase // is a special transaction created by miners that has no inputs. This is -// represented in the block chain by a transaction with a single input that has +// represented in the blockchain by a transaction with a single input that has // a previous output transaction index set to the maximum value along with a // zero hash. // @@ -446,7 +451,7 @@ func CheckBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, // A block timestamp must not have a greater precision than one second. // This check is necessary because Go time.Time values support // nanosecond precision whereas the consensus rules only apply to - // seconds and it's much nicer to deal with standard Go time values + // seconds, and it's much nicer to deal with standard Go time values // instead of converting to seconds everywhere. if !header.Timestamp.Equal(time.Unix(header.Timestamp.Unix(), 0)) { str := fmt.Sprintf("block timestamp of %v has a higher "+ @@ -669,7 +674,7 @@ func compareScript(height int32, script []byte) error { } // CheckBlockHeaderContext performs several validation checks on the block header -// which depend on its position within the block chain. +// which depend on its position within the blockchain. // // The flags modify the behavior of this function as follows: // - BFFastAdd: All checks except those involving comparing the header against @@ -684,6 +689,10 @@ 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 + fastAdd := flags&BFFastAdd == BFFastAdd if !fastAdd { // Ensure the difficulty specified in the block header matches @@ -710,11 +719,22 @@ 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 c.ChainParams().EnforceBIP94 { + // Check timestamp for the first block of each difficulty adjustment + // interval, except the genesis block. + if blockHeight%c.BlocksPerRetarget() == 0 { + prevBlockTimestamp := time.Unix(prevNode.Timestamp(), 0) + if header.Timestamp.Before(prevBlockTimestamp.Add(-maxTimeWarp)) { + str := "block's timestamp %v is too early on diff adjustment block %v" + str = fmt.Sprintf(str, header.Timestamp, prevBlockTimestamp) + return ruleError(ErrTimewarpAttack, str) + } + } + } + } // Reject outdated block versions once a majority of the network // has upgraded. These were originally voted on by BIP0034, @@ -762,7 +782,7 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx, } // checkBlockContext performs several validation checks on the block which depend -// on its position within the block chain. +// on its position within the blockchain. // // The flags modify the behavior of this function as follows: // - BFFastAdd: The transaction are not checked to see if they are finalized @@ -1067,7 +1087,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi // // In addition, as of BIP0034, duplicate coinbases are no longer // possible due to its requirement for including the block height in the - // coinbase and thus it is no longer possible to create transactions + // coinbase, and thus it is no longer possible to create transactions // that 'overwrite' older ones. Therefore, only enforce the rule if // BIP0034 is not yet active. This is a useful optimization because the // BIP0030 check is expensive since it involves a ton of cache misses in @@ -1230,7 +1250,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 diff --git a/btcd.go b/btcd.go index c7f292cbc9..5c81776bcc 100644 --- a/btcd.go +++ b/btcd.go @@ -64,6 +64,11 @@ func btcdMain(serverChan chan<- *server) error { // Show version at startup. btcdLog.Infof("Version %s", version()) + if cfg.TestNet3 { + btcdLog.Warn("Support for testnet3 is deprecated and will be removed in an upcoming release. " + + "Consider switching to testnet4.") + } + // Enable http profiling server if requested. if cfg.Profile != "" { go func() { diff --git a/chaincfg/genesis.go b/chaincfg/genesis.go index 73d286102b..f734feb27a 100644 --- a/chaincfg/genesis.go +++ b/chaincfg/genesis.go @@ -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. diff --git a/chaincfg/genesis_test.go b/chaincfg/genesis_test.go index 1daf847916..e82d3105a2 100644 --- a/chaincfg/genesis_test.go +++ b/chaincfg/genesis_test.go @@ -6,6 +6,7 @@ package chaincfg import ( "bytes" + "github.com/stretchr/testify/require" "testing" "github.com/davecgh/go-spew/spew" @@ -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) + if err != nil { + t.Fatalf("TestTestNet4GenesisBlock: %v", 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)) + } + require.Equal(t, "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043", 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, + 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, 0x04, 0x6f, 0x35, 0x66, + 0xff, 0xff, 0x00, 0x1d, 0xbb, 0x0c, 0x78, 0x17, + 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, + 0x01, 0x04, 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, + 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{ diff --git a/chaincfg/params.go b/chaincfg/params.go index 1c329cb50f..44675a6a93 100644 --- a/chaincfg/params.go +++ b/chaincfg/params.go @@ -189,6 +189,8 @@ type Params struct { // regtest like networks. PoWNoRetargeting bool + EnforceBIP94 bool + // These fields define the block heights at which the specified softfork // BIP became active. BIP0034Height int32 @@ -297,6 +299,7 @@ var MainNetParams = Params{ GenesisHash: &genesisHash, PowLimit: mainPowLimit, PowLimitBits: 0x1d00ffff, + EnforceBIP94: false, BIP0034Height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8 BIP0065Height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 BIP0066Height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 @@ -445,6 +448,7 @@ var RegressionNetParams = Params{ PowLimitBits: 0x207fffff, PoWNoRetargeting: true, CoinbaseMaturity: 100, + EnforceBIP94: false, BIP0034Height: 100000000, // Not active - Permit ver 1 blocks BIP0065Height: 1351, // Used by regression tests BIP0066Height: 1251, // Used by regression tests @@ -556,6 +560,7 @@ var TestNet3Params = Params{ GenesisHash: &testNet3GenesisHash, PowLimit: testNet3PowLimit, PowLimitBits: 0x1d00ffff, + EnforceBIP94: false, BIP0034Height: 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8 BIP0065Height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 BIP0066Height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 @@ -673,6 +678,87 @@ var TestNet3Params = Params{ HDCoinType: 1, } +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 + ), + }, + DeploymentTaproot: { + BitNumber: 2, + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Unix(0, 0), // Always true + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Unix(0, 0), // Always true + ), + MinActivationHeight: 0, + }, + }, + + // 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 @@ -691,6 +777,7 @@ var SimNetParams = Params{ GenesisHash: &simNetGenesisHash, PowLimit: simNetPowLimit, PowLimitBits: 0x207fffff, + EnforceBIP94: false, BIP0034Height: 0, // Always active on simnet BIP0065Height: 0, // Always active on simnet BIP0066Height: 0, // Always active on simnet @@ -819,6 +906,7 @@ func CustomSignetParams(challenge []byte, dnsSeeds []DNSSeed) Params { GenesisHash: &sigNetGenesisHash, PowLimit: sigNetPowLimit, PowLimitBits: 0x1e0377ae, + EnforceBIP94: false, BIP0034Height: 1, BIP0065Height: 1, BIP0066Height: 1, @@ -1075,6 +1163,7 @@ func init() { // Register all default networks when the package is initialized. mustRegister(&MainNetParams) mustRegister(&TestNet3Params) + mustRegister(&TestNet4Params) mustRegister(&RegressionNetParams) mustRegister(&SimNetParams) } diff --git a/chaincfg/register_test.go b/chaincfg/register_test.go index bcb5b3c6f6..db2544159f 100644 --- a/chaincfg/register_test.go +++ b/chaincfg/register_test.go @@ -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[:], diff --git a/cmd/addblock/config.go b/cmd/addblock/config.go index d49df0a11d..0eb9bf3d41 100644 --- a/cmd/addblock/config.go +++ b/cmd/addblock/config.go @@ -30,7 +30,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 { @@ -41,7 +41,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). Support for testnet3 is deprecated. Consider moving to testnet4 now by using -testnet4"` + 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"` } @@ -79,6 +80,7 @@ func netName(chainParams *chaincfg.Params) string { switch chainParams.Net { case wire.TestNet3: return "testnet" + // TODO do we need to use "testnet" dir for testnet4 as well? default: return chainParams.Name } @@ -113,6 +115,10 @@ func loadConfig() (*config, []string, error) { numNets++ activeNetParams = &chaincfg.TestNet3Params } + if cfg.TestNet4 { + numNets++ + activeNetParams = &chaincfg.TestNet4Params + } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams diff --git a/cmd/btcctl/config.go b/cmd/btcctl/config.go index f6ca8846ec..dfd13961ab 100644 --- a/cmd/btcctl/config.go +++ b/cmd/btcctl/config.go @@ -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). Support for testnet3 is deprecated. Consider moving to testnet4 now by using -testnet4"` + 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 diff --git a/cmd/findcheckpoint/config.go b/cmd/findcheckpoint/config.go index 203ed27faf..7c6e3865ea 100644 --- a/cmd/findcheckpoint/config.go +++ b/cmd/findcheckpoint/config.go @@ -41,7 +41,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). Support for testnet3 is deprecated. Consider moving to testnet4 now by using -testnet4"` + TestNet4 bool `long:"testnet4" description:"Use the test network (version 4)"` } // validDbType returns whether or not dbType is a supported database type. @@ -101,6 +102,10 @@ func loadConfig() (*config, []string, error) { numNets++ activeNetParams = &chaincfg.TestNet3Params } + if cfg.TestNet4 { + numNets++ + activeNetParams = &chaincfg.TestNet4Params + } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams diff --git a/config.go b/config.go index 9bbce7f69a..083eda5208 100644 --- a/config.go +++ b/config.go @@ -169,7 +169,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). Support for testnet3 is deprecated. Consider moving to testnet4 now by using -testnet4"` + 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"` @@ -552,6 +553,10 @@ func loadConfig() (*config, []string, error) { numNets++ activeNetParams = &testNet3Params } + if cfg.TestNet4 { + numNets++ + activeNetParams = &testNet4Params + } if cfg.RegressionTest { numNets++ activeNetParams = ®ressionNetParams diff --git a/database/cmd/dbtool/globalconfig.go b/database/cmd/dbtool/globalconfig.go index 4e58168a33..5393d734b7 100644 --- a/database/cmd/dbtool/globalconfig.go +++ b/database/cmd/dbtool/globalconfig.go @@ -36,6 +36,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. @@ -89,6 +90,10 @@ func setupGlobalConfig() error { numNets++ activeNetParams = &chaincfg.TestNet3Params } + if cfg.TestNet4 { + numNets++ + activeNetParams = &chaincfg.TestNet4Params + } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams diff --git a/params.go b/params.go index b4d1453dfb..0f3f26aefd 100644 --- a/params.go +++ b/params.go @@ -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{ diff --git a/peer/peer.go b/peer/peer.go index 5767cbbf66..d8c92fb754 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -2377,6 +2377,7 @@ func newPeerBase(origCfg *Config, inbound bool) *Peer { // Set the chain parameters to testnet if the caller did not specify any. if cfg.ChainParams == nil { + // TODO: change to testnet4 because testnet3 will be deprecated soon cfg.ChainParams = &chaincfg.TestNet3Params } diff --git a/rpcclient/infrastructure.go b/rpcclient/infrastructure.go index 4fe1d894df..942575184d 100644 --- a/rpcclient/infrastructure.go +++ b/rpcclient/infrastructure.go @@ -1505,6 +1505,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: diff --git a/rpcserver.go b/rpcserver.go index 363661d787..30080438d7 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2359,7 +2359,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(), } @@ -2414,7 +2414,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 } diff --git a/wire/protocol.go b/wire/protocol.go index e1344f3aa7..4cc57259e2 100644 --- a/wire/protocol.go +++ b/wire/protocol.go @@ -183,6 +183,9 @@ const ( // TestNet3 represents the test network (version 3). TestNet3 BitcoinNet = 0x0709110b + // TestNet4 represents the test network (version 4). + TestNet4 BitcoinNet = 0x1c163f28 + // SimNet represents the simulation test network. SimNet BitcoinNet = 0x12141c16 ) @@ -193,6 +196,7 @@ var bnStrings = map[BitcoinNet]string{ MainNet: "MainNet", TestNet: "TestNet", TestNet3: "TestNet3", + TestNet4: "TestNet4", SimNet: "SimNet", } diff --git a/wire/protocol_test.go b/wire/protocol_test.go index eeeffb600a..7fb85fd41f 100644 --- a/wire/protocol_test.go +++ b/wire/protocol_test.go @@ -49,6 +49,7 @@ func TestBitcoinNetStringer(t *testing.T) { {MainNet, "MainNet"}, {TestNet, "TestNet"}, {TestNet3, "TestNet3"}, + {TestNet4, "TestNet4"}, {SimNet, "SimNet"}, {0xffffffff, "Unknown BitcoinNet (4294967295)"}, }