mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
Extend signetchallenge to set target block spacing
Inspired by https://github.com/bitcoin/bitcoin/pull/27446, this commit implements the proposal detailed in the comment https://github.com/bitcoin/bitcoin/pull/27446#issuecomment-1516600820. Rationale. Introduce the ability to configure a custom target time between blocks in a custom Bitcoin signet network. This enhancement enables users to create a signet that is more conducive to testing. The change enhances the flexibility of signet, rendering it more versatile for various testing scenarios. For instance, I am currently working on setting up a signet with a 30-second block time. However, this caused numerous difficulty adjustments, resulting in an inconsistent network state. Regtest isn't a viable alternative for me in this context since we prefer defaults to utilize our custom signet when configured, without impeding local regtest development. Implementation. If the challenge format is "OP_RETURN PUSHDATA<params> PUSHDATA<actual challenge>", the actual challenge from the second data push is used as the signet challenge, and the parameters from the first push are used to configure the network. Otherwise the challenge is used as is. Under the previous rules, such a signet challenge would always evaluate to false, suggesting that it is likely not in use by anyone. Updating bitcoind to a version that includes this change will not cause any disruptions - existing signet challenges will retain their original meaning without alteration. The only parameter currently available is "target_spacing" (default 600 seconds). To set it, place "0x01<target_spacing as uint64_t, little endian>" in the params. Empty params are also valid. If other network parameters are added in the future, they should use "0x02<option 2 value>", "0x03<option 3 value>", etc., following the protobuf style. Two public functions were added to chainparams.h: - ParseWrappedSignetChallenge: Extracts signet params and signet challenge from a wrapped signet challenge. - ParseSignetParams: Parses <params> bytes of the first data push. Function ReadSigNetArgs calls ParseWrappedSignetChallenge and ParseSignetParams to implement the new meaning of signetchallenge. The description of the flag -signetchallenge was updated to reflect the changes. A new unit tests file, chainparams_tests.cpp, was added, containing tests for ParseWrappedSignetChallenge and ParseSignetParams. The test signet_parse_tests from the file validation_tests.cpp was modified to ensure proper understanding of the new logic. In the functional test feature_signet.py, a test case was added with the value of -signetchallenge set to the wrapped challenge, setting spacing to 30 seconds and having the actual challenge OP_TRUE. The Signet miner was updated, introducing a new option --target-spacing with a default of 600 seconds. It must be set to the value used by the network. Example. I tested this commit against Mutinynet, a signet running on a custom fork of Bitcoin Core, implementing 30s target spacing. I successfully synchronized the blockchain using the following config: signet=1 [signet] signetchallenge=6a4c09011e000000000000004c25512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae addnode=45.79.52.207:38333 dnsseed=0 The content of this wrapped challenge: 6a OP_RETURN 4c OP_PUSHDATA1 09 (length of signet params = 9) 011e00000000000000 (signet params: 0x01, pow_target_spacing=30) 4c OP_PUSHDATA1 25 (length of challenge = 37) 512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae (original Mutinynet challenge, can be found here: https://blog.mutinywallet.com/mutinynet/ )
This commit is contained in:
parent
228aba2c4d
commit
7416b18392
10 changed files with 320 additions and 19 deletions
|
@ -223,12 +223,10 @@ def seconds_to_hms(s):
|
|||
return out
|
||||
|
||||
class Generate:
|
||||
INTERVAL = 600.0*2016/2015 # 10 minutes, adjusted for the off-by-one bug
|
||||
|
||||
|
||||
def __init__(self, multiminer=None, ultimate_target=None, poisson=False, max_interval=1800,
|
||||
standby_delay=0, backup_delay=0, set_block_time=None,
|
||||
poolid=None):
|
||||
poolid=None, target_spacing=600):
|
||||
if multiminer is None:
|
||||
multiminer = (0, 1, 1)
|
||||
(self.multi_low, self.multi_high, self.multi_period) = multiminer
|
||||
|
@ -240,6 +238,10 @@ class Generate:
|
|||
self.set_block_time = set_block_time
|
||||
self.poolid = poolid
|
||||
|
||||
# Set INTERVAL. If target_spacing=600 (the default), it is 10 minutes,
|
||||
# adjusted for the off-by-one bug.
|
||||
self.INTERVAL = target_spacing * 2016 / 2015
|
||||
|
||||
def next_block_delta(self, last_nbits, last_hash):
|
||||
# strategy:
|
||||
# 1) work out how far off our desired target we are
|
||||
|
@ -377,8 +379,9 @@ def do_generate(args):
|
|||
return 1
|
||||
my_blocks = (start-1, stop, total)
|
||||
|
||||
if args.max_interval < 960:
|
||||
logging.error("--max-interval must be at least 960 (16 minutes)")
|
||||
max_interval_limit = args.target_spacing * 16 / 10
|
||||
if args.max_interval < max_interval_limit:
|
||||
logging.error("--max-interval must be at least %d (%f minutes)" % (max_interval_limit, max_interval_limit/60))
|
||||
return 1
|
||||
|
||||
poolid = get_poolid(args)
|
||||
|
@ -386,7 +389,7 @@ def do_generate(args):
|
|||
ultimate_target = nbits_to_target(int(args.nbits,16))
|
||||
|
||||
gen = Generate(multiminer=my_blocks, ultimate_target=ultimate_target, poisson=args.poisson, max_interval=args.max_interval,
|
||||
standby_delay=args.standby_delay, backup_delay=args.backup_delay, set_block_time=args.set_block_time, poolid=poolid)
|
||||
standby_delay=args.standby_delay, backup_delay=args.backup_delay, set_block_time=args.set_block_time, poolid=poolid, target_spacing=args.target_spacing)
|
||||
|
||||
mined_blocks = 0
|
||||
bestheader = {"hash": None}
|
||||
|
@ -529,6 +532,7 @@ def main():
|
|||
generate.add_argument("--backup-delay", default=300, type=int, help="Seconds to delay before mining blocks reserved for other miners (default=300)")
|
||||
generate.add_argument("--standby-delay", default=0, type=int, help="Seconds to delay before mining blocks (default=0)")
|
||||
generate.add_argument("--max-interval", default=1800, type=int, help="Maximum interblock interval (seconds)")
|
||||
generate.add_argument("--target-spacing", default=600, type=int, help="Target interval between blocks (seconds), property of the network (default 600)")
|
||||
|
||||
calibrate = cmds.add_parser("calibrate", help="Calibrate difficulty")
|
||||
calibrate.set_defaults(fn=do_calibrate)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <consensus/params.h>
|
||||
#include <deploymentinfo.h>
|
||||
#include <logging.h>
|
||||
#include <script/script.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/chaintype.h>
|
||||
#include <util/strencodings.h>
|
||||
|
@ -21,8 +22,83 @@
|
|||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using util::SplitString;
|
||||
|
||||
void ParseWrappedSignetChallenge(const std::vector<uint8_t>& wrappedChallenge, std::vector<uint8_t>& outParams, std::vector<uint8_t>& outChallenge) {
|
||||
if (wrappedChallenge.empty() || wrappedChallenge[0] != OP_RETURN) {
|
||||
// Not a wrapped challenge.
|
||||
outChallenge = wrappedChallenge;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> params;
|
||||
std::vector<uint8_t> challenge;
|
||||
|
||||
const CScript script(wrappedChallenge.begin(), wrappedChallenge.end());
|
||||
CScript::const_iterator it = script.begin(), itend = script.end();
|
||||
int i;
|
||||
for (i = 0; it != itend; i++) {
|
||||
if (i > 2) {
|
||||
throw std::runtime_error("too many operations in wrapped challenge, must be 3.");
|
||||
}
|
||||
std::vector<unsigned char> push_data;
|
||||
opcodetype opcode;
|
||||
if (!script.GetOp(it, opcode, push_data)) {
|
||||
throw std::runtime_error(strprintf("failed to parse operation %d in wrapped challenge script.", i));
|
||||
}
|
||||
if (i == 0) {
|
||||
// OP_RETURN.
|
||||
continue;
|
||||
}
|
||||
if (opcode != OP_PUSHDATA1 && opcode != OP_PUSHDATA2 && opcode != OP_PUSHDATA4) {
|
||||
throw std::runtime_error(strprintf("operation %d of wrapped challenge script must be a PUSHDATA opcode, got 0x%02x.", i, opcode));
|
||||
}
|
||||
if (i == 1) {
|
||||
params.swap(push_data);
|
||||
} else if (i == 2) {
|
||||
challenge.swap(push_data);
|
||||
}
|
||||
}
|
||||
if (i != 3) {
|
||||
throw std::runtime_error(strprintf("too few operations in wrapped challenge, must be 3, got %d.", i));
|
||||
}
|
||||
|
||||
outParams.swap(params);
|
||||
outChallenge.swap(challenge);
|
||||
}
|
||||
|
||||
void ParseSignetParams(const std::vector<uint8_t>& params, CChainParams::SigNetOptions& options) {
|
||||
if (params.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The format of params is extendable in case more fields are added in the future.
|
||||
// It is encoded as a concatenation of (field_id, value) tuples, protobuf style.
|
||||
// Currently there is only one field defined: pow_target_spacing, whose field_id
|
||||
// is 0x01 and the lendth of encoding is 8 (int64_t). So valid values of params are:
|
||||
// - empty string (checked in if block above),
|
||||
// - 0x01 followed by 8 bytes of pow_target_spacing (9 bytes in total).
|
||||
// If length is not 0 and not 9, the value can not be parsed.
|
||||
|
||||
if (params.size() != 1 + 8) {
|
||||
throw std::runtime_error(strprintf("signet params must have length %d, got %d.", 1+8, params.size()));
|
||||
}
|
||||
if (params[0] != 0x01) {
|
||||
throw std::runtime_error(strprintf("signet params[0] must be 0x01, got 0x%02x.", params[0]));
|
||||
}
|
||||
// Parse little-endian 64 bit number to uint8_t.
|
||||
const uint8_t* bytes = ¶ms[1];
|
||||
const uint64_t value = uint64_t(bytes[0]) | uint64_t(bytes[1])<<8 | uint64_t(bytes[2])<<16 | uint64_t(bytes[3])<<24 |
|
||||
uint64_t(bytes[4])<<32 | uint64_t(bytes[5])<<40 | uint64_t(bytes[6])<<48 | uint64_t(bytes[7])<<56;
|
||||
auto pow_target_spacing = int64_t(value);
|
||||
if (pow_target_spacing <= 0) {
|
||||
throw std::runtime_error("signet param pow_target_spacing <= 0.");
|
||||
}
|
||||
|
||||
options.pow_target_spacing = pow_target_spacing;
|
||||
}
|
||||
|
||||
void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options)
|
||||
{
|
||||
if (args.IsArgSet("-signetseednode")) {
|
||||
|
@ -37,7 +113,11 @@ void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& option
|
|||
if (!val) {
|
||||
throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0]));
|
||||
}
|
||||
options.challenge.emplace(*val);
|
||||
std::vector<unsigned char> params;
|
||||
std::vector<unsigned char> challenge;
|
||||
ParseWrappedSignetChallenge(*val, params, challenge);
|
||||
ParseSignetParams(params, options);
|
||||
options.challenge.emplace(challenge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,4 +28,26 @@ const CChainParams &Params();
|
|||
*/
|
||||
void SelectParams(const ChainType chain);
|
||||
|
||||
/**
|
||||
* Extracts signet params and signet challenge from wrapped signet challenge.
|
||||
* Format of wrapped signet challenge is:
|
||||
* If the challenge is in the form "OP_RETURN PUSHDATA<params> PUSHDATA<actual challenge>",
|
||||
* If the input challenge does not start with OP_RETURN,
|
||||
* sets outParams="" and outChallenge=input.
|
||||
* If the input challenge starts with OP_RETURN, but does not satisfy the format,
|
||||
* throws an exception.
|
||||
*/
|
||||
void ParseWrappedSignetChallenge(const std::vector<uint8_t>& wrappedChallenge, std::vector<uint8_t>& outParams, std::vector<uint8_t>& outChallenge);
|
||||
|
||||
/**
|
||||
* Parses signet options.
|
||||
* The format currently supports only setting pow_target_spacing, but
|
||||
* can be extended in the future.
|
||||
* Possible values:
|
||||
* - Empty (then do nothing)
|
||||
* - 0x01 (pow_target_spacing as int64_t little endian) => set pow_target_spacing.
|
||||
* If the format is wrong, throws an exception.
|
||||
*/
|
||||
void ParseSignetParams(const std::vector<uint8_t>& params, CChainParams::SigNetOptions& options);
|
||||
|
||||
#endif // BITCOIN_CHAINPARAMS_H
|
||||
|
|
|
@ -21,7 +21,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
|
|||
argsman.AddArg("-testnet4", "Use the testnet4 chain. Equivalent to -chain=testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
||||
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
|
||||
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
||||
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
|
||||
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge); in case -signetchallenge is in the form of 'OP_RETURN PUSHDATA<params> PUSHDATA<actual challenge>', then <actual challenge> is used as a challenge and <params> is used to set parameters of signet; currently the only supported parameter is target spacing, the format of <params> to set it is 01<8 bytes value of target spacing, seconds, little endian>", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
|
||||
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
|
||||
}
|
||||
|
||||
|
|
|
@ -461,7 +461,7 @@ public:
|
|||
consensus.CSVHeight = 1;
|
||||
consensus.SegwitHeight = 1;
|
||||
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
||||
consensus.nPowTargetSpacing = 10 * 60;
|
||||
consensus.nPowTargetSpacing = options.pow_target_spacing;
|
||||
consensus.fPowAllowMinDifficultyBlocks = false;
|
||||
consensus.enforce_BIP94 = false;
|
||||
consensus.fPowNoRetargeting = false;
|
||||
|
|
|
@ -137,6 +137,7 @@ public:
|
|||
struct SigNetOptions {
|
||||
std::optional<std::vector<uint8_t>> challenge{};
|
||||
std::optional<std::vector<std::string>> seeds{};
|
||||
int64_t pow_target_spacing{10 * 60};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,6 +47,7 @@ add_executable(test_bitcoin
|
|||
blockmanager_tests.cpp
|
||||
bloom_tests.cpp
|
||||
bswap_tests.cpp
|
||||
chainparams_tests.cpp
|
||||
checkqueue_tests.cpp
|
||||
cluster_linearize_tests.cpp
|
||||
coins_tests.cpp
|
||||
|
|
170
src/test/chainparams_tests.cpp
Normal file
170
src/test/chainparams_tests.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) 2011-2024 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <chainparams.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <util/strencodings.h>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(chainparams_tests)
|
||||
|
||||
struct ParseWrappedSignetChallenge_TestCase
|
||||
{
|
||||
std::string wrappedChallengeHex;
|
||||
std::string wantParamsHex;
|
||||
std::string wantChallengeHex;
|
||||
std::string wantError;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(parse_wrapped_signet_challenge)
|
||||
{
|
||||
static const ParseWrappedSignetChallenge_TestCase cases[] = {
|
||||
{
|
||||
"512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae",
|
||||
"",
|
||||
"512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"6a4c09011e000000000000004c25512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae",
|
||||
"011e00000000000000",
|
||||
"512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"6a4c004c25512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae",
|
||||
"",
|
||||
"512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"6a4c004c25512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae4c00",
|
||||
"",
|
||||
"",
|
||||
"too many operations in wrapped challenge, must be 3.",
|
||||
},
|
||||
{
|
||||
"6a4c09011e00000000000000",
|
||||
"",
|
||||
"",
|
||||
"too few operations in wrapped challenge, must be 3, got 2.",
|
||||
},
|
||||
{
|
||||
"6a4c01",
|
||||
"",
|
||||
"",
|
||||
"failed to parse operation 1 in wrapped challenge script.",
|
||||
},
|
||||
{
|
||||
"6a4c004c25512102f7561d208dd9ae99bf497273",
|
||||
"",
|
||||
"",
|
||||
"failed to parse operation 2 in wrapped challenge script.",
|
||||
},
|
||||
{
|
||||
"6a6a4c25512102f7561d208dd9ae99bf497273e16f389bdbd6c4742ddb8e6b216e64fa2928ad8f51ae4c00",
|
||||
"",
|
||||
"",
|
||||
"operation 1 of wrapped challenge script must be a PUSHDATA opcode, got 0x6a.",
|
||||
},
|
||||
{
|
||||
"6a4c09011e0000000000000051",
|
||||
"",
|
||||
"",
|
||||
"operation 2 of wrapped challenge script must be a PUSHDATA opcode, got 0x51.",
|
||||
},
|
||||
};
|
||||
|
||||
for (unsigned int i=0; i<std::size(cases); i++)
|
||||
{
|
||||
const auto wrappedChallenge = ParseHex(cases[i].wrappedChallengeHex);
|
||||
const auto wantParamsHex = cases[i].wantParamsHex;
|
||||
const auto wantChallengeHex = cases[i].wantChallengeHex;
|
||||
const auto wantError = cases[i].wantError;
|
||||
|
||||
std::vector<uint8_t> gotParams;
|
||||
std::vector<uint8_t> gotChallenge;
|
||||
std::string gotError;
|
||||
try {
|
||||
ParseWrappedSignetChallenge(wrappedChallenge, gotParams, gotChallenge);
|
||||
} catch (const std::exception& e) {
|
||||
gotError = e.what();
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(HexStr(gotParams), wantParamsHex);
|
||||
BOOST_CHECK_EQUAL(HexStr(gotChallenge), wantChallengeHex);
|
||||
BOOST_CHECK_EQUAL(gotError, wantError);
|
||||
}
|
||||
}
|
||||
|
||||
struct ParseSignetParams_TestCase
|
||||
{
|
||||
std::string paramsHex;
|
||||
int64_t wantPowTargetSpacing;
|
||||
std::string wantError;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(parse_signet_params)
|
||||
{
|
||||
static const ParseSignetParams_TestCase cases[] = {
|
||||
{
|
||||
"",
|
||||
600,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"011e00000000000000",
|
||||
30,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"01e803000000000000",
|
||||
1000,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"015802000000000000",
|
||||
600,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"012502",
|
||||
600,
|
||||
"signet params must have length 9, got 3.",
|
||||
},
|
||||
{
|
||||
"022502000000000000",
|
||||
600,
|
||||
"signet params[0] must be 0x01, got 0x02.",
|
||||
},
|
||||
{
|
||||
"01ffffffffffffffff",
|
||||
600,
|
||||
"signet param pow_target_spacing <= 0.",
|
||||
},
|
||||
};
|
||||
|
||||
for (unsigned int i=0; i<std::size(cases); i++)
|
||||
{
|
||||
const auto params = ParseHex(cases[i].paramsHex);
|
||||
const auto wantPowTargetSpacing = cases[i].wantPowTargetSpacing;
|
||||
const auto wantError = cases[i].wantError;
|
||||
|
||||
CChainParams::SigNetOptions gotOptions;
|
||||
std::string gotError;
|
||||
try {
|
||||
ParseSignetParams(params, gotOptions);
|
||||
} catch (const std::exception& e) {
|
||||
gotError = e.what();
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(gotOptions.pow_target_spacing, wantPowTargetSpacing);
|
||||
BOOST_CHECK_EQUAL(gotError, wantError);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -75,6 +75,15 @@ BOOST_AUTO_TEST_CASE(signet_parse_tests)
|
|||
BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector<uint8_t>{OP_TRUE});
|
||||
CScript challenge{OP_TRUE};
|
||||
|
||||
{
|
||||
// Wrapped challenge case.
|
||||
ArgsManager signet_argsman_custom_spacing;
|
||||
signet_argsman_custom_spacing.ForceSetArg("-signetchallenge", "6a4c09011e000000000000004c0151"); // set challenge to OP_TRUE and spacing to 30 seconds
|
||||
const auto signet_params_custom_spacing = CreateChainParams(signet_argsman_custom_spacing, ChainType::SIGNET);
|
||||
BOOST_CHECK(signet_params_custom_spacing->GetConsensus().signet_challenge == std::vector<uint8_t>{OP_TRUE});
|
||||
BOOST_CHECK(signet_params_custom_spacing->GetConsensus().nPowTargetSpacing == 30);
|
||||
}
|
||||
|
||||
// empty block is invalid
|
||||
BOOST_CHECK(!SignetTxs::Create(block, challenge));
|
||||
BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
|
||||
|
|
|
@ -25,7 +25,7 @@ signet_blocks = [
|
|||
]
|
||||
|
||||
class SignetParams:
|
||||
def __init__(self, challenge=None):
|
||||
def __init__(self, challenge=None, internal_challenge=None):
|
||||
if challenge is None:
|
||||
self.challenge = SIGNET_DEFAULT_CHALLENGE
|
||||
self.shared_args = []
|
||||
|
@ -33,22 +33,28 @@ class SignetParams:
|
|||
self.challenge = challenge
|
||||
self.shared_args = [f"-signetchallenge={challenge}"]
|
||||
|
||||
if internal_challenge is None:
|
||||
internal_challenge = self.challenge
|
||||
self.internal_challenge = internal_challenge
|
||||
|
||||
class SignetBasicTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.chain = "signet"
|
||||
self.num_nodes = 6
|
||||
self.num_nodes = 8
|
||||
self.setup_clean_chain = True
|
||||
self.signets = [
|
||||
SignetParams(challenge='51'), # OP_TRUE
|
||||
SignetParams(), # default challenge
|
||||
# default challenge as a 2-of-2, which means it should fail
|
||||
SignetParams(challenge='522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae')
|
||||
SignetParams(challenge='522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae'),
|
||||
SignetParams(challenge='6a4c09011e000000000000004c0151', internal_challenge='51'), # OP_TRUE, target_spacing=30
|
||||
]
|
||||
|
||||
self.extra_args = [
|
||||
self.signets[0].shared_args, self.signets[0].shared_args,
|
||||
self.signets[1].shared_args, self.signets[1].shared_args,
|
||||
self.signets[2].shared_args, self.signets[2].shared_args,
|
||||
self.signets[3].shared_args, self.signets[3].shared_args,
|
||||
]
|
||||
|
||||
def setup_network(self):
|
||||
|
@ -58,6 +64,7 @@ class SignetBasicTest(BitcoinTestFramework):
|
|||
self.connect_nodes(0, 1)
|
||||
self.connect_nodes(2, 3)
|
||||
self.connect_nodes(4, 5)
|
||||
self.connect_nodes(6, 7)
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("basic tests using OP_TRUE challenge")
|
||||
|
@ -66,10 +73,11 @@ class SignetBasicTest(BitcoinTestFramework):
|
|||
def check_getblockchaininfo(node_idx, signet_idx):
|
||||
blockchain_info = self.nodes[node_idx].getblockchaininfo()
|
||||
assert_equal(blockchain_info['chain'], 'signet')
|
||||
assert_equal(blockchain_info['signet_challenge'], self.signets[signet_idx].challenge)
|
||||
assert_equal(blockchain_info['signet_challenge'], self.signets[signet_idx].internal_challenge)
|
||||
check_getblockchaininfo(node_idx=1, signet_idx=0)
|
||||
check_getblockchaininfo(node_idx=2, signet_idx=1)
|
||||
check_getblockchaininfo(node_idx=5, signet_idx=2)
|
||||
check_getblockchaininfo(node_idx=6, signet_idx=3)
|
||||
|
||||
self.log.info('getmininginfo')
|
||||
def check_getmininginfo(node_idx, signet_idx):
|
||||
|
@ -80,20 +88,26 @@ class SignetBasicTest(BitcoinTestFramework):
|
|||
assert 'currentblockweight' not in mining_info
|
||||
assert_equal(mining_info['networkhashps'], Decimal('0'))
|
||||
assert_equal(mining_info['pooledtx'], 0)
|
||||
assert_equal(mining_info['signet_challenge'], self.signets[signet_idx].challenge)
|
||||
assert_equal(mining_info['signet_challenge'], self.signets[signet_idx].internal_challenge)
|
||||
check_getmininginfo(node_idx=0, signet_idx=0)
|
||||
check_getmininginfo(node_idx=3, signet_idx=1)
|
||||
check_getmininginfo(node_idx=4, signet_idx=2)
|
||||
check_getmininginfo(node_idx=7, signet_idx=3)
|
||||
|
||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
|
||||
|
||||
self.log.info("pregenerated signet blocks check")
|
||||
|
||||
height = 0
|
||||
for block in signet_blocks:
|
||||
assert_equal(self.nodes[2].submitblock(block), None)
|
||||
height += 1
|
||||
assert_equal(self.nodes[2].getblockcount(), height)
|
||||
# Verify that nodes accept blocks mined using the default signet challenge.
|
||||
# This test includes one default signet node (2) and another node (6)
|
||||
# configured with an extended signet challenge that uses the actual script
|
||||
# OP_TRUE (accepts all blocks).
|
||||
for node_idx in [2, 6]:
|
||||
height = 0
|
||||
for block in signet_blocks:
|
||||
assert_equal(self.nodes[node_idx].submitblock(block), None)
|
||||
height += 1
|
||||
assert_equal(self.nodes[node_idx].getblockcount(), height)
|
||||
|
||||
self.log.info("pregenerated signet blocks check (incompatible solution)")
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue