mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
Merge #9701: Make bumpfee tests less fragile
f85ac54
[qa] Expand bumpfee test docstring (Russell Yanofsky)0b94e49
[qa] Rename python input variable to tx_input (Russell Yanofsky)1dfd64f
[qa] Make bumpfee.py test function order consistent (Russell Yanofsky)e6b2963
[qa] Get rid of nondeterminism in bumpfee.py (Russell Yanofsky)94b528b
[qa] Remove bumpfee.py get_change_address hack (Russell Yanofsky) Tree-SHA512: 4017d58fe73837a0dfcca69c43f804498833f946efd9bc394877be242aa40b03d60bd6c3672ed5d24db88b3318304c1f2838050ec5fa6458d1a7e1f566ccda3e
This commit is contained in:
commit
a230b05887
@ -2,7 +2,17 @@
|
|||||||
# Copyright (c) 2016 The Bitcoin Core developers
|
# Copyright (c) 2016 The Bitcoin Core developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test the bumpfee RPC."""
|
"""Test the bumpfee RPC.
|
||||||
|
|
||||||
|
Verifies that the bumpfee RPC creates replacement transactions successfully when
|
||||||
|
its preconditions are met, and returns appropriate errors in other cases.
|
||||||
|
|
||||||
|
This module consists of around a dozen individual test cases implemented in the
|
||||||
|
top-level functions named as test_<test_case_description>. The test functions
|
||||||
|
can be disabled or reordered if needed for debugging. If new test cases are
|
||||||
|
added in the the future, they should try to follow the same convention and not
|
||||||
|
make assumptions about execution order.
|
||||||
|
"""
|
||||||
|
|
||||||
from segwit import send_to_witness
|
from segwit import send_to_witness
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
@ -57,13 +67,13 @@ class BumpFeeTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
self.log.info("Running tests")
|
self.log.info("Running tests")
|
||||||
dest_address = peer_node.getnewaddress()
|
dest_address = peer_node.getnewaddress()
|
||||||
test_small_output_fails(rbf_node, dest_address)
|
|
||||||
test_dust_to_fee(rbf_node, dest_address)
|
|
||||||
test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address)
|
test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address)
|
||||||
test_segwit_bumpfee_succeeds(rbf_node, dest_address)
|
test_segwit_bumpfee_succeeds(rbf_node, dest_address)
|
||||||
test_nonrbf_bumpfee_fails(peer_node, dest_address)
|
test_nonrbf_bumpfee_fails(peer_node, dest_address)
|
||||||
test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
|
test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
|
||||||
test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
|
test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
|
||||||
|
test_small_output_fails(rbf_node, dest_address)
|
||||||
|
test_dust_to_fee(rbf_node, dest_address)
|
||||||
test_settxfee(rbf_node, dest_address)
|
test_settxfee(rbf_node, dest_address)
|
||||||
test_rebumping(rbf_node, dest_address)
|
test_rebumping(rbf_node, dest_address)
|
||||||
test_rebumping_not_replaceable(rbf_node, dest_address)
|
test_rebumping_not_replaceable(rbf_node, dest_address)
|
||||||
@ -74,7 +84,7 @@ class BumpFeeTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
|
|
||||||
def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
|
def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
|
||||||
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000})
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
rbftx = rbf_node.gettransaction(rbfid)
|
rbftx = rbf_node.gettransaction(rbfid)
|
||||||
sync_mempools((rbf_node, peer_node))
|
sync_mempools((rbf_node, peer_node))
|
||||||
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
|
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
|
||||||
@ -115,7 +125,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
|
|||||||
'vout': 0,
|
'vout': 0,
|
||||||
"sequence": BIP125_SEQUENCE_NUMBER
|
"sequence": BIP125_SEQUENCE_NUMBER
|
||||||
}], {dest_address: Decimal("0.0005"),
|
}], {dest_address: Decimal("0.0005"),
|
||||||
get_change_address(rbf_node): Decimal("0.0003")})
|
rbf_node.getrawchangeaddress(): Decimal("0.0003")})
|
||||||
rbfsigned = rbf_node.signrawtransaction(rbfraw)
|
rbfsigned = rbf_node.signrawtransaction(rbfraw)
|
||||||
rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"])
|
rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"])
|
||||||
assert rbfid in rbf_node.getrawmempool()
|
assert rbfid in rbf_node.getrawmempool()
|
||||||
@ -127,7 +137,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
|
|||||||
|
|
||||||
def test_nonrbf_bumpfee_fails(peer_node, dest_address):
|
def test_nonrbf_bumpfee_fails(peer_node, dest_address):
|
||||||
# cannot replace a non RBF transaction (from node which did not enable RBF)
|
# cannot replace a non RBF transaction (from node which did not enable RBF)
|
||||||
not_rbfid = create_fund_sign_send(peer_node, {dest_address: 0.00090000})
|
not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
|
||||||
assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
|
assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
|
||||||
|
|
||||||
|
|
||||||
@ -155,7 +165,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
|
|||||||
def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
|
def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
|
||||||
# cannot bump fee if the transaction has a descendant
|
# cannot bump fee if the transaction has a descendant
|
||||||
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
|
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
|
||||||
parent_id = create_fund_sign_send(rbf_node, {rbf_node_address: 0.00050000})
|
parent_id = spend_one_input(rbf_node, rbf_node_address)
|
||||||
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
|
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
|
||||||
tx = rbf_node.signrawtransaction(tx)
|
tx = rbf_node.signrawtransaction(tx)
|
||||||
txid = rbf_node.sendrawtransaction(tx["hex"])
|
txid = rbf_node.sendrawtransaction(tx["hex"])
|
||||||
@ -164,58 +174,50 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
|
|||||||
|
|
||||||
def test_small_output_fails(rbf_node, dest_address):
|
def test_small_output_fails(rbf_node, dest_address):
|
||||||
# cannot bump fee with a too-small output
|
# cannot bump fee with a too-small output
|
||||||
rbfid = spend_one_input(rbf_node,
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
Decimal("0.00100000"),
|
rbf_node.bumpfee(rbfid, {"totalFee": 50000})
|
||||||
{dest_address: 0.00080000,
|
|
||||||
get_change_address(rbf_node): Decimal("0.00010000")})
|
|
||||||
rbf_node.bumpfee(rbfid, {"totalFee": 20000})
|
|
||||||
|
|
||||||
rbfid = spend_one_input(rbf_node,
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
Decimal("0.00100000"),
|
assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
|
||||||
{dest_address: 0.00080000,
|
|
||||||
get_change_address(rbf_node): Decimal("0.00010000")})
|
|
||||||
assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 20001})
|
|
||||||
|
|
||||||
|
|
||||||
def test_dust_to_fee(rbf_node, dest_address):
|
def test_dust_to_fee(rbf_node, dest_address):
|
||||||
# check that if output is reduced to dust, it will be converted to fee
|
# check that if output is reduced to dust, it will be converted to fee
|
||||||
# the bumped tx sets fee=9900, but it converts to 10,000
|
# the bumped tx sets fee=49,900, but it converts to 50,000
|
||||||
rbfid = spend_one_input(rbf_node,
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
Decimal("0.00100000"),
|
|
||||||
{dest_address: 0.00080000,
|
|
||||||
get_change_address(rbf_node): Decimal("0.00010000")})
|
|
||||||
fulltx = rbf_node.getrawtransaction(rbfid, 1)
|
fulltx = rbf_node.getrawtransaction(rbfid, 1)
|
||||||
bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 19900})
|
bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 49900})
|
||||||
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
|
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
|
||||||
assert_equal(bumped_tx["fee"], Decimal("0.00020000"))
|
assert_equal(bumped_tx["fee"], Decimal("0.00050000"))
|
||||||
assert_equal(len(fulltx["vout"]), 2)
|
assert_equal(len(fulltx["vout"]), 2)
|
||||||
assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated
|
assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated
|
||||||
|
|
||||||
|
|
||||||
def test_settxfee(rbf_node, dest_address):
|
def test_settxfee(rbf_node, dest_address):
|
||||||
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
|
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
|
||||||
# increase feerate by 2.5x, test that fee increased at least 2x
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
rbf_node.settxfee(Decimal("0.00001000"))
|
|
||||||
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000})
|
|
||||||
rbftx = rbf_node.gettransaction(rbfid)
|
rbftx = rbf_node.gettransaction(rbfid)
|
||||||
rbf_node.settxfee(Decimal("0.00002500"))
|
requested_feerate = Decimal("0.00025000")
|
||||||
|
rbf_node.settxfee(requested_feerate)
|
||||||
bumped_tx = rbf_node.bumpfee(rbfid)
|
bumped_tx = rbf_node.bumpfee(rbfid)
|
||||||
assert bumped_tx["fee"] > 2 * abs(rbftx["fee"])
|
actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["size"]
|
||||||
|
# Assert that the difference between the requested feerate and the actual
|
||||||
|
# feerate of the bumped transaction is small.
|
||||||
|
assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate))
|
||||||
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
|
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
|
||||||
|
|
||||||
|
|
||||||
def test_rebumping(rbf_node, dest_address):
|
def test_rebumping(rbf_node, dest_address):
|
||||||
# check that re-bumping the original tx fails, but bumping the bumper succeeds
|
# check that re-bumping the original tx fails, but bumping the bumper succeeds
|
||||||
rbf_node.settxfee(Decimal("0.00001000"))
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000})
|
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
|
||||||
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 1000})
|
assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
|
||||||
assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000})
|
rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000})
|
||||||
rbf_node.bumpfee(bumped["txid"], {"totalFee": 2000})
|
|
||||||
|
|
||||||
|
|
||||||
def test_rebumping_not_replaceable(rbf_node, dest_address):
|
def test_rebumping_not_replaceable(rbf_node, dest_address):
|
||||||
# check that re-bumping a non-replaceable bump tx fails
|
# check that re-bumping a non-replaceable bump tx fails
|
||||||
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000})
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
|
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
|
||||||
assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
|
assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
|
||||||
{"totalFee": 20000})
|
{"totalFee": 20000})
|
||||||
@ -223,7 +225,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address):
|
|||||||
|
|
||||||
def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
|
def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
|
||||||
# check that unconfirmed outputs from bumped transactions are not spendable
|
# check that unconfirmed outputs from bumped transactions are not spendable
|
||||||
rbfid = create_fund_sign_send(rbf_node, {rbf_node_address: 0.00090000})
|
rbfid = spend_one_input(rbf_node, rbf_node_address)
|
||||||
rbftx = rbf_node.gettransaction(rbfid)["hex"]
|
rbftx = rbf_node.gettransaction(rbfid)["hex"]
|
||||||
assert rbfid in rbf_node.getrawmempool()
|
assert rbfid in rbf_node.getrawmempool()
|
||||||
bumpid = rbf_node.bumpfee(rbfid)["txid"]
|
bumpid = rbf_node.bumpfee(rbfid)["txid"]
|
||||||
@ -258,7 +260,7 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
|
|||||||
|
|
||||||
|
|
||||||
def test_bumpfee_metadata(rbf_node, dest_address):
|
def test_bumpfee_metadata(rbf_node, dest_address):
|
||||||
rbfid = rbf_node.sendtoaddress(dest_address, 0.00090000, "comment value", "to value")
|
rbfid = rbf_node.sendtoaddress(dest_address, Decimal("0.00100000"), "comment value", "to value")
|
||||||
bumped_tx = rbf_node.bumpfee(rbfid)
|
bumped_tx = rbf_node.bumpfee(rbfid)
|
||||||
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
|
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
|
||||||
assert_equal(bumped_wtx["comment"], "comment value")
|
assert_equal(bumped_wtx["comment"], "comment value")
|
||||||
@ -266,43 +268,23 @@ def test_bumpfee_metadata(rbf_node, dest_address):
|
|||||||
|
|
||||||
|
|
||||||
def test_locked_wallet_fails(rbf_node, dest_address):
|
def test_locked_wallet_fails(rbf_node, dest_address):
|
||||||
rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000})
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
rbf_node.walletlock()
|
rbf_node.walletlock()
|
||||||
assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
|
assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
|
||||||
rbf_node.bumpfee, rbfid)
|
rbf_node.bumpfee, rbfid)
|
||||||
|
|
||||||
|
|
||||||
def create_fund_sign_send(node, outputs):
|
def spend_one_input(node, dest_address):
|
||||||
rawtx = node.createrawtransaction([], outputs)
|
tx_input = dict(
|
||||||
fundtx = node.fundrawtransaction(rawtx)
|
sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
|
||||||
signedtx = node.signrawtransaction(fundtx["hex"])
|
rawtx = node.createrawtransaction(
|
||||||
txid = node.sendrawtransaction(signedtx["hex"])
|
[tx_input], {dest_address: Decimal("0.00050000"),
|
||||||
return txid
|
node.getrawchangeaddress(): Decimal("0.00049000")})
|
||||||
|
|
||||||
|
|
||||||
def spend_one_input(node, input_amount, outputs):
|
|
||||||
input = dict(sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == input_amount))
|
|
||||||
rawtx = node.createrawtransaction([input], outputs)
|
|
||||||
signedtx = node.signrawtransaction(rawtx)
|
signedtx = node.signrawtransaction(rawtx)
|
||||||
txid = node.sendrawtransaction(signedtx["hex"])
|
txid = node.sendrawtransaction(signedtx["hex"])
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
|
|
||||||
def get_change_address(node):
|
|
||||||
"""Get a wallet change address.
|
|
||||||
|
|
||||||
There is no wallet RPC to access unused change addresses, so this creates a
|
|
||||||
dummy transaction, calls fundrawtransaction to give add an input and change
|
|
||||||
output, then returns the change address."""
|
|
||||||
dest_address = node.getnewaddress()
|
|
||||||
dest_amount = Decimal("0.00012345")
|
|
||||||
rawtx = node.createrawtransaction([], {dest_address: dest_amount})
|
|
||||||
fundtx = node.fundrawtransaction(rawtx)
|
|
||||||
info = node.decoderawtransaction(fundtx["hex"])
|
|
||||||
return next(address for out in info["vout"]
|
|
||||||
if out["value"] != dest_amount for address in out["scriptPubKey"]["addresses"])
|
|
||||||
|
|
||||||
|
|
||||||
def submit_block_with_tx(node, tx):
|
def submit_block_with_tx(node, tx):
|
||||||
ctx = CTransaction()
|
ctx = CTransaction()
|
||||||
ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
|
ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
|
||||||
|
Loading…
Reference in New Issue
Block a user