Skip to content

Commit 2539980

Browse files
committed
Merge bitcoin/bitcoin#23371: test: MiniWallet: add P2TR support and use it per default
041abfe test: MiniWallet: add P2TR support and use it per default (Sebastian Falbesoner) 4a2edf2 test: generate blocks to MiniWallet address in rpc_blockchain.py (Sebastian Falbesoner) Pull request description: Taproot activates in [about 19 days](https://taproot.watch/) (2716 blocks), and it'd be nice if we set a good example and also support it in our MiniWallet. This PR changes the default mode from P2WSH (segwit v0 output, bech32 address) to P2TR (segwit v1 output, bech32m address) transactions type with the _anyone-can-spend_ policy, i.e. a witness script of `OP_TRUE`. The transition is actually quite painless, one only needs one extra piece in the form of a internal public key that is passed in the control block on the witness stack, in order to trigger script-path spending. To keep things simple, the lowest possible valid x-only-public key with the value of 1 was chosen as internal key. Since many tests expect to find outputs for the default scriptPubKey of MiniWallet in the pre-mined chain of the test framework, the generation address is also changed from `ADDRESS_BCRT1_P2WSH_OP_TRUE` to `create_deterministic_address_bcrt1_p2tr_op_true()[0]` accordingly (see method `BitcoinTestFramework._initialize_chain(...)`). Note that the pre-mined chain is cached locally, so you probably have to delete the `./test/cache` folder first for the tests to pass again. In order to avoid unnecessary renames, the import of `ADDRESS_BCRT1_P2WSH_OP_TRUE` is eliminated in rpc_blockchain.py by generating blocks directly to the MiniWallet address by using the `self.generate(self.wallet, ...)` interface (see first commit). ACKs for top commit: laanwj: Code review ACK 041abfe Tree-SHA512: 876a5b0595333f9c96c68d5ecf2b4530aee2715aebb75a953f4f75ca12258bd7239210fcfa1ae044bee91489804c9c2f2a6a335bd46c3ac701873d32e3a4f49d
2 parents 8ae4ba4 + 041abfe commit 2539980

File tree

7 files changed

+52
-28
lines changed

7 files changed

+52
-28
lines changed

test/functional/feature_rbf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ def set_test_params(self):
4747
def run_test(self):
4848
self.wallet = MiniWallet(self.nodes[0])
4949
# the pre-mined test framework chain contains coinbase outputs to the
50-
# MiniWallet's default address ADDRESS_BCRT1_P2WSH_OP_TRUE in blocks
51-
# 76-100 (see method BitcoinTestFramework._initialize_chain())
50+
# MiniWallet's default address in blocks 76-100 (see method
51+
# BitcoinTestFramework._initialize_chain())
5252
self.wallet.rescan_utxos()
5353

5454
self.log.info("Running test simple doublespend...")

test/functional/feature_utxo_set_hash.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ def test_muhash_implementation(self):
6969
assert_equal(finalized[::-1].hex(), node_muhash)
7070

7171
self.log.info("Test deterministic UTXO set hash results")
72-
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "5b1b44097406226c0eb8e1362cd17a1f346522cf9390a8175a57a5262cb1963f")
73-
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "4b8803075d7151d06fad3e88b68ba726886794873fbfa841d12aefb2cc2b881b")
72+
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "221f245cf4c9010eeb7f5183d342c002ae6c1c27e98aa357dccb788c21d98049")
73+
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "7c0890c68501f7630d36aeb3999dc924e63af084ae1bbfba11dd462144637635")
7474

7575
def run_test(self):
7676
self.test_muhash_implementation()

