mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03:00
Merge bitcoin/bitcoin#30076: test: fix MiniWallet script-path spend (missing parity bit in leaf version)
e4b0dabb21
test: add functional test for tagged MiniWallet instances (Sebastian Falbesoner)3162c917e9
test: fix MiniWallet internal key derivation for tagged instances (Sebastian Falbesoner)c9f7364ab2
test: fix MiniWallet script-path spend (missing parity bit in leaf version) (Sebastian Falbesoner)7774c314fb
test: refactor: return TaprootInfo from P2TR address creation routine (Sebastian Falbesoner) Pull request description: This PR fixes a dormant bug in MiniWallet that exists since support for P2TR was initially added in #23371 (see commit041abfebe4
). In the course of spending the output, the leaf version byte of the control block in the witness stack doesn't set the parity bit, i.e. we were so far just lucky that the used combinations of relevant data (internal pubkey, leaf script / version) didn't result in a tweaked pubkey with odd y-parity. If that was the case, we'd get the following validation error: `mandatory-script-verify-flag-failed (Witness program hash mismatch) (-26)` Since MiniWallets can now optionally be tagged (#29939), resulting in different internal pubkeys, the issue is more prevalent now. Fix it by passing the parity bit, as specified in BIP341. Can be tested with the following patch (fails on master, succeeds on PR): ```diff diff --git a/test/functional/test_framework/mempool_util.py b/test/functional/test_framework/mempool_util.py index 148cc935ed..7ebe858681 100644 --- a/test/functional/test_framework/mempool_util.py +++ b/test/functional/test_framework/mempool_util.py @@ -42,7 +42,7 @@ def fill_mempool(test_framework, node): # Generate UTXOs to flood the mempool # 1 to create a tx initially that will be evicted from the mempool later # 75 transactions each with a fee rate higher than the previous one - ephemeral_miniwallet = MiniWallet(node, tag_name="fill_mempool_ephemeral_wallet") + ephemeral_miniwallet = MiniWallet(node, tag_name="fill_mempool_ephemeral_wallet3") test_framework.generate(ephemeral_miniwallet, 1 + num_of_batches * tx_batch_size) # Mine enough blocks so that the UTXOs are allowed to be spent ``` In addition to that, another bug is fixed where the internal key derivation failed, as not every pseudorandom hash results in a valid x-only pubkey. Fix this by treating the hash result as private key and calculate the x-only public key out of that, to be used then as internal key. Fixes #30528. ACKs for top commit: glozow: ACKe4b0dabb21
rkrux: reACK [e4b0dab
](e4b0dabb21
) hodlinator: ACKe4b0dabb21
Tree-SHA512: a16f33f76bcb1012857cc3129438a9f6badf28aa2b1d25696da0d385ba5866b46de0f1f93ba777ed9263fe6952f98d7d9c44ea0c0170a2bcc86cbef90bf6ac58
This commit is contained in:
commit
ec700f0a76
3 changed files with 30 additions and 7 deletions
|
@ -3,6 +3,9 @@
|
|||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test MiniWallet."""
|
||||
import random
|
||||
import string
|
||||
|
||||
from test_framework.blocktools import COINBASE_MATURITY
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
|
@ -31,6 +34,20 @@ class FeatureFrameworkMiniWalletTest(BitcoinTestFramework):
|
|||
assert_greater_than_or_equal(tx.get_weight(), target_weight)
|
||||
assert_greater_than_or_equal(target_weight + 3, tx.get_weight())
|
||||
|
||||
def test_wallet_tagging(self):
|
||||
"""Verify that tagged wallet instances are able to send funds."""
|
||||
self.log.info(f"Test tagged wallet instances...")
|
||||
node = self.nodes[0]
|
||||
untagged_wallet = self.wallets[0][1]
|
||||
for i in range(10):
|
||||
tag = ''.join(random.choice(string.ascii_letters) for _ in range(20))
|
||||
self.log.debug(f"-> ({i}) tag name: {tag}")
|
||||
tagged_wallet = MiniWallet(node, tag_name=tag)
|
||||
untagged_wallet.send_to(from_node=node, scriptPubKey=tagged_wallet.get_scriptPubKey(), amount=100000)
|
||||
tagged_wallet.rescan_utxos()
|
||||
tagged_wallet.send_self_transfer(from_node=node)
|
||||
self.generate(node, 1) # clear mempool
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
self.wallets = [
|
||||
|
@ -43,6 +60,7 @@ class FeatureFrameworkMiniWalletTest(BitcoinTestFramework):
|
|||
self.generate(wallet, COINBASE_MATURITY)
|
||||
|
||||
self.test_tx_padding()
|
||||
self.test_wallet_tagging()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -53,13 +53,14 @@ def create_deterministic_address_bcrt1_p2tr_op_true(explicit_internal_key=None):
|
|||
can be spent with a witness stack of OP_TRUE and the control block
|
||||
with internal public key (script-path spending).
|
||||
|
||||
Returns a tuple with the generated address and the internal key.
|
||||
Returns a tuple with the generated address and the TaprootInfo object.
|
||||
"""
|
||||
internal_key = explicit_internal_key or (1).to_bytes(32, 'big')
|
||||
address = output_key_to_p2tr(taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).output_pubkey)
|
||||
taproot_info = taproot_construct(internal_key, [("only-path", CScript([OP_TRUE]))])
|
||||
address = output_key_to_p2tr(taproot_info.output_pubkey)
|
||||
if explicit_internal_key is None:
|
||||
assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
|
||||
return (address, internal_key)
|
||||
return (address, taproot_info)
|
||||
|
||||
|
||||
def byte_to_base58(b, version):
|
||||
|
|
|
@ -39,7 +39,6 @@ from test_framework.messages import (
|
|||
)
|
||||
from test_framework.script import (
|
||||
CScript,
|
||||
LEAF_VERSION_TAPSCRIPT,
|
||||
OP_1,
|
||||
OP_NOP,
|
||||
OP_RETURN,
|
||||
|
@ -106,8 +105,8 @@ class MiniWallet:
|
|||
pub_key = self._priv_key.get_pubkey()
|
||||
self._scriptPubKey = key_to_p2pk_script(pub_key.get_bytes())
|
||||
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
||||
internal_key = None if tag_name is None else hash256(tag_name.encode())
|
||||
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true(internal_key)
|
||||
internal_key = None if tag_name is None else compute_xonly_pubkey(hash256(tag_name.encode()))[0]
|
||||
self._address, self._taproot_info = create_deterministic_address_bcrt1_p2tr_op_true(internal_key)
|
||||
self._scriptPubKey = address_to_scriptpubkey(self._address)
|
||||
|
||||
# When the pre-mined test framework chain is used, it contains coinbase
|
||||
|
@ -195,7 +194,12 @@ class MiniWallet:
|
|||
elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
||||
tx.wit.vtxinwit = [CTxInWitness()] * len(tx.vin)
|
||||
for i in tx.wit.vtxinwit:
|
||||
i.scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
|
||||
assert_equal(len(self._taproot_info.leaves), 1)
|
||||
leaf_info = list(self._taproot_info.leaves.values())[0]
|
||||
i.scriptWitness.stack = [
|
||||
leaf_info.script,
|
||||
bytes([leaf_info.version | self._taproot_info.negflag]) + self._taproot_info.internal_pubkey,
|
||||
]
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
|
Loading…
Reference in a new issue