Add listunspent() test for spendable/unspendable UTXO

Github-Pull: #7822
Rebased-From: fa942c755ab513829dcab27487ba1e7ab5a806ee 5d217decc1145823a3c126658c82c60cf7dbfec8
This commit is contained in:
Joao Fonseca 2016-04-19 12:22:11 +01:00 committed by MarcoFalke
parent 28ba22c202
commit 6862627ce6
7 changed files with 98 additions and 145 deletions

View File

@ -6,28 +6,6 @@
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
def check_array_result(object_array, to_match, expected):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
"""
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
import threading import threading
class LongpollThread(threading.Thread): class LongpollThread(threading.Thread):

View File

@ -10,28 +10,6 @@ from binascii import a2b_hex, b2a_hex
from hashlib import sha256 from hashlib import sha256
from struct import pack from struct import pack
def check_array_result(object_array, to_match, expected):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
"""
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
def b2x(b): def b2x(b):
return b2a_hex(b).decode('ascii') return b2a_hex(b).decode('ascii')

View File

@ -10,28 +10,6 @@
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
def check_array_result(object_array, to_match, expected):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
"""
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
class KeyPoolTest(BitcoinTestFramework): class KeyPoolTest(BitcoinTestFramework):
def run_test(self): def run_test(self):

View File

@ -16,55 +16,34 @@ def txFromHex(hexstring):
tx.deserialize(f) tx.deserialize(f)
return tx return tx
def check_array_result(object_array, to_match, expected):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
"""
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
class ListTransactionsTest(BitcoinTestFramework): class ListTransactionsTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
# Simple send, 0 to 1: # Simple send, 0 to 1:
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
self.sync_all() self.sync_all()
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0}) {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0}) {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
# mine a block, confirmations should change: # mine a block, confirmations should change:
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all() self.sync_all()
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1}) {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1}) {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
# send-to-self: # send-to-self:
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"txid":txid, "category":"send"}, {"txid":txid, "category":"send"},
{"amount":Decimal("-0.2")}) {"amount":Decimal("-0.2")})
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"txid":txid, "category":"receive"}, {"txid":txid, "category":"receive"},
{"amount":Decimal("0.2")}) {"amount":Decimal("0.2")})
@ -75,28 +54,28 @@ class ListTransactionsTest(BitcoinTestFramework):
self.nodes[1].getaccountaddress("toself") : 0.44 } self.nodes[1].getaccountaddress("toself") : 0.44 }
txid = self.nodes[1].sendmany("", send_to) txid = self.nodes[1].sendmany("", send_to)
self.sync_all() self.sync_all()
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.11")}, {"category":"send","amount":Decimal("-0.11")},
{"txid":txid} ) {"txid":txid} )
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"category":"receive","amount":Decimal("0.11")}, {"category":"receive","amount":Decimal("0.11")},
{"txid":txid} ) {"txid":txid} )
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.22")}, {"category":"send","amount":Decimal("-0.22")},
{"txid":txid} ) {"txid":txid} )
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"receive","amount":Decimal("0.22")}, {"category":"receive","amount":Decimal("0.22")},
{"txid":txid} ) {"txid":txid} )
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.33")}, {"category":"send","amount":Decimal("-0.33")},
{"txid":txid} ) {"txid":txid} )
check_array_result(self.nodes[0].listtransactions(), assert_array_result(self.nodes[0].listtransactions(),
{"category":"receive","amount":Decimal("0.33")}, {"category":"receive","amount":Decimal("0.33")},
{"txid":txid, "account" : "from1"} ) {"txid":txid, "account" : "from1"} )
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.44")}, {"category":"send","amount":Decimal("-0.44")},
{"txid":txid, "account" : ""} ) {"txid":txid, "account" : ""} )
check_array_result(self.nodes[1].listtransactions(), assert_array_result(self.nodes[1].listtransactions(),
{"category":"receive","amount":Decimal("0.44")}, {"category":"receive","amount":Decimal("0.44")},
{"txid":txid, "account" : "toself"} ) {"txid":txid, "account" : "toself"} )
@ -106,7 +85,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.nodes[1].generate(1) self.nodes[1].generate(1)
self.sync_all() self.sync_all()
assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0)
check_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), assert_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True),
{"category":"receive","amount":Decimal("0.1")}, {"category":"receive","amount":Decimal("0.1")},
{"txid":txid, "account" : "watchonly"} ) {"txid":txid, "account" : "watchonly"} )
@ -134,9 +113,9 @@ class ListTransactionsTest(BitcoinTestFramework):
# 1. Chain a few transactions that don't opt-in. # 1. Chain a few transactions that don't opt-in.
txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
assert(not is_opt_in(self.nodes[0], txid_1)) assert(not is_opt_in(self.nodes[0], txid_1))
check_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"})
sync_mempools(self.nodes) sync_mempools(self.nodes)
check_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"})
# Tx2 will build off txid_1, still not opting in to RBF. # Tx2 will build off txid_1, still not opting in to RBF.
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1) utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1)
@ -150,9 +129,9 @@ class ListTransactionsTest(BitcoinTestFramework):
# ...and check the result # ...and check the result
assert(not is_opt_in(self.nodes[1], txid_2)) assert(not is_opt_in(self.nodes[1], txid_2))
check_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"})
sync_mempools(self.nodes) sync_mempools(self.nodes)
check_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"})
# Tx3 will opt-in to RBF # Tx3 will opt-in to RBF
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2) utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2)
@ -166,9 +145,9 @@ class ListTransactionsTest(BitcoinTestFramework):
txid_3 = self.nodes[0].sendrawtransaction(tx3_signed) txid_3 = self.nodes[0].sendrawtransaction(tx3_signed)
assert(is_opt_in(self.nodes[0], txid_3)) assert(is_opt_in(self.nodes[0], txid_3))
check_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"})
sync_mempools(self.nodes) sync_mempools(self.nodes)
check_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"})
# Tx4 will chain off tx3. Doesn't signal itself, but depends on one # Tx4 will chain off tx3. Doesn't signal itself, but depends on one
# that does. # that does.
@ -180,9 +159,9 @@ class ListTransactionsTest(BitcoinTestFramework):
txid_4 = self.nodes[1].sendrawtransaction(tx4_signed) txid_4 = self.nodes[1].sendrawtransaction(tx4_signed)
assert(not is_opt_in(self.nodes[1], txid_4)) assert(not is_opt_in(self.nodes[1], txid_4))
check_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"})
sync_mempools(self.nodes) sync_mempools(self.nodes)
check_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"})
# Replace tx3, and check that tx4 becomes unknown # Replace tx3, and check that tx4 becomes unknown
tx3_b = tx3_modified tx3_b = tx3_modified
@ -192,9 +171,9 @@ class ListTransactionsTest(BitcoinTestFramework):
txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True) txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True)
assert(is_opt_in(self.nodes[0], txid_3b)) assert(is_opt_in(self.nodes[0], txid_3b))
check_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"})
sync_mempools(self.nodes) sync_mempools(self.nodes)
check_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"})
# Check gettransaction as well: # Check gettransaction as well:
for n in self.nodes[0:2]: for n in self.nodes[0:2]:

