Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_basic.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2019 The Bitcoin Core developers | # Copyright (c) 2014-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test the wallet.""" | """Test the wallet.""" | ||||
from decimal import Decimal | from decimal import Decimal | ||||
from test_framework.messages import CTransaction, FromHex | from test_framework.messages import CTransaction, FromHex | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_array_result, | assert_array_result, | ||||
assert_equal, | assert_equal, | ||||
assert_fee_amount, | assert_fee_amount, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
connect_nodes, | connect_nodes, | ||||
count_bytes, | count_bytes, | ||||
satoshi_round, | |||||
) | ) | ||||
from test_framework.wallet_util import test_address | from test_framework.wallet_util import test_address | ||||
class WalletTest(BitcoinTestFramework): | class WalletTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 4 | self.num_nodes = 4 | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
▲ Show 20 Lines • Show All 340 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 58 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# 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 | ||||
rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], { | amount = satoshi_round(node0_balance / 2 - Decimal('10000')) | ||||
chain_addrs[0]: node0_balance / 2 - Decimal('10000'), chain_addrs[1]: node0_balance / 2 - Decimal('10000')}) | rawtx = self.nodes[0].createrawtransaction( | ||||
[{"txid": singletxid, "vout": 0}], | |||||
{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 | ||||
self.wait_until( | self.wait_until( | ||||
lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2) | lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2) | ||||
# Prevent potential race condition when calling wallet RPCs right after | # Prevent potential race condition when calling wallet RPCs right after | ||||
# restart | # restart | ||||
self.nodes[0].syncwithvalidationinterfacequeue() | self.nodes[0].syncwithvalidationinterfacequeue() | ||||
▲ Show 20 Lines • Show All 96 Lines • Show Last 20 Lines |