test/functional/mempool_compatibility.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77
NOTE: The test is designed to prevent cases when compatibility is broken accidentally.
88
In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
99
10-
The previous release v0.15.2 is required by this test, see test/README.md.
10+
The previous release v0.19.1 is required by this test, see test/README.md.
1111
"""
1212

1313
import os
1414

1515
from test_framework.blocktools import COINBASE_MATURITY
1616
from test_framework.test_framework import BitcoinTestFramework
17-
from test_framework.wallet import MiniWallet
17+
from test_framework.wallet import (
18+
MiniWallet,
19+
MiniWalletMode,
20+
)
1821

1922

2023
class MempoolCompatibilityTest(BitcoinTestFramework):
@@ -37,7 +40,7 @@ def run_test(self):
3740
self.log.info("Test that mempool.dat is compatible between versions")
3841

3942
old_node, new_node = self.nodes
40-
new_wallet = MiniWallet(new_node)
43+
new_wallet = MiniWallet(new_node, mode=MiniWalletMode.RAW_P2PK)
4144
self.generate(new_wallet, 1, sync_fun=self.no_op)
4245
self.generate(new_node, COINBASE_MATURITY, sync_fun=self.no_op)
4346
# Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend.

test/functional/rpc_blockchain.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import os
2626
import subprocess
2727

28-
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
2928
from test_framework.blocktools import (
3029
create_block,
3130
create_coinbase,
@@ -64,6 +63,7 @@ def set_test_params(self):
6463
self.supports_cli = False
6564

6665
def run_test(self):
66+
self.wallet = MiniWallet(self.nodes[0])
6767
self.mine_chain()
6868
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete
6969

@@ -82,7 +82,7 @@ def mine_chain(self):
8282
self.log.info(f"Generate {HEIGHT} blocks after the genesis block in ten-minute steps")
8383
for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
8484
self.nodes[0].setmocktime(t)
85-
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
85+
self.generate(self.wallet, 1)
8686
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], HEIGHT)
8787

8888
def _test_getblockchaininfo(self):
@@ -371,12 +371,12 @@ def _test_getnetworkhashps(self):
371371
def _test_stopatheight(self):
372372
self.log.info("Test stopping at height")
373373
assert_equal(self.nodes[0].getblockcount(), HEIGHT)
374-
self.generatetoaddress(self.nodes[0], 6, ADDRESS_BCRT1_P2WSH_OP_TRUE)
374+
self.generate(self.wallet, 6)
375375
assert_equal(self.nodes[0].getblockcount(), HEIGHT + 6)
376376
self.log.debug('Node should not stop at this height')
377377
assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
378378
try:
379-
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2WSH_OP_TRUE, sync_fun=self.no_op)
379+
self.generatetoaddress(self.nodes[0], 1, self.wallet.get_address(), sync_fun=self.no_op)
380380
except (ConnectionError, http.client.BadStatusLine):
381381
pass # The node already shut down before response
382382
self.log.debug('Node should stop at this height...')
@@ -424,14 +424,10 @@ def assert_waitforheight(height, timeout=2):
424424

425425
def _test_getblock(self):
426426
node = self.nodes[0]
427-
428-
miniwallet = MiniWallet(node)
429-
miniwallet.rescan_utxos()
430-
431427
fee_per_byte = Decimal('0.00000010')
432428
fee_per_kb = 1000 * fee_per_byte
433429

434-
miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
430+
self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
435431
blockhash = self.generate(node, 1)[0]
436432

437433
def assert_fee_not_in_block(verbosity):

test/functional/test_framework/address.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@
55
"""Encode and decode Bitcoin addresses.
66
77
- base58 P2PKH and P2SH addresses.
8-
- bech32 segwit v0 P2WPKH and P2WSH addresses."""
8+
- bech32 segwit v0 P2WPKH and P2WSH addresses.
9+
- bech32m segwit v1 P2TR addresses."""
910

1011
import enum
1112
import unittest
1213

13-
from .script import hash256, hash160, sha256, CScript, OP_0
14+
from .script import (
15+
CScript,
16+
OP_0,
17+
OP_TRUE,
18+
hash160,
19+
hash256,
20+
sha256,
21+
taproot_construct,
22+
)
1423
from .segwit_addr import encode_segwit_address
1524
from .util import assert_equal
1625

@@ -29,6 +38,21 @@ class AddressType(enum.Enum):
2938
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
3039

3140

