mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-08-31 00:51:20 +02:00
Merge bitcoin/bitcoin#29032: signet: omit commitment for some trivial challenges
6ee32aaaca
test: signet tool genpsbt and solvepsbt commands (Sjors Provoost)0a99d99fe4
signet: miner skips PSBT step for OP_TRUE (Sjors Provoost)cdfb70e5a6
signet: split decode_psbt miner helper (Sjors Provoost) Pull request description: [BIP325](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki) mentions the following rule: > In the special case where an empty solution is valid (ie scriptSig and scriptWitness are both empty) this additional commitment can optionally be left out. This special case is to allow non-signet-aware block generation code to be used to test a custom signet chain where the challenge is trivially true. Such a signet can be created using e.g. `-signetchallenge=51` (`OP_TRUE`). However `contrib/signet/miner` won't omit the commitment. This PR improves the miner by skipping the PSBT for known trivial scripts (just `OP_TRUE` and trivial pushes for now). This prevents it from appending the 4 byte signet header to the witness commitment, as allowed by the above rule. --- Previously the script would fail with `PSBT signing failed`, making it difficult to mine. This is no longer the case. ACKs for top commit: achow101: ACK6ee32aaaca
theStack: re-ACK6ee32aaaca
danielabrozzoni: ACK6ee32aaaca
Tree-SHA512: e47fbf471f2909286a6c1c073799ea388b9c19551afcce96cf9af45cc48d25c02f1e48e08861a88b604361e2c107a759d5baf393da8a37360de419f31651758a
This commit is contained in:
@@ -81,6 +81,9 @@ DIFF_4_N_BITS = 0x1c3fffc0
|
||||
DIFF_4_TARGET = int(DIFF_1_TARGET / 4)
|
||||
assert_equal(uint256_from_compact(DIFF_4_N_BITS), DIFF_4_TARGET)
|
||||
|
||||
# From BIP325
|
||||
SIGNET_HEADER = b"\xec\xc7\xda\xa2"
|
||||
|
||||
def nbits_str(nbits):
|
||||
return f"{nbits:08x}"
|
||||
|
||||
|
@@ -4,15 +4,16 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test signet miner tool"""
|
||||
|
||||
import json
|
||||
import os.path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from test_framework.blocktools import DIFF_1_N_BITS
|
||||
from test_framework.blocktools import DIFF_1_N_BITS, SIGNET_HEADER
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.script_util import key_to_p2wpkh_script
|
||||
from test_framework.script_util import CScript, key_to_p2wpkh_script
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
@@ -23,31 +24,49 @@ from test_framework.wallet_util import bytes_to_wif
|
||||
|
||||
CHALLENGE_PRIVATE_KEY = (42).to_bytes(32, 'big')
|
||||
|
||||
def get_segwit_commitment(node):
|
||||
coinbase = node.getblock(node.getbestblockhash(), 2)['tx'][0]
|
||||
commitment = coinbase['vout'][1]['scriptPubKey']['hex']
|
||||
assert_equal(commitment[0:12], '6a24aa21a9ed')
|
||||
return commitment
|
||||
|
||||
def get_signet_commitment(segwit_commitment):
|
||||
for el in CScript.fromhex(segwit_commitment):
|
||||
if isinstance(el, bytes) and el[0:4] == SIGNET_HEADER:
|
||||
return el[4:].hex()
|
||||
return None
|
||||
|
||||
class SignetMinerTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.chain = "signet"
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.num_nodes = 4
|
||||
|
||||
# generate and specify signet challenge (simple p2wpkh script)
|
||||
privkey = ECKey()
|
||||
privkey.set(CHALLENGE_PRIVATE_KEY, True)
|
||||
pubkey = privkey.get_pubkey().get_bytes()
|
||||
challenge = key_to_p2wpkh_script(pubkey)
|
||||
self.extra_args = [[f'-signetchallenge={challenge.hex()}']]
|
||||
|
||||
self.extra_args = [
|
||||
[f'-signetchallenge={challenge.hex()}'],
|
||||
["-signetchallenge=51"], # OP_TRUE
|
||||
["-signetchallenge=60"], # OP_16
|
||||
["-signetchallenge=202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"], # sha256("hello")
|
||||
]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_cli()
|
||||
self.skip_if_no_wallet()
|
||||
self.skip_if_no_bitcoin_util()
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
# import private key needed for signing block
|
||||
wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), "now")
|
||||
def setup_network(self):
|
||||
self.setup_nodes()
|
||||
# Nodes with different signet networks are not connected
|
||||
|
||||
# generate block with signet miner tool
|
||||
# generate block with signet miner tool
|
||||
def mine_block(self, node):
|
||||
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.cli.datadir}"]
|
||||
@@ -63,7 +82,73 @@ class SignetMinerTest(BitcoinTestFramework):
|
||||
f'--set-block-time={int(time.time())}',
|
||||
'--poolnum=99',
|
||||
], check=True, stderr=subprocess.STDOUT)
|
||||
assert_equal(node.getblockcount(), 1)
|
||||
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):
|
||||
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.cli.datadir}"]
|
||||
util_argv = node.binaries.util_argv() + ["grind"]
|
||||
base_cmd = [
|
||||
sys.executable,
|
||||
signet_miner_path,
|
||||
f'--cli={shlex.join(rpc_argv)}',
|
||||
]
|
||||
|
||||
template = node.getblocktemplate(dict(rules=["signet","segwit"]))
|
||||
genpsbt = subprocess.run(base_cmd + [
|
||||
'genpsbt',
|
||||
f'--address={node.getnewaddress()}',
|
||||
'--poolnum=98',
|
||||
], check=True, input=json.dumps(template).encode('utf8'), capture_output=True)
|
||||
psbt = genpsbt.stdout.decode('utf8').strip()
|
||||
if sign:
|
||||
self.log.debug("Sign the PSBT")
|
||||
res = node.walletprocesspsbt(psbt=psbt, sign=True, sighashtype='ALL')
|
||||
assert res['complete']
|
||||
psbt = res['psbt']
|
||||
solvepsbt = subprocess.run(base_cmd + [
|
||||
'solvepsbt',
|
||||
f'--grind-cmd={shlex.join(util_argv)}',
|
||||
], check=True, input=psbt.encode('utf8'), capture_output=True)
|
||||
node.submitblock(solvepsbt.stdout.decode('utf8').strip())
|
||||
assert_equal(node.getblockcount(), n_blocks + 1)
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("Signet node with single signature challenge")
|
||||
node = self.nodes[0]
|
||||
# import private key needed for signing block
|
||||
wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), 0)
|
||||
self.mine_block(node)
|
||||
# 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)
|
||||
assert get_signet_commitment(get_segwit_commitment(node))
|
||||
|
||||
node = self.nodes[1]
|
||||
self.log.info("Signet node with trivial challenge (OP_TRUE)")
|
||||
self.mine_block(node)
|
||||
# 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]
|
||||
self.log.info("Signet node with trivial challenge (OP_16)")
|
||||
self.mine_block(node)
|
||||
assert get_signet_commitment(get_segwit_commitment(node)) is None
|
||||
|
||||
node = self.nodes[3]
|
||||
self.log.info("Signet node with trivial challenge (push sha256 hash)")
|
||||
self.mine_block(node)
|
||||
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)
|
||||
assert get_signet_commitment(get_segwit_commitment(node)) is None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Reference in New Issue
Block a user