From 2478c6730a81dda3c56cb99087caf6abe49c85f5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 28 Oct 2021 13:46:52 -0400 Subject: [PATCH 1/7] Make signing follow BIP340 exactly w.r.t. aux randomness libsecp256k1's secp256k1_schnorrsig_sign only follows BIP340 exactly if an aux_rand32 argument is passed. When no randomness is used (as is the case in the current codebase here), there is no impact on security between not providing aux_rand32 at all, or providing an empty one. Yet, for repeatability/testability it is simpler to always use an all-zero one. --- src/key.cpp | 4 ++-- src/key.h | 4 ++-- src/script/sign.cpp | 3 ++- src/test/key_tests.cpp | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/key.cpp b/src/key.cpp index 76882545155..86081b3464e 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -275,7 +275,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) return true; } -bool CKey::SignSchnorr(const uint256& hash, Span sig, const uint256* merkle_root, const uint256* aux) const +bool CKey::SignSchnorr(const uint256& hash, Span sig, const uint256* merkle_root, const uint256& aux) const { assert(sig.size() == 64); secp256k1_keypair keypair; @@ -288,7 +288,7 @@ bool CKey::SignSchnorr(const uint256& hash, Span sig, const uint2 uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root); if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false; } - bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, aux ? (unsigned char*)aux->data() : nullptr); + bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, (unsigned char*)aux.data()); if (ret) { // Additional verification step to prevent using a potentially corrupted signature secp256k1_xonly_pubkey pubkey_verify; diff --git a/src/key.h b/src/key.h index af8d2e72d80..eab18b284ac 100644 --- a/src/key.h +++ b/src/key.h @@ -130,7 +130,7 @@ public: /** * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this, - * optionally tweaked by *merkle_root. Additional nonce entropy can be provided through + * optionally tweaked by *merkle_root. Additional nonce entropy is provided through * aux. * * merkle_root is used to optionally perform tweaking of the private key, as specified @@ -143,7 +143,7 @@ public: * (this is used for key path spending, with specific * Merkle root of the script tree). */ - bool SignSchnorr(const uint256& hash, Span sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const; + bool SignSchnorr(const uint256& hash, Span sig, const uint256* merkle_root, const uint256& aux) const; //! Derive BIP32 child key. bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 4cb21257474..b282f39e6d2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -81,7 +81,8 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& uint256 hash; if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false; sig.resize(64); - if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false; + // Use uint256{} as aux_rnd for now. + if (!key.SignSchnorr(hash, sig, merkle_root, {})) return false; if (nHashType) sig.push_back(nHashType); return true; } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index b915982d98a..2769dde3676 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -321,7 +321,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors) key.Set(sec.begin(), sec.end(), true); XOnlyPubKey pubkey(key.GetPubKey()); BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end())); - bool ok = key.SignSchnorr(msg256, sig64, nullptr, &aux256); + bool ok = key.SignSchnorr(msg256, sig64, nullptr, aux256); BOOST_CHECK(ok); BOOST_CHECK(std::vector(sig64, sig64 + 64) == sig); // Verify those signatures for good measure. @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors) BOOST_CHECK(tweaked); XOnlyPubKey tweaked_key = tweaked->first; aux256 = InsecureRand256(); - bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, &aux256); + bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, aux256); BOOST_CHECK(ok); BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64)); } From 51408250969e7ed171378369a995c90d4f813189 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 26 Oct 2021 15:34:28 -0400 Subject: [PATCH 2/7] tests: add more fields to TaprootInfo --- test/functional/test_framework/script.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 3c9b8a6e69e..8dcc8c58fe9 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -805,20 +805,20 @@ def taproot_tree_helper(scripts): h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code)) if name is None: return ([], h) - return ([(name, version, code, bytes())], h) + return ([(name, version, code, bytes(), h)], h) elif len(scripts) == 2 and callable(scripts[1]): # Two entries, and the right one is a function left, left_h = taproot_tree_helper(scripts[0:1]) right_h = scripts[1](left_h) - left = [(name, version, script, control + right_h) for name, version, script, control in left] + left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left] right = [] else: # Two or more entries: descend into each side split_pos = len(scripts) // 2 left, left_h = taproot_tree_helper(scripts[0:split_pos]) right, right_h = taproot_tree_helper(scripts[split_pos:]) - left = [(name, version, script, control + right_h) for name, version, script, control in left] - right = [(name, version, script, control + left_h) for name, version, script, control in right] + left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left] + right = [(name, version, script, control + left_h, leaf) for name, version, script, control, leaf in right] if right_h < left_h: right_h, left_h = left_h, right_h h = TaggedHash("TapBranch", left_h + right_h) @@ -830,13 +830,14 @@ def taproot_tree_helper(scripts): # - negflag: whether the pubkey in the scriptPubKey was negated from internal_pubkey+tweak*G (bool). # - tweak: the tweak (32 bytes) # - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves -TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves") +# - merkle_root: the script tree's Merkle root, or bytes() if no leaves are present +TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves,merkle_root,output_pubkey") # A TaprootLeafInfo object has the following fields: # - script: the leaf script (CScript or bytes) # - version: the leaf version (0xc0 for BIP342 tapscript) # - merklebranch: the merkle branch to use for this leaf (32*N bytes) -TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch") +TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch,leaf_hash") def taproot_construct(pubkey, scripts=None): """Construct a tree of Taproot spending conditions @@ -858,8 +859,8 @@ def taproot_construct(pubkey, scripts=None): ret, h = taproot_tree_helper(scripts) tweak = TaggedHash("TapTweak", pubkey + h) tweaked, negated = tweak_add_pubkey(pubkey, tweak) - leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret) - return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves) + leaves = dict((name, TaprootLeafInfo(script, version, merklebranch, leaf)) for name, version, script, merklebranch, leaf in ret) + return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked) def is_op_success(o): return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe) From a5bde018b42cd38979fee71d870e0140b10c73d6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 26 Oct 2021 14:34:04 -0400 Subject: [PATCH 3/7] tests: give feature_taproot access to sighash preimages --- test/functional/feature_taproot.py | 34 ++++++++++++++----- test/functional/test_framework/script.py | 42 ++++++++++++++++-------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index 50a25ee1ef5..085b37a74d7 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -25,8 +25,9 @@ from test_framework.script import ( CScript, CScriptNum, CScriptOp, + hash256, LEAF_VERSION_TAPSCRIPT, - LegacySignatureHash, + LegacySignatureMsg, LOCKTIME_THRESHOLD, MAX_SCRIPT_ELEMENT_SIZE, OP_0, @@ -70,8 +71,9 @@ from test_framework.script import ( SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_ANYONECANPAY, - SegwitV0SignatureHash, - TaprootSignatureHash, + SegwitV0SignatureMsg, + TaggedHash, + TaprootSignatureMsg, is_op_success, taproot_construct, ) @@ -194,8 +196,8 @@ def default_controlblock(ctx): """Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch.""" return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch") -def default_sighash(ctx): - """Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash.""" +def default_sigmsg(ctx): + """Default expression for "sigmsg": depending on mode, compute BIP341, BIP143, or legacy sigmsg.""" tx = get(ctx, "tx") idx = get(ctx, "idx") hashtype = get(ctx, "hashtype_actual") @@ -208,18 +210,30 @@ def default_sighash(ctx): codeseppos = get(ctx, "codeseppos") leaf_ver = get(ctx, "leafversion") script = get(ctx, "script_taproot") - return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex) + return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex) else: - return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=False, annex=annex) + return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex) elif mode == "witv0": # BIP143 signature hash scriptcode = get(ctx, "scriptcode") utxos = get(ctx, "utxos") - return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue) + return SegwitV0SignatureMsg(scriptcode, tx, idx, hashtype, utxos[idx].nValue) else: # Pre-segwit signature hash scriptcode = get(ctx, "scriptcode") - return LegacySignatureHash(scriptcode, tx, idx, hashtype)[0] + return LegacySignatureMsg(scriptcode, tx, idx, hashtype)[0] + +def default_sighash(ctx): + """Default expression for "sighash": depending on mode, compute tagged hash or dsha256 of sigmsg.""" + msg = get(ctx, "sigmsg") + mode = get(ctx, "mode") + if mode == "taproot": + return TaggedHash("TapSighash", msg) + else: + if msg is None: + return (1).to_bytes(32, 'little') + else: + return hash256(msg) def default_tweak(ctx): """Default expression for "tweak": None if a leaf is specified, tap[0] otherwise.""" @@ -340,6 +354,8 @@ DEFAULT_CONTEXT = { "key_tweaked": default_key_tweaked, # The tweak to use (None for script path spends, the actual tweak for key path spends). "tweak": default_tweak, + # The sigmsg value (preimage of sighash) + "sigmsg": default_sigmsg, # The sighash value (32 bytes) "sighash": default_sighash, # The information about the chosen script path spend (TaprootLeafInfo object). diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 8dcc8c58fe9..46c723b06ab 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -619,16 +619,15 @@ def FindAndDelete(script, sig): r += script[last_sop_idx:] return CScript(r) -def LegacySignatureHash(script, txTo, inIdx, hashtype): - """Consensus-correct SignatureHash +def LegacySignatureMsg(script, txTo, inIdx, hashtype): + """Preimage of the signature hash, if it exists. - Returns (hash, err) to precisely match the consensus-critical behavior of - the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity) + Returns either (None, err) to indicate error (which translates to sighash 1), + or (msg, None). """ - HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' if inIdx >= len(txTo.vin): - return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) + return (None, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) txtmp = CTransaction(txTo) for txin in txtmp.vin: @@ -645,7 +644,7 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype): elif (hashtype & 0x1f) == SIGHASH_SINGLE: outIdx = inIdx if outIdx >= len(txtmp.vout): - return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) + return (None, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) tmp = txtmp.vout[outIdx] txtmp.vout = [] @@ -665,15 +664,27 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype): s = txtmp.serialize_without_witness() s += struct.pack(b" Date: Wed, 27 Oct 2021 15:07:35 -0400 Subject: [PATCH 4/7] tests: abstract out precomputed BIP341 signature hash elements --- test/functional/test_framework/script.py | 25 +++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 46c723b06ab..de71e19251c 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -755,6 +755,21 @@ class TestFrameworkScript(unittest.TestCase): for value in values: self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value) +def BIP341_sha_prevouts(txTo): + return sha256(b"".join(i.prevout.serialize() for i in txTo.vin)) + +def BIP341_sha_amounts(spent_utxos): + return sha256(b"".join(struct.pack(" Date: Wed, 27 Oct 2021 15:46:03 -0400 Subject: [PATCH 5/7] tests: add deterministic signing mode to ECDSA This does the following: * Adds a rfc6979 argument to test_framework/key.py's sign_ecdsa to select (deterministic) RFC6979-based nonce generation. * Add a flag in feature_taproot.py's framework called "deterministic". * Make the Schnorr signing in feature_taproot.py randomized by default, reverting to the old deterministic (aux_rnd=0x0000...00) behavior if the deterministic context flag is set. * Make the ECDSA signing in feature_taproot.py use RFC6979-based nonces when the deterministic context flag is set (keeping the old randomized behavior otherwise). --- test/functional/feature_taproot.py | 10 ++++++++-- test/functional/test_framework/key.py | 20 +++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index 085b37a74d7..f2ae8b302dc 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -253,14 +253,18 @@ def default_key_tweaked(ctx): def default_signature(ctx): """Default expression for "signature": BIP340 signature or ECDSA signature depending on mode.""" sighash = get(ctx, "sighash") + deterministic = get(ctx, "deterministic") if get(ctx, "mode") == "taproot": key = get(ctx, "key_tweaked") flip_r = get(ctx, "flag_flip_r") flip_p = get(ctx, "flag_flip_p") - return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p) + aux = bytes([0] * 32) + if not deterministic: + aux = random.getrandbits(256).to_bytes(32, 'big') + return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p, aux=aux) else: key = get(ctx, "key") - return key.sign_ecdsa(sighash) + return key.sign_ecdsa(sighash, rfc6979=deterministic) def default_hashtype_actual(ctx): """Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot.""" @@ -392,6 +396,8 @@ DEFAULT_CONTEXT = { "leaf": None, # The input arguments to provide to the executed script "inputs": [], + # Use deterministic signing nonces + "deterministic": False, # == Parameters to be set before evaluation: == # - mode: what spending style to use ("taproot", "witv0", or "legacy"). diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index 26526e35faf..e5dea66963d 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -8,6 +8,7 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for anything but tests.""" import csv import hashlib +import hmac import os import random import unittest @@ -326,6 +327,16 @@ def generate_privkey(): """Generate a valid random 32-byte private key.""" return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big') +def rfc6979_nonce(key): + """Compute signing nonce using RFC6979.""" + v = bytes([1] * 32) + k = bytes([0] * 32) + k = hmac.new(k, v + b"\x00" + key, 'sha256').digest() + v = hmac.new(k, v, 'sha256').digest() + k = hmac.new(k, v + b"\x01" + key, 'sha256').digest() + v = hmac.new(k, v, 'sha256').digest() + return hmac.new(k, v, 'sha256').digest() + class ECKey(): """A secp256k1 private key""" @@ -368,15 +379,18 @@ class ECKey(): ret.compressed = self.compressed return ret - def sign_ecdsa(self, msg, low_s=True): + def sign_ecdsa(self, msg, low_s=True, rfc6979=False): """Construct a DER-encoded ECDSA signature with this key. See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the ECDSA signer algorithm.""" assert(self.valid) z = int.from_bytes(msg, 'big') - # Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation) - k = random.randrange(1, SECP256K1_ORDER) + # Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation) + if rfc6979: + k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big') + else: + k = random.randrange(1, SECP256K1_ORDER) R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)])) r = R[0] % SECP256K1_ORDER s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER From ac3037df1196b1d95ade2dfad4699ad3a6074903 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 26 Oct 2021 15:34:39 -0400 Subject: [PATCH 6/7] tests: BIP341 test vector generation --- test/functional/feature_taproot.py | 257 ++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 7 deletions(-) diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index f2ae8b302dc..dccf8a718cc 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -22,6 +22,11 @@ from test_framework.messages import ( ) from test_framework.script import ( ANNEX_TAG, + BIP341_sha_amounts, + BIP341_sha_outputs, + BIP341_sha_prevouts, + BIP341_sha_scriptpubkeys, + BIP341_sha_sequences, CScript, CScriptNum, CScriptOp, @@ -79,6 +84,7 @@ from test_framework.script import ( ) from test_framework.script_util import ( key_to_p2pk_script, + key_to_p2pkh_script, key_to_p2wpkh_script, keyhash_to_p2pkh_script, script_to_p2sh_script, @@ -89,6 +95,7 @@ from test_framework.util import assert_raises_rpc_error, assert_equal from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey from test_framework.address import ( hash160, + program_to_witness ) from collections import OrderedDict, namedtuple from io import BytesIO @@ -97,6 +104,9 @@ import hashlib import os import random +# Whether or not to output generated test vectors, in JSON format. +GEN_TEST_VECTORS = False + # === Framework for building spending transactions. === # # The computation is represented as a "context" dict, whose entries store potentially-unevaluated expressions that @@ -418,6 +428,7 @@ def flatten(lst): ret.append(elem) return ret + def spend(tx, idx, utxos, **kwargs): """Sign transaction input idx of tx, provided utxos is the list of outputs being spent. @@ -1276,6 +1287,14 @@ class TaprootTest(BitcoinTestFramework): else: assert node.getbestblockhash() == self.lastblockhash, "Failed to reject: " + msg + def init_blockinfo(self, node): + # Initialize variables used by block_submit(). + self.lastblockhash = node.getbestblockhash() + self.tip = int(self.lastblockhash, 16) + block = node.getblock(self.lastblockhash) + self.lastblockheight = block['height'] + self.lastblocktime = block['time'] + def test_spenders(self, node, spenders, input_counts): """Run randomized tests with a number of "spenders". @@ -1302,12 +1321,7 @@ class TaprootTest(BitcoinTestFramework): host_spks.append(spk) host_pubkeys.append(bytes.fromhex(info['pubkey'])) - # Initialize variables used by block_submit(). - self.lastblockhash = node.getbestblockhash() - self.tip = int(self.lastblockhash, 16) - block = node.getblock(self.lastblockhash) - self.lastblockheight = block['height'] - self.lastblocktime = block['time'] + self.init_blockinfo(node) # Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and # one change output at the end. The transaction is constructed on the Python side to enable @@ -1481,10 +1495,239 @@ class TaprootTest(BitcoinTestFramework): assert len(mismatching_utxos) == 0 self.log.info(" - Done") + def gen_test_vectors(self): + """Run a scenario that corresponds (and optionally produces) to BIP341 test vectors.""" + + self.log.info("Unit test scenario...") + + # Deterministically mine coins to OP_TRUE in block 1 + assert self.nodes[1].getblockcount() == 0 + coinbase = CTransaction() + coinbase.nVersion = 1 + coinbase.vin = [CTxIn(COutPoint(0, 0xffffffff), CScript([OP_1, OP_1]), 0xffffffff)] + coinbase.vout = [CTxOut(5000000000, CScript([OP_1]))] + coinbase.nLockTime = 0 + coinbase.rehash() + assert coinbase.hash == "f60c73405d499a956d3162e3483c395526ef78286458a4cb17b125aa92e49b20" + # Mine it + block = create_block(hashprev=int(self.nodes[1].getbestblockhash(), 16), coinbase=coinbase) + block.rehash() + block.solve() + self.nodes[1].submitblock(block.serialize().hex()) + assert self.nodes[1].getblockcount() == 1 + self.generate(self.nodes[1], COINBASE_MATURITY) + + SEED = 317 + VALID_LEAF_VERS = list(range(0xc0, 0x100, 2)) + [0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe] + # Generate private keys + prvs = [hashlib.sha256(SEED.to_bytes(2, 'big') + bytes([i])).digest() for i in range(100)] + # Generate corresponding public x-only pubkeys + pubs = [compute_xonly_pubkey(prv)[0] for prv in prvs] + # Generate taproot objects + inner_keys = [pubs[i] for i in range(7)] + + script_lists = [ + None, + [("0", CScript([pubs[50], OP_CHECKSIG]), 0xc0)], + [("0", CScript([pubs[51], OP_CHECKSIG]), 0xc0)], + [("0", CScript([pubs[52], OP_CHECKSIG]), 0xc0), ("1", CScript([b"BIP341"]), VALID_LEAF_VERS[pubs[99][0] % 41])], + [("0", CScript([pubs[53], OP_CHECKSIG]), 0xc0), ("1", CScript([b"Taproot"]), VALID_LEAF_VERS[pubs[99][1] % 41])], + [("0", CScript([pubs[54], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[55], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[56], OP_CHECKSIG]), 0xc0)]], + [("0", CScript([pubs[57], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[58], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[59], OP_CHECKSIG]), 0xc0)]], + ] + taps = [taproot_construct(inner_keys[i], script_lists[i]) for i in range(len(inner_keys))] + + # Require negated taps[0] + assert taps[0].negflag + # Require one negated and one non-negated in taps 1 and 2. + assert taps[1].negflag != taps[2].negflag + # Require one negated and one non-negated in taps 3 and 4. + assert taps[3].negflag != taps[4].negflag + # Require one negated and one non-negated in taps 5 and 6. + assert taps[5].negflag != taps[6].negflag + + cblks = [{leaf: get({**DEFAULT_CONTEXT, 'tap': taps[i], 'leaf': leaf}, 'controlblock') for leaf in taps[i].leaves} for i in range(7)] + # Require one swapped and one unswapped in taps 3 and 4. + assert (cblks[3]['0'][33:65] < cblks[3]['1'][33:65]) != (cblks[4]['0'][33:65] < cblks[4]['1'][33:65]) + # Require one swapped and one unswapped in taps 5 and 6, both at the top and child level. + assert (cblks[5]['0'][33:65] < cblks[5]['1'][65:]) != (cblks[6]['0'][33:65] < cblks[6]['1'][65:]) + assert (cblks[5]['1'][33:65] < cblks[5]['2'][33:65]) != (cblks[6]['1'][33:65] < cblks[6]['2'][33:65]) + # Require within taps 5 (and thus also 6) that one level is swapped and the other is not. + assert (cblks[5]['0'][33:65] < cblks[5]['1'][65:]) != (cblks[5]['1'][33:65] < cblks[5]['2'][33:65]) + + # Compute a deterministic set of scriptPubKeys + tap_spks = [] + old_spks = [] + spend_info = {} + # First, taproot scriptPubKeys, for the tap objects constructed above + for i, tap in enumerate(taps): + tap_spks.append(tap.scriptPubKey) + d = {'key': prvs[i], 'tap': tap, 'mode': 'taproot'} + spend_info[tap.scriptPubKey] = d + # Then, a number of deterministically generated (keys 0x1,0x2,0x3) with 2x P2PKH, 1x P2WPKH spks. + for i in range(1, 4): + prv = ECKey() + prv.set(i.to_bytes(32, 'big'), True) + pub = prv.get_pubkey().get_bytes() + d = {"key": prv} + d["scriptcode"] = key_to_p2pkh_script(pub) + d["inputs"] = [getter("sign"), pub] + if i < 3: + # P2PKH + d['spk'] = key_to_p2pkh_script(pub) + d['mode'] = 'legacy' + else: + # P2WPKH + d['spk'] = key_to_p2wpkh_script(pub) + d['mode'] = 'witv0' + old_spks.append(d['spk']) + spend_info[d['spk']] = d + + # Construct a deterministic chain of transactions creating UTXOs to the test's spk's (so that they + # come from distinct txids). + txn = [] + lasttxid = coinbase.sha256 + amount = 5000000000 + for i, spk in enumerate(old_spks + tap_spks): + val = 42000000 * (i + 7) + tx = CTransaction() + tx.nVersion = 1 + tx.vin = [CTxIn(COutPoint(lasttxid, i & 1), CScript([]), 0xffffffff)] + tx.vout = [CTxOut(val, spk), CTxOut(amount - val, CScript([OP_1]))] + if i & 1: + tx.vout = list(reversed(tx.vout)) + tx.nLockTime = 0 + tx.rehash() + amount -= val + lasttxid = tx.sha256 + txn.append(tx) + spend_info[spk]['prevout'] = COutPoint(tx.sha256, i & 1) + spend_info[spk]['utxo'] = CTxOut(val, spk) + # Mine those transactions + self.init_blockinfo(self.nodes[1]) + self.block_submit(self.nodes[1], txn, "Crediting txn", None, sigops_weight=10, accept=True) + + # scriptPubKey computation + tests = {"version": 1} + spk_tests = tests.setdefault("scriptPubKey", []) + for i, tap in enumerate(taps): + test_case = {} + given = test_case.setdefault("given", {}) + given['internalPubkey'] = tap.internal_pubkey.hex() + + def pr(node): + if node is None: + return None + elif isinstance(node, tuple): + return {"id": int(node[0]), "script": node[1].hex(), "leafVersion": node[2]} + elif len(node) == 1: + return pr(node[0]) + elif len(node) == 2: + return [pr(node[0]), pr(node[1])] + else: + assert False + + given['scriptTree'] = pr(script_lists[i]) + intermediary = test_case.setdefault("intermediary", {}) + if len(tap.leaves): + leafhashes = intermediary.setdefault('leafHashes', [None] * len(tap.leaves)) + for leaf in tap.leaves: + leafhashes[int(leaf)] = tap.leaves[leaf].leaf_hash.hex() + intermediary['merkleRoot'] = tap.merkle_root.hex() if tap.merkle_root else None + intermediary['tweak'] = tap.tweak.hex() + intermediary['tweakedPubkey'] = tap.output_pubkey.hex() + expected = test_case.setdefault("expected", {}) + expected['scriptPubKey'] = tap.scriptPubKey.hex() + expected['bip350Address'] = program_to_witness(1, bytes(tap.output_pubkey), True) + if len(tap.leaves): + control_blocks = expected.setdefault("scriptPathControlBlocks", [None] * len(tap.leaves)) + for leaf in tap.leaves: + ctx = {**DEFAULT_CONTEXT, 'tap': tap, 'leaf': leaf} + control_blocks[int(leaf)] = get(ctx, "controlblock").hex() + spk_tests.append(test_case) + + # Construct a deterministic transaction spending all outputs created above. + tx = CTransaction() + tx.nVersion = 2 + tx.vin = [] + inputs = [] + input_spks = [tap_spks[0], tap_spks[1], old_spks[0], tap_spks[2], tap_spks[5], old_spks[2], tap_spks[6], tap_spks[3], tap_spks[4]] + sequences = [0, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffffe, 0, 0, 0xffffffff, 0xffffffff] + hashtypes = [SIGHASH_SINGLE, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, SIGHASH_ALL, SIGHASH_ALL, SIGHASH_DEFAULT, SIGHASH_ALL, SIGHASH_NONE, SIGHASH_NONE|SIGHASH_ANYONECANPAY, SIGHASH_ALL|SIGHASH_ANYONECANPAY] + for i, spk in enumerate(input_spks): + tx.vin.append(CTxIn(spend_info[spk]['prevout'], CScript(), sequences[i])) + inputs.append(spend_info[spk]['utxo']) + tx.vout.append(CTxOut(1000000000, old_spks[1])) + tx.vout.append(CTxOut(3410000000, pubs[98])) + tx.nLockTime = 500000000 + precomputed = { + "hashAmounts": BIP341_sha_amounts(inputs), + "hashPrevouts": BIP341_sha_prevouts(tx), + "hashScriptPubkeys": BIP341_sha_scriptpubkeys(inputs), + "hashSequences": BIP341_sha_sequences(tx), + "hashOutputs": BIP341_sha_outputs(tx) + } + keypath_tests = tests.setdefault("keyPathSpending", []) + tx_test = {} + global_given = tx_test.setdefault("given", {}) + global_given['rawUnsignedTx'] = tx.serialize().hex() + utxos_spent = global_given.setdefault("utxosSpent", []) + for i in range(len(input_spks)): + utxos_spent.append({"scriptPubKey": inputs[i].scriptPubKey.hex(), "amountSats": inputs[i].nValue}) + global_intermediary = tx_test.setdefault("intermediary", {}) + for key in sorted(precomputed.keys()): + global_intermediary[key] = precomputed[key].hex() + test_list = tx_test.setdefault('inputSpending', []) + for i in range(len(input_spks)): + ctx = { + **DEFAULT_CONTEXT, + **spend_info[input_spks[i]], + 'tx': tx, + 'utxos': inputs, + 'idx': i, + 'hashtype': hashtypes[i], + 'deterministic': True + } + if ctx['mode'] == 'taproot': + test_case = {} + given = test_case.setdefault("given", {}) + given['txinIndex'] = i + given['internalPrivkey'] = get(ctx, 'key').hex() + if get(ctx, "tap").merkle_root != bytes(): + given['merkleRoot'] = get(ctx, "tap").merkle_root.hex() + else: + given['merkleRoot'] = None + given['hashType'] = get(ctx, "hashtype") + intermediary = test_case.setdefault("intermediary", {}) + intermediary['internalPubkey'] = get(ctx, "tap").internal_pubkey.hex() + intermediary['tweak'] = get(ctx, "tap").tweak.hex() + intermediary['tweakedPrivkey'] = get(ctx, "key_tweaked").hex() + sigmsg = get(ctx, "sigmsg") + intermediary['sigMsg'] = sigmsg.hex() + intermediary['precomputedUsed'] = [key for key in sorted(precomputed.keys()) if sigmsg.count(precomputed[key])] + intermediary['sigHash'] = get(ctx, "sighash").hex() + expected = test_case.setdefault("expected", {}) + expected['witness'] = [get(ctx, "sign").hex()] + test_list.append(test_case) + tx.wit.vtxinwit.append(CTxInWitness()) + tx.vin[i].scriptSig = CScript(flatten(get(ctx, "scriptsig"))) + tx.wit.vtxinwit[i].scriptWitness.stack = flatten(get(ctx, "witness")) + aux = tx_test.setdefault("auxiliary", {}) + aux['fullySignedTx'] = tx.serialize().hex() + keypath_tests.append(tx_test) + assert_equal(hashlib.sha256(tx.serialize()).hexdigest(), "24bab662cb55a7f3bae29b559f651674c62bcc1cd442d44715c0133939107b38") + # Mine the spending transaction + self.block_submit(self.nodes[1], [tx], "Spending txn", None, sigops_weight=10000, accept=True, witness=True) + + if GEN_TEST_VECTORS: + print(json.dumps(tests, indent=4, sort_keys=False)) + + def run_test(self): + self.gen_test_vectors() + # Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot). self.log.info("Post-activation tests...") - self.generate(self.nodes[1], COINBASE_MATURITY + 1) self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3]) # Re-connect nodes in case they have been disconnected From f1c33ee4ac1056289f2e67b75755388549ada4ca Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 27 Oct 2021 19:16:01 -0400 Subject: [PATCH 7/7] tests: implement BIP341 test vectors --- src/Makefile.test.include | 1 + src/script/interpreter.cpp | 2 +- src/script/interpreter.h | 1 + src/test/data/bip341_wallet_vectors.json | 452 +++++++++++++++++++++++ src/test/script_standard_tests.cpp | 46 +++ src/test/script_tests.cpp | 76 ++++ 6 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 src/test/data/bip341_wallet_vectors.json diff --git a/src/Makefile.test.include b/src/Makefile.test.include index da4b85665f7..402cf188c4c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -16,6 +16,7 @@ FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT) JSON_TEST_FILES = \ test/data/script_tests.json \ + test/data/bip341_wallet_vectors.json \ test/data/base58_encode_decode.json \ test/data/blockfilters.json \ test/data/key_io_valid.json \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index eafa9840d74..d83ec7192b4 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1483,7 +1483,7 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); -static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash"); +const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash"); const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf"); const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch"); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index ab49e845778..513eaaf94c7 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -229,6 +229,7 @@ static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32; static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128; static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; +extern const CHashWriter HASHER_TAPSIGHASH; //!< Hasher with tag "TapSighash" pre-fed to it. extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it. extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it. diff --git a/src/test/data/bip341_wallet_vectors.json b/src/test/data/bip341_wallet_vectors.json new file mode 100644 index 00000000000..11261b00ba2 --- /dev/null +++ b/src/test/data/bip341_wallet_vectors.json @@ -0,0 +1,452 @@ +{ + "version": 1, + "scriptPubKey": [ + { + "given": { + "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", + "scriptTree": null + }, + "intermediary": { + "merkleRoot": null, + "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", + "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343" + }, + "expected": { + "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + "bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5" + } + }, + { + "given": { + "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", + "scriptTree": { + "id": 0, + "script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac", + "leafVersion": 192 + } + }, + "intermediary": { + "leafHashes": [ + "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21" + ], + "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", + "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", + "tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3" + }, + "expected": { + "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + "bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586", + "scriptPathControlBlocks": [ + "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27" + ] + } + }, + { + "given": { + "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", + "scriptTree": { + "id": 0, + "script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac", + "leafVersion": 192 + } + }, + "intermediary": { + "leafHashes": [ + "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b" + ], + "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", + "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", + "tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e" + }, + "expected": { + "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + "bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5", + "scriptPathControlBlocks": [ + "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820" + ] + } + }, + { + "given": { + "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", + "scriptTree": [ + { + "id": 0, + "script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac", + "leafVersion": 192 + }, + { + "id": 1, + "script": "06424950333431", + "leafVersion": 250 + } + ] + }, + "intermediary": { + "leafHashes": [ + "8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7", + "f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a" + ], + "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", + "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", + "tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5" + }, + "expected": { + "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", + "bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm", + "scriptPathControlBlocks": [ + "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a", + "faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7" + ] + } + }, + { + "given": { + "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", + "scriptTree": [ + { + "id": 0, + "script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac", + "leafVersion": 192 + }, + { + "id": 1, + "script": "07546170726f6f74", + "leafVersion": 192 + } + ] + }, + "intermediary": { + "leafHashes": [ + "64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89", + "2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb" + ], + "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", + "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", + "tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220" + }, + "expected": { + "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", + "bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq", + "scriptPathControlBlocks": [ + "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb", + "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89" + ] + } + }, + { + "given": { + "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", + "scriptTree": [ + { + "id": 0, + "script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac", + "leafVersion": 192 + }, + [ + { + "id": 1, + "script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac", + "leafVersion": 192 + }, + { + "id": 2, + "script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac", + "leafVersion": 192 + } + ] + ] + }, + "intermediary": { + "leafHashes": [ + "2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", + "ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c", + "9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6" + ], + "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", + "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", + "tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605" + }, + "expected": { + "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + "bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e", + "scriptPathControlBlocks": [ + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553", + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817" + ] + } + }, + { + "given": { + "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", + "scriptTree": [ + { + "id": 0, + "script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac", + "leafVersion": 192 + }, + [ + { + "id": 1, + "script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac", + "leafVersion": 192 + }, + { + "id": 2, + "script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac", + "leafVersion": 192 + } + ] + ] + }, + "intermediary": { + "leafHashes": [ + "f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", + "737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711", + "d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7" + ], + "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", + "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", + "tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831" + }, + "expected": { + "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + "bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe", + "scriptPathControlBlocks": [ + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91", + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d" + ] + } + } + ], + "keyPathSpending": [ + { + "given": { + "rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d", + "utxosSpent": [ + { + "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + "amountSats": 420000000 + }, + { + "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + "amountSats": 462000000 + }, + { + "scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac", + "amountSats": 294000000 + }, + { + "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + "amountSats": 504000000 + }, + { + "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + "amountSats": 630000000 + }, + { + "scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", + "amountSats": 378000000 + }, + { + "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + "amountSats": 672000000 + }, + { + "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", + "amountSats": 546000000 + }, + { + "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", + "amountSats": 588000000 + } + ] + }, + "intermediary": { + "hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6", + "hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5", + "hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f", + "hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21", + "hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e" + }, + "inputSpending": [ + { + "given": { + "txinIndex": 0, + "internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa", + "merkleRoot": null, + "hashType": 3 + }, + "intermediary": { + "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", + "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", + "tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9", + "sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0", + "precomputedUsed": [ + "hashAmounts", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555" + }, + "expected": { + "witness": [ + "ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03" + ] + } + }, + { + "given": { + "txinIndex": 1, + "internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f", + "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", + "hashType": 131 + }, + "intermediary": { + "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", + "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", + "tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080", + "sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d", + "precomputedUsed": [], + "sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d" + }, + "expected": { + "witness": [ + "052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83" + ] + } + }, + { + "given": { + "txinIndex": 3, + "internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64", + "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", + "hashType": 1 + }, + "intermediary": { + "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", + "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", + "tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d", + "sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000", + "precomputedUsed": [ + "hashAmounts", + "hashOutputs", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669" + }, + "expected": { + "witness": [ + "ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01" + ] + } + }, + { + "given": { + "txinIndex": 4, + "internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e", + "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", + "hashType": 0 + }, + "intermediary": { + "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", + "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", + "tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501", + "sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000", + "precomputedUsed": [ + "hashAmounts", + "hashOutputs", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef" + }, + "expected": { + "witness": [ + "b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f" + ] + } + }, + { + "given": { + "txinIndex": 6, + "internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8", + "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", + "hashType": 2 + }, + "intermediary": { + "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", + "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", + "tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901", + "sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000", + "precomputedUsed": [ + "hashAmounts", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85" + }, + "expected": { + "witness": [ + "a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002" + ] + } + }, + { + "given": { + "txinIndex": 7, + "internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103", + "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", + "hashType": 130 + }, + "intermediary": { + "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", + "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", + "tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b", + "sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff", + "precomputedUsed": [], + "sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10" + }, + "expected": { + "witness": [ + "ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482" + ] + } + }, + { + "given": { + "txinIndex": 8, + "internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa", + "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", + "hashType": 129 + }, + "intermediary": { + "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", + "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", + "tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5", + "sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff", + "precomputedUsed": [ + "hashOutputs" + ], + "sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2" + }, + "expected": { + "witness": [ + "bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981" + ] + } + } + ], + "auxiliary": { + "fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d" + } + } + ] +} diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index bf8ff5f5e22..5a5cc6ab292 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -2,6 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include #include