View File

@ -25,32 +25,6 @@ def get_sub_array_from_array(object_array, to_match):
return item return item
return [] return []
def check_array_result(object_array, to_match, expected, should_not_find = False):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
If the should_not_find flag is true, to_match should not be found in object_array
"""
if should_not_find == True:
expected = { }
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0 and should_not_find != True:
raise AssertionError("No objects matched %s"%(str(to_match)))
if num_matched > 0 and should_not_find == True:
raise AssertionError("Objects was matched %s"%(str(to_match)))
class ReceivedByTest(BitcoinTestFramework): class ReceivedByTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
@ -63,26 +37,26 @@ class ReceivedByTest(BitcoinTestFramework):
self.sync_all() self.sync_all()
#Check not listed in listreceivedbyaddress because has 0 confirmations #Check not listed in listreceivedbyaddress because has 0 confirmations
check_array_result(self.nodes[1].listreceivedbyaddress(), assert_array_result(self.nodes[1].listreceivedbyaddress(),
{"address":addr}, {"address":addr},
{ }, { },
True) True)
#Bury Tx under 10 block so it will be returned by listreceivedbyaddress #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
self.nodes[1].generate(10) self.nodes[1].generate(10)
self.sync_all() self.sync_all()
check_array_result(self.nodes[1].listreceivedbyaddress(), assert_array_result(self.nodes[1].listreceivedbyaddress(),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
#With min confidence < 10 #With min confidence < 10
check_array_result(self.nodes[1].listreceivedbyaddress(5), assert_array_result(self.nodes[1].listreceivedbyaddress(5),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
#With min confidence > 10, should not find Tx #With min confidence > 10, should not find Tx
check_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True) assert_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
#Empty Tx #Empty Tx
addr = self.nodes[1].getnewaddress() addr = self.nodes[1].getnewaddress()
check_array_result(self.nodes[1].listreceivedbyaddress(0,True), assert_array_result(self.nodes[1].listreceivedbyaddress(0,True),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]}) {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
@ -126,7 +100,7 @@ class ReceivedByTest(BitcoinTestFramework):
self.sync_all() self.sync_all()
# listreceivedbyaccount should return received_by_account_json because of 0 confirmations # listreceivedbyaccount should return received_by_account_json because of 0 confirmations
check_array_result(self.nodes[1].listreceivedbyaccount(), assert_array_result(self.nodes[1].listreceivedbyaccount(),
{"account":account}, {"account":account},
received_by_account_json) received_by_account_json)
@ -138,7 +112,7 @@ class ReceivedByTest(BitcoinTestFramework):
self.nodes[1].generate(10) self.nodes[1].generate(10)
self.sync_all() self.sync_all()
# listreceivedbyaccount should return updated account balance # listreceivedbyaccount should return updated account balance
check_array_result(self.nodes[1].listreceivedbyaccount(), assert_array_result(self.nodes[1].listreceivedbyaccount(),
{"account":account}, {"account":account},
{"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))}) {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})

View File

@ -437,6 +437,35 @@ def assert_is_hash_string(string, length=64):
raise AssertionError( raise AssertionError(
"String %r contains invalid characters for a hash." % string) "String %r contains invalid characters for a hash." % string)
def assert_array_result(object_array, to_match, expected, should_not_find = False):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
If the should_not_find flag is true, to_match should not be found
in object_array
"""
if should_not_find == True:
expected = { }
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
elif should_not_find == True:
num_matched = num_matched+1
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0 and should_not_find != True:
raise AssertionError("No objects matched %s"%(str(to_match)))
if num_matched > 0 and should_not_find == True:
raise AssertionError("Objects were found %s"%(str(to_match)))
def satoshi_round(amount): def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)

