-
Notifications
You must be signed in to change notification settings - Fork 35.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: test mempool conflict tracking in the wallet
- Loading branch information
Showing
1 changed file
with
145 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2022 The Bitcoin Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
"""Test the setting and updating of transaction states.""" | ||
|
||
from decimal import Decimal | ||
|
||
from test_framework.test_framework import BitcoinTestFramework | ||
from test_framework.blocktools import COINBASE_MATURITY | ||
from test_framework.util import ( | ||
assert_equal, | ||
) | ||
|
||
class TxStatesTest(BitcoinTestFramework): | ||
def add_options(self, parser): | ||
self.add_wallet_options(parser) | ||
|
||
def skip_test_if_missing_module(self): | ||
self.skip_if_no_wallet() | ||
|
||
def set_test_params(self): | ||
self.num_nodes = 2 | ||
self.setup_clean_chain = True | ||
|
||
def run_test(self): | ||
self.log.info("Setting up wallets...") | ||
|
||
self.nodes[0].createwallet("alice") | ||
alice = self.nodes[0].get_wallet_rpc("alice") | ||
|
||
self.nodes[1].createwallet("bob") | ||
bob = self.nodes[1].get_wallet_rpc("bob") | ||
|
||
self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 3, alice.getnewaddress()) | ||
|
||
""" | ||
self.log.info("Test a scenario where a transaction has a mempool conflict") | ||
unspents = [{"txid" : element["txid"], "vout" : element["vout"]} for element in alice.listunspent()] | ||
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{bob.getnewaddress() : 99.9999}]) | ||
tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
raw_tx = alice.createrawtransaction(inputs=[unspents[1], unspents[2]], outputs=[{bob.getnewaddress() : 99.99}]) | ||
tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{bob.getnewaddress() : 49.9899}]) | ||
tx3 = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
tx1 = alice.sendrawtransaction(tx1) | ||
assert_equal(alice.listunspent()[0]["txid"], unspents[2]["txid"]) | ||
assert_equal(alice.getbalance(), 50) | ||
tx2 = alice.sendrawtransaction(tx2) | ||
# Check that the 0th unspent is now available because the transaction spending it has been replaced in the mempool | ||
assert_equal(alice.listunspent()[0]["txid"], unspents[0]["txid"]) | ||
assert_equal(alice.getbalance(), 50) | ||
self.log.info("Test scenario where a mempool conflict is removed") | ||
tx3 = alice.sendrawtransaction(tx3) | ||
# now all of alice's outputs should be considered spent | ||
assert_equal(alice.listunspent(), []) | ||
assert_equal(alice.getbalance(), 0) | ||
alice.sendrawtransaction(alice.gettransaction(tx1)['hex']) | ||
self.generate(self.nodes[0], 3) | ||
assert_equal(alice.getbalance(), 150) | ||
""" | ||
|
||
self.log.info("Test a scenario where a transaction has both a block conflict and a mempool conflict") | ||
unspents = [{"txid" : element["txid"], "vout" : element["vout"]} for element in alice.listunspent()] | ||
|
||
raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : 49.99999}]) | ||
tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
tx1 = bob.sendrawtransaction(tx1) | ||
|
||
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[2]], outputs=[{alice.getnewaddress() : 99.999}]) | ||
tx1_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
|
||
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{alice.getnewaddress() : 49.99}]) | ||
tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
|
||
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{bob.getnewaddress() : 49.9999}]) | ||
tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
tx2 = bob.sendrawtransaction(tx2) | ||
|
||
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{alice.getnewaddress() : 49.9999}]) | ||
tx2_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex'] | ||
|
||
bob_unspents = [{"txid" : element, "vout" : 0} for element in [tx1, tx2]] | ||
|
||
raw_tx = bob.createrawtransaction(inputs=[bob_unspents[0], bob_unspents[1]], outputs=[{alice.getnewaddress() : 99.999}]) | ||
tx3 = bob.signrawtransactionwithwallet(raw_tx)['hex'] | ||
tx3 = bob.sendrawtransaction(tx3) | ||
|
||
self.disconnect_nodes(0, 1) | ||
|
||
# alice has all 0 txs, bob has 3 | ||
assert_equal(len(alice.getrawmempool()), 0) | ||
assert_equal(len(bob.getrawmempool()), 3) | ||
|
||
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0) | ||
|
||
# bob broadcasts tx_1 conflict | ||
tx1_conflict = bob.sendrawtransaction(tx1_conflict) | ||
assert_equal(len(alice.getrawmempool()), 0) | ||
assert_equal(len(bob.getrawmempool()), 2) | ||
|
||
assert tx2 in bob.getrawmempool() | ||
assert tx1_conflict in bob.getrawmempool() | ||
|
||
# check that tx3 is now conflicted, so the output from tx2 can now be spent | ||
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("49.99990000")) | ||
|
||
# we will be disconnecting this block in the future | ||
alice.sendrawtransaction(tx2_conflict) | ||
assert_equal(len(alice.getrawmempool()), 1) | ||
blk = self.generate(self.nodes[0], 11, sync_fun=self.no_op)[0] | ||
assert_equal(len(alice.getrawmempool()), 0) | ||
|
||
# check that tx3 and tx1 are now conflicted | ||
self.connect_nodes(0, 1) | ||
self.sync_blocks() | ||
assert_equal(alice.getbestblockhash(), bob.getbestblockhash()) | ||
|
||
assert tx1_conflict in bob.getrawmempool() | ||
assert_equal(len(bob.getrawmempool()), 1) | ||
|
||
assert_equal(bob.gettransaction(tx3)["confirmations"], -11) | ||
bob.invalidateblock(blk) | ||
assert_equal(len(bob.getrawmempool()), 1) | ||
|
||
bob.sendrawtransaction(tx1_conflict_conflict) | ||
assert_equal(len(bob.getrawmempool()), 1) | ||
|
||
# self.log.info("Test a scenario where a transaction has two mempool conflicts which get removed") | ||
# self.log.info("Test a scenario where a transaction has two mempool conflicts sequentially") | ||
|
||
if __name__ == '__main__': | ||
TxStatesTest().main() |