Dash Core  0.12.2.1
P2P Digital Currency
abandonconflict.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2014-2015 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 
6 
7 from test_framework.test_framework import BitcoinTestFramework
8 from test_framework.util import *
9 try:
10  import urllib.parse as urlparse
11 except ImportError:
12  import urlparse
13 
15 
16  def setup_network(self):
17  self.nodes = []
18  self.nodes.append(start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"]))
19  self.nodes.append(start_node(1, self.options.tmpdir, ["-debug","-logtimemicros"]))
20  connect_nodes(self.nodes[0], 1)
21 
22  def run_test(self):
23  self.nodes[1].generate(100)
24  sync_blocks(self.nodes)
25  balance = self.nodes[0].getbalance()
26  txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
27  txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
28  txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
29  sync_mempools(self.nodes)
30  self.nodes[1].generate(1)
31 
32  sync_blocks(self.nodes)
33  newbalance = self.nodes[0].getbalance()
34  assert(balance - newbalance < Decimal("0.001")) #no more than fees lost
35  balance = newbalance
36 
37  url = urlparse.urlparse(self.nodes[1].url)
38  self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
39 
40  # Identify the 10btc outputs
41  nA = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txA, 1)["vout"]) if vout["value"] == Decimal("10"))
42  nB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txB, 1)["vout"]) if vout["value"] == Decimal("10"))
43  nC = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txC, 1)["vout"]) if vout["value"] == Decimal("10"))
44 
45  inputs =[]
46  # spend 10btc outputs from txA and txB
47  inputs.append({"txid":txA, "vout":nA})
48  inputs.append({"txid":txB, "vout":nB})
49  outputs = {}
50 
51  outputs[self.nodes[0].getnewaddress()] = Decimal("14.99998")
52  outputs[self.nodes[1].getnewaddress()] = Decimal("5")
53  signed = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs))
54  txAB1 = self.nodes[0].sendrawtransaction(signed["hex"])
55 
56  # Identify the 14.99998btc output
57  nAB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txAB1, 1)["vout"]) if vout["value"] == Decimal("14.99998"))
58 
59  #Create a child tx spending AB1 and C
60  inputs = []
61  inputs.append({"txid":txAB1, "vout":nAB})
62  inputs.append({"txid":txC, "vout":nC})
63  outputs = {}
64  outputs[self.nodes[0].getnewaddress()] = Decimal("24.9996")
65  signed2 = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs))
66  txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"])
67 
68  # In mempool txs from self should increase balance from change
69  newbalance = self.nodes[0].getbalance()
70  assert(newbalance == balance - Decimal("30") + Decimal("24.9996"))
71  balance = newbalance
72 
73  # Restart the node with a higher min relay fee so the parent tx is no longer in mempool
74  # TODO: redo with eviction
75  # Note had to make sure tx did not have AllowFree priority
76  stop_node(self.nodes[0],0)
77  self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
78 
79  # Verify txs no longer in mempool
80  assert(len(self.nodes[0].getrawmempool()) == 0)
81 
82  # Not in mempool txs from self should only reduce balance
83  # inputs are still spent, but change not received
84  newbalance = self.nodes[0].getbalance()
85  assert(newbalance == balance - Decimal("24.9996"))
86  # Unconfirmed received funds that are not in mempool, also shouldn't show
87  # up in unconfirmed balance
88  unconfbalance = self.nodes[0].getunconfirmedbalance() + self.nodes[0].getbalance()
89  assert(unconfbalance == newbalance)
90  # Also shouldn't show up in listunspent
91  assert(not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)])
92  balance = newbalance
93 
94  # Abandon original transaction and verify inputs are available again
95  # including that the child tx was also abandoned
96  self.nodes[0].abandontransaction(txAB1)
97  newbalance = self.nodes[0].getbalance()
98  assert(newbalance == balance + Decimal("30"))
99  balance = newbalance
100 
101  # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
102  stop_node(self.nodes[0],0)
103  self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"])
104  assert(len(self.nodes[0].getrawmempool()) == 0)
105  assert(self.nodes[0].getbalance() == balance)
106 
107  # But if its received again then it is unabandoned
108  # And since now in mempool, the change is available
109  # But its child tx remains abandoned
110  self.nodes[0].sendrawtransaction(signed["hex"])
111  newbalance = self.nodes[0].getbalance()
112  assert(newbalance == balance - Decimal("20") + Decimal("14.99998"))
113  balance = newbalance
114 
115  # Send child tx again so its unabandoned
116  self.nodes[0].sendrawtransaction(signed2["hex"])
117  newbalance = self.nodes[0].getbalance()
118  assert(newbalance == balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
119  balance = newbalance
120 
121  # Remove using high relay fee again
122  stop_node(self.nodes[0],0)
123  self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
124  assert(len(self.nodes[0].getrawmempool()) == 0)
125  newbalance = self.nodes[0].getbalance()
126  assert(newbalance == balance - Decimal("24.9996"))
127  balance = newbalance
128 
129  # Create a double spend of AB1 by spending again from only A's 10 output
130  # Mine double spend from node 1
131  inputs =[]
132  inputs.append({"txid":txA, "vout":nA})
133  outputs = {}
134  outputs[self.nodes[1].getnewaddress()] = Decimal("9.9999")
135  tx = self.nodes[0].createrawtransaction(inputs, outputs)
136  signed = self.nodes[0].signrawtransaction(tx)
137  self.nodes[1].sendrawtransaction(signed["hex"])
138  self.nodes[1].generate(1)
139 
140  connect_nodes(self.nodes[0], 1)
141  sync_blocks(self.nodes)
142 
143  # Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
144  newbalance = self.nodes[0].getbalance()
145  assert(newbalance == balance + Decimal("20"))
146  balance = newbalance
147 
148  # There is currently a minor bug around this and so this test doesn't work. See Issue #7315
149  # Invalidate the block with the double spend and B's 10 BTC output should no longer be available
150  # Don't think C's should either
151  self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
152  newbalance = self.nodes[0].getbalance()
153  #assert(newbalance == balance - Decimal("10"))
154  print "If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer"
155  print "conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315"
156  print balance , " -> " , newbalance , " ?"
157 
158 if __name__ == '__main__':
UniValue abandontransaction(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1902
UniValue getunconfirmedbalance(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:869
UniValue getbalance(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:792
UniValue getrawtransaction(const UniValue &params, bool fHelp)
UniValue listunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2533
UniValue sendtoaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:409
def connect_nodes(from_connection, node_num)
Definition: util.py:343
UniValue disconnectnode(const UniValue &params, bool fHelp)
Definition: net.cpp:234
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
def sync_mempools(rpc_connections, wait=1)
Definition: util.py:127
UniValue signrawtransaction(const UniValue &params, bool fHelp)
UniValue createrawtransaction(const UniValue &params, bool fHelp)
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
Definition: util.py:281
def stop_node(node, i)
Definition: util.py:323
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
UniValue sendrawtransaction(const UniValue &params, bool fHelp)
def sync_blocks(rpc_connections, wait=1)
Definition: util.py:117
UniValue getrawmempool(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:234
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
def p2p_port(n)
Definition: util.py:93