Dash Core  0.12.2.1
P2P Digital Currency
wallet.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # coding=utf-8
3 # ^^^^^^^^^^^^ TODO remove when supporting only Python3
4 # Copyright (c) 2014-2015 The Bitcoin Core developers
5 # Distributed under the MIT software license, see the accompanying
6 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import *
10 
12 
13  def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
14  """Return curr_balance after asserting the fee was in range"""
15  fee = balance_with_fee - curr_balance
16  target_fee = fee_per_byte * tx_size
17  if fee < target_fee:
18  raise AssertionError("Fee of %s DASH too low! (Should be %s DASH)"%(str(fee), str(target_fee)))
19  # allow the node's estimation to be at most 2 bytes off
20  if fee > fee_per_byte * (tx_size + 2):
21  raise AssertionError("Fee of %s DASH too high! (Should be %s DASH)"%(str(fee), str(target_fee)))
22  return curr_balance
23 
24  def setup_chain(self):
25  print("Initializing test directory "+self.options.tmpdir)
26  initialize_chain_clean(self.options.tmpdir, 4)
27 
28  def setup_network(self, split=False):
29  self.nodes = start_nodes(3, self.options.tmpdir)
30  connect_nodes_bi(self.nodes,0,1)
31  connect_nodes_bi(self.nodes,1,2)
32  connect_nodes_bi(self.nodes,0,2)
33  self.is_network_split=False
34  self.sync_all()
35 
36  def run_test (self):
37 
38  # Check that there's no UTXO on none of the nodes
39  assert_equal(len(self.nodes[0].listunspent()), 0)
40  assert_equal(len(self.nodes[1].listunspent()), 0)
41  assert_equal(len(self.nodes[2].listunspent()), 0)
42 
43  print "Mining blocks..."
44 
45  self.nodes[0].generate(1)
46 
47  walletinfo = self.nodes[0].getwalletinfo()
48  assert_equal(walletinfo['immature_balance'], 500)
49  assert_equal(walletinfo['balance'], 0)
50 
51  self.sync_all()
52  self.nodes[1].generate(101)
53  self.sync_all()
54 
55  assert_equal(self.nodes[0].getbalance(), 500)
56  assert_equal(self.nodes[1].getbalance(), 500)
57  assert_equal(self.nodes[2].getbalance(), 0)
58 
59  # Check that only first and second nodes have UTXOs
60  assert_equal(len(self.nodes[0].listunspent()), 1)
61  assert_equal(len(self.nodes[1].listunspent()), 1)
62  assert_equal(len(self.nodes[2].listunspent()), 0)
63 
64  # Send 210 DASH from 0 to 2 using sendtoaddress call.
65  # Second transaction will be child of first, and will require a fee
66  self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 110)
67  self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 100)
68 
69  walletinfo = self.nodes[0].getwalletinfo()
70  assert_equal(walletinfo['immature_balance'], 0)
71 
72  # Have node0 mine a block, thus it will collect its own fee.
73  self.nodes[0].generate(1)
74  self.sync_all()
75 
76  # Exercise locking of unspent outputs
77  unspent_0 = self.nodes[2].listunspent()[0]
78  unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
79  self.nodes[2].lockunspent(False, [unspent_0])
80  assert_raises(JSONRPCException, self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 200)
81  assert_equal([unspent_0], self.nodes[2].listlockunspent())
82  self.nodes[2].lockunspent(True, [unspent_0])
83  assert_equal(len(self.nodes[2].listlockunspent()), 0)
84 
85  # Have node1 generate 100 blocks (so node0 can recover the fee)
86  self.nodes[1].generate(100)
87  self.sync_all()
88 
89  # node0 should end up with 1000 DASH in block rewards plus fees, but
90  # minus the 210 plus fees sent to node2
91  assert_equal(self.nodes[0].getbalance(), 1000-210)
92  assert_equal(self.nodes[2].getbalance(), 210)
93 
94  # Node0 should have two unspent outputs.
95  # Create a couple of transactions to send them to node2, submit them through
96  # node1, and make sure both node0 and node2 pick them up properly:
97  node0utxos = self.nodes[0].listunspent(1)
98  assert_equal(len(node0utxos), 2)
99 
100  # create both transactions
101  txns_to_send = []
102  for utxo in node0utxos:
103  inputs = []
104  outputs = {}
105  inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]})
106  outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"]
107  raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
108  txns_to_send.append(self.nodes[0].signrawtransaction(raw_tx))
109 
110  # Have node 1 (miner) send the transactions
111  self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True)
112  self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True)
113 
114  # Have node1 mine a block to confirm transactions:
115  self.nodes[1].generate(1)
116  self.sync_all()
117 
118  assert_equal(self.nodes[0].getbalance(), 0)
119  assert_equal(self.nodes[2].getbalance(), 1000)
120  assert_equal(self.nodes[2].getbalance("from1"), 1000-210)
121 
122  # Send 100 DASH normal
123  address = self.nodes[0].getnewaddress("test")
124  fee_per_byte = Decimal('0.001') / 1000
125  self.nodes[2].settxfee(fee_per_byte * 1000)
126  txid = self.nodes[2].sendtoaddress(address, 100, "", "", False)
127  self.nodes[2].generate(1)
128  self.sync_all()
129  node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('900'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
130  assert_equal(self.nodes[0].getbalance(), Decimal('100'))
131 
132  # Send 100 DASH with subtract fee from amount
133  txid = self.nodes[2].sendtoaddress(address, 100, "", "", True)
134  self.nodes[2].generate(1)
135  self.sync_all()
136  node_2_bal -= Decimal('100')
137  assert_equal(self.nodes[2].getbalance(), node_2_bal)
138  node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('200'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
139 
140  # Sendmany 100 DASH
141  txid = self.nodes[2].sendmany('from1', {address: 100}, 0, False, "", [])
142  self.nodes[2].generate(1)
143  self.sync_all()
144  node_0_bal += Decimal('100')
145  node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('100'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
146  assert_equal(self.nodes[0].getbalance(), node_0_bal)
147 
148  # Sendmany 100 DASH with subtract fee from amount
149  txid = self.nodes[2].sendmany('from1', {address: 100}, 0, False, "", [address])
150  self.nodes[2].generate(1)
151  self.sync_all()
152  node_2_bal -= Decimal('100')
153  assert_equal(self.nodes[2].getbalance(), node_2_bal)
154  node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('100'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
155 
156  # Test ResendWalletTransactions:
157  # Create a couple of transactions, then start up a fourth
158  # node (nodes[3]) and ask nodes[0] to rebroadcast.
159  # EXPECT: nodes[3] should have those transactions in its mempool.
160  txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
161  txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
162  sync_mempools(self.nodes)
163 
164  self.nodes.append(start_node(3, self.options.tmpdir))
165  connect_nodes_bi(self.nodes, 0, 3)
166  sync_blocks(self.nodes)
167 
168  relayed = self.nodes[0].resendwallettransactions()
169  assert_equal(set(relayed), {txid1, txid2})
170  sync_mempools(self.nodes)
171 
172  assert(txid1 in self.nodes[3].getrawmempool())
173 
174  # Exercise balance rpcs
175  assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1)
177 
178  #check if we can list zero value tx as available coins
179  #1. create rawtx
180  #2. hex-changed one output to 0.0
181  #3. sign and send
182  #4. check if recipient (node0) can list the zero value tx
183  usp = self.nodes[1].listunspent()
184  inputs = [{"txid":usp[0]['txid'], "vout":usp[0]['vout']}]
185  outputs = {self.nodes[1].getnewaddress(): 499.998, self.nodes[0].getnewaddress(): 11.11}
186 
187  rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") #replace 11.11 with 0.0 (int32)
188  decRawTx = self.nodes[1].decoderawtransaction(rawTx)
189  signedRawTx = self.nodes[1].signrawtransaction(rawTx)
190  decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex'])
191  zeroValueTxid= decRawTx['txid']
192  sendResp = self.nodes[1].sendrawtransaction(signedRawTx['hex'])
193 
194  self.sync_all()
195  self.nodes[1].generate(1) #mine a block
196  self.sync_all()
197 
198  unspentTxs = self.nodes[0].listunspent() #zero value tx must be in listunspents output
199  found = False
200  for uTx in unspentTxs:
201  if uTx['txid'] == zeroValueTxid:
202  found = True
203  assert_equal(uTx['amount'], Decimal('0'))
204  assert(found)
205 
206  #do some -walletbroadcast tests
207  stop_nodes(self.nodes)
209  self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
210  connect_nodes_bi(self.nodes,0,1)
211  connect_nodes_bi(self.nodes,1,2)
212  connect_nodes_bi(self.nodes,0,2)
213  self.sync_all()
214 
215  txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
216  txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
217  self.nodes[1].generate(1) #mine a block, tx should not be in there
218  self.sync_all()
219  assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted
220 
221  #now broadcast from another node, mine a block, sync, and check the balance
222  self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex'])
223  self.nodes[1].generate(1)
224  self.sync_all()
225  node_2_bal += 2
226  txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
227  assert_equal(self.nodes[2].getbalance(), node_2_bal)
228 
229  #create another tx
230  txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
231 
232  #restart the nodes with -walletbroadcast=1
233  stop_nodes(self.nodes)
235  self.nodes = start_nodes(3, self.options.tmpdir)
236  connect_nodes_bi(self.nodes,0,1)
237  connect_nodes_bi(self.nodes,1,2)
238  connect_nodes_bi(self.nodes,0,2)
239  sync_blocks(self.nodes)
240 
241  self.nodes[0].generate(1)
242  sync_blocks(self.nodes)
243  node_2_bal += 2
244 
245  #tx should be added to balance because after restarting the nodes tx should be broadcastet
246  assert_equal(self.nodes[2].getbalance(), node_2_bal)
247 
248  #send a tx with value in a string (PR#6380 +)
249  txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
250  txObj = self.nodes[0].gettransaction(txId)
251  assert_equal(txObj['amount'], Decimal('-2'))
252 
253  txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")
254  txObj = self.nodes[0].gettransaction(txId)
255  assert_equal(txObj['amount'], Decimal('-0.0001'))
256 
257  #check if JSON parser can handle scientific notation in strings
258  txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")
259  txObj = self.nodes[0].gettransaction(txId)
260  assert_equal(txObj['amount'], Decimal('-0.0001'))
261 
262  try:
263  txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
264  except JSONRPCException as e:
265  assert("Invalid amount" in e.error['message'])
266  else:
267  raise AssertionError("Must not parse invalid amounts")
268 
269 
270  try:
271  self.nodes[0].generate("2")
272  raise AssertionError("Must not accept strings as numeric")
273  except JSONRPCException as e:
274  assert("not an integer" in e.error['message'])
275 
276  # Import address and private key to check correct behavior of spendable unspents
277  # 1. Send some coins to generate new UTXO
278  address_to_import = self.nodes[2].getnewaddress()
279  txid = self.nodes[0].sendtoaddress(address_to_import, 1)
280  self.nodes[0].generate(1)
281  self.sync_all()
282 
283  # 2. Import address from node2 to node1
284  self.nodes[1].importaddress(address_to_import)
285 
286  # 3. Validate that the imported address is watch-only on node1
287  assert(self.nodes[1].validateaddress(address_to_import)["iswatchonly"])
288 
289  # 4. Check that the unspents after import are not spendable
291  {"address": address_to_import},
292  {"spendable": False})
293 
294  # 5. Import private key of the previously imported address on node1
295  priv_key = self.nodes[2].dumpprivkey(address_to_import)
296  self.nodes[1].importprivkey(priv_key)
297 
298  # 6. Check that the unspents are now spendable on node1
300  {"address": address_to_import},
301  {"spendable": True})
302 
303  #check if wallet or blochchain maintenance changes the balance
304  self.sync_all()
305  blocks = self.nodes[0].generate(2)
306  self.sync_all()
307  balance_nodes = [self.nodes[i].getbalance() for i in range(3)]
308  block_count = self.nodes[0].getblockcount()
309 
310  # Check modes:
311  # - True: unicode escaped as \u....
312  # - False: unicode directly as UTF-8
313  for mode in [True, False]:
314  self.nodes[0].ensure_ascii = mode
315  # unicode check: Basic Multilingual Plane, Supplementary Plane respectively
316  for s in [u'рыба', u'𝅘𝅥𝅯']:
317  addr = self.nodes[0].getaccountaddress(s)
318  label = self.nodes[0].getaccount(addr)
319  assert_equal(label.encode('utf-8'), s.encode('utf-8')) # TODO remove encode(...) when supporting only Python3
320  assert(s in self.nodes[0].listaccounts().keys())
321  self.nodes[0].ensure_ascii = True # restore to default
322 
323  # maintenance tests
324  maintenance = [
325  '-rescan',
326  '-reindex',
327  '-zapwallettxes=1',
328  '-zapwallettxes=2',
329  '-salvagewallet',
330  ]
331  for m in maintenance:
332  print "check " + m
333  stop_nodes(self.nodes)
335  self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3)
336  while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
337  # reindex will leave rpc warm up "early"; Wait for it to finish
338  time.sleep(0.1)
339  assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
340 
341  # Exercise listsinceblock with the last two blocks
342  coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
343  assert_equal(coinbase_tx_1["lastblock"], blocks[1])
344  assert_equal(len(coinbase_tx_1["transactions"]), 1)
345  assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
346  assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0)
347 
348 if __name__ == '__main__':
349  WalletTest ().main ()
def wait_bitcoinds()
Definition: util.py:337
UniValue importprivkey(const UniValue &params, bool fHelp)
Definition: rpcdump.cpp:76
UniValue resendwallettransactions(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2505
UniValue getunconfirmedbalance(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:869
def run_test(self)
Definition: wallet.py:36
def count_bytes(hex_string)
Definition: util.py:105
def assert_array_result(object_array, to_match, expected, should_not_find=False)
Definition: util.py:496
UniValue validateaddress(const UniValue &params, bool fHelp)
Definition: misc.cpp:270
UniValue settxfee(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2349
UniValue dumpprivkey(const UniValue &params, bool fHelp)
Definition: rpcdump.cpp:546
UniValue getbalance(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:792
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None)
Definition: util.py:305
def assert_raises(exc, fun, args, kwds)
Definition: util.py:469
UniValue getrawtransaction(const UniValue &params, bool fHelp)
UniValue listaccounts(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1645
UniValue getaccount(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:305
UniValue sendmany(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1024
UniValue listunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2533
UniValue getaccountaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:192
UniValue sendtoaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:409
def setup_chain(self)
Definition: wallet.py:24
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
def sync_mempools(rpc_connections, wait=1)
Definition: util.py:127
UniValue getblockcount(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:131
def initialize_chain_clean(test_dir, num_nodes)
Definition: util.py:252
UniValue getwalletinfo(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2376
UniValue listlockunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2300
UniValue signrawtransaction(const UniValue &params, bool fHelp)
UniValue createrawtransaction(const UniValue &params, bool fHelp)
UniValue listsinceblock(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1726
UniValue decoderawtransaction(const UniValue &params, bool fHelp)
UniValue importaddress(const UniValue &params, bool fHelp)
Definition: rpcdump.cpp:181
UniValue lockunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2216
def stop_nodes(nodes)
Definition: util.py:328
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
Definition: util.py:281
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
def setup_network(self, split=False)
Definition: wallet.py:28
def assert_equal(thing1, thing2)
Definition: util.py:461
def connect_nodes_bi(nodes, a, b)
Definition: util.py:351
UniValue gettransaction(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1821
def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size)
Definition: wallet.py:13