diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -10,10 +10,7 @@ from test_framework.p2p import P2PInterface, p2p_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal - - -def hashToHex(hash): - return format(hash, '064x') +from test_framework.wallet import MiniWallet class FeefilterConn(P2PInterface): @@ -35,7 +32,7 @@ def on_inv(self, message): for i in message.inv: if (i.type == MSG_TX): - self.txinvs.append(hashToHex(i.hash)) + self.txinvs.append('{:064x}'.format(i.hash)) def wait_for_invs_to_match(self, invs_expected): invs_expected.sort() @@ -62,9 +59,6 @@ "-whitelist=noban@127.0.0.1", ]] * self.num_nodes - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def run_test(self): self.test_feefilter_forcerelay() self.test_feefilter() @@ -88,13 +82,18 @@ def test_feefilter(self): node1 = self.nodes[1] node0 = self.nodes[0] + miniwallet = MiniWallet(node1) + # Add enough mature utxos to the wallet, so that all txs spend + # confirmed coins + miniwallet.generate(5) + node1.generate(100) conn = self.nodes[0].add_p2p_connection(TestP2PConn()) self.log.info( "Test txs paying 0.2 sat/byte are received by test connection") - node1.settxfee(Decimal("2")) - txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) + txids = [miniwallet.send_self_transfer(fee_rate=Decimal('2.00'), + from_node=node1)['txid'] for _ in range(3)] conn.wait_for_invs_to_match(txids) conn.clear_invs() @@ -104,16 +103,17 @@ self.log.info( "Test txs paying 0.15 sat/byte are received by test connection") - node1.settxfee(Decimal("1.5")) - txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) + txids = [miniwallet.send_self_transfer(fee_rate=Decimal('1.50'), + from_node=node1)['txid'] for _ in range(3)] conn.wait_for_invs_to_match(txids) conn.clear_invs() self.log.info( "Test txs paying 0.1 sat/byte are no longer received by test connection") - node1.settxfee(Decimal("1")) - [node1.sendtoaddress(node1.getnewaddress(), 1000000) for _ in range(3)] + txids = [miniwallet.send_self_transfer(fee_rate=Decimal('1.00'), + from_node=node1)['txid'] + for _ in range(3)] self.sync_mempools() # must be sure node 0 has received all txs # Send one transaction from node0 that should be received, so that we @@ -123,14 +123,16 @@ # to 35 entries in an inv, which means that when this next transaction # is eligible for relay, the prior transactions from node1 are eligible # as well. - node0.settxfee(Decimal("200.00")) - txids = [node0.sendtoaddress(node0.getnewaddress(), 1000000)] + txids = [miniwallet.send_self_transfer(fee_rate=Decimal('200.00'), + from_node=node0)['txid'] + for _ in range(3)] conn.wait_for_invs_to_match(txids) conn.clear_invs() self.log.info("Remove fee filter and check txs are received again") conn.send_and_ping(msg_feefilter(0)) - txids = [node1.sendtoaddress(node1.getnewaddress(), 1000000) + txids = [miniwallet.send_self_transfer(fee_rate=Decimal('200.00'), + from_node=node1)['txid'] for _ in range(3)] conn.wait_for_invs_to_match(txids) conn.clear_invs() diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py --- a/test/functional/p2p_permissions.py +++ b/test/functional/p2p_permissions.py @@ -7,10 +7,12 @@ Test that permissions are correctly calculated and applied """ -from test_framework.address import ADDRESS_ECREG_P2SH_OP_TRUE +from test_framework.address import ( + ADDRESS_ECREG_P2SH_OP_TRUE, + SCRIPTSIG_OP_TRUE, +) from test_framework.messages import CTransaction, FromHex from test_framework.p2p import P2PDataStore -from test_framework.script import OP_TRUE, CScript, CScriptOp from test_framework.test_framework import BitcoinTestFramework from test_framework.test_node import ErrorMatch from test_framework.txtools import pad_tx @@ -139,7 +141,7 @@ inputs=[{'txid': block_op_true['tx'][0], 'vout': 0}], outputs=[{ADDRESS_ECREG_P2SH_OP_TRUE: 50}])) # push the one byte script to the stack - tx.vin[0].scriptSig = CScriptOp.encode_op_pushdata(CScript([OP_TRUE])) + tx.vin[0].scriptSig = SCRIPTSIG_OP_TRUE pad_tx(tx) txid = tx.rehash() diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -6,13 +6,14 @@ import unittest -from .script import CScript, hash160, hash256 +from .script import OP_TRUE, CScript, CScriptOp, hash160, hash256 from .util import assert_equal, hex_str_to_bytes ADDRESS_ECREG_UNSPENDABLE = 'ecregtest:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcrl5mqkt' ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR = 'addr(ecregtest:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcrl5mqkt)#u6xx93xc' # Coins sent to this address can be spent with a scriptSig of just OP_TRUE ADDRESS_ECREG_P2SH_OP_TRUE = 'ecregtest:prdpw30fk4ym6zl6rftfjuw806arpn26fvkgfu97xt' +SCRIPTSIG_OP_TRUE = CScriptOp.encode_op_pushdata(CScript([OP_TRUE])) chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py new file mode 100644 --- /dev/null +++ b/test/functional/test_framework/wallet.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""A limited-functionality wallet, which may replace a real wallet in tests""" + +from decimal import Decimal + +from test_framework.address import ( + ADDRESS_ECREG_P2SH_OP_TRUE, + SCRIPTSIG_OP_TRUE, +) +from test_framework.messages import XEC, COutPoint, CTransaction, CTxIn, CTxOut +from test_framework.txtools import pad_tx +from test_framework.util import assert_equal, hex_str_to_bytes, satoshi_round + + +class MiniWallet: + def __init__(self, test_node): + self._test_node = test_node + self._utxos = [] + self._address = ADDRESS_ECREG_P2SH_OP_TRUE + self._scriptPubKey = hex_str_to_bytes( + self._test_node.validateaddress( + self._address)['scriptPubKey']) + + 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.generatetoaddress(num_blocks, self._address) + for b in blocks: + cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0] + self._utxos.append( + {'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']}) + return blocks + + def send_self_transfer(self, *, fee_rate, from_node): + """Create and send a tx with the specified fee_rate. Fee may be exact + or at most one satoshi higher than needed.""" + self._utxos = sorted(self._utxos, key=lambda k: k['value']) + # Pick the largest utxo and hope it covers the fee + largest_utxo = self._utxos.pop() + + # The size will be enforced by pad_tx() + size = 100 + send_value = satoshi_round( + largest_utxo['value'] - fee_rate * (Decimal(size) / 1000)) + fee = largest_utxo['value'] - send_value + assert send_value > 0 + + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(largest_utxo['txid'], 16), + largest_utxo['vout']))] + tx.vout = [CTxOut(int(send_value * XEC), self._scriptPubKey)] + tx.vin[0].scriptSig = SCRIPTSIG_OP_TRUE + pad_tx(tx, size) + tx_hex = tx.serialize().hex() + + txid = from_node.sendrawtransaction(tx_hex) + self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value}) + tx_info = from_node.getmempoolentry(txid) + assert_equal(tx_info['size'], size) + assert_equal(tx_info['fee'], fee) + return {'txid': txid, 'hex': tx_hex}