View File

@ -32,6 +32,12 @@ class WalletTest (BitcoinTestFramework):
self.sync_all() self.sync_all()
def run_test (self): def run_test (self):
# Check that there's no UTXO on none of the nodes
assert_equal(len(self.nodes[0].listunspent()), 0)
assert_equal(len(self.nodes[1].listunspent()), 0)
assert_equal(len(self.nodes[2].listunspent()), 0)
print "Mining blocks..." print "Mining blocks..."
self.nodes[0].generate(1) self.nodes[0].generate(1)
@ -48,6 +54,11 @@ class WalletTest (BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 50) assert_equal(self.nodes[1].getbalance(), 50)
assert_equal(self.nodes[2].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0)
# Check that only first and second nodes have UTXOs
assert_equal(len(self.nodes[0].listunspent()), 1)
assert_equal(len(self.nodes[1].listunspent()), 1)
assert_equal(len(self.nodes[2].listunspent()), 0)
# Send 21 BTC from 0 to 2 using sendtoaddress call. # Send 21 BTC from 0 to 2 using sendtoaddress call.
# Second transaction will be child of first, and will require a fee # Second transaction will be child of first, and will require a fee
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
@ -260,6 +271,32 @@ class WalletTest (BitcoinTestFramework):
except JSONRPCException as e: except JSONRPCException as e:
assert("not an integer" in e.error['message']) assert("not an integer" in e.error['message'])
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
address_to_import = self.nodes[2].getnewaddress()
txid = self.nodes[0].sendtoaddress(address_to_import, 1)
self.nodes[0].generate(1)
self.sync_all()
# 2. Import address from node2 to node1
self.nodes[1].importaddress(address_to_import)
# 3. Validate that the imported address is watch-only on node1
assert(self.nodes[1].validateaddress(address_to_import)["iswatchonly"])
# 4. Check that the unspents after import are not spendable
assert_array_result(self.nodes[1].listunspent(),
{"address": address_to_import},
{"spendable": False})
# 5. Import private key of the previously imported address on node1
priv_key = self.nodes[2].dumpprivkey(address_to_import)
self.nodes[1].importprivkey(priv_key)
# 6. Check that the unspents are now spendable on node1
assert_array_result(self.nodes[1].listunspent(),
{"address": address_to_import},
{"spendable": True})
#check if wallet or blochchain maintenance changes the balance #check if wallet or blochchain maintenance changes the balance
self.sync_all() self.sync_all()