doc: spelling fixes
[bitcoinplatinum.git] / test / functional / bumpfee.py
blobddc7c23e8ac555cd645057333eda17d2c4f4f0e2
1 #!/usr/bin/env python3
2 # Copyright (c) 2016 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test the bumpfee RPC.
7 Verifies that the bumpfee RPC creates replacement transactions successfully when
8 its preconditions are met, and returns appropriate errors in other cases.
10 This module consists of around a dozen individual test cases implemented in the
11 top-level functions named as test_<test_case_description>. The test functions
12 can be disabled or reordered if needed for debugging. If new test cases are
13 added in the future, they should try to follow the same convention and not
14 make assumptions about execution order.
15 """
17 from segwit import send_to_witness
18 from test_framework.test_framework import BitcoinTestFramework
19 from test_framework import blocktools
20 from test_framework.mininode import CTransaction
21 from test_framework.util import *
23 import io
25 # Sequence number that is BIP 125 opt-in and BIP 68-compliant
26 BIP125_SEQUENCE_NUMBER = 0xfffffffd
28 WALLET_PASSPHRASE = "test"
29 WALLET_PASSPHRASE_TIMEOUT = 3600
32 class BumpFeeTest(BitcoinTestFramework):
33 def __init__(self):
34 super().__init__()
35 self.num_nodes = 2
36 self.setup_clean_chain = True
38 def setup_network(self, split=False):
39 extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)]
40 for i in range(self.num_nodes)]
41 self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
43 # Encrypt wallet for test_locked_wallet_fails test
44 self.nodes[1].encryptwallet(WALLET_PASSPHRASE)
45 self.bitcoind_processes[1].wait()
46 self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1])
47 self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
49 connect_nodes_bi(self.nodes, 0, 1)
50 self.sync_all()
52 def run_test(self):
53 peer_node, rbf_node = self.nodes
54 rbf_node_address = rbf_node.getnewaddress()
56 # fund rbf node with 10 coins of 0.001 btc (100,000 satoshis)
57 self.log.info("Mining blocks...")
58 peer_node.generate(110)
59 self.sync_all()
60 for i in range(25):
61 peer_node.sendtoaddress(rbf_node_address, 0.001)
62 self.sync_all()
63 peer_node.generate(1)
64 self.sync_all()
65 assert_equal(rbf_node.getbalance(), Decimal("0.025"))
67 self.log.info("Running tests")
68 dest_address = peer_node.getnewaddress()
69 test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address)
70 test_segwit_bumpfee_succeeds(rbf_node, dest_address)
71 test_nonrbf_bumpfee_fails(peer_node, dest_address)
72 test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
73 test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
74 test_small_output_fails(rbf_node, dest_address)
75 test_dust_to_fee(rbf_node, dest_address)
76 test_settxfee(rbf_node, dest_address)
77 test_rebumping(rbf_node, dest_address)
78 test_rebumping_not_replaceable(rbf_node, dest_address)
79 test_unconfirmed_not_spendable(rbf_node, rbf_node_address)
80 test_bumpfee_metadata(rbf_node, dest_address)
81 test_locked_wallet_fails(rbf_node, dest_address)
82 self.log.info("Success")
85 def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
86 rbfid = spend_one_input(rbf_node, dest_address)
87 rbftx = rbf_node.gettransaction(rbfid)
88 sync_mempools((rbf_node, peer_node))
89 assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
90 bumped_tx = rbf_node.bumpfee(rbfid)
91 assert_equal(bumped_tx["errors"], [])
92 assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0
93 # check that bumped_tx propagates, original tx was evicted and has a wallet conflict
94 sync_mempools((rbf_node, peer_node))
95 assert bumped_tx["txid"] in rbf_node.getrawmempool()
96 assert bumped_tx["txid"] in peer_node.getrawmempool()
97 assert rbfid not in rbf_node.getrawmempool()
98 assert rbfid not in peer_node.getrawmempool()
99 oldwtx = rbf_node.gettransaction(rbfid)
100 assert len(oldwtx["walletconflicts"]) > 0
101 # check wallet transaction replaces and replaced_by values
102 bumpedwtx = rbf_node.gettransaction(bumped_tx["txid"])
103 assert_equal(oldwtx["replaced_by_txid"], bumped_tx["txid"])
104 assert_equal(bumpedwtx["replaces_txid"], rbfid)
107 def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
108 # Create a transaction with segwit output, then create an RBF transaction
109 # which spends it, and make sure bumpfee can be called on it.
111 segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001"))
112 segwit_out = rbf_node.validateaddress(rbf_node.getnewaddress())
113 rbf_node.addwitnessaddress(segwit_out["address"])
114 segwitid = send_to_witness(
115 use_p2wsh=False,
116 node=rbf_node,
117 utxo=segwit_in,
118 pubkey=segwit_out["pubkey"],
119 encode_p2sh=False,
120 amount=Decimal("0.0009"),
121 sign=True)
123 rbfraw = rbf_node.createrawtransaction([{
124 'txid': segwitid,
125 'vout': 0,
126 "sequence": BIP125_SEQUENCE_NUMBER
127 }], {dest_address: Decimal("0.0005"),
128 rbf_node.getrawchangeaddress(): Decimal("0.0003")})
129 rbfsigned = rbf_node.signrawtransaction(rbfraw)
130 rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"])
131 assert rbfid in rbf_node.getrawmempool()
133 bumped_tx = rbf_node.bumpfee(rbfid)
134 assert bumped_tx["txid"] in rbf_node.getrawmempool()
135 assert rbfid not in rbf_node.getrawmempool()
138 def test_nonrbf_bumpfee_fails(peer_node, dest_address):
139 # cannot replace a non RBF transaction (from node which did not enable RBF)
140 not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
141 assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
144 def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
145 # cannot bump fee unless the tx has only inputs that we own.
146 # here, the rbftx has a peer_node coin and then adds a rbf_node input
147 # Note that this test depends upon the RPC code checking input ownership prior to change outputs
148 # (since it can't use fundrawtransaction, it lacks a proper change output)
149 utxos = [node.listunspent()[-1] for node in (rbf_node, peer_node)]
150 inputs = [{
151 "txid": utxo["txid"],
152 "vout": utxo["vout"],
153 "address": utxo["address"],
154 "sequence": BIP125_SEQUENCE_NUMBER
155 } for utxo in utxos]
156 output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("0.001")
157 rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val})
158 signedtx = rbf_node.signrawtransaction(rawtx)
159 signedtx = peer_node.signrawtransaction(signedtx["hex"])
160 rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
161 assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet",
162 rbf_node.bumpfee, rbfid)
165 def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
166 # cannot bump fee if the transaction has a descendant
167 # parent is send-to-self, so we don't have to check which output is change when creating the child tx
168 parent_id = spend_one_input(rbf_node, rbf_node_address)
169 tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
170 tx = rbf_node.signrawtransaction(tx)
171 txid = rbf_node.sendrawtransaction(tx["hex"])
172 assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
175 def test_small_output_fails(rbf_node, dest_address):
176 # cannot bump fee with a too-small output
177 rbfid = spend_one_input(rbf_node, dest_address)
178 rbf_node.bumpfee(rbfid, {"totalFee": 50000})
180 rbfid = spend_one_input(rbf_node, dest_address)
181 assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
184 def test_dust_to_fee(rbf_node, dest_address):
185 # check that if output is reduced to dust, it will be converted to fee
186 # the bumped tx sets fee=49,900, but it converts to 50,000
187 rbfid = spend_one_input(rbf_node, dest_address)
188 fulltx = rbf_node.getrawtransaction(rbfid, 1)
189 bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 49900})
190 full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
191 assert_equal(bumped_tx["fee"], Decimal("0.00050000"))
192 assert_equal(len(fulltx["vout"]), 2)
193 assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated
196 def test_settxfee(rbf_node, dest_address):
197 # check that bumpfee reacts correctly to the use of settxfee (paytxfee)
198 rbfid = spend_one_input(rbf_node, dest_address)
199 requested_feerate = Decimal("0.00025000")
200 rbf_node.settxfee(requested_feerate)
201 bumped_tx = rbf_node.bumpfee(rbfid)
202 actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["size"]
203 # Assert that the difference between the requested feerate and the actual
204 # feerate of the bumped transaction is small.
205 assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate))
206 rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
209 def test_rebumping(rbf_node, dest_address):
210 # check that re-bumping the original tx fails, but bumping the bumper succeeds
211 rbfid = spend_one_input(rbf_node, dest_address)
212 bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
213 assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
214 rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000})
217 def test_rebumping_not_replaceable(rbf_node, dest_address):
218 # check that re-bumping a non-replaceable bump tx fails
219 rbfid = spend_one_input(rbf_node, dest_address)
220 bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
221 assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
222 {"totalFee": 20000})
225 def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
226 # check that unconfirmed outputs from bumped transactions are not spendable
227 rbfid = spend_one_input(rbf_node, rbf_node_address)
228 rbftx = rbf_node.gettransaction(rbfid)["hex"]
229 assert rbfid in rbf_node.getrawmempool()
230 bumpid = rbf_node.bumpfee(rbfid)["txid"]
231 assert bumpid in rbf_node.getrawmempool()
232 assert rbfid not in rbf_node.getrawmempool()
234 # check that outputs from the bump transaction are not spendable
235 # due to the replaces_txid check in CWallet::AvailableCoins
236 assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], [])
238 # submit a block with the rbf tx to clear the bump tx out of the mempool,
239 # then call abandon to make sure the wallet doesn't attempt to resubmit the
240 # bump tx, then invalidate the block so the rbf tx will be put back in the
241 # mempool. this makes it possible to check whether the rbf tx outputs are
242 # spendable before the rbf tx is confirmed.
243 block = submit_block_with_tx(rbf_node, rbftx)
244 rbf_node.abandontransaction(bumpid)
245 rbf_node.invalidateblock(block.hash)
246 assert bumpid not in rbf_node.getrawmempool()
247 assert rbfid in rbf_node.getrawmempool()
249 # check that outputs from the rbf tx are not spendable before the
250 # transaction is confirmed, due to the replaced_by_txid check in
251 # CWallet::AvailableCoins
252 assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == rbfid], [])
254 # check that the main output from the rbf tx is spendable after confirmed
255 rbf_node.generate(1)
256 assert_equal(
257 sum(1 for t in rbf_node.listunspent(minconf=0, include_unsafe=False)
258 if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1)
261 def test_bumpfee_metadata(rbf_node, dest_address):
262 rbfid = rbf_node.sendtoaddress(dest_address, Decimal("0.00100000"), "comment value", "to value")
263 bumped_tx = rbf_node.bumpfee(rbfid)
264 bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
265 assert_equal(bumped_wtx["comment"], "comment value")
266 assert_equal(bumped_wtx["to"], "to value")
269 def test_locked_wallet_fails(rbf_node, dest_address):
270 rbfid = spend_one_input(rbf_node, dest_address)
271 rbf_node.walletlock()
272 assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
273 rbf_node.bumpfee, rbfid)
276 def spend_one_input(node, dest_address):
277 tx_input = dict(
278 sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
279 rawtx = node.createrawtransaction(
280 [tx_input], {dest_address: Decimal("0.00050000"),
281 node.getrawchangeaddress(): Decimal("0.00049000")})
282 signedtx = node.signrawtransaction(rawtx)
283 txid = node.sendrawtransaction(signedtx["hex"])
284 return txid
287 def submit_block_with_tx(node, tx):
288 ctx = CTransaction()
289 ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
291 tip = node.getbestblockhash()
292 height = node.getblockcount() + 1
293 block_time = node.getblockheader(tip)["mediantime"] + 1
294 block = blocktools.create_block(int(tip, 16), blocktools.create_coinbase(height), block_time)
295 block.vtx.append(ctx)
296 block.rehash()
297 block.hashMerkleRoot = block.calc_merkle_root()
298 block.solve()
299 node.submitblock(bytes_to_hex_str(block.serialize(True)))
300 return block
303 if __name__ == "__main__":
304 BumpFeeTest().main()