Merge bitcoin/bitcoin#34566: feature: Use different datadirs for different signets
What changed, and why it matters
This change lets Bitcoin Core store different custom signet blockchains in separate data folders, using a unique suffix derived from each signet's network identifier. It also adds a friendlier error hint in bitcoin-cli when an RPC authentication failure might be due to using the wrong custom signet. There is no direct security vulnerability being fixed; it is a usability and data-isolation feature.
No security response required. Treat as a normal feature/quality improvement. Operators running custom signets should consult the release note about renaming existing signet datadirs to avoid resyncs after upgrade.
Security signals we found
Data isolation between distinct custom signets reduces risk of cross-network state corruption or accidental mainnet/testnet confusion
No memory-safety, cryptographic, or consensus changes observed
No privilege escalation, remote code execution, or denial-of-service vectors introduced in the diff
bitcoin-cli hint is a defensive UX improvement, not a vulnerability fix
Evidence from the diff
The commit refactors signet data directory selection so that each distinct -signetchallenge gets its own datadir (signet_
Changed components
src/chainparamsbase.cppsrc/chainparamsbase.hsrc/init.cppsrc/bitcoin-cli.cppsrc/kernel/chainparams.cppsrc/kernel/signet.htest/functional/feature_signet.pytest/functional/test_framework/test_node.pytest/functional/tool_signet_miner.pyInspect captured patch +211 / −44
### doc/files.md
@@ -44,6 +44,14 @@ Chain option | Data directory path
`-chain=signet` or `-signet` | *path_to_datadir*`/signet/`
`-chain=regtest` or `-regtest` | *path_to_datadir*`/regtest/`
+4. Data directories for custom signets include the message start bytes as the suffix:
+
+Chain option | Data directory path
+-------------------------------|--------------------
+`-signet -signetchallenge=...` | *path_to_datadir*`/signet_XXXXXXXX/`
+
+(It will still use `/signet/` for the default signet or if the default signet challenge is passed to `-signetchallenge`.)
+
## Data directory layout
Subdirectory | File(s) | Description
### doc/release-notes-34566.md
@@ -0,0 +1,20 @@
+Network changes
+---------------
+
+Custom signets now use separate data directories, with a suffix equivalent to
+the network magic (message start), so multiple signets can be synced. The
+default signet continues to use the unsuffixed directory for backward
+compatibility, including when the default challenge is set explicitly via
+`-signetchallenge`.
+
+Users currently running a custom signet will have their node select a new data
+directory after upgrading. To avoid a resync, they should rename the existing
+signet directory to the new signet_XXXXXXXX format, using the `getchainparams`
+command from `bitcoin-util` to determine the suffix:
+
+```
+$ bitcoin-util -signet -signetchallenge=<challenge> getchainparams | jq -r .net.magic
+```
+
+(#34566)
+
### src/bitcoin-cli.cpp
@@ -142,11 +142,11 @@ static void SetupCliArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
- const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN);
- const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET);
- const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4);
- const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET);
- const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST);
+ const auto defaultBaseParams = CreateBaseChainParams(argsman, ChainType::MAIN);
+ const auto testnetBaseParams = CreateBaseChainParams(argsman, ChainType::TESTNET);
+ const auto testnet4BaseParams = CreateBaseChainParams(argsman, ChainType::TESTNET4);
+ const auto signetBaseParams = CreateBaseChainParams(argsman, ChainType::SIGNET);
+ const auto regtestBaseParams = CreateBaseChainParams(argsman, ChainType::REGTEST);
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -1236,10 +1236,12 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
if (response.status == HTTP_UNAUTHORIZED) {
std::string error{"Authorization failed: "};
+ bool read_err = false;
if (auth_cookie_result.has_value()) {
switch (*auth_cookie_result) {
case AuthCookieResult::Error:
error += "Failed to read cookie file and no rpcpassword was specified.";
+ read_err = true;
break;
case AuthCookieResult::Disabled:
error += "Cookie file was disabled via -norpccookiefile and no rpcpassword was specified.";
@@ -1252,6 +1254,9 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
error += "Incorrect rpcuser or rpcpassword were specified.";
}
error += strprintf(" Configuration file: (%s)", fs::PathToString(gArgs.GetConfigFilePath()));
+ if (gArgs.GetChainType() == ChainType::SIGNET && read_err) {
+ error += "\nIs your -signetchallenge correct for custom signets?";
+ }
throw std::runtime_error(error);
} else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
throw std::runtime_error(strprintf("Server response: %s", response.body));
### src/chainparamsbase.cpp
@@ -6,8 +6,11 @@
#include <chainparamsbase.h>
#include <common/args.h>
+#include <crypto/hex_base.h>
+#include <kernel/signet.h>
#include <tinyformat.h>
#include <util/chaintype.h>
+#include <util/strencodings.h>
#include <cassert>
@@ -33,11 +36,29 @@ const CBaseChainParams& BaseParams()
return *globalChainBaseParams;
}
+std::string GetSignetDataDir(const ArgsManager& args)
+{
+ std::string base_data_dir = "signet";
+ // no default to treat no -signetchallenge (default signet) as distinct from
+ // the empty -signetchallenge= (custom signet)
+ const auto challenge_hex = args.GetArg("-signetchallenge");
+ if (!challenge_hex) {
+ return base_data_dir;
+ }
+ // -signetchallenge can be invalid hex here (it's checked later in
+ // ReadSigNetArgs), but we don't mind to keep validation in one place.
+ const auto challenge_bytes = TryParseHex<uint8_t>(*challenge_hex);
+ if (!challenge_bytes || *challenge_bytes == kernel::SIGNET_DEFAULT_CHALLENGE) {
+ return base_data_dir;
+ }
+ return base_data_dir + "_" + HexStr(kernel::GetSignetMessageStart(*challenge_bytes));
+}
+
/**
* Port numbers for incoming Tor connections (8334, 18334, 38334, 48334, 18445) have
* been chosen arbitrarily to keep ranges of used ports tight.
*/
-std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain)
+std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ArgsManager& args, const ChainType chain)
{
switch (chain) {
case ChainType::MAIN:
@@ -47,7 +68,7 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain)
case ChainType::TESTNET4:
return std::make_unique<CBaseChainParams>("testnet4", 48332);
case ChainType::SIGNET:
- return std::make_unique<CBaseChainParams>("signet", 38332);
+ return std::make_unique<CBaseChainParams>(GetSignetDataDir(args), 38332);
case ChainType::REGTEST:
return std::make_unique<CBaseChainParams>("regtest", 18443);
}
@@ -56,6 +77,6 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain)
void SelectBaseParams(const ChainType chain)
{
- globalChainBaseParams = CreateBaseChainParams(chain);
gArgs.SelectConfigNetwork(ChainTypeToString(chain));
+ globalChainBaseParams = CreateBaseChainParams(gArgs, chain);
}
### src/chainparamsbase.h
@@ -35,7 +35,7 @@ class CBaseChainParams
/**
* Creates and returns a std::unique_ptr<CBaseChainParams> of the chosen chain.
*/
-std::unique_ptr<CBaseChainParams> CreateBaseChainParams(ChainType chain);
+std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ArgsManager& args, ChainType chain);
/**
*Set the arguments for chainparams
### src/init.cpp
@@ -487,11 +487,11 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
init::AddLoggingArgs(argsman);
- const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN);
- const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET);
- const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4);
- const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET);
- const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST);
+ const auto defaultBaseParams = CreateBaseChainParams(argsman, ChainType::MAIN);
+ const auto testnetBaseParams = CreateBaseChainParams(argsman, ChainType::TESTNET);
+ const auto testnet4BaseParams = CreateBaseChainParams(argsman, ChainType::TESTNET4);
+ const auto signetBaseParams = CreateBaseChainParams(argsman, ChainType::SIGNET);
+ const auto regtestBaseParams = CreateBaseChainParams(argsman, ChainType::REGTEST);
const auto defaultChainParams = CreateChainParams(argsman, ChainType::MAIN);
const auto testnetChainParams = CreateChainParams(argsman, ChainType::TESTNET);
const auto testnet4ChainParams = CreateChainParams(argsman, ChainType::TESTNET4);
### src/kernel/chainparams.cpp
@@ -10,8 +10,8 @@
#include <consensus/merkle.h>
#include <consensus/params.h>
#include <crypto/hex_base.h>
-#include <hash.h>
#include <kernel/messagestartchars.h>
+#include <kernel/signet.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
@@ -467,7 +467,7 @@ class SigNetParams : public CChainParams {
vSeeds.clear();
if (!options.challenge) {
- bin = "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"_hex_v_u8;
+ bin = kernel::SIGNET_DEFAULT_CHALLENGE;
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_signet), std::end(chainparams_seed_signet));
vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl.");
vSeeds.emplace_back("seed.signet.achownodes.xyz."); // Ava Chow, only supports x1, x5, x9, x49, x809, x849, xd, x400, x404, x408, x448, xc08, xc48, x40c
@@ -526,11 +526,7 @@ class SigNetParams : public CChainParams {
ApplyDeploymentOptions(options.dep_opts);
- // message start is defined as the first 4 bytes of the sha256d of the block script
- HashWriter h{};
- h << consensus.signet_challenge;
- uint256 hash = h.GetHash();
- std::copy_n(hash.begin(), 4, pchMessageStart.begin());
+ pchMessageStart = kernel::GetSignetMessageStart(consensus.signet_challenge);
nDefaultPort = 38333;
nPruneAfterHeight = 1000;
### src/kernel/signet.h
@@ -0,0 +1,38 @@
+// Copyright (c) The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_KERNEL_SIGNET_H
+#define BITCOIN_KERNEL_SIGNET_H
+
+#include <hash.h>
+#include <kernel/messagestartchars.h>
+#include <uint256.h>
+#include <util/strencodings.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <vector>
+
+namespace kernel {
+using namespace util::hex_literals;
+
+inline const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE{
+ "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"_hex_v_u8};
+
+/**
+ * Return the message start bytes for a signet: the first four bytes of the
+ * double-SHA256 hash of the serialized signet challenge script (BIP325)
+ */
+inline MessageStartChars GetSignetMessageStart(const std::vector<uint8_t>& signet_challenge)
+{
+ HashWriter h{};
+ h << signet_challenge;
+ const uint256 hash = h.GetHash();
+ MessageStartChars msg_start;
+ std::copy_n(hash.begin(), 4, msg_start.begin());
+ return msg_start;
+}
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_SIGNET_H
### test/functional/feature_signet.py
@@ -5,11 +5,11 @@
"""Test basic signet functionality"""
from decimal import Decimal
+from os import path
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-SIGNET_DEFAULT_CHALLENGE = '512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae'
+from test_framework.signet import SIGNET_DEFAULT_CHALLENGE, message_start
+from test_framework.util import assert_equal, assert_raises_process_error
signet_blocks = [
'00000020f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000f575c83235984e7dc4afc1f30944c170462e84437ab6f2d52e16878a79e4678bd1914d5fae77031eccf4070001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025151feffffff0200f2052a010000001600149243f727dd5343293eb83174324019ec16c2630f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205e423a8754336ca99dbe16509b877ef1bf98d008836c725005b3c787c41ebe46022047246e4467ad7cc7f1ad98662afcaf14c115e0095a227c7b05c5182591c23e7e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
@@ -38,19 +38,24 @@ def __init__(self, challenge=None):
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'),
+ # explicit default challenge
+ SignetParams(challenge=SIGNET_DEFAULT_CHALLENGE),
+ # explicit empty challenge, which is a distinct network from the default signet
+ SignetParams(challenge=''),
]
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[4].shared_args,
]
def setup_network(self):
@@ -101,6 +106,32 @@ def check_getmininginfo(node_idx, signet_idx):
assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
+ def assert_node_datadir(node, expected_dirname):
+ datadir = node.chain_path
+ self.log.info(f"Checking node datadir: {datadir}")
+ # check directory name
+ assert_equal(path.basename(datadir), expected_dirname)
+ # check if the directory exists
+ assert datadir.is_dir()
+ # check if the directory is being used
+ rpc_log_path = node.getrpcinfo()['logpath']
+ assert rpc_log_path.startswith(str(datadir))
+
+ self.log.info("Test that the signet data directory with custom -signetchallenge uses network magic as suffix")
+ assert_node_datadir(self.nodes[0], f"signet_{message_start(self.signets[0].challenge)}")
+ assert_node_datadir(self.nodes[4], f"signet_{message_start(self.signets[2].challenge)}")
+
+ self.log.info("Test that the main signet data directory is 'signet'")
+ assert_node_datadir(self.nodes[3], "signet")
+
+ self.log.info("Test that the signet data directory with -signetchallenge=SIGNET_DEFAULT_CHALLENGE is 'signet'")
+ assert_node_datadir(self.nodes[6], "signet")
+
+ self.log.info("Test that an explicit empty -signetchallenge= is a distinct network, not the default signet")
+ assert_node_datadir(self.nodes[7], f"signet_{message_start(self.signets[4].challenge)}")
+
+ self.test_cli_signetchallenge_hint()
+
self.log.info("test that signet logs the network magic on node start")
with self.nodes[0].assert_debug_log(["Signet derived magic (message start)"]):
self.restart_node(0)
@@ -109,5 +140,19 @@ def check_getmininginfo(node_idx, signet_idx):
self.nodes[0].assert_start_raises_init_error(extra_args=["-signetchallenge=abc"] * 2, expected_msg="Error: -signetchallenge cannot be multiple values.")
+ def test_cli_signetchallenge_hint(self):
+ if not self.is_cli_compiled():
+ self.log.info("Skipping bitcoin-cli -signetchallenge hint test")
+ return
+
+ self.log.info("Test that bitcoin-cli hints about -signetchallenge on a signet RPC auth failure")
+ hint = "Is your -signetchallenge correct for custom signets?"
+ # test custom signet node (0) and default signet node (3)
+ for node_idx in [0, 3]:
+ node = self.nodes[node_idx]
+ missing_cookie = node.datadir_path / "nonexistent.cookie"
+ assert_raises_process_error(1, hint, node.cli(f"-rpccookiefile={missing_cookie}").getblockcount)
+
+
if __name__ == '__main__':
SignetBasicTest(__file__).main()
### test/functional/test_framework/signet.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# Copyright (c) The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Signet constants and helper functions used in tests."""
+
+import hashlib
+
+from test_framework.messages import ser_compact_size
+
+SIGNET_DEFAULT_CHALLENGE = '512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae'
+
+def message_start(challenge_hex: str) -> str:
+ raw = bytes.fromhex(challenge_hex)
+ ser = ser_compact_size(len(raw)) + raw
+ digest = hashlib.sha256(hashlib.sha256(ser).digest()).digest()
+ return digest[:4].hex()
### test/functional/test_framework/test_node.py
@@ -42,6 +42,7 @@
p2p_port,
tor_port,
)
+from .signet import SIGNET_DEFAULT_CHALLENGE, message_start
BITCOIND_PROC_WAIT_TIMEOUT = 60
# The size of the blocks xor key
@@ -276,7 +277,7 @@ def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, env=None
# Delete any existing cookie file -- if such a file exists (eg due to
# unclean shutdown), it will get overwritten anyway by bitcoind, and
# potentially interfere with our attempt to authenticate
- delete_cookie_file(self.datadir_path, self.chain)
+ delete_cookie_file(self.datadir_path, self.chain_dir)
# add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal
subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")
@@ -333,19 +334,21 @@ def create_new_rpc_connection(self, *, mode="AUTO", client_timeout=None):
else:
host = self.rpchost
if mode == RPCConnectionType.AUTHPROXY:
- rpc_u, rpc_p = get_auth_cookie(self.datadir_path, self.chain)
+ rpc_u, rpc_p = get_auth_cookie(self.datadir_path, self.chain_dir)
url = f"http://{rpc_u}:{rpc_p}@{host}:{port}"
proxy = AuthServiceProxy(url, timeout=int(client_timeout))
coverage_logfile = coverage.get_filename(self.coverage_dir, self.index) if self.coverage_dir else None
rpc = coverage.AuthServiceProxyWrapper(proxy, url, coverage_logfile)
rpc.auth_service_proxy_instance.reuse_http_connections = self.reuse_http_connections
return rpc
else: # mode==CLI
+ extra_args = [arg for arg in self.extra_args if arg.startswith("-signetchallenge")]
return TestNodeCLI(self.binaries)(
f"-datadir={self.datadir_path}",
f"-rpcclienttimeout={client_timeout}",
f"-rpcconnect={host}",
f"-rpcport={port}",
+ *extra_args
)
def wait_for_rpc_connection(self, *, wait_for_import=True):
@@ -449,7 +452,7 @@ def wait_for_cookie_credentials(self):
poll_per_s = 4
for _ in range(poll_per_s * self.rpc_timeout):
try:
- get_auth_cookie(self.datadir_path, self.chain)
+ get_auth_cookie(self.datadir_path, self.chain_dir)
self.log.debug("Cookie credentials successfully retrieved")
return
except ValueError: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
@@ -581,7 +584,21 @@ def replace_in_config(self, replacements):
@property
def chain_path(self) -> Path:
- return self.datadir_path / self.chain
+ return self.datadir_path / self.chain_dir
+
+ @property
+ def chain_dir(self) -> str:
+ if self.chain != "signet":
+ return self.chain
+ for arg in self.extra_args:
+ if not arg.startswith("-signetchallenge"):
+ continue
+ signetchallenge = arg.split('=')[1]
+ if signetchallenge.lower() == SIGNET_DEFAULT_CHALLENGE:
+ return self.chain
+ suffix = message_start(signetchallenge)
+ return f"signet_{suffix}"
+ return self.chain
@property
def debug_log_path(self) -> Path:
### test/functional/tool_signet_miner.py
@@ -65,11 +65,11 @@ def setup_network(self):
# Nodes with different signet networks are not connected
# generate block with signet miner tool
- def mine_block(self, node):
+ def mine_block(self, node, extra_args):
n_blocks = node.getblockcount()
base_dir = self.config["environment"]["SRCDIR"]
signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
- rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]
+ rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"] + extra_args
util_argv = node.binaries.util_argv() + ["grind"]
subprocess.run([
sys.executable,
@@ -85,11 +85,11 @@ def mine_block(self, node):
assert_equal(node.getblockcount(), n_blocks + 1)
# generate block using the signet miner tool genpsbt and solvepsbt commands
- def mine_block_manual(self, node, *, sign):
+ def mine_block_manual(self, node, extra_args, *, sign):
n_blocks = node.getblockcount()
base_dir = self.config["environment"]["SRCDIR"]
signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
- rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]
+ rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"] + extra_args
util_argv = node.binaries.util_argv() + ["grind"]
base_cmd = [
sys.executable,
@@ -118,36 +118,36 @@ def mine_block_manual(self, node, *, sign):
def run_test(self):
self.log.info("Signet node with single signature challenge")
- node = self.nodes[0]
+ node, extra_args = self.nodes[0], self.extra_args[0]
# import private key needed for signing block
wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), 0)
- self.mine_block(node)
+ self.mine_block(node, extra_args)
# MUST include signet commitment
assert get_signet_commitment(get_segwit_commitment(node))
self.log.info("Mine manually using genpsbt and solvepsbt")
- self.mine_block_manual(node, sign=True)
+ self.mine_block_manual(node, extra_args, sign=True)
assert get_signet_commitment(get_segwit_commitment(node))
- node = self.nodes[1]
+ node, extra_args = self.nodes[1], self.extra_args[1]
self.log.info("Signet node with trivial challenge (OP_TRUE)")
- self.mine_block(node)
+ self.mine_block(node, extra_args)
# MAY omit signet commitment (BIP 325). Do so for better compatibility
# with signet unaware mining software and hardware.
assert get_signet_commitment(get_segwit_commitment(node)) is None
- node = self.nodes[2]
+ node, extra_args = self.nodes[2], self.extra_args[2]
self.log.info("Signet node with trivial challenge (OP_16)")
- self.mine_block(node)
+ self.mine_block(node, extra_args)
assert get_signet_commitment(get_segwit_commitment(node)) is None
- node = self.nodes[3]
+ node, extra_args = self.nodes[3], self.extra_args[3]
self.log.info("Signet node with trivial challenge (push sha256 hash)")
- self.mine_block(node)
+ self.mine_block(node, extra_args)
assert get_signet_commitment(get_segwit_commitment(node)) is None
self.log.info("Manual mining with a trivial challenge doesn't require a PSBT")
- self.mine_block_manual(node, sign=False)
+ self.mine_block_manual(node, extra_args, sign=False)
assert get_signet_commitment(get_segwit_commitment(node)) is None
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.