diff options
Diffstat (limited to 'test/functional')
-rwxr-xr-x | test/functional/feature_cltv.py | 7 | ||||
-rwxr-xr-x | test/functional/feature_csv_activation.py | 21 | ||||
-rwxr-xr-x | test/functional/feature_includeconf.py | 9 | ||||
-rw-r--r-- | test/functional/test_framework/wallet.py | 64 |
4 files changed, 87 insertions, 14 deletions
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 6c51944d81..d25aaa070d 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -26,7 +26,10 @@ from test_framework.script import ( ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal -from test_framework.wallet import MiniWallet +from test_framework.wallet import ( + MiniWallet, + MiniWalletMode, +) CLTV_HEIGHT = 1351 @@ -97,7 +100,7 @@ class BIP65Test(BitcoinTestFramework): def run_test(self): peer = self.nodes[0].add_p2p_connection(P2PInterface()) - wallet = MiniWallet(self.nodes[0], raw_script=True) + wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE) self.test_cltv_info(is_active=False) diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 28062590fd..5081867319 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -55,7 +55,10 @@ from test_framework.util import ( assert_equal, softfork_active, ) -from test_framework.wallet import MiniWallet +from test_framework.wallet import ( + MiniWallet, + MiniWalletMode, +) TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above) COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs @@ -90,7 +93,6 @@ class BIP68_112_113Test(BitcoinTestFramework): self.setup_clean_chain = True self.extra_args = [[ '-whitelist=noban@127.0.0.1', - '-acceptnonstdtxn=1', '-par=1', # Use only one script thread to get the exact reject reason for testing ]] self.supports_cli = False @@ -103,12 +105,14 @@ class BIP68_112_113Test(BitcoinTestFramework): def create_bip112special(self, input, txversion): tx = self.create_self_transfer_from_utxo(input) tx.nVersion = txversion + self.miniwallet.sign_tx(tx) tx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) return tx def create_bip112emptystack(self, input, txversion): tx = self.create_self_transfer_from_utxo(input) tx.nVersion = txversion + self.miniwallet.sign_tx(tx) tx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(tx.vin[0].scriptSig))) return tx @@ -126,6 +130,7 @@ class BIP68_112_113Test(BitcoinTestFramework): tx = self.create_self_transfer_from_utxo(bip68inputs[i]) tx.nVersion = txversion tx.vin[0].nSequence = locktime + locktime_delta + self.miniwallet.sign_tx(tx) tx.rehash() txs.append({'tx': tx, 'sdf': sdf, 'stf': stf}) @@ -143,6 +148,7 @@ class BIP68_112_113Test(BitcoinTestFramework): else: # vary nSequence instead, OP_CSV is fixed tx.vin[0].nSequence = locktime + locktime_delta tx.nVersion = txversion + self.miniwallet.sign_tx(tx) if (varyOP_CSV): tx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) else: @@ -178,7 +184,7 @@ class BIP68_112_113Test(BitcoinTestFramework): def run_test(self): self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore()) - self.miniwallet = MiniWallet(self.nodes[0], raw_script=True) + self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK) self.log.info("Generate blocks in the past for coinbase outputs.") long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future @@ -285,7 +291,7 @@ class BIP68_112_113Test(BitcoinTestFramework): success_txs = [] # BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block - bip113tx_v1.rehash() + self.miniwallet.sign_tx(bip113tx_v1) success_txs.append(bip113tx_v1) success_txs.append(bip112tx_special_v1) success_txs.append(bip112tx_emptystack_v1) @@ -305,7 +311,7 @@ class BIP68_112_113Test(BitcoinTestFramework): success_txs = [] # BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block - bip113tx_v2.rehash() + self.miniwallet.sign_tx(bip113tx_v2) success_txs.append(bip113tx_v2) success_txs.append(bip112tx_special_v2) success_txs.append(bip112tx_emptystack_v2) @@ -331,16 +337,20 @@ class BIP68_112_113Test(BitcoinTestFramework): self.log.info("BIP 113 tests") # BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block + self.miniwallet.sign_tx(bip113tx_v1) bip113tx_v1.rehash() bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block + self.miniwallet.sign_tx(bip113tx_v2) bip113tx_v2.rehash() for bip113tx in [bip113tx_v1, bip113tx_v2]: self.send_blocks([self.create_test_block([bip113tx])], success=False, reject_reason='bad-txns-nonfinal') # BIP 113 tests should now pass if the locktime is < MTP bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block + self.miniwallet.sign_tx(bip113tx_v1) bip113tx_v1.rehash() bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block + self.miniwallet.sign_tx(bip113tx_v2) bip113tx_v2.rehash() for bip113tx in [bip113tx_v1, bip113tx_v2]: self.send_blocks([self.create_test_block([bip113tx])]) @@ -465,6 +475,7 @@ class BIP68_112_113Test(BitcoinTestFramework): time_txs = [] for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]: tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG + self.miniwallet.sign_tx(tx) tx.rehash() time_txs.append(tx) diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py index f22b7f266a..448182eded 100755 --- a/test/functional/feature_includeconf.py +++ b/test/functional/feature_includeconf.py @@ -42,7 +42,14 @@ class IncludeConfTest(BitcoinTestFramework): self.log.info("-includeconf cannot be used as command-line arg") self.stop_node(0) - self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf") + self.nodes[0].assert_start_raises_init_error( + extra_args=['-noincludeconf=0'], + expected_msg='Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=true', + ) + self.nodes[0].assert_start_raises_init_error( + extra_args=['-includeconf=relative2.conf', '-includeconf=no_warn.conf'], + expected_msg='Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf="relative2.conf"', + ) self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'") with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f: diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 57b0a170f0..05bda5d899 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -5,7 +5,9 @@ """A limited-functionality wallet, which may replace a real wallet in tests""" from decimal import Decimal +from enum import Enum from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE +from test_framework.key import ECKey from test_framework.messages import ( COIN, COutPoint, @@ -16,8 +18,11 @@ from test_framework.messages import ( ) from test_framework.script import ( CScript, + LegacySignatureHash, + OP_CHECKSIG, OP_TRUE, OP_NOP, + SIGHASH_ALL, ) from test_framework.util import ( assert_equal, @@ -26,14 +31,46 @@ from test_framework.util import ( ) +class MiniWalletMode(Enum): + """Determines the transaction type the MiniWallet is creating and spending. + + For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient; + it simply uses a fixed bech32 P2WSH address whose coins are spent with a + witness stack of OP_TRUE, i.e. following an anyone-can-spend policy. + However, if the transactions need to be modified by the user (e.g. prepending + scriptSig for testing opcodes that are activated by a soft-fork), or the txs + should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK + can be useful. Summary of modes: + + | output | | tx is | can modify | needs + mode | description | address | standard | scriptSig | signing + ----------------+-------------------+-----------+----------+------------+---------- + ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no + RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no + RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes + """ + ADDRESS_OP_TRUE = 1 + RAW_OP_TRUE = 2 + RAW_P2PK = 3 + + class MiniWallet: - def __init__(self, test_node, *, raw_script=False): + def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE): self._test_node = test_node self._utxos = [] - if raw_script: - self._address = None + self._priv_key = None + self._address = None + + assert isinstance(mode, MiniWalletMode) + if mode == MiniWalletMode.RAW_OP_TRUE: self._scriptPubKey = bytes(CScript([OP_TRUE])) - else: + elif mode == MiniWalletMode.RAW_P2PK: + # use simple deterministic private key (k=1) + self._priv_key = ECKey() + self._priv_key.set((1).to_bytes(32, 'big'), True) + pub_key = self._priv_key.get_pubkey() + self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG])) + elif mode == MiniWalletMode.ADDRESS_OP_TRUE: self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey']) @@ -50,6 +87,13 @@ class MiniWallet: if out['scriptPubKey']['hex'] == self._scriptPubKey.hex(): self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']}) + def sign_tx(self, tx): + """Sign tx that has been created by MiniWallet in P2PK mode""" + assert self._priv_key is not None + (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL) + assert err is None + tx.vin[0].scriptSig = CScript([self._priv_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))]) + def generate(self, num_blocks): """Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list""" blocks = self._test_node.generatetodescriptor(num_blocks, f'raw({self._scriptPubKey.hex()})') @@ -99,7 +143,12 @@ class MiniWallet: tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)] if not self._address: # raw script - tx.vin[0].scriptSig = CScript([OP_NOP] * 35) # pad to identical size + if self._priv_key is not None: + # P2PK, need to sign + self.sign_tx(tx) + else: + # anyone-can-spend + tx.vin[0].scriptSig = CScript([OP_NOP] * 35) # pad to identical size else: tx.wit.vtxinwit = [CTxInWitness()] tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] @@ -108,7 +157,10 @@ class MiniWallet: tx_info = from_node.testmempoolaccept([tx_hex])[0] assert_equal(mempool_valid, tx_info['allowed']) if mempool_valid: - assert_equal(tx_info['vsize'], vsize) + # TODO: for P2PK, vsize is not constant due to varying scriptSig length, + # so only check this for anyone-can-spend outputs right now + if self._priv_key is None: + assert_equal(tx_info['vsize'], vsize) assert_equal(tx_info['fees']['base'], fee) return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx} |