41+
def create_deterministic_address_bcrt1_p2tr_op_true():
42+
"""
43+
Generates a deterministic bech32m address (segwit v1 output) that
44+
can be spent with a witness stack of OP_TRUE and the control block
45+
with internal public key (script-path spending).
46+
47+
Returns a tuple with the generated address and the internal key.
48+
"""
49+
internal_key = (1).to_bytes(32, 'big')
50+
scriptPubKey = taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).scriptPubKey
51+
address = encode_segwit_address("bcrt", 1, scriptPubKey[2:])
52+
assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
53+
return (address, internal_key)
54+
55+
3256
def byte_to_base58(b, version):
3357
result = ''
3458
str = b.hex()

test/functional/test_framework/test_framework.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import time
2020

2121
from typing import List
22-
from .address import ADDRESS_BCRT1_P2WSH_OP_TRUE
22+
from .address import create_deterministic_address_bcrt1_p2tr_op_true
2323
from .authproxy import JSONRPCException
2424
from . import coverage
2525
from .p2p import NetworkThread
@@ -777,7 +777,7 @@ def _initialize_chain(self):
777777
# block in the cache does not age too much (have an old tip age).
778778
# This is needed so that we are out of IBD when the test starts,
779779
# see the tip age check in IsInitialBlockDownload().
780-
gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
780+
gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [create_deterministic_address_bcrt1_p2tr_op_true()[0]]
781781
assert_equal(len(gen_addresses), 4)
782782
for i in range(8):
783783
self.generatetoaddress(

test/functional/test_framework/wallet.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from enum import Enum
1010
from random import choice
1111
from typing import Optional
12-
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
12+
from test_framework.address import create_deterministic_address_bcrt1_p2tr_op_true
1313
from test_framework.descriptors import descsum_create
1414
from test_framework.key import ECKey
1515
from test_framework.messages import (
@@ -24,8 +24,9 @@
2424
from test_framework.script import (
2525
CScript,
2626
LegacySignatureHash,
27-
OP_TRUE,
27+
LEAF_VERSION_TAPSCRIPT,
2828
OP_NOP,
29+
OP_TRUE,
2930
SIGHASH_ALL,
3031
)
3132
from test_framework.script_util import (
@@ -43,7 +44,7 @@ class MiniWalletMode(Enum):
4344
"""Determines the transaction type the MiniWallet is creating and spending.
4445
4546
For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient;
46-
it simply uses a fixed bech32 P2WSH address whose coins are spent with a
47+
it simply uses a fixed bech32m P2TR address whose coins are spent with a
4748
witness stack of OP_TRUE, i.e. following an anyone-can-spend policy.
4849
However, if the transactions need to be modified by the user (e.g. prepending
4950
scriptSig for testing opcodes that are activated by a soft-fork), or the txs
@@ -53,7 +54,7 @@ class MiniWalletMode(Enum):
5354
| output | | tx is | can modify | needs
5455
mode | description | address | standard | scriptSig | signing
5556
----------------+-------------------+-----------+----------+------------+----------
56-
ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no
57+
ADDRESS_OP_TRUE | anyone-can-spend | bech32m | yes | no | no
5758
RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no
5859
RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes
5960
"""
@@ -79,7 +80,7 @@ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
7980
pub_key = self._priv_key.get_pubkey()
8081
self._scriptPubKey = key_to_p2pk_script(pub_key.get_bytes())
8182
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
82-
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
83+
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
8384
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
8485

8586
def rescan_utxos(self):
@@ -174,7 +175,7 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_
174175
self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height']))
175176
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
176177
if self._priv_key is None:
177-
vsize = Decimal(96) # anyone-can-spend
178+
vsize = Decimal(104) # anyone-can-spend
178179
else:
179180
vsize = Decimal(168) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
180181
send_value = int(COIN * (utxo_to_spend['value'] - fee_rate * (vsize / 1000)))
@@ -191,10 +192,10 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_
191192
self.sign_tx(tx)
192193
else:
193194
# anyone-can-spend
194-
tx.vin[0].scriptSig = CScript([OP_NOP] * 35) # pad to identical size
195+
tx.vin[0].scriptSig = CScript([OP_NOP] * 43) # pad to identical size
195196
else:
196197
tx.wit.vtxinwit = [CTxInWitness()]
197-
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
198+
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
198199
tx_hex = tx.serialize().hex()
199200

200201
tx_info = from_node.testmempoolaccept([tx_hex])[0]

0 commit comments

Comments
 (0)