Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_basic.py
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | class WalletTest(BitcoinTestFramework): | ||||
def check_fee_amount(self, curr_balance, | def check_fee_amount(self, curr_balance, | ||||
balance_with_fee, fee_per_byte, tx_size): | balance_with_fee, fee_per_byte, tx_size): | ||||
"""Return curr_balance after asserting the fee was in range""" | """Return curr_balance after asserting the fee was in range""" | ||||
fee = balance_with_fee - curr_balance | fee = balance_with_fee - curr_balance | ||||
assert_fee_amount(fee, tx_size, fee_per_byte * 1000) | assert_fee_amount(fee, tx_size, fee_per_byte * 1000) | ||||
return curr_balance | return curr_balance | ||||
def run_test(self): | def run_test(self): | ||||
# Check that there's no UTXO on none of the nodes | # Check that there's no UTXO on none of the nodes | ||||
assert_equal(len(self.nodes[0].listunspent()), 0) | assert_equal(len(self.nodes[0].listunspent()), 0) | ||||
assert_equal(len(self.nodes[1].listunspent()), 0) | assert_equal(len(self.nodes[1].listunspent()), 0) | ||||
assert_equal(len(self.nodes[2].listunspent()), 0) | assert_equal(len(self.nodes[2].listunspent()), 0) | ||||
self.log.info("Mining blocks...") | self.log.info("Mining blocks...") | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
▲ Show 20 Lines • Show All 228 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
connect_nodes(self.nodes[0], self.nodes[1]) | connect_nodes(self.nodes[0], self.nodes[1]) | ||||
connect_nodes(self.nodes[1], self.nodes[2]) | connect_nodes(self.nodes[1], self.nodes[2]) | ||||
connect_nodes(self.nodes[0], self.nodes[2]) | connect_nodes(self.nodes[0], self.nodes[2]) | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
txid_not_broadcast = self.nodes[0].sendtoaddress( | txid_not_broadcast = self.nodes[0].sendtoaddress( | ||||
self.nodes[2].getnewaddress(), 2000000) | self.nodes[2].getnewaddress(), 2000000) | ||||
tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) | ||||
self.nodes[1].generate(1) # mine a block, tx should not be in there | # mine a block, tx should not be in there | ||||
self.nodes[1].generate(1) | |||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
# should not be changed because tx was not broadcasted | # should not be changed because tx was not broadcasted | ||||
assert_equal(self.nodes[2].getbalance(), node_2_bal) | assert_equal(self.nodes[2].getbalance(), node_2_bal) | ||||
# now broadcast from another node, mine a block, sync, and check the | # now broadcast from another node, mine a block, sync, and check the | ||||
# balance | # balance | ||||
self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) | self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# This will raise an exception because the amount type is wrong | # This will raise an exception because the amount type is wrong | ||||
assert_raises_rpc_error(-3, "Invalid amount", | assert_raises_rpc_error(-3, "Invalid amount", | ||||
self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") | self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") | ||||
# This will raise an exception since generate does not accept a string | # This will raise an exception since generate does not accept a string | ||||
assert_raises_rpc_error(-1, "not an integer", | assert_raises_rpc_error(-1, "not an integer", | ||||
self.nodes[0].generate, "2") | self.nodes[0].generate, "2") | ||||
if not self.options.descriptors: | |||||
# This will raise an exception for the invalid private key format | # This will raise an exception for the invalid private key format | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Invalid private key encoding", | "Invalid private key encoding", | ||||
self.nodes[0].importprivkey, | self.nodes[0].importprivkey, | ||||
"invalid") | "invalid") | ||||
# This will raise an exception for importing an address with the PS2H | # This will raise an exception for importing an address with the | ||||
# flag | # PS2H flag | ||||
temp_address = self.nodes[1].getnewaddress() | temp_address = self.nodes[1].getnewaddress() | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Cannot use the p2sh flag with an address - use a script instead", | "Cannot use the p2sh flag with an address - use a script instead", | ||||
self.nodes[0].importaddress, | self.nodes[0].importaddress, | ||||
temp_address, | temp_address, | ||||
"label", | "label", | ||||
False, | False, | ||||
True) | True) | ||||
# This will raise an exception for attempting to dump the private key | # This will raise an exception for attempting to dump the private | ||||
# of an address you do not own | # key of an address you do not own | ||||
assert_raises_rpc_error(-4, | assert_raises_rpc_error(-4, | ||||
"Private key for address", | "Private key for address", | ||||
self.nodes[0].dumpprivkey, | self.nodes[0].dumpprivkey, | ||||
temp_address) | temp_address) | ||||
# This will raise an exception for attempting to get the private key of | # This will raise an exception for attempting to get the private key of | ||||
# an invalid Bitcoin address | # an invalid Bitcoin address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Invalid Bitcoin address", | "Invalid Bitcoin address", | ||||
self.nodes[0].dumpprivkey, | self.nodes[0].dumpprivkey, | ||||
"invalid") | "invalid") | ||||
# This will raise an exception for attempting to set a label for an | # This will raise an exception for attempting to set a label for an | ||||
# invalid Bitcoin address | # invalid Bitcoin address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Invalid Bitcoin address", | "Invalid Bitcoin address", | ||||
self.nodes[0].setlabel, | self.nodes[0].setlabel, | ||||
"invalid address", | "invalid address", | ||||
"label") | "label") | ||||
# This will raise an exception for importing an invalid address | # This will raise an exception for importing an invalid address | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Invalid Bitcoin address or script", | "Invalid Bitcoin address or script", | ||||
self.nodes[0].importaddress, | self.nodes[0].importaddress, | ||||
"invalid") | "invalid") | ||||
# This will raise an exception for attempting to import a pubkey that | # This will raise an exception for attempting to import a pubkey that | ||||
# isn't in hex | # isn't in hex | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Pubkey must be a hex string", | "Pubkey must be a hex string", | ||||
self.nodes[0].importpubkey, | self.nodes[0].importpubkey, | ||||
"not hex") | "not hex") | ||||
# This will raise an exception for importing an invalid pubkey | # This will raise an exception for importing an invalid pubkey | ||||
assert_raises_rpc_error(-5, | assert_raises_rpc_error(-5, | ||||
"Pubkey is not a valid public key", | "Pubkey is not a valid public key", | ||||
self.nodes[0].importpubkey, | self.nodes[0].importpubkey, | ||||
"5361746f736869204e616b616d6f746f") | "5361746f736869204e616b616d6f746f") | ||||
# Import address and private key to check correct behavior of spendable unspents | # Import address and private key to check correct behavior of spendable unspents | ||||
# 1. Send some coins to generate new UTXO | # 1. Send some coins to generate new UTXO | ||||
address_to_import = self.nodes[2].getnewaddress() | address_to_import = self.nodes[2].getnewaddress() | ||||
txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) | txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
# 2. Import address from node2 to node1 | # 2. Import address from node2 to node1 | ||||
self.nodes[1].importaddress(address_to_import) | self.nodes[1].importaddress(address_to_import) | ||||
# 3. Validate that the imported address is watch-only on node1 | # 3. Validate that the imported address is watch-only on node1 | ||||
assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] | assert self.nodes[1].getaddressinfo( | ||||
address_to_import)["iswatchonly"] | |||||
# 4. Check that the unspents after import are not spendable | # 4. Check that the unspents after import are not spendable | ||||
assert_array_result(self.nodes[1].listunspent(), | assert_array_result(self.nodes[1].listunspent(), | ||||
{"address": address_to_import}, | {"address": address_to_import}, | ||||
{"spendable": False}) | {"spendable": False}) | ||||
# 5. Import private key of the previously imported address on node1 | # 5. Import private key of the previously imported address on node1 | ||||
priv_key = self.nodes[2].dumpprivkey(address_to_import) | priv_key = self.nodes[2].dumpprivkey(address_to_import) | ||||
self.nodes[1].importprivkey(priv_key) | self.nodes[1].importprivkey(priv_key) | ||||
# 6. Check that the unspents are now spendable on node1 | # 6. Check that the unspents are now spendable on node1 | ||||
assert_array_result(self.nodes[1].listunspent(), | assert_array_result(self.nodes[1].listunspent(), | ||||
{"address": address_to_import}, | {"address": address_to_import}, | ||||
{"spendable": True}) | {"spendable": True}) | ||||
# Mine a block from node0 to an address from node1 | # Mine a block from node0 to an address from node1 | ||||
coinbase_addr = self.nodes[1].getnewaddress() | coinbase_addr = self.nodes[1].getnewaddress() | ||||
block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0] | block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0] | ||||
coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] | coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] | ||||
self.sync_all(self.nodes[0:3]) | self.sync_all(self.nodes[0:3]) | ||||
# Check that the txid and balance is found by node1 | # Check that the txid and balance is found by node1 | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
assert_equal(len(coinbase_tx_1["transactions"]), 1) | assert_equal(len(coinbase_tx_1["transactions"]), 1) | ||||
assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) | assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) | ||||
assert_equal(len(self.nodes[0].listsinceblock( | assert_equal(len(self.nodes[0].listsinceblock( | ||||
blocks[1])["transactions"]), 0) | blocks[1])["transactions"]), 0) | ||||
# ==Check that wallet prefers to use coins that don't exceed mempool li | # ==Check that wallet prefers to use coins that don't exceed mempool li | ||||
# Get all non-zero utxos together | # Get all non-zero utxos together | ||||
chain_addrs = [self.nodes[0].getnewaddress( | chain_addrs = [ | ||||
), self.nodes[0].getnewaddress()] | self.nodes[0].getnewaddress(), | ||||
self.nodes[0].getnewaddress()] | |||||
singletxid = self.nodes[0].sendtoaddress( | singletxid = self.nodes[0].sendtoaddress( | ||||
chain_addrs[0], self.nodes[0].getbalance(), "", "", True) | chain_addrs[0], self.nodes[0].getbalance(), "", "", True) | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
node0_balance = self.nodes[0].getbalance() | node0_balance = self.nodes[0].getbalance() | ||||
# Split into two chains | # Split into two chains | ||||
amount = node0_balance / 2 - Decimal('10000') | |||||
if self.options.descriptors: | |||||
# In this case, "node0_balance / 2" splits a satoshi in half, which | |||||
# creates an invalid amount with 3 decimal places. | |||||
amount -= Decimal("0.005") | |||||
rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], { | rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], { | ||||
chain_addrs[0]: node0_balance / 2 - Decimal('10000'), chain_addrs[1]: node0_balance / 2 - Decimal('10000')}) | chain_addrs[0]: amount, chain_addrs[1]: amount}) | ||||
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | ||||
singletxid = self.nodes[0].sendrawtransaction( | singletxid = self.nodes[0].sendrawtransaction( | ||||
hexstring=signedtx["hex"], maxfeerate=0) | hexstring=signedtx["hex"], maxfeerate=0) | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
# Make a long chain of unconfirmed payments without hitting mempool limit | # Make a long chain of unconfirmed payments without hitting mempool limit | ||||
# Each tx we make leaves only one output of change on a chain 1 longer | # Each tx we make leaves only one output of change on a chain 1 longer | ||||
# Since the amount to send is always much less than the outputs, we only ever need one output | # Since the amount to send is always much less than the outputs, we only ever need one output | ||||
Show All 16 Lines | def run_test(self): | ||||
for tx in self.nodes[0].listtransactions()] | for tx in self.nodes[0].listtransactions()] | ||||
self.nodes[0].abandontransaction(extra_txid) | self.nodes[0].abandontransaction(extra_txid) | ||||
total_txs = len(self.nodes[0].listtransactions("*", 99999)) | total_txs = len(self.nodes[0].listtransactions("*", 99999)) | ||||
# Try with walletrejectlongchains | # Try with walletrejectlongchains | ||||
# Double chain limit but require combining inputs, so we pass | # Double chain limit but require combining inputs, so we pass | ||||
# SelectCoinsMinConf | # SelectCoinsMinConf | ||||
self.stop_node(0) | self.stop_node(0) | ||||
self.start_node(0, | extra_args = ["-acceptnonstdtxn=1", "-walletrejectlongchains", | ||||
self.extra_args[0] + ["-walletrejectlongchains", | "-limitancestorcount=" + str(2 * chainlimit)] | ||||
"-limitancestorcount=" + str(2 * chainlimit)]) | self.start_node(0, extra_args=extra_args) | ||||
# wait until the wallet has submitted all transactions to the mempool | # wait until the wallet has submitted all transactions to the mempool | ||||
wait_until( | wait_until( | ||||
lambda: len( | lambda: len( | ||||
self.nodes[0].getrawmempool()) == chainlimit * | self.nodes[0].getrawmempool()) == chainlimit * | ||||
2) | 2) | ||||
node0_balance = self.nodes[0].getbalance() | node0_balance = self.nodes[0].getbalance() | ||||
▲ Show 20 Lines • Show All 94 Lines • Show Last 20 Lines |