Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test: Fixes tests that are broken (or out of date) due to new max fee defaults #1

Draft
wants to merge 11 commits into
base: 2022-11-sane-fee-warnings
Choose a base branch
from
10 changes: 5 additions & 5 deletions test/functional/rpc_fundrawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,9 @@ def test_option_feerate(self):

# With no arguments passed, expect fee of 141 satoshis.
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
# Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
result = node.fundrawtransaction(rawtx, {"fee_rate": 10000})
assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)
# Expect fee to be 10x higher when an explicit fee rate 10x greater is specified.
result = node.fundrawtransaction(rawtx, {"fee_rate": 10})
assert_approx(result["fee"], vexp=0.0000141, vspan=0.0000001)

self.log.info("Test fundrawtxn with invalid estimate_mode settings")
for k, v in {"number": 42, "object": {"foo": "bar"}}.items():
Expand Down Expand Up @@ -1280,7 +1280,7 @@ def test_22670(self):
# Make sure the default wallet will not be loaded when restarted with a high minrelaytxfee
self.nodes[0].unloadwallet(self.default_wallet_name, False)
feerate = Decimal("0.1")
self.restart_node(0, [f"-minrelaytxfee={feerate}", "-discardfee=0"]) # Set high minrelayfee, set discardfee to 0 for easier calculation
self.restart_node(0, [f"-minrelaytxfee={feerate}", "-maxtxfee=0.1", "-discardfee=0"]) # Set high minrelayfee and maxtxfee, set discardfee to 0 for easier calculation

self.nodes[0].loadwallet(self.default_wallet_name, True)
funds = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
Expand Down Expand Up @@ -1310,7 +1310,7 @@ def do_fund_send(target):
assert signed_tx["complete"]
decoded_tx = tester.decoderawtransaction(signed_tx["hex"])
assert_equal(len(decoded_tx["vin"]), 3)
assert tester.testmempoolaccept([signed_tx["hex"]])[0]["allowed"]
assert tester.testmempoolaccept(rawtxs=[signed_tx["hex"]], maxfeerate='0.1')[0]["allowed"] # Must override default maxfeerate to pass

# We want to choose more value than is available in 2 inputs when considering the fee,
# but not enough to need 3 inputs when not considering the fee.
Expand Down
76 changes: 45 additions & 31 deletions test/functional/rpc_rawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,33 +269,44 @@ def sendrawtransaction_testmempoolaccept_tests(self):
self.log.info("Test sendrawtransaction/testmempoolaccept with maxfeerate")
fee_exceeds_max = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"

# Test a transaction with a small fee.
# Fee rate is 0.00100000 BTC/kvB
tx = self.wallet.create_self_transfer(fee_rate=Decimal('0.00100000'))
# Thus, testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept([tx['hex']], 0.00001000)[0]
# Test a transaction above the maxfeerate default
# TODO: Make maxfeerate retrievable from rpc rather than hard-coding here.
max_fee_rate = Decimal("0.00500000") # Current maxfeerate is 0.00500000 BTC/kvB
test_fee_rate = max_fee_rate + Decimal("0.00000001")
tx = self.wallet.create_self_transfer(fee_rate=test_fee_rate)
# We're above maxfeerate so testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']])[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'], 0.00001000)
# and the following calls should both succeed

# Test a transaction exactly at the maxfeerate default
test_fee_rate = max_fee_rate
tx = self.wallet.create_self_transfer(fee_rate=test_fee_rate)
# We're at maxfeerate so testmempoolaccept should accept
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']])[0]
assert_equal(testres['allowed'], True)
self.nodes[2].sendrawtransaction(hexstring=tx['hex'])

# Test a transaction with a large fee.
# Fee rate is 0.20000000 BTC/kvB
tx = self.wallet.create_self_transfer(fee_rate=Decimal("0.20000000"))
# Thus, testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept([tx['hex']])[0]
# Test a transaction below the maxfeerate default
test_fee_rate = max_fee_rate - Decimal("0.00000001")
tx = self.wallet.create_self_transfer(fee_rate=test_fee_rate)
# We're below maxfeerate so testmempoolaccept should accept
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']])[0]
assert_equal(testres['allowed'], True)

# Test a transaction with maxfeerate manually decreased
test_max_fee_rate = test_fee_rate - Decimal("0.00000010") # Must be at least a 10 sat diff to create a noticeable effect
# Our previous tx should now be rejected
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']], maxfeerate=test_max_fee_rate)[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'])
# and the following calls should both succeed
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']], maxfeerate='0.20000000')[0]
assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'], test_max_fee_rate)

# Test a transaction with maxfeerate manually increased
test_fee_rate = 2*max_fee_rate
tx = self.wallet.create_self_transfer(fee_rate=test_fee_rate)
# high-fee tx should now be accepted
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']], maxfeerate=test_fee_rate)[0]
assert_equal(testres['allowed'], True)
self.nodes[2].sendrawtransaction(hexstring=tx['hex'], maxfeerate='0.20000000')
self.nodes[2].sendrawtransaction(hexstring=tx['hex'], maxfeerate=test_fee_rate)

self.log.info("Test sendrawtransaction/testmempoolaccept with tx already in the chain")
self.generate(self.nodes[2], 1)
Expand Down Expand Up @@ -369,11 +380,12 @@ def raw_multisig_transaction_legacy_tests(self):
bal = self.nodes[2].getbalance()

# send 1.2 BTC to msig adr
txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
test_amount = Decimal('1.20000000')
txId = self.nodes[0].sendtoaddress(mSigObj, test_amount)
self.sync_all()
self.generate(self.nodes[0], 1)
# node2 has both keys of the 2of2 ms addr, tx should affect the balance
assert_equal(self.nodes[2].getbalance(), bal + Decimal('1.20000000'))
assert_equal(self.nodes[2].getbalance(), bal + test_amount)


# 2of3 test from different nodes
Expand All @@ -388,7 +400,8 @@ def raw_multisig_transaction_legacy_tests(self):

mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])['address']

txId = self.nodes[0].sendtoaddress(mSigObj, 2.2)
test_amount = Decimal('2.20000000')
txId = self.nodes[0].sendtoaddress(mSigObj, test_amount)
decTx = self.nodes[0].gettransaction(txId)
rawTx = self.nodes[0].decoderawtransaction(decTx['hex'])
self.sync_all()
Expand All @@ -400,11 +413,12 @@ def raw_multisig_transaction_legacy_tests(self):

txDetails = self.nodes[0].gettransaction(txId, True)
rawTx = self.nodes[0].decoderawtransaction(txDetails['hex'])
vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('2.20000000'))
vout = next(o for o in rawTx['vout'] if o['value'] == test_amount)

bal = self.nodes[0].getbalance()
inputs = [{"txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": vout['value']}]
outputs = {self.nodes[0].getnewaddress(): 2.19}
inputs = [{"txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": test_amount}]
output_amount = test_amount - Decimal("0.00000500") # Keep the fee modest to stay under maxtxfee
outputs = {self.nodes[0].getnewaddress(): output_amount}
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet(rawTx, inputs)
assert_equal(rawTxPartialSigned['complete'], False) # node1 only has one key, can't comp. sign the tx
Expand All @@ -415,7 +429,7 @@ def raw_multisig_transaction_legacy_tests(self):
rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex'])
self.sync_all()
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) # block reward + tx
assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + output_amount) # block reward + tx

# 2of2 test for combining transactions
bal = self.nodes[2].getbalance()
Expand All @@ -429,7 +443,7 @@ def raw_multisig_transaction_legacy_tests(self):
mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address']
mSigObjValid = self.nodes[2].getaddressinfo(mSigObj)

txId = self.nodes[0].sendtoaddress(mSigObj, 2.2)
txId = self.nodes[0].sendtoaddress(mSigObj, test_amount)
decTx = self.nodes[0].gettransaction(txId)
rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex'])
self.sync_all()
Expand All @@ -439,11 +453,11 @@ def raw_multisig_transaction_legacy_tests(self):

txDetails = self.nodes[0].gettransaction(txId, True)
rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex'])
vout = next(o for o in rawTx2['vout'] if o['value'] == Decimal('2.20000000'))
vout = next(o for o in rawTx2['vout'] if o['value'] == test_amount)

bal = self.nodes[0].getbalance()
inputs = [{"txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value']}]
outputs = {self.nodes[0].getnewaddress(): 2.19}
outputs = {self.nodes[0].getnewaddress(): output_amount}
rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet(rawTx2, inputs)
self.log.debug(rawTxPartialSigned1)
Expand All @@ -458,7 +472,7 @@ def raw_multisig_transaction_legacy_tests(self):
rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb)
self.sync_all()
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) # block reward + tx
assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + output_amount) # block reward + tx


if __name__ == '__main__':
Expand Down
6 changes: 4 additions & 2 deletions test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,11 @@ def run_test(self):
# 4. check if recipient (node0) can list the zero value tx
usp = self.nodes[1].listunspent(query_options={'minimumAmount': '49.998'})[0]
inputs = [{"txid": usp['txid'], "vout": usp['vout']}]
outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11}
test_amount = Decimal("0.00001000") # this output amount will be zeroed and thereby added to the fee
fee = Decimal("0.00000400") # start with a modest fee so our final fee won't exceed default maxtxfee
outputs = {self.nodes[1].getnewaddress(): usp['amount'] - test_amount - fee, self.nodes[0].getnewaddress(): test_amount}

raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32)
raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("e8030000", "00000000") # replace test_amount with 0.0 (int32)
signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx)
decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex'])
zero_value_txid = decoded_raw_tx['txid']
Expand Down
23 changes: 13 additions & 10 deletions test/functional/wallet_bumpfee.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@
WALLET_PASSPHRASE_TIMEOUT = 3600

# Fee rates (sat/vB)
INSUFFICIENT = 1
ECONOMICAL = 50
NORMAL = 100
HIGH = 500
TOO_HIGH = 100000
# TODO: Make maxfeerate retrievable from rpc and set these rates accordingly.
INSUFFICIENT = 1
ECONOMICAL = 50
NORMAL = 100
HIGH = 450 # stay below the maxfeerate (500)
TOO_HIGH = 10*HIGH


class BumpFeeTest(BitcoinTestFramework):
Expand Down Expand Up @@ -110,14 +111,15 @@ def test_invalid_parameters(self, rbf_node, peer_node, dest_address):
self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()

# Test incorrect json key names
for key in ["totalFee", "feeRate"]:
assert_raises_rpc_error(-3, "Unexpected key {}".format(key), rbf_node.bumpfee, rbfid, {key: NORMAL})

# Bumping to just above minrelay should fail to increase the total fee enough.
assert_raises_rpc_error(-8, "Insufficient total fee 0.00000141", rbf_node.bumpfee, rbfid, {"fee_rate": INSUFFICIENT})

self.log.info("Test invalid fee rate settings")
assert_raises_rpc_error(-4, "Specified or calculated fee 0.141 is too high (cannot be higher than -maxtxfee 0.10",
assert_raises_rpc_error(-4, "is too high (cannot be higher than -maxtxfee",
rbf_node.bumpfee, rbfid, {"fee_rate": TOO_HIGH})
# Test fee_rate with zero values.
msg = "Insufficient total fee 0.00"
Expand Down Expand Up @@ -239,7 +241,7 @@ def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address):
# here, the rbftx has a peer_node coin and then adds a rbf_node input
# Note that this test depends upon the RPC code checking input ownership prior to change outputs
# (since it can't use fundrawtransaction, it lacks a proper change output)
fee = Decimal("0.001")
fee = Decimal("0.00001") # Must stay below maxtxfee
utxos = [node.listunspent(query_options={'minimumAmount': fee})[-1] for node in (rbf_node, peer_node)]
inputs = [{
"txid": utxo["txid"],
Expand Down Expand Up @@ -459,12 +461,13 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):

funding_address1 = watcher.getnewaddress(address_type='bech32')
funding_address2 = watcher.getnewaddress(address_type='bech32')
peer_node.sendmany("", {funding_address1: 0.001, funding_address2: 0.001})
funding_amount = Decimal("0.001")
peer_node.sendmany("", {funding_address1: funding_amount, funding_address2: funding_amount})
self.generate(peer_node, 1)

# Create single-input PSBT for transaction to be bumped
# Ensure the payment amount + change can be fully funded using one of the 0.001BTC inputs.
psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: 0.0005}, 0,
# Ensure the payment amount can be fully funded using one of the 0.001BTC inputs but won't be enough for additional bump further below.
psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: funding_amount - Decimal("0.00005000")}, 0,
{"fee_rate": 1, "add_inputs": False}, True)['psbt']
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
psbt_final = watcher.finalizepsbt(psbt_signed["psbt"])
Expand Down
5 changes: 3 additions & 2 deletions test/functional/wallet_create_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def test_tx_size_too_large(self):
outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for _ in range(400)}
raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)

for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
# default maxtxfee is 0.005
for fee_setting in ['-minrelaytxfee=0.001', '-mintxfee=0.001', '-paytxfee=0.001']:
self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
self.restart_node(0, extra_args=[fee_setting])
assert_raises_rpc_error(
Expand All @@ -64,7 +65,7 @@ def test_tx_size_too_large(self):

self.log.info('Check maxtxfee in combination with settxfee')
self.restart_node(0)
self.nodes[0].settxfee(0.01)
self.nodes[0].settxfee(0.001)
assert_raises_rpc_error(
-6,
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
Expand Down
12 changes: 7 additions & 5 deletions test/functional/wallet_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet group functionality."""

from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import (
Expand Down Expand Up @@ -160,21 +161,22 @@ def run_test(self):
self.generate(self.nodes[0], 1)

self.log.info("Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey")
output_amount = Decimal("0.05")
for _ in range(5):
raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}])
raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: output_amount}])
tx = tx_from_hex(raw_tx)
tx.vin = []
tx.vout = [tx.vout[0]] * 2000
funded_tx = self.nodes[0].fundrawtransaction(tx.serialize().hex())
funded_tx = self.nodes[0].fundrawtransaction(hexstring=tx.serialize().hex(), options={"fee_rate": 5.0}) # Specify fee_rate so we get change instead of exceeding maxfeerate
signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex'])
self.nodes[0].sendrawtransaction(signed_tx['hex'])
self.generate(self.nodes[0], 1)

# Check that we can create a transaction that only requires ~100 of our
# Check that we can create a transaction that only requires 100 of our
# utxos, without pulling in all outputs and creating a transaction that
# is way too big.
self.log.info("Test creating txn that only requires ~100 of our UTXOs without pulling in all outputs")
assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)
self.log.info("Test creating txn that only requires 100 of our UTXOs without pulling in all outputs")
assert self.nodes[2].sendtoaddress(address=addr2[0], amount=100*output_amount)


if __name__ == '__main__':
Expand Down