def test_getpeerinfo(self): self.log.info("Test getpeerinfo") # Create a few getpeerinfo last_block/last_transaction values. if self.is_wallet_compiled(): self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) self.nodes[1].generate(1) self.sync_all() time_now = int(time.time()) peer_info = [x.getpeerinfo() for x in self.nodes] # Verify last_block and last_transaction keys/values. for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']): assert field in peer_info[node][peer].keys() if peer_info[node][peer][field] != 0: assert_approx(peer_info[node][peer][field], time_now, vspan=60) # check both sides of bidirectional connection between nodes # the address bound to on one side will be the source address for the other node assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500")) assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000")) # check the `servicesnames` field for info in peer_info: assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"]) assert_equal(peer_info[0][0]['connection_type'], 'inbound') assert_equal(peer_info[0][1]['connection_type'], 'manual') assert_equal(peer_info[1][0]['connection_type'], 'manual') assert_equal(peer_info[1][1]['connection_type'], 'inbound')
def test_getpeerinfo(self): self.log.info("Test getpeerinfo") # Create a few getpeerinfo last_block/last_transaction values. self.wallet.send_self_transfer( from_node=self.nodes[0] ) # Make a transaction so we can see it in the getpeerinfo results self.generate(self.nodes[1], 1) time_now = int(time.time()) peer_info = [x.getpeerinfo() for x in self.nodes] # Verify last_block and last_transaction keys/values. for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']): assert field in peer_info[node][peer].keys() if peer_info[node][peer][field] != 0: assert_approx(peer_info[node][peer][field], time_now, vspan=60) # check both sides of bidirectional connection between nodes # the address bound to on one side will be the source address for the other node assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500")) assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000")) # check the `servicesnames` field for info in peer_info: assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"]) assert_equal(peer_info[0][0]['connection_type'], 'inbound') assert_equal(peer_info[0][1]['connection_type'], 'manual') assert_equal(peer_info[1][0]['connection_type'], 'manual') assert_equal(peer_info[1][1]['connection_type'], 'inbound') # Check dynamically generated networks list in getpeerinfo help output. assert "(ipv4, ipv6, onion, i2p, cjdns, not_publicly_routable)" in self.nodes[ 0].help("getpeerinfo")
def _check_psbt(psbt, to, value, multisig): """Helper function for any of the N participants to check the psbt with decodepsbt and verify it is OK before signing.""" tx = multisig.decodepsbt(psbt)["tx"] amount = 0 for vout in tx["vout"]: address = vout["scriptPubKey"]["address"] assert_equal(multisig.getaddressinfo(address)["ischange"], address != to) if address == to: amount += vout["value"] assert_approx(amount, float(value), vspan=0.001)
def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None): '''Make assertions about a node's unspent output statistics''' stats = count_unspent(node) if total_count is not None: assert_equal(stats["total"]["count"], total_count) if total_sum is not None: assert_approx(stats["total"]["sum"], total_sum, 0.001) if reused_supported is not None: assert_equal(stats["reused"]["supported"], reused_supported) if reused_count is not None: assert_equal(stats["reused"]["count"], reused_count) if reused_sum is not None: assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
def test_sending_from_reused_address_without_avoid_reuse(self): ''' Test the same as test_sending_from_reused_address_fails, except send the 10 ECC with the avoid_reuse flag set to false. This means the 10 ECC send should succeed, where it fails in test_sending_from_reused_address_fails. ''' self.log.info("Test sending from reused address with avoid_reuse=false") fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 ecc output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 ecc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) # node 0 should not show a used entry, as it does not enable avoid_reuse assert("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 ecc output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 ecc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 ecc), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 ecc trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False) # listunspent should show 1 total outputs (5 ecc), unused assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0) # getbalances should show no used, 5 ecc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # node 1 should now have about 5 ecc left (for both cases) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001)
def test_fund_send_fund_send(self): ''' Test the simple case where [1] generates a new address A, then [0] sends 10 DIAZ to A. [1] spends 5 DIAZ from A. (leaving roughly 5 DIAZ useable) [0] sends 10 DIAZ to A again. [1] tries to spend 10 DIAZ (fails; dirty). [1] tries to spend 4 DIAZ (succeeds; change address sufficient) ''' self.log.info("Test fund send fund send") fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 diaz output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 diaz trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 diaz output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 diaz trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 diaz), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 diaz trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) self.nodes[1].sendtoaddress(retaddr, 4) # listunspent should show 2 total outputs (1, 10 diaz), one unused (1), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) # getbalances should show 10 used, 1 diaz trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 diaz left (no dirty) and 11 (including dirty) assert_approx(self.nodes[1].getbalance(), 1, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
def test_getpeerinfo(self): self.log.info("Test getpeerinfo") # Create a few getpeerinfo last_block/last_transaction/last_proof # values. if self.is_wallet_compiled(): self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1000000) tip = self.nodes[1].generate(1)[0] self.sync_all() stake = create_coinbase_stakes( self.nodes[1], [tip], self.nodes[1].get_deterministic_priv_key().key) privkey = ECKey() privkey.generate() proof = self.nodes[1].buildavalancheproof( 42, 2000000000, bytes_to_wif(privkey.get_bytes()), stake) self.nodes[1].sendavalancheproof(proof) self.sync_proofs() time_now = int(time.time()) peer_info = [x.getpeerinfo() for x in self.nodes] # Verify last_block, last_transaction and last_proof keys/values. for node, peer, field in product( range(self.num_nodes), range(2), ['last_block', 'last_transaction', 'last_proof']): assert field in peer_info[node][peer].keys() if peer_info[node][peer][field] != 0: assert_approx(peer_info[node][peer][field], time_now, vspan=60) # check both sides of bidirectional connection between nodes # the address bound to on one side will be the source address for the # other node assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) assert_equal(peer_info[0][0]['minfeefilter'], Decimal("5.00")) assert_equal(peer_info[1][0]['minfeefilter'], Decimal("10.00")) # check the `servicesnames` field for info in peer_info: assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"]) assert_equal(peer_info[0][0]['connection_type'], 'inbound') assert_equal(peer_info[0][1]['connection_type'], 'manual') assert_equal(peer_info[1][0]['connection_type'], 'manual') assert_equal(peer_info[1][1]['connection_type'], 'inbound')
def run_test(self): """Main test logic""" assert_equal(self.nodes[0].getbalance(), 0) self.log.info("Improrting priv keys") self.nodes[0].importprivkey("cU1QaDgufU6FH24feZJ3f7RToVVbriuw7cZ91427mgbVA4Sk1kAi") self.nodes[0].importprivkey("cUNUxiqMtffurfGu9BKocHTvvnujynZh1Yrc4CVhgTBeQJfC9MqU") self.nodes[0].importprivkey("cTD9hmreLGnNPBPTz4TY4YbKCXZXfC6cyEXhcZTCzzycVmWwS643") expectedAmount = 30 remaining = 1 assert_equal(self.nodes[0].getbalance(), expectedAmount) node2addr = self.nodes[1].getnewaddress() self.log.info("attempt to spend coinbase in genesis block") amount = expectedAmount - remaining txid = self.nodes[0].sendtoaddress(node2addr, amount) self.log.info("node0 sent {} to node1 in tx={}".format(amount, txid)) self.sync_mempools() confirmations = 10 self.nodes[1].generate(nblocks=confirmations) self.sync_all() try: self.nodes[1].getrawtransaction(txid) except: raise Exception("node1 does not know about tx={}. it's a sign that coinbase tx can not be spent.".format(txid)) assert_approx(float(self.nodes[0].getbalance()), remaining, vspan=0.001) balance = self.nodes[1].getbalance() txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), balance - remaining) self.nodes[1].generate(nblocks=confirmations) assert_approx(float(self.nodes[0].getbalance()), float((balance - remaining) + remaining), vspan=0.001) self.log.info("Trying to getrawtransaction on coinbase in genesis block") genesishash = self.nodes[0].getblockhash(0) genesis = self.nodes[0].getblock(genesishash) try: coinbase = self.nodes[0].getrawtransaction(genesis['tx'][0]) self.log.info("Successfully fetched genesis coinbase tx with getrawtransaction") except Exception as e: self.log.error('Can not fetch genesis coinbase tx with getrawtransaction: {}'.format(e)) raise
def run_test(self): address_0 = self.nodes[0].getaccountaddress('') address_1 = self.nodes[1].getaccountaddress('') self.log.info('Address 0: ' + str(address_0)) self.log.info('Address 1: ' + str(address_1)) self.mine_blocks(0, 30) assert_equal(self.nodes[0].getblockcount(), 30) assert_equal(self.nodes[1].getblockcount(), 30) balance_0 = float(self.nodes[0].getbalance()) balance_1 = float(self.nodes[1].getbalance()) assert_approx(balance_0, 95.064278) # 3.8025705377 * 5 assert_equal(balance_1, Decimal('0.0')) self.log.info('Balances after initial mining') self.log.info('Balance node 0: ' + str(balance_0)) self.log.info('Balance node 1: ' + str(balance_1)) self.log_accounts("after 10") transaction_id = self.nodes[0].sendtoaddress(address_1, 2) tx_details = self.nodes[0].gettransaction(transaction_id) self.log.info(str(tx_details)) assert_equal(tx_details['vout'][1]['value'], Decimal('2')) assert_equal(tx_details['vout'][1]['scriptPubKey']['addresses'][0], address_1) assert_equal(tx_details['confirmations'], Decimal('0')) self.mine_blocks(0, 10) assert_equal(self.nodes[0].getblockcount(), 40) balance_0 = self.nodes[0].getbalance() balance_1 = self.nodes[1].getbalance() assert_equal(balance_1, Decimal('2')) node_0_accounts = self.nodes[0].listaccounts() node_1_accounts = self.nodes[1].listaccounts() assert_equal(node_0_accounts[''], balance_0) assert_equal(node_1_accounts[''], balance_1) tx_details = self.nodes[0].gettransaction(transaction_id) self.log.info(str(tx_details)) assert_equal(tx_details['confirmations'], Decimal('10'))
def test_setfeerate(self, rbf_node, dest_address): self.log.info("Test setfeerate") def test_response(*, wallet="RBF wallet", requested=0, expected=0, error=None, msg): assert_equal(rbf_node.setfeerate(requested), {"wallet_name": wallet, "fee_rate": expected, ("error" if error else "result"): msg}) # Test setfeerate with too high/low values returns expected errors new = Decimal("10000.001") test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.") new = Decimal("0.999") test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.") fee_rate = Decimal("2.001") test_response(requested=fee_rate, expected=fee_rate, msg=f"Fee rate for transactions with this wallet successfully set to {fee_rate} sat/vB") new = Decimal("1.999") test_response(requested=new, expected=fee_rate, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be less than the wallet min fee rate of 2.000 sat/vB. The current setting of {fee_rate} sat/vB for this wallet remains unchanged.") # Test setfeerate with valid values returns expected results rbfid = spend_one_input(rbf_node, dest_address) fee_rate = 25 test_response(requested=fee_rate, expected=fee_rate, msg="Fee rate for transactions with this wallet successfully set to 25.000 sat/vB") bumped_tx = rbf_node.bumpfee(rbfid) bumped_txdetails = rbf_node.getrawtransaction(bumped_tx["txid"], True) allow_for_bytes_offset = len(bumped_txdetails['vout']) * 2 # potentially up to 2 bytes per output actual_fee = bumped_tx["fee"] * COIN assert_approx(actual_fee, fee_rate * bumped_txdetails['vsize'], fee_rate * allow_for_bytes_offset) test_response(msg="Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used.") # Test setfeerate with a different -maxtxfee self.restart_node(1, ["-maxtxfee=0.000025"] + self.extra_args[1]) new = "2.501" test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be greater than the wallet max fee rate of 2.500 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.") self.restart_node(1, self.extra_args[1]) rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) self.connect_nodes(1, 0) self.clear_mempool()
def mine_blocks(self, nodeId, numberOfBlocks, blockvalue, moneysupplyAtEnd, difficulty): timeBetweenBlocks = 120 for _ in range(numberOfBlocks): self.log.info("Mining block " + str(self.blocks_mined + 1)) self.setmocktimeforallnodes(self.mocktime) self.mocktime = self.mocktime + timeBetweenBlocks self.nodes[nodeId].generate(1) self.blocks_mined += 1 info = self.nodes[nodeId].getinfo() self.log.info("moneysupply " + str(info['moneysupply'])) mininginfo = self.nodes[nodeId].getmininginfo() assert_equal( mininginfo['blockvalue'], blockvalue) # use blockvalue instead of powreward for checking assert_approx(mininginfo['difficulty']['proof-of-work'], difficulty, vspan=1E-12) self.log.info( str(self.blocks_mined) + " Difficulty " + str(mininginfo['difficulty']['proof-of-work'])) info = self.nodes[nodeId].getinfo() assert_equal(int(info['moneysupply'] * 1000000), moneysupplyAtEnd) self.sync_all()
def test_option_feerate(self): self.log.info( "Test fundrawtxn with explicit fee rates (fee_rate sat/vB and feeRate BTC/kvB)" ) node = self.nodes[3] # Make sure there is exactly one input so coin selection can't skew the result. assert_equal(len(self.nodes[3].listunspent(1)), 1) inputs = [] outputs = {node.getnewaddress(): 1} rawtx = node.createrawtransaction(inputs, outputs) result = node.fundrawtransaction( rawtx) # uses self.min_relay_tx_fee (set by settxfee) btc_kvb_to_sat_vb = 100000 # (1e5) result1 = node.fundrawtransaction( rawtx, {"fee_rate": 2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee}) result2 = node.fundrawtransaction( rawtx, {"feeRate": 2 * self.min_relay_tx_fee}) result3 = node.fundrawtransaction( rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee}) result4 = node.fundrawtransaction( rawtx, {"feeRate": 10 * self.min_relay_tx_fee}) result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex']) assert_fee_amount(result1['fee'], count_bytes(result2['hex']), 2 * result_fee_rate) assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate) assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate) assert_fee_amount(result4['fee'], count_bytes(result3['hex']), 10 * result_fee_rate) # With no arguments passed, expect fee of 141 satoshis. assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001) # Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified. result = node.fundrawtransaction(rawtx, {"fee_rate": 10000}) assert_approx(result["fee"], vexp=0.0141, vspan=0.0001) self.log.info("Test fundrawtxn with invalid estimate_mode settings") for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): assert_raises_rpc_error( -3, "Expected type string for estimate_mode, got {}".format(k), node.fundrawtransaction, rawtx, { "estimate_mode": v, "conf_target": 0.1, "add_inputs": True }) for mode in ["", "foo", Decimal("3.141592")]: assert_raises_rpc_error( -8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', node.fundrawtransaction, rawtx, { "estimate_mode": mode, "conf_target": 0.1, "add_inputs": True }) self.log.info("Test fundrawtxn with invalid conf_target settings") for mode in ["unset", "economical", "conservative"]: self.log.debug("{}".format(mode)) for k, v in {"string": "", "object": {"foo": "bar"}}.items(): assert_raises_rpc_error( -3, "Expected type number for conf_target, got {}".format(k), node.fundrawtransaction, rawtx, { "estimate_mode": mode, "conf_target": v, "add_inputs": True }) for n in [-1, 0, 1009]: assert_raises_rpc_error( -8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h node.fundrawtransaction, rawtx, { "estimate_mode": mode, "conf_target": n, "add_inputs": True }) self.log.info("Test invalid fee rate settings") assert_raises_rpc_error( -8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)", node.fundrawtransaction, rawtx, { "fee_rate": 0, "add_inputs": True }) assert_raises_rpc_error( -8, "Invalid feeRate 0.00000000 SUGAR/kvB (must be greater than 0)", node.fundrawtransaction, rawtx, { "feeRate": 0, "add_inputs": True }) for param, value in {("fee_rate", 100000), ("feeRate", 1.000)}: assert_raises_rpc_error( -4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", node.fundrawtransaction, rawtx, { param: value, "add_inputs": True }) assert_raises_rpc_error(-3, "Amount out of range", node.fundrawtransaction, rawtx, { "fee_rate": -1, "add_inputs": True }) assert_raises_rpc_error(-3, "Amount is not a number or string", node.fundrawtransaction, rawtx, { "fee_rate": { "foo": "bar" }, "add_inputs": True }) assert_raises_rpc_error(-3, "Invalid amount", node.fundrawtransaction, rawtx, { "fee_rate": "", "add_inputs": True }) self.log.info( "Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed" ) node.fundrawtransaction(rawtx, { "fee_rate": 0.99999999, "add_inputs": True }) node.fundrawtransaction(rawtx, { "feeRate": 0.00000999, "add_inputs": True }) self.log.info( "- raises RPC error if both feeRate and fee_rate are passed") assert_raises_rpc_error( -8, "Cannot specify both fee_rate (sat/vB) and feeRate (SUGAR/kvB)", node.fundrawtransaction, rawtx, { "fee_rate": 0.1, "feeRate": 0.1, "add_inputs": True }) self.log.info( "- raises RPC error if both feeRate and estimate_mode passed") assert_raises_rpc_error( -8, "Cannot specify both estimate_mode and feeRate", node.fundrawtransaction, rawtx, { "estimate_mode": "economical", "feeRate": 0.1, "add_inputs": True }) for param in ["feeRate", "fee_rate"]: self.log.info( "- raises RPC error if both {} and conf_target are passed". format(param)) assert_raises_rpc_error( -8, "Cannot specify both conf_target and {}. Please provide either a confirmation " "target in blocks for automatic fee estimation, or an explicit fee rate." .format(param), node.fundrawtransaction, rawtx, { param: 1, "conf_target": 1, "add_inputs": True }) self.log.info( "- raises RPC error if both fee_rate and estimate_mode are passed") assert_raises_rpc_error( -8, "Cannot specify both estimate_mode and fee_rate", node.fundrawtransaction, rawtx, { "fee_rate": 1, "estimate_mode": "economical", "add_inputs": True })
def run_test(self): self.log.info("Setting up") # Mine some coins self.generate(self.nodes[0], COINBASE_MATURITY + 1) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for _ in range(3)] addr2 = [self.nodes[2].getnewaddress() for _ in range(3)] addrs = addr1 + addr2 # Send 1 + 0.5 coin to each address [self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs] [self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs] self.generate(self.nodes[0], 1) # For each node, send 0.2 coins back to 0; # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest self.log.info( "Test sending transactions picks one UTXO group and leaves the rest" ) txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs assert_equal(1, len(tx1["vin"])) assert_equal(2, len(tx1["vout"])) # one output should be 0.2, the other should be ~0.3 v = [vout["value"] for vout in tx1["vout"]] v.sort() assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[1], vexp=0.3, vspan=0.0001) txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx2 = self.nodes[2].getrawtransaction(txid2, True) # txid2 should have 2 inputs and 2 outputs assert_equal(2, len(tx2["vin"])) assert_equal(2, len(tx2["vout"])) # one output should be 0.2, the other should be ~1.3 v = [vout["value"] for vout in tx2["vout"]] v.sort() assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[1], vexp=1.3, vspan=0.0001) self.log.info( "Test avoiding partial spends if warranted, even if avoidpartialspends is disabled" ) self.sync_all() self.generate(self.nodes[0], 1) # Nodes 1-2 now have confirmed UTXOs (letters denote destinations): # Node #1: Node #2: # - A 1.0 - D0 1.0 # - B0 1.0 - D1 0.5 # - B1 0.5 - E0 1.0 # - C0 1.0 - E1 0.5 # - C1 0.5 - F ~1.3 # - D ~0.3 assert_approx(self.nodes[1].getbalance(), vexp=4.3, vspan=0.0001) assert_approx(self.nodes[2].getbalance(), vexp=4.3, vspan=0.0001) # Sending 1.4 umk should pick one 1.0 + one more. For node #1, # this could be (A / B0 / C0) + (B1 / C1 / D). We ensure that it is # B0 + B1 or C0 + C1, because this avoids partial spends while not being # detrimental to transaction cost txid3 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.4) tx3 = self.nodes[1].getrawtransaction(txid3, True) # tx3 should have 2 inputs and 2 outputs assert_equal(2, len(tx3["vin"])) assert_equal(2, len(tx3["vout"])) # the accumulated value should be 1.5, so the outputs should be # ~0.1 and 1.4 and should come from the same destination values = [vout["value"] for vout in tx3["vout"]] values.sort() assert_approx(values[0], vexp=0.1, vspan=0.0001) assert_approx(values[1], vexp=1.4, vspan=0.0001) input_txids = [vin["txid"] for vin in tx3["vin"]] input_addrs = [ self.nodes[1].gettransaction(txid)['details'][0]['address'] for txid in input_txids ] assert_equal(input_addrs[0], input_addrs[1]) # Node 2 enforces avoidpartialspends so needs no checking here tx4_ungrouped_fee = 2820 tx4_grouped_fee = 4160 tx5_6_ungrouped_fee = 5520 tx5_6_grouped_fee = 8240 self.log.info("Test wallet option maxapsfee") addr_aps = self.nodes[3].getnewaddress() self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].sendtoaddress(addr_aps, 1.0) self.generate(self.nodes[0], 1) with self.nodes[3].assert_debug_log([ f'Fee non-grouped = {tx4_ungrouped_fee}, grouped = {tx4_grouped_fee}, using grouped' ]): txid4 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 0.1) tx4 = self.nodes[3].getrawtransaction(txid4, True) # tx4 should have 2 inputs and 2 outputs although one output would # have been enough and the transaction caused higher fees assert_equal(2, len(tx4["vin"])) assert_equal(2, len(tx4["vout"])) addr_aps2 = self.nodes[3].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps2, 1.0) for _ in range(5)] self.generate(self.nodes[0], 1) with self.nodes[3].assert_debug_log([ f'Fee non-grouped = {tx5_6_ungrouped_fee}, grouped = {tx5_6_grouped_fee}, using non-grouped' ]): txid5 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 2.95) tx5 = self.nodes[3].getrawtransaction(txid5, True) # tx5 should have 3 inputs (1.0, 1.0, 1.0) and 2 outputs assert_equal(3, len(tx5["vin"])) assert_equal(2, len(tx5["vout"])) # Test wallet option maxapsfee with node 4, which sets maxapsfee # 1 sat higher, crossing the threshold from non-grouped to grouped. self.log.info( "Test wallet option maxapsfee threshold from non-grouped to grouped" ) addr_aps3 = self.nodes[4].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)] self.generate(self.nodes[0], 1) with self.nodes[4].assert_debug_log([ f'Fee non-grouped = {tx5_6_ungrouped_fee}, grouped = {tx5_6_grouped_fee}, using grouped' ]): txid6 = self.nodes[4].sendtoaddress(self.nodes[0].getnewaddress(), 2.95) tx6 = self.nodes[4].getrawtransaction(txid6, True) # tx6 should have 5 inputs and 2 outputs assert_equal(5, len(tx6["vin"])) assert_equal(2, len(tx6["vout"])) # Empty out node2's wallet self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance(), subtractfeefromamount=True) self.sync_all() self.generate(self.nodes[0], 1) self.log.info( "Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey" ) for _ in range(5): raw_tx = self.nodes[0].createrawtransaction([{ "txid": "0" * 64, "vout": 0 }], [{ addr2[0]: 0.05 }]) tx = tx_from_hex(raw_tx) tx.vin = [] tx.vout = [tx.vout[0]] * 2000 funded_tx = self.nodes[0].fundrawtransaction(tx.serialize().hex()) signed_tx = self.nodes[0].signrawtransactionwithwallet( funded_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.generate(self.nodes[0], 1) # Check that we can create a transaction that only requires ~100 of our # utxos, without pulling in all outputs and creating a transaction that # is way too big. self.log.info( "Test creating txn that only requires ~100 of our UTXOs without pulling in all outputs" ) assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)
def run_test(self): # Mine some coins self.nodes[0].generate(110) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for i in range(3)] addr2 = [self.nodes[2].getnewaddress() for i in range(3)] addrs = addr1 + addr2 # Send 1 + 0.5 coin to each address [self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs] [self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs] self.nodes[0].generate(1) self.sync_all() # For each node, send 0.2 coins back to 0; # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs assert_equal(1, len(tx1["vin"])) assert_equal(2, len(tx1["vout"])) # one output should be 0.2, the other should be ~0.3 v = [vout["value"] for vout in tx1["vout"]] v.sort() assert_approx(v[0], 0.2) assert_approx(v[1], 0.3, 0.0001) txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx2 = self.nodes[2].getrawtransaction(txid2, True) # txid2 should have 2 inputs and 2 outputs assert_equal(2, len(tx2["vin"])) assert_equal(2, len(tx2["vout"])) # one output should be 0.2, the other should be ~1.3 v = [vout["value"] for vout in tx2["vout"]] v.sort() assert_approx(v[0], 0.2) assert_approx(v[1], 1.3, 0.0001) # Empty out node2's wallet self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance(), subtractfeefromamount=True) self.sync_all() self.nodes[0].generate(1) # Fill node2's wallet with 10000 outputs corresponding to the same # scriptPubKey for i in range(5): raw_tx = self.nodes[0].createrawtransaction([{ "txid": "0" * 64, "vout": 0 }], [{ addr2[0]: 0.05 }]) tx = FromHex(CTransaction(), raw_tx) tx.vin = [] tx.vout = [tx.vout[0]] * 2000 funded_tx = self.nodes[0].fundrawtransaction(ToHex(tx)) signed_tx = self.nodes[0].signrawtransactionwithwallet( funded_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].generate(1) self.sync_all() # Check that we can create a transaction that only requires ~100 of our # utxos, without pulling in all outputs and creating a transaction that # is way too big. assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)
def run_test(self): self.nodes[0].generate(161) # block 161 self.log.info( "Verify sigops are counted in GBT with pre-BIP141 rules before the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert tmpl['sizelimit'] == 1000000 assert 'weightlimit' not in tmpl assert tmpl['sigoplimit'] == 20000 assert tmpl['transactions'][0]['hash'] == txid assert tmpl['transactions'][0]['sigops'] == 2 assert '!segwit' not in tmpl['rules'] self.nodes[0].generate(1) # block 162 balance_presetup = self.nodes[0].getbalance() self.pubkey = [] p2sh_ids = [ ] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh wit_ids = [ ] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness for i in range(3): newaddress = self.nodes[i].getnewaddress() self.pubkey.append( self.nodes[i].getaddressinfo(newaddress)["pubkey"]) multiscript = CScript([ OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG ]) p2sh_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'p2sh-segwit')['address'] bip173_ms_addr = self.nodes[i].addmultisigaddress( 1, [self.pubkey[-1]], '', 'bech32')['address'] assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ids.append([]) wit_ids.append([]) for v in range(2): p2sh_ids[i].append([]) wit_ids[i].append([]) for i in range(5): for n in range(3): for v in range(2): wit_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) p2sh_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) self.nodes[0].generate(1) # block 163 self.sync_blocks() # Make sure all nodes recognize the transactions as theirs assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50) assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999")) assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999")) self.nodes[0].generate(260) # block 423 self.sync_blocks() self.log.info( "Verify witness txs are skipped for mining before the fork") self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) # block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) # block 425 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) # block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) # block 427 self.log.info( "Verify unsigned p2sh witness txs without a redeem script are invalid" ) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False) self.nodes[2].generate(4) # blocks 428-431 self.log.info( "Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) blockhash = self.nodes[2].generate(1)[ 0] # block 432 (first block with new rules; 432 = 144 * 3) self.sync_blocks() assert_equal(len(self.nodes[2].getrawmempool()), 0) segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"] assert_equal(len(segwit_tx_list), 5) self.log.info( "Verify default node can't accept txs with missing witness") # unsigned, no scriptsig self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V1][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False) # unsigned with redeem script self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False, witness_script(False, self.pubkey[0])) self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0])) self.log.info( "Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag" ) assert self.nodes[2].getblock( blockhash, False) != self.nodes[0].getblock(blockhash, False) assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock( blockhash, False) for tx_id in segwit_tx_list: tx = FromHex(CTransaction(), self.nodes[2].gettransaction(tx_id)["hex"]) assert self.nodes[2].getrawtransaction( tx_id, False, blockhash) != self.nodes[0].getrawtransaction( tx_id, False, blockhash) assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].getrawtransaction( tx_id, False, blockhash) assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) == tx.serialize_without_witness().hex() self.log.info( "Verify witness txs without witness data are invalid after the fork" ) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', wit_ids[NODE_2][WIT_V0][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', wit_ids[NODE_2][WIT_V1][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', p2sh_ids[NODE_2][WIT_V0][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', p2sh_ids[NODE_2][WIT_V1][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) self.log.info("Verify default node can now use witness txs") self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) # block 432 self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) # block 433 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) # block 434 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) # block 435 self.log.info( "Verify sigops are counted in GBT with BIP141 rules after the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert tmpl[ 'sizelimit'] >= 3999577 # actual maximum size is lower due to minimum mandatory non-witness data assert tmpl['weightlimit'] == 4000000 assert tmpl['sigoplimit'] == 80000 assert tmpl['transactions'][0]['txid'] == txid assert tmpl['transactions'][0]['sigops'] == 8 assert '!segwit' in tmpl['rules'] self.nodes[0].generate(1) # Mine a block to clear the gbt cache self.log.info( "Non-segwit miners are able to use GBT response after activation.") # Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) -> # tx2 (segwit input, paying to a non-segwit output) -> # tx3 (non-segwit input, paying to a non-segwit output). # tx1 is allowed to appear in the block, but no others. txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) hex_tx = self.nodes[0].gettransaction(txid)['hex'] tx = FromHex(CTransaction(), hex_tx) assert tx.wit.is_null() # This should not be a segwit input assert txid1 in self.nodes[0].getrawmempool() tx1_hex = self.nodes[0].gettransaction(txid1)['hex'] tx1 = FromHex(CTransaction(), tx1_hex) # Check that hash and wtxid are properly reported in mempool entry (txid1) assert_equal(int(self.nodes[0].getmempoolentry(txid1)["hash"], 16), tx1.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid1) assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness()) * 3 + len(tx1.serialize_with_witness())) # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) tx.vout.append( CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) assert not tx.wit.is_null() # Check that hash and wtxid are properly reported in mempool entry (txid2) assert_equal(int(self.nodes[0].getmempoolentry(txid2)["hash"], 16), tx.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid2) assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness()) * 3 + len(tx.serialize_with_witness())) # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) tx.vout.append( CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0) assert tx.wit.is_null() assert txid3 in self.nodes[0].getrawmempool() # Check that getblocktemplate includes all transactions. template = self.nodes[0].getblocktemplate({"rules": ["segwit"]}) template_txids = [t['txid'] for t in template['transactions']] assert txid1 in template_txids assert txid2 in template_txids assert txid3 in template_txids # Check that hash and wtxid are properly reported in mempool entry (txid3) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["hash"], 16), tx.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid3) assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4) assert_equal( self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness()) * 3 + len(tx.serialize_with_witness())) # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) self.log.info("Signing with all-segwit inputs reveals fee rate") addr = self.nodes[0].getnewaddress(address_type='p2sh-segwit') txid = self.nodes[0].sendtoaddress(addr, 1) tx = self.nodes[0].getrawtransaction(txid, True) n = -1 value = -1 for o in tx["vout"]: if o["scriptPubKey"]["addresses"][0] == addr: n = o["n"] value = Decimal(o["value"]) break assert n > -1 # failure means we could not find the address in the outputs despite sending to it assert_equal( value, 1 ) # failure means we got an unexpected amount of coins, despite trying to send 1 fee = Decimal("0.00010000") value_out = value - fee self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress()) raw = self.nodes[0].createrawtransaction([{ "txid": txid, "vout": n }], [{ self.nodes[0].getnewaddress(): value_out }]) signed = self.nodes[0].signrawtransactionwithwallet(raw) assert_equal(signed["complete"], True) txsize = self.nodes[0].decoderawtransaction(signed['hex'])['vsize'] exp_feerate = 1000 * fee / Decimal(txsize) assert_approx(signed["feerate"], exp_feerate, Decimal("0.00000010")) # discrepancy = 100000000 * (exp_feerate - signed["feerate"]) # assert -10 < discrepancy < 10 assert_equal(Decimal(signed["fee"]), fee) self.log.info("Verify behaviour of importaddress and listunspent") # Some public keys to be used later pubkeys = [ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97 "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66 "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ ] # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn") uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"] self.nodes[0].importprivkey( "cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR") compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"] assert not self.nodes[0].getaddressinfo( uncompressed_spendable_address[0])['iscompressed'] assert self.nodes[0].getaddressinfo( compressed_spendable_address[0])['iscompressed'] self.nodes[0].importpubkey(pubkeys[0]) compressed_solvable_address = [key_to_p2pkh(pubkeys[0])] self.nodes[0].importpubkey(pubkeys[1]) compressed_solvable_address.append(key_to_p2pkh(pubkeys[1])) self.nodes[0].importpubkey(pubkeys[2]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])] spendable_anytime = [ ] # These outputs should be seen anytime after importprivkey and addmultisigaddress spendable_after_importaddress = [ ] # These outputs should be seen after importaddress solvable_after_importaddress = [ ] # These outputs should be seen after importaddress but not spendable unsolvable_after_importaddress = [ ] # These outputs should be unsolvable after importaddress solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [ compressed_spendable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], compressed_solvable_address[1] ])['address']) # Test multisig_without_privkey # We have 2 public keys without private keys, use addmultisigaddress to add to wallet. # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address. multisig_without_privkey_address = self.nodes[0].addmultisigaddress( 2, [pubkeys[3], pubkeys[4]])['address'] script = CScript([ OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG ]) solvable_after_importaddress.append( CScript([OP_HASH160, hash160(script), OP_EQUAL])) for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with compressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with compressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with uncompressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # Multisig without private is not seen after addmultisigaddress, but seen after importaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) solvable_after_importaddress.extend( [bare, p2sh, p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress solvable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress solvable_after_importaddress.extend([bare, p2sh]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) op1 = CScript([OP_1]) op0 = CScript([OP_0]) # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V unsolvable_address_key = hex_str_to_bytes( "02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D" ) unsolvablep2pkh = CScript([ OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG ]) unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)]) p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL]) p2wshop1 = CScript([OP_0, sha256(op1)]) unsolvable_after_importaddress.append(unsolvablep2pkh) unsolvable_after_importaddress.append(unsolvablep2wshp2pkh) unsolvable_after_importaddress.append( op1) # OP_1 will be imported as script unsolvable_after_importaddress.append(p2wshop1) unseen_anytime.append( op0 ) # OP_0 will be imported as P2SH address with no script provided unsolvable_after_importaddress.append(p2shop0) spendable_txid = [] solvable_txid = [] spendable_txid.append( self.mine_and_test_listunspent(spendable_anytime, 2)) solvable_txid.append( self.mine_and_test_listunspent(solvable_anytime, 1)) self.mine_and_test_listunspent( spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0) importlist = [] for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): bare = hex_str_to_bytes(v['hex']) importlist.append(bare.hex()) importlist.append(CScript([OP_0, sha256(bare)]).hex()) else: pubkey = hex_str_to_bytes(v['pubkey']) p2pk = CScript([pubkey, OP_CHECKSIG]) p2pkh = CScript([ OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG ]) importlist.append(p2pk.hex()) importlist.append(p2pkh.hex()) importlist.append(CScript([OP_0, hash160(pubkey)]).hex()) importlist.append(CScript([OP_0, sha256(p2pk)]).hex()) importlist.append(CScript([OP_0, sha256(p2pkh)]).hex()) importlist.append(unsolvablep2pkh.hex()) importlist.append(unsolvablep2wshp2pkh.hex()) importlist.append(op1.hex()) importlist.append(p2wshop1.hex()) for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. try_rpc( -4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress( script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress( multisig_without_privkey_address) # Test multisig_without_privkey spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Repeat some tests. This time we don't add witness scripts with importaddress # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH") uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"] self.nodes[0].importprivkey( "cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw") compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"] self.nodes[0].importpubkey(pubkeys[5]) compressed_solvable_address = [key_to_p2pkh(pubkeys[5])] self.nodes[0].importpubkey(pubkeys[6]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])] unseen_anytime = [] # These outputs should never be seen solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_solvable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append(self.nodes[0].addmultisigaddress( 2, [compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) premature_witaddress = [] for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH are always spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable solvable_anytime.extend([p2wpkh, p2sh_p2wpkh]) self.mine_and_test_listunspent(spendable_anytime, 2) self.mine_and_test_listunspent(solvable_anytime, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works v1_addr = program_to_witness(1, [3, 5]) v1_tx = self.nodes[0].createrawtransaction( [getutxo(spendable_txid[0])], {v1_addr: 1}) v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) # import all the private keys so solvable addresses become spendable self.nodes[0].importprivkey( "cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb") self.nodes[0].importprivkey( "cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97") self.nodes[0].importprivkey( "91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV") self.nodes[0].importprivkey( "cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd") self.nodes[0].importprivkey( "cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66") self.nodes[0].importprivkey( "cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.create_and_mine_tx_from_txids(solvable_txid) # Test that importing native P2WPKH/P2WSH scripts works for use_p2wsh in [False, True]: if use_p2wsh: scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" else: scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" self.nodes[1].importaddress(scriptPubKey, "", False) rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] rawtxfund = self.nodes[1].signrawtransactionwithwallet( rawtxfund)["hex"] txid = self.nodes[1].sendrawtransaction(rawtxfund) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) # Assert it is properly saved self.stop_node(1) self.start_node(1) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def run_test(self): # Create and fund a raw tx for sending 10 BTC psbtx1 = self.nodes[0].walletcreatefundedpsbt( [], {self.nodes[2].getnewaddress(): 10})['psbt'] # If inputs are specified, do not automatically add more: utxo1 = self.nodes[0].listunspent()[0] assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[0].walletcreatefundedpsbt, [{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 90}) psbtx1 = self.nodes[0].walletcreatefundedpsbt( [{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 90}, 0, {"add_inputs": True})['psbt'] assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2) # Inputs argument can be null self.nodes[0].walletcreatefundedpsbt( None, {self.nodes[2].getnewaddress(): 10}) # Node 1 should not be able to add anything to it but still return the psbtx same as before psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt'] assert_equal(psbtx1, psbtx) # Sign the transaction and send signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt'] final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] self.nodes[0].sendrawtransaction(final_tx) # Manually selected inputs can be locked: assert_equal(len(self.nodes[0].listlockunspent()), 0) utxo1 = self.nodes[0].listunspent()[0] psbtx1 = self.nodes[0].walletcreatefundedpsbt( [{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 1}, 0, {"lockUnspents": True})["psbt"] assert_equal(len(self.nodes[0].listlockunspent()), 1) # Locks are ignored for manually selected inputs self.nodes[0].walletcreatefundedpsbt([{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 1}, 0) # Create p2sh, p2wpkh, and p2wsh addresses pubkey0 = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['pubkey'] pubkey1 = self.nodes[1].getaddressinfo( self.nodes[1].getnewaddress())['pubkey'] pubkey2 = self.nodes[2].getaddressinfo( self.nodes[2].getnewaddress())['pubkey'] # Setup watchonly wallets self.nodes[2].createwallet(wallet_name='wmulti', disable_private_keys=True) wmulti = self.nodes[2].get_wallet_rpc('wmulti') # Create all the addresses p2sh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "legacy")['address'] p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "bech32")['address'] p2sh_p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "p2sh-segwit")['address'] if not self.options.descriptors: wmulti.importaddress(p2sh) wmulti.importaddress(p2wsh) wmulti.importaddress(p2sh_p2wsh) p2wpkh = self.nodes[1].getnewaddress("", "bech32") p2pkh = self.nodes[1].getnewaddress("", "legacy") p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit") # fund those addresses rawtx = self.nodes[0].createrawtransaction( [], { p2sh: 10, p2wsh: 10, p2wpkh: 10, p2sh_p2wsh: 10, p2sh_p2wpkh: 10, p2pkh: 10 }) rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition": 3}) signed_tx = self.nodes[0].signrawtransactionwithwallet( rawtx['hex'])['hex'] txid = self.nodes[0].sendrawtransaction(signed_tx) self.nodes[0].generate(6) self.sync_all() # Find the output pos p2sh_pos = -1 p2wsh_pos = -1 p2wpkh_pos = -1 p2pkh_pos = -1 p2sh_p2wsh_pos = -1 p2sh_p2wpkh_pos = -1 decoded = self.nodes[0].decoderawtransaction(signed_tx) for out in decoded['vout']: if out['scriptPubKey']['address'] == p2sh: p2sh_pos = out['n'] elif out['scriptPubKey']['address'] == p2wsh: p2wsh_pos = out['n'] elif out['scriptPubKey']['address'] == p2wpkh: p2wpkh_pos = out['n'] elif out['scriptPubKey']['address'] == p2sh_p2wsh: p2sh_p2wsh_pos = out['n'] elif out['scriptPubKey']['address'] == p2sh_p2wpkh: p2sh_p2wpkh_pos = out['n'] elif out['scriptPubKey']['address'] == p2pkh: p2pkh_pos = out['n'] inputs = [{ "txid": txid, "vout": p2wpkh_pos }, { "txid": txid, "vout": p2sh_p2wpkh_pos }, { "txid": txid, "vout": p2pkh_pos }] outputs = [{self.nodes[1].getnewaddress(): 29.99}] # spend single key from node 1 created_psbt = self.nodes[1].walletcreatefundedpsbt(inputs, outputs) walletprocesspsbt_out = self.nodes[1].walletprocesspsbt( created_psbt['psbt']) # Make sure it has both types of UTXOs decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out['psbt']) assert 'non_witness_utxo' in decoded['inputs'][0] assert 'witness_utxo' in decoded['inputs'][0] # Check decodepsbt fee calculation (input values shall only be counted once per UTXO) assert_equal(decoded['fee'], created_psbt['fee']) assert_equal(walletprocesspsbt_out['complete'], True) self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt( walletprocesspsbt_out['psbt'])['hex']) self.log.info( "Test walletcreatefundedpsbt fee rate of 10000 sat/vB and 0.1 BTC/kvB produces a total fee at or slightly below -maxtxfee (~0.05290000)" ) res1 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, { "fee_rate": 10000, "add_inputs": True }) assert_approx(res1["fee"], 0.055, 0.005) res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, { "feeRate": "0.1", "add_inputs": True }) assert_approx(res2["fee"], 0.055, 0.005) self.log.info( "Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed" ) res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, { "fee_rate": "0.999", "add_inputs": True }) assert_approx(res3["fee"], 0.00000381, 0.0000001) res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, { "feeRate": 0.00000999, "add_inputs": True }) assert_approx(res4["fee"], 0.00000381, 0.0000001) self.log.info( "Test min fee rate checks with walletcreatefundedpsbt are bypassed and that funding non-standard 'zero-fee' transactions is valid" ) for param, zero_value in product( ["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]): assert_equal( 0, self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, { param: zero_value, "add_inputs": True })["fee"]) self.log.info("Test invalid fee rate settings") for param, value in {("fee_rate", 100000), ("feeRate", 1)}: assert_raises_rpc_error( -4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { param: value, "add_inputs": True }) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { param: -1, "add_inputs": True }) assert_raises_rpc_error(-3, "Amount is not a number or string", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { param: { "foo": "bar" }, "add_inputs": True }) # Test fee rate values that don't pass fixed-point parsing checks. for invalid_value in [ "", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999" ]: assert_raises_rpc_error(-3, "Invalid amount", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { param: invalid_value, "add_inputs": True }) # Test fee_rate values that cannot be represented in sat/vB. for invalid_value in [ 0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999" ]: assert_raises_rpc_error(-3, "Invalid amount", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "fee_rate": invalid_value, "add_inputs": True }) self.log.info( "- raises RPC error if both feeRate and fee_rate are passed") assert_raises_rpc_error( -8, "Cannot specify both fee_rate (rie/vB) and feeRate (RIC/kvB)", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "fee_rate": 0.1, "feeRate": 0.1, "add_inputs": True }) self.log.info( "- raises RPC error if both feeRate and estimate_mode passed") assert_raises_rpc_error( -8, "Cannot specify both estimate_mode and feeRate", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "estimate_mode": "economical", "feeRate": 0.1, "add_inputs": True }) for param in ["feeRate", "fee_rate"]: self.log.info( "- raises RPC error if both {} and conf_target are passed". format(param)) assert_raises_rpc_error( -8, "Cannot specify both conf_target and {}. Please provide either a confirmation " "target in blocks for automatic fee estimation, or an explicit fee rate." .format(param), self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { param: 1, "conf_target": 1, "add_inputs": True }) self.log.info( "- raises RPC error if both fee_rate and estimate_mode are passed") assert_raises_rpc_error( -8, "Cannot specify both estimate_mode and fee_rate", self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "fee_rate": 1, "estimate_mode": "economical", "add_inputs": True }) self.log.info("- raises RPC error with invalid estimate_mode settings") for k, v in {"number": 42, "object": {"foo": "bar"}}.items(): assert_raises_rpc_error( -3, "Expected type string for estimate_mode, got {}".format(k), self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "estimate_mode": v, "conf_target": 0.1, "add_inputs": True }) for mode in ["", "foo", Decimal("3.141592")]: assert_raises_rpc_error( -8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "estimate_mode": mode, "conf_target": 0.1, "add_inputs": True }) self.log.info("- raises RPC error with invalid conf_target settings") for mode in ["unset", "economical", "conservative"]: self.log.debug("{}".format(mode)) for k, v in {"string": "", "object": {"foo": "bar"}}.items(): assert_raises_rpc_error( -3, "Expected type number for conf_target, got {}".format(k), self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "estimate_mode": mode, "conf_target": v, "add_inputs": True }) for n in [-1, 0, 1009]: assert_raises_rpc_error( -8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, { "estimate_mode": mode, "conf_target": n, "add_inputs": True }) self.log.info( "Test walletcreatefundedpsbt with too-high fee rate produces total fee well above -maxtxfee and raises RPC error" ) # previously this was silently capped at -maxtxfee for bool_add, outputs_array in { True: outputs, False: [{ self.nodes[1].getnewaddress(): 1 }] }.items(): msg = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)" assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, { "fee_rate": 1000000, "add_inputs": bool_add }) assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, { "feeRate": 1, "add_inputs": bool_add }) self.log.info("Test various PSBT operations") # partially sign multisig things with node 1 psbtx = wmulti.walletcreatefundedpsbt( inputs=[{ "txid": txid, "vout": p2wsh_pos }, { "txid": txid, "vout": p2sh_pos }, { "txid": txid, "vout": p2sh_p2wsh_pos }], outputs={self.nodes[1].getnewaddress(): 29.99}, options={'changeAddress': self.nodes[1].getrawchangeaddress()})['psbt'] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx) psbtx = walletprocesspsbt_out['psbt'] assert_equal(walletprocesspsbt_out['complete'], False) # Unload wmulti, we don't need it anymore wmulti.unloadwallet() # partially sign with node 2. This should be complete and sendable walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx) assert_equal(walletprocesspsbt_out['complete'], True) self.nodes[2].sendrawtransaction(self.nodes[2].finalizepsbt( walletprocesspsbt_out['psbt'])['hex']) # check that walletprocesspsbt fails to decode a non-psbt rawtx = self.nodes[1].createrawtransaction( [{ "txid": txid, "vout": p2wpkh_pos }], {self.nodes[1].getnewaddress(): 9.99}) assert_raises_rpc_error(-22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx) # Convert a non-psbt to psbt and make sure we can decode it rawtx = self.nodes[0].createrawtransaction( [], {self.nodes[1].getnewaddress(): 10}) rawtx = self.nodes[0].fundrawtransaction(rawtx) new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) self.nodes[0].decodepsbt(new_psbt) # Make sure that a non-psbt with signatures cannot be converted # Error could be either "TX decode failed" (segwit inputs causes parsing to fail) or "Inputs must not have scriptSigs and scriptWitnesses" # We must set iswitness=True because the serialized transaction has inputs and is therefore a witness transaction signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex']) assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], iswitness=True) assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False, iswitness=True) # Unless we allow it to convert and strip signatures self.nodes[0].converttopsbt(signedtx['hex'], True) # Explicitly allow converting non-empty txs new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) self.nodes[0].decodepsbt(new_psbt) # Create outputs to nodes 1 and 2 node1_addr = self.nodes[1].getnewaddress() node2_addr = self.nodes[2].getnewaddress() txid1 = self.nodes[0].sendtoaddress(node1_addr, 13) txid2 = self.nodes[0].sendtoaddress(node2_addr, 13) blockhash = self.nodes[0].generate(6)[0] self.sync_all() vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash) vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash) # Create a psbt spending outputs from nodes 1 and 2 psbt_orig = self.nodes[0].createpsbt( [{ "txid": txid1, "vout": vout1 }, { "txid": txid2, "vout": vout2 }], {self.nodes[0].getnewaddress(): 25.999}) # Update psbts, should only have data for one input and not the other psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt'] psbt1_decoded = self.nodes[0].decodepsbt(psbt1) assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1] # Check that BIP32 path was added assert "bip32_derivs" in psbt1_decoded['inputs'][0] psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt'] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1] # Check that BIP32 paths were not added assert "bip32_derivs" not in psbt2_decoded['inputs'][1] # Sign PSBTs (workaround issue #18039) psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt'] psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt'] # Combine, finalize, and send the psbts combined = self.nodes[0].combinepsbt([psbt1, psbt2]) finalized = self.nodes[0].finalizepsbt(combined)['hex'] self.nodes[0].sendrawtransaction(finalized) self.nodes[0].generate(6) self.sync_all() # Test additional args in walletcreatepsbt # Make sure both pre-included and funded inputs # have the correct sequence numbers based on # replaceable arg block_height = self.nodes[0].getblockcount() unspent = self.nodes[0].listunspent()[0] psbtx_info = self.nodes[0].walletcreatefundedpsbt( [{ "txid": unspent["txid"], "vout": unspent["vout"] }], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], block_height + 2, { "replaceable": False, "add_inputs": True }, False) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE) assert "bip32_derivs" not in psbt_in assert_equal(decoded_psbt["tx"]["locktime"], block_height + 2) # Same construction with only locktime set and RBF explicitly enabled psbtx_info = self.nodes[0].walletcreatefundedpsbt( [{ "txid": unspent["txid"], "vout": unspent["vout"] }], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], block_height, { "replaceable": True, "add_inputs": True }, True) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE) assert "bip32_derivs" in psbt_in assert_equal(decoded_psbt["tx"]["locktime"], block_height) # Same construction without optional arguments psbtx_info = self.nodes[0].walletcreatefundedpsbt( [], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }]) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE) assert "bip32_derivs" in psbt_in assert_equal(decoded_psbt["tx"]["locktime"], 0) # Same construction without optional arguments, for a node with -walletrbf=0 unspent1 = self.nodes[1].listunspent()[0] psbtx_info = self.nodes[1].walletcreatefundedpsbt( [{ "txid": unspent1["txid"], "vout": unspent1["vout"] }], [{ self.nodes[2].getnewaddress(): unspent1["amount"] + 1 }], block_height, {"add_inputs": True}) decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"]) for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE) assert "bip32_derivs" in psbt_in # Make sure change address wallet does not have P2SH innerscript access to results in success # when attempting BnB coin selection self.nodes[0].walletcreatefundedpsbt( [], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], block_height + 2, {"changeAddress": self.nodes[1].getnewaddress()}, False) # Make sure the wallet's change type is respected by default small_output = {self.nodes[0].getnewaddress(): 0.1} psbtx_native = self.nodes[0].walletcreatefundedpsbt([], [small_output]) self.assert_change_type(psbtx_native, "witness_v0_keyhash") psbtx_legacy = self.nodes[1].walletcreatefundedpsbt([], [small_output]) self.assert_change_type(psbtx_legacy, "pubkeyhash") # Make sure the change type of the wallet can also be overwritten psbtx_np2wkh = self.nodes[1].walletcreatefundedpsbt( [], [small_output], 0, {"change_type": "p2sh-segwit"}) self.assert_change_type(psbtx_np2wkh, "scripthash") # Make sure the change type cannot be specified if a change address is given invalid_options = { "change_type": "legacy", "changeAddress": self.nodes[0].getnewaddress() } assert_raises_rpc_error( -8, "both change address and address type options", self.nodes[0].walletcreatefundedpsbt, [], [small_output], 0, invalid_options) # Regression test for 14473 (mishandling of already-signed witness transaction): psbtx_info = self.nodes[0].walletcreatefundedpsbt( [{ "txid": unspent["txid"], "vout": unspent["vout"] }], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], 0, {"add_inputs": True}) complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"]) double_processed_psbt = self.nodes[0].walletprocesspsbt( complete_psbt["psbt"]) assert_equal(complete_psbt, double_processed_psbt) # We don't care about the decode result, but decoding must succeed. self.nodes[0].decodepsbt(double_processed_psbt["psbt"]) # Make sure unsafe inputs are included if specified self.nodes[2].createwallet(wallet_name="unsafe") wunsafe = self.nodes[2].get_wallet_rpc("unsafe") self.nodes[0].sendtoaddress(wunsafe.getnewaddress(), 2) self.sync_mempools() assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{ self.nodes[0].getnewaddress(): 1 }]) wunsafe.walletcreatefundedpsbt([], [{ self.nodes[0].getnewaddress(): 1 }], 0, {"include_unsafe": True}) # BIP 174 Test Vectors # Check that unknown values are just passed through unknown_psbt = "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)['psbt'] assert_equal(unknown_psbt, unknown_out) # Open the data file with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_psbt.json'), encoding='utf-8') as f: d = json.load(f) invalids = d['invalid'] valids = d['valid'] creators = d['creator'] signers = d['signer'] combiners = d['combiner'] finalizers = d['finalizer'] extractors = d['extractor'] # Invalid PSBTs for invalid in invalids: assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decodepsbt, invalid) # Valid PSBTs for valid in valids: self.nodes[0].decodepsbt(valid) # Creator Tests for creator in creators: created_tx = self.nodes[0].createpsbt(creator['inputs'], creator['outputs']) assert_equal(created_tx, creator['result']) # Signer tests for i, signer in enumerate(signers): self.nodes[2].createwallet(wallet_name="wallet{}".format(i)) wrpc = self.nodes[2].get_wallet_rpc("wallet{}".format(i)) for key in signer['privkeys']: wrpc.importprivkey(key) signed_tx = wrpc.walletprocesspsbt(signer['psbt'])['psbt'] assert_equal(signed_tx, signer['result']) # Combiner test for combiner in combiners: combined = self.nodes[2].combinepsbt(combiner['combine']) assert_equal(combined, combiner['result']) # Empty combiner test assert_raises_rpc_error(-8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, []) # Finalizer test for finalizer in finalizers: finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt'] assert_equal(finalized, finalizer['result']) # Extractor test for extractor in extractors: extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex'] assert_equal(extracted, extractor['result']) # Unload extra wallets for i, signer in enumerate(signers): self.nodes[2].unloadwallet("wallet{}".format(i)) # TODO: Re-enable this for segwit v1 # self.test_utxo_conversion() # Test that psbts with p2pkh outputs are created properly p2pkh = self.nodes[0].getnewaddress(address_type='legacy') psbt = self.nodes[1].walletcreatefundedpsbt([], [{ p2pkh: 1 }], 0, {"includeWatching": True}, True) self.nodes[0].decodepsbt(psbt['psbt']) # Test decoding error: invalid base64 assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;") # Send to all types of addresses addr1 = self.nodes[1].getnewaddress("", "bech32") txid1 = self.nodes[0].sendtoaddress(addr1, 11) vout1 = find_output(self.nodes[0], txid1, 11) addr2 = self.nodes[1].getnewaddress("", "legacy") txid2 = self.nodes[0].sendtoaddress(addr2, 11) vout2 = find_output(self.nodes[0], txid2, 11) addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit") txid3 = self.nodes[0].sendtoaddress(addr3, 11) vout3 = find_output(self.nodes[0], txid3, 11) self.sync_all() def test_psbt_input_keys(psbt_input, keys): """Check that the psbt input has only the expected keys.""" assert_equal(set(keys), set(psbt_input.keys())) # Create a PSBT. None of the inputs are filled initially psbt = self.nodes[1].createpsbt([{ "txid": txid1, "vout": vout1 }, { "txid": txid2, "vout": vout2 }, { "txid": txid3, "vout": vout3 }], {self.nodes[0].getnewaddress(): 32.999}) decoded = self.nodes[1].decodepsbt(psbt) test_psbt_input_keys(decoded['inputs'][0], []) test_psbt_input_keys(decoded['inputs'][1], []) test_psbt_input_keys(decoded['inputs'][2], []) # Update a PSBT with UTXOs from the node # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness updated = self.nodes[1].utxoupdatepsbt(psbt) decoded = self.nodes[1].decodepsbt(updated) test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo']) test_psbt_input_keys(decoded['inputs'][1], []) test_psbt_input_keys(decoded['inputs'][2], []) # Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in descs = [ self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1, addr2, addr3] ] updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs) decoded = self.nodes[1].decodepsbt(updated) test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'bip32_derivs']) test_psbt_input_keys(decoded['inputs'][1], []) test_psbt_input_keys(decoded['inputs'][2], ['witness_utxo', 'bip32_derivs', 'redeem_script']) # Two PSBTs with a common input should not be joinable psbt1 = self.nodes[1].createpsbt( [{ "txid": txid1, "vout": vout1 }], {self.nodes[0].getnewaddress(): Decimal('10.999')}) assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated]) # Join two distinct PSBTs addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit") txid4 = self.nodes[0].sendtoaddress(addr4, 5) vout4 = find_output(self.nodes[0], txid4, 5) self.nodes[0].generate(6) self.sync_all() psbt2 = self.nodes[1].createpsbt( [{ "txid": txid4, "vout": vout4 }], {self.nodes[0].getnewaddress(): Decimal('4.999')}) psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt'] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) assert "final_scriptwitness" in psbt2_decoded['inputs'][ 0] and "final_scriptSig" in psbt2_decoded['inputs'][0] joined = self.nodes[0].joinpsbts([psbt, psbt2]) joined_decoded = self.nodes[0].decodepsbt(joined) assert len(joined_decoded['inputs']) == 4 and len( joined_decoded['outputs'] ) == 2 and "final_scriptwitness" not in joined_decoded['inputs'][ 3] and "final_scriptSig" not in joined_decoded['inputs'][3] # Check that joining shuffles the inputs and outputs # 10 attempts should be enough to get a shuffled join shuffled = False for _ in range(10): shuffled_joined = self.nodes[0].joinpsbts([psbt, psbt2]) shuffled |= joined != shuffled_joined if shuffled: break assert shuffled # Newly created PSBT needs UTXOs and updating addr = self.nodes[1].getnewaddress("", "p2sh-segwit") txid = self.nodes[0].sendtoaddress(addr, 7) addrinfo = self.nodes[1].getaddressinfo(addr) blockhash = self.nodes[0].generate(6)[0] self.sync_all() vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash) psbt = self.nodes[1].createpsbt( [{ "txid": txid, "vout": vout }], {self.nodes[0].getnewaddress("", "p2sh-segwit"): Decimal('6.999')}) analyzed = self.nodes[0].analyzepsbt(psbt) assert not analyzed['inputs'][0]['has_utxo'] and not analyzed[ 'inputs'][0]['is_final'] and analyzed['inputs'][0][ 'next'] == 'updater' and analyzed['next'] == 'updater' # After update with wallet, only needs signing updated = self.nodes[1].walletprocesspsbt(psbt, False, 'ALL', True)['psbt'] analyzed = self.nodes[0].analyzepsbt(updated) assert analyzed['inputs'][0][ 'has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed[ 'inputs'][0]['next'] == 'signer' and analyzed[ 'next'] == 'signer' and analyzed['inputs'][0]['missing'][ 'signatures'][0] == addrinfo['embedded'][ 'witness_program'] # Check fee and size things assert analyzed['fee'] == Decimal( '0.001') and analyzed['estimated_vsize'] == 134 and analyzed[ 'estimated_feerate'] == Decimal('0.00746268') # After signing and finalizing, needs extracting signed = self.nodes[1].walletprocesspsbt(updated)['psbt'] analyzed = self.nodes[0].analyzepsbt(signed) assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0][ 'is_final'] and analyzed['next'] == 'extractor' self.log.info( "PSBT spending unspendable outputs should have error message and Creator as next" ) analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 spends unspendable output') self.log.info( "PSBT with invalid values should have error message and Creator as next" ) analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAR8AgIFq49AeABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 has invalid value') self.log.info( "PSBT with signed, but not finalized, inputs should have Finalizer as next" ) analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAHECAAAAAZYezcxdnbXoQCmrD79t/LzDgtUo9ERqixk8wgioAobrAAAAAAD9////AlDDAAAAAAAAFgAUy/UxxZuzZswcmFnN/E9DGSiHLUsuGPUFAAAAABYAFLsH5o0R38wXx+X2cCosTMCZnQ4baAAAAAABAR8A4fUFAAAAABYAFOBI2h5thf3+Lflb2LGCsVSZwsltIgIC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnJHMEQCIGx7zKcMIGr7cEES9BR4Kdt/pzPTK3fKWcGyCJXb7MVnAiALOBgqlMH4GbC1HDh/HmylmO54fyEy4lKde7/BT/PWxwEBAwQBAAAAIgYC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnIYDwVpQ1QAAIABAACAAAAAgAAAAAAAAAAAAAAiAgL+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+xgPBWlDVAAAgAEAAIAAAACAAQAAAAAAAAAA' ) assert_equal(analysis['next'], 'finalizer') analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAXABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAR8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Output amount invalid') analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 specifies invalid prevout') assert_raises_rpc_error( -25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==' )
def run_test(self): # Mine some coins self.nodes[0].generate(110) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for _ in range(3)] addr2 = [self.nodes[2].getnewaddress() for _ in range(3)] addrs = addr1 + addr2 # Send 1 + 0.5 coin to each address [self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs] [self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs] self.nodes[0].generate(1) self.sync_all() # For each node, send 0.2 coins back to 0; # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs assert_equal(1, len(tx1["vin"])) assert_equal(2 + 1, len(tx1["vout"])) # one output should be 0.2, the other should be ~0.3 v = [ vout["value"] for vout in tx1["vout"] if vout["scriptPubKey"]["type"] != "fee" ] v.sort() assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[1], vexp=0.3, vspan=0.0001) txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx2 = self.nodes[2].getrawtransaction(txid2, True) # txid2 should have 2 inputs and 2 outputs assert_equal(2, len(tx2["vin"])) assert_equal(2 + 1, len(tx2["vout"])) # one output should be 0.2, the other should be ~1.3 v = [ vout["value"] for vout in tx2["vout"] if vout["scriptPubKey"]["type"] != "fee" ] v.sort() assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[1], vexp=1.3, vspan=0.0001) # Test 'avoid partial if warranted, even if disabled' self.sync_all() self.nodes[0].generate(1) # Nodes 1-2 now have confirmed UTXOs (letters denote destinations): # Node #1: Node #2: # - A 1.0 - D0 1.0 # - B0 1.0 - D1 0.5 # - B1 0.5 - E0 1.0 # - C0 1.0 - E1 0.5 # - C1 0.5 - F ~1.3 # - D ~0.3 assert_approx(self.nodes[1].getbalance()['bitcoin'], vexp=4.3, vspan=0.0001) assert_approx(self.nodes[2].getbalance()['bitcoin'], vexp=4.3, vspan=0.0001) # Sending 1.4 btc should pick one 1.0 + one more. For node #1, # this could be (A / B0 / C0) + (B1 / C1 / D). We ensure that it is # B0 + B1 or C0 + C1, because this avoids partial spends while not being # detrimental to transaction cost txid3 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.4) tx3 = self.nodes[1].getrawtransaction(txid3, True) # tx3 should have 2 inputs and 2 outputs assert_equal(2, len(tx3["vin"])) assert_equal(3, len(tx3["vout"])) ## ELEMENTS: 2 outputs plus fee # the accumulated value should be 1.5, so the outputs should be # ~0.1 and 1.4 and should come from the same destination values = [vout["value"] for vout in tx3["vout"]] values.sort() assert_approx(values[0], vexp=0.00006, vspan=0.00001) assert_approx(values[1], vexp=0.1, vspan=0.0001) assert_approx(values[2], vexp=1.4, vspan=0.0001) input_txids = [vin["txid"] for vin in tx3["vin"]] input_addrs = [ self.nodes[1].gettransaction(txid)['details'][0]['address'] for txid in input_txids ] assert_equal(input_addrs[0], input_addrs[1]) # Node 2 enforces avoidpartialspends so needs no checking here # Test wallet option maxapsfee with Node 3 addr_aps = self.nodes[3].getnewaddress() self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].generate(1) self.sync_all() with self.nodes[3].assert_debug_log( ['Fee non-grouped = 5140, grouped = 6520, using grouped']): txid4 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 0.1) tx4 = self.nodes[3].getrawtransaction(txid4, True) # tx4 should have 2 inputs and 2 outputs although one output would # have been enough and the transaction caused higher fees assert_equal(2, len(tx4["vin"])) assert_equal(3, len(tx4["vout"])) # ELEMENTS: plus fee addr_aps2 = self.nodes[3].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps2, 1.0) for _ in range(5)] self.nodes[0].generate(1) self.sync_all() with self.nodes[3].assert_debug_log( ['Fee non-grouped = 7880, grouped = 10620, using non-grouped']): txid5 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 2.95) tx5 = self.nodes[3].getrawtransaction(txid5, True) # tx5 should have 3 inputs (1.0, 1.0, 1.0) and 2 outputs assert_equal(3, len(tx5["vin"])) assert_equal(3, len(tx5["vout"])) # ELEMENTS: plus fee # Test wallet option maxapsfee with node 4, which sets maxapsfee # 1 sat higher, crossing the threshold from non-grouped to grouped. addr_aps3 = self.nodes[4].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)] self.nodes[0].generate(1) self.sync_all() with self.nodes[4].assert_debug_log( ['Fee non-grouped = 7880, grouped = 10620, using grouped']): txid6 = self.nodes[4].sendtoaddress(self.nodes[0].getnewaddress(), 2.95) tx6 = self.nodes[4].getrawtransaction(txid6, True) # tx6 should have 5 inputs and 2 outputs assert_equal(5, len(tx6["vin"])) assert_equal(3, len(tx6["vout"])) # ELEMENTS: plus fee # Empty out node2's wallet self.nodes[2].sendtoaddress( address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance()['bitcoin'], subtractfeefromamount=True) self.sync_all() self.nodes[0].generate(1) # Fill node2's wallet with 10000 outputs corresponding to the same # scriptPubKey for _ in range(5): raw_tx = self.nodes[0].createrawtransaction([{ "txid": "0" * 64, "vout": 0 }], [{ addr2[0]: 0.10 }]) tx = FromHex(CTransaction(), raw_tx) tx.vin = [] # ELEMENTS: lower number because outputs are bigger (otherwise tx gets too large) tx.vout = [tx.vout[0]] * 1000 funded_tx = self.nodes[0].fundrawtransaction(ToHex(tx)) signed_tx = self.nodes[0].signrawtransactionwithwallet( funded_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].generate(1) self.sync_all() # Check that we can create a transaction that only requires ~100 of our # utxos, without pulling in all outputs and creating a transaction that # is way too big. assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)
def run_test(self): # mine a few blocks and check that the balance is available after block 6 self.mine_blocks(0, 20) mining_info = self.nodes[0].getmininginfo() for i in range(5): self.log.info(">>> MINING block "+str(i+21)) self.mine_blocks(0,1) mining_info = self.nodes[0].getmininginfo() balance = self.nodes[0].getbalance() assert_equal(mining_info['blocks'], (20+i+1)) assert_equal(mining_info['currentblocksize'], 1000) assert_equal(mining_info['N'], 32) # the low difficulty is only valid because of the low difficulty flag assert_equal(mining_info['difficulty']['proof-of-work'], Decimal('0.0000000777459625')) # low nfactor is set for testing assert_equal(mining_info['Nfactor'], 4) # powreward is based on 100,000,000 initial money supply for testing assert_approx(mining_info['powreward'], 3.80257) assert_approx(balance,60.841127 + i*3.80257) # mine block 6 self.mine_blocks(0, 1) mining_info = self.nodes[0].getmininginfo() balance=self.nodes[0].getbalance() assert_equal(mining_info['blocks'], 26) assert_approx(balance,79.853986) # mine till block 9 (epoch) for i in range(3): self.mine_blocks(0, 1) mining_info = self.nodes[0].getmininginfo() assert_equal(mining_info['difficulty']['proof-of-work'], Decimal('0.0000000777459625')) assert_equal(mining_info['blocks'], 29) assert_approx(mining_info['powreward'], 3.8025705377) # mine block 10 (epoch calculation) and difficulty change self.mine_blocks(0, 1) mining_info = self.nodes[0].getmininginfo() balance=self.nodes[0].getbalance() assert_equal(mining_info['blocks'], 30) assert_equal(mining_info['difficulty']['proof-of-work'], Decimal('0.0000000777459625')) assert_approx(mining_info['powreward'], 3.8025705377) assert_approx(float(balance), 95.064278) # mine block 15 (pow reward available) self.mine_blocks(0, 5) mining_info = self.nodes[0].getmininginfo() balance=self.nodes[0].getbalance() assert_equal(mining_info['blocks'], 35) assert_equal(mining_info['difficulty']['proof-of-work'], Decimal('0.0000000777459625')) assert_approx(balance, 114.077144)
def run_test(self): node = self.nodes[0] self.generate(node, 1, sync_fun=self.no_op) # Leave IBD node.createwallet(wallet_name='w0') node.createwallet(wallet_name='w1') node.createwallet(wallet_name='w2', disable_private_keys=True) w0 = node.get_wallet_rpc('w0') w1 = node.get_wallet_rpc('w1') w2 = node.get_wallet_rpc('w2') self.generatetoaddress(node, COINBASE_MATURITY + 1, w0.getnewaddress()) assert_equal(w0.getbalance(), 50.0) assert_equal(w1.getbalance(), 0.0) address1 = w1.getnewaddress() address2 = w1.getnewaddress() # Add address1 as watch-only to w2 w2.importpubkey(pubkey=w1.getaddressinfo(address1)["pubkey"]) tx1 = node.createrawtransaction([], [{address1: 5.0}]) tx2 = node.createrawtransaction([], [{address2: 10.0}]) # w0 should be unaffected, w2 should see +5 for tx1 assert_equal(w0.simulaterawtransaction([tx1])["balance_change"], 0.0) assert_equal(w2.simulaterawtransaction([tx1])["balance_change"], 5.0) # w1 should see +5 balance for tx1 assert_equal(w1.simulaterawtransaction([tx1])["balance_change"], 5.0) # w0 should be unaffected, w2 should see +5 for both transactions assert_equal(w0.simulaterawtransaction([tx1, tx2])["balance_change"], 0.0) assert_equal(w2.simulaterawtransaction([tx1, tx2])["balance_change"], 5.0) # w1 should see +15 balance for both transactions assert_equal(w1.simulaterawtransaction([tx1, tx2])["balance_change"], 15.0) # w0 funds transaction; it should now see a decrease in (tx fee and payment), and w1 should see the same as above funding = w0.fundrawtransaction(tx1) tx1 = funding["hex"] tx1changepos = funding["changepos"] bitcoin_fee = Decimal(funding["fee"]) # w0 sees fee + 5 btc decrease, w2 sees + 5 btc assert_approx(w0.simulaterawtransaction([tx1])["balance_change"], -(Decimal("5") + bitcoin_fee)) assert_approx(w2.simulaterawtransaction([tx1])["balance_change"], Decimal("5")) # w1 sees same as before assert_equal(w1.simulaterawtransaction([tx1])["balance_change"], 5.0) # same inputs (tx) more than once should error assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w0.simulaterawtransaction, [tx1,tx1]) tx1ob = node.decoderawtransaction(tx1) tx1hex = tx1ob["txid"] tx1vout = 1 - tx1changepos # tx3 spends new w1 UTXO paying to w0 tx3 = node.createrawtransaction([{"txid": tx1hex, "vout": tx1vout}], {w0.getnewaddress(): 4.9999}) # tx4 spends new w1 UTXO paying to w1 tx4 = node.createrawtransaction([{"txid": tx1hex, "vout": tx1vout}], {w1.getnewaddress(): 4.9999}) # on their own, both should fail due to missing input(s) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w0.simulaterawtransaction, [tx3]) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w1.simulaterawtransaction, [tx3]) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w0.simulaterawtransaction, [tx4]) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w1.simulaterawtransaction, [tx4]) # they should succeed when including tx1: # wallet tx3 tx4 # w0 -5 - bitcoin_fee + 4.9999 -5 - bitcoin_fee # w1 0 +4.9999 assert_approx(w0.simulaterawtransaction([tx1, tx3])["balance_change"], -Decimal("5") - bitcoin_fee + Decimal("4.9999")) assert_approx(w1.simulaterawtransaction([tx1, tx3])["balance_change"], 0) assert_approx(w0.simulaterawtransaction([tx1, tx4])["balance_change"], -Decimal("5") - bitcoin_fee) assert_approx(w1.simulaterawtransaction([tx1, tx4])["balance_change"], Decimal("4.9999")) # they should fail if attempting to include both tx3 and tx4 assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w0.simulaterawtransaction, [tx1, tx3, tx4]) assert_raises_rpc_error(-8, "Transaction(s) are spending the same output more than once", w1.simulaterawtransaction, [tx1, tx3, tx4]) # send tx1 to avoid reusing same UTXO below node.sendrawtransaction(w0.signrawtransactionwithwallet(tx1)["hex"]) self.generate(node, 1, sync_fun=self.no_op) # Confirm tx to trigger error below self.sync_all() # w0 funds transaction 2; it should now see a decrease in (tx fee and payment), and w1 should see the same as above funding = w0.fundrawtransaction(tx2) tx2 = funding["hex"] bitcoin_fee2 = Decimal(funding["fee"]) assert_approx(w0.simulaterawtransaction([tx2])["balance_change"], -(Decimal("10") + bitcoin_fee2)) assert_approx(w1.simulaterawtransaction([tx2])["balance_change"], +(Decimal("10"))) assert_approx(w2.simulaterawtransaction([tx2])["balance_change"], 0) # w0-w2 error due to tx1 already being mined assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w0.simulaterawtransaction, [tx1, tx2]) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w1.simulaterawtransaction, [tx1, tx2]) assert_raises_rpc_error(-8, "One or more transaction inputs have been spent already", w2.simulaterawtransaction, [tx1, tx2])
def test_sending_from_reused_address_fails(self): ''' Test the simple case where [1] generates a new address A, then [0] sends 10 BCH to A. [1] spends 5 BCH from A. (leaving roughly 5 BCH useable) [0] sends 10 BCH to A again. [1] tries to spend 10 BCH (fails; dirty). [1] tries to spend 4 BCH (succeeds; change address sufficient) ''' self.log.info("Test sending from reused address fails") fundaddr = self.nodes[1].getnewaddress(label="", address_type="legacy") retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 BCH output assert_unspent( self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 BCH trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 BCH output assert_unspent( self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 BCH trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # For the second send, we transmute it to a related single-key address # to make sure it's also detected as re-use # NB: this is not very useful for ABC, but we keep the new variable # name for consistency. new_fundaddr = fundaddr self.nodes[0].sendtoaddress(new_fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 BCH), one unused (5), # one reused (10) assert_unspent( self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 BCH trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including # dirty) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) self.nodes[1].sendtoaddress(retaddr, 4) # listunspent should show 2 total outputs (1, 10 BCH), one unused (1), # one reused (10) assert_unspent( self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) # getbalances should show 10 used, 1 BCH trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 BCH left (no dirty) and 11 (including # dirty) assert_approx(self.nodes[1].getbalance(), 1, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
def test_fund_send_fund_senddirty(self): ''' Test the same as test_fund_send_fund_send, except send the 10 AUR with the avoid_reuse flag set to false. This means the 10 AUR send should succeed, where it fails in test_fund_send_fund_send. ''' fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) # node 0 should not show a used entry, as it does not enable avoid_reuse assert("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 aur), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5} self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False) # listunspent should show 1 total outputs (5 aur), unused assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # node 1 should now have about 5 aur left (for both cases) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001) def test_fund_send_fund_send(self, second_addr_type): ''' Test the simple case where [1] generates a new address A, then [0] sends 10 AUR to A. [1] spends 5 AUR from A. (leaving roughly 5 BTC useable) [0] sends 10 AUR to A again. [1] tries to spend 10 AUR (fails; dirty). [1] tries to spend 4 AUR (succeeds; change address sufficient) ''' fundaddr = self.nodes[1].getnewaddress(label="", address_type="legacy") retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # For the second send, we transmute it to a related single-key address # to make sure it's also detected as re-use fund_spk = self.nodes[0].getaddressinfo(fundaddr)["scriptPubKey"] fund_decoded = self.nodes[0].decodescript(fund_spk) if second_addr_type == "p2sh-segwit": new_fundaddr = fund_decoded["segwit"]["p2sh-segwit"] elif second_addr_type == "bech32": new_fundaddr = fund_decoded["segwit"]["addresses"][0] else: new_fundaddr = fundaddr assert_equal(second_addr_type, "legacy") self.nodes[0].sendtoaddress(new_fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 aur), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) self.nodes[1].sendtoaddress(retaddr, 4) # listunspent should show 2 total outputs (1, 10 aur), one unused (1), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) # getbalances should show 10 used, 1 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 aur left (no dirty) and 11 (including dirty) assert_approx(self.nodes[1].getbalance(), 1, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001) def test_getbalances_used(self): ''' getbalances and listunspent should pick up on reused addresses immediately, even for address reusing outputs created before the first transaction was spending from that address ''' self.log.info("Test getbalances used category") # node under test should be completely empty assert_equal(self.nodes[1].getbalance(avoid_reuse=False), 0) new_addr = self.nodes[1].getnewaddress() ret_addr = self.nodes[0].getnewaddress() # send multiple transactions, reusing one address for _ in range(11): self.nodes[0].sendtoaddress(new_addr, 1) self.nodes[0].generate(1) self.sync_all() # send transaction that should not use all the available outputs # per the current coin selection algorithm self.nodes[1].sendtoaddress(ret_addr, 5) # getbalances and listunspent should show the remaining outputs # in the reused address as used/reused assert_unspent(self.nodes[1], total_count=2, total_sum=6, reused_count=1, reused_sum=1) assert_balances(self.nodes[1], mine={"used": 1, "trusted": 5}) if __name__ == '__main__': AvoidReuseTest().main()
def run_test(self): # Create and fund a raw tx for sending 10 BTC psbtx1 = self.nodes[0].walletcreatefundedpsbt( [], {self.nodes[2].getnewaddress(): 10})['psbt'] # If inputs are specified, do not automatically add more: utxo1 = self.nodes[0].listunspent()[0] assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[0].walletcreatefundedpsbt, [{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 90}) psbtx1 = self.nodes[0].walletcreatefundedpsbt( [{ "txid": utxo1['txid'], "vout": utxo1['vout'] }], {self.nodes[2].getnewaddress(): 90}, 0, {"add_inputs": True})['psbt'] assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2) # Node 1 should not be able to add anything to it but still return the # psbtx same as before psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt'] assert_equal(psbtx1, psbtx) # Sign the transaction and send signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt'] final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] self.nodes[0].sendrawtransaction(final_tx) # Create p2sh, p2pkh addresses pubkey0 = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['pubkey'] pubkey1 = self.nodes[1].getaddressinfo( self.nodes[1].getnewaddress())['pubkey'] pubkey2 = self.nodes[2].getaddressinfo( self.nodes[2].getnewaddress())['pubkey'] p2sh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "")['address'] p2pkh = self.nodes[1].getnewaddress("") # fund those addresses rawtx = self.nodes[0].createrawtransaction([], {p2sh: 10, p2pkh: 10}) rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition": 0}) signed_tx = self.nodes[0].signrawtransactionwithwallet( rawtx['hex'])['hex'] txid = self.nodes[0].sendrawtransaction(signed_tx) self.nodes[0].generate(6) self.sync_all() # Find the output pos p2sh_pos = -1 p2pkh_pos = -1 decoded = self.nodes[0].decoderawtransaction(signed_tx) for out in decoded['vout']: if out['scriptPubKey']['addresses'][0] == p2sh: p2sh_pos = out['n'] elif out['scriptPubKey']['addresses'][0] == p2pkh: p2pkh_pos = out['n'] # spend single key from node 1 rawtx = self.nodes[1].walletcreatefundedpsbt( [{ "txid": txid, "vout": p2pkh_pos }], {self.nodes[1].getnewaddress(): 9.99})['psbt'] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(rawtx) assert_equal(walletprocesspsbt_out['complete'], True) self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt( walletprocesspsbt_out['psbt'])['hex']) # feeRate of 0.1 BCH / KB produces a total fee slightly below -maxtxfee res = self.nodes[1].walletcreatefundedpsbt( [{ "txid": txid, "vout": p2sh_pos }, { "txid": txid, "vout": p2pkh_pos }], {self.nodes[1].getnewaddress(): 29.99}, 0, { "feeRate": 0.1, "add_inputs": True }) assert_approx(res["fee"], 0.065, 0.005) # feeRate of 10 BCH / KB produces a total fee well above -maxtxfee # previously this was silently capped at -maxtxfee assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{ "txid": txid, "vout": p2sh_pos }, { "txid": txid, "vout": p2pkh_pos }], {self.nodes[1].getnewaddress(): 29.99}, 0, { "feeRate": 10, "add_inputs": True }) assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{ "txid": txid, "vout": p2sh_pos }, { "txid": txid, "vout": p2pkh_pos }], {self.nodes[1].getnewaddress(): 1}, 0, { "feeRate": 10, "add_inputs": False }) # partially sign multisig things with node 1 psbtx = self.nodes[1].walletcreatefundedpsbt( [{ "txid": txid, "vout": p2sh_pos }], {self.nodes[1].getnewaddress(): 9.99})['psbt'] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx) psbtx = walletprocesspsbt_out['psbt'] assert_equal(walletprocesspsbt_out['complete'], False) # partially sign with node 2. This should be complete and sendable walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx) assert_equal(walletprocesspsbt_out['complete'], True) self.nodes[2].sendrawtransaction(self.nodes[2].finalizepsbt( walletprocesspsbt_out['psbt'])['hex']) # check that walletprocesspsbt fails to decode a non-psbt rawtx = self.nodes[1].createrawtransaction( [{ "txid": txid, "vout": p2pkh_pos }], {self.nodes[1].getnewaddress(): 9.99}) assert_raises_rpc_error(-22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx) # Convert a non-psbt to psbt and make sure we can decode it rawtx = self.nodes[0].createrawtransaction( [], {self.nodes[1].getnewaddress(): 10}) rawtx = self.nodes[0].fundrawtransaction(rawtx) new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) self.nodes[0].decodepsbt(new_psbt) # Make sure that a non-psbt with signatures cannot be converted # Error is "Inputs must not have scriptSigs" signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex']) assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, signedtx['hex']) assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, signedtx['hex'], False) # Unless we allow it to convert and strip signatures self.nodes[0].converttopsbt(signedtx['hex'], True) # Explicilty allow converting non-empty txs new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) self.nodes[0].decodepsbt(new_psbt) # Create outputs to nodes 1 and 2 node1_addr = self.nodes[1].getnewaddress() node2_addr = self.nodes[2].getnewaddress() txid1 = self.nodes[0].sendtoaddress(node1_addr, 13) txid2 = self.nodes[0].sendtoaddress(node2_addr, 13) blockhash = self.nodes[0].generate(6)[0] self.sync_all() vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash) vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash) # Create a psbt spending outputs from nodes 1 and 2 psbt_orig = self.nodes[0].createpsbt( [{ "txid": txid1, "vout": vout1 }, { "txid": txid2, "vout": vout2 }], {self.nodes[0].getnewaddress(): 25.999}) # Update psbts, should only have data for one input and not the other psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL|FORKID")['psbt'] psbt1_decoded = self.nodes[0].decodepsbt(psbt1) assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1] # Check that BIP32 path was added assert "bip32_derivs" in psbt1_decoded['inputs'][0] psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL|FORKID", False)['psbt'] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1] # Check that BIP32 paths were not added assert "bip32_derivs" not in psbt2_decoded['inputs'][1] # Sign PSBTs (workaround issue #18039) psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt'] psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt'] # Combine, finalize, and send the psbts combined = self.nodes[0].combinepsbt([psbt1, psbt2]) finalized = self.nodes[0].finalizepsbt(combined)['hex'] self.nodes[0].sendrawtransaction(finalized) self.nodes[0].generate(6) self.sync_all() block_height = self.nodes[0].getblockcount() unspent = self.nodes[0].listunspent()[0] # Make sure change address wallet does not have P2SH innerscript access to results in success # when attempting BnB coin selection self.nodes[0].walletcreatefundedpsbt( [], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], block_height + 2, {"changeAddress": self.nodes[1].getnewaddress()}, False) # Regression test for 14473 (mishandling of already-signed # transaction): psbtx_info = self.nodes[0].walletcreatefundedpsbt( [{ "txid": unspent["txid"], "vout": unspent["vout"] }], [{ self.nodes[2].getnewaddress(): unspent["amount"] + 1 }], 0, {"add_inputs": True}) complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"]) double_processed_psbt = self.nodes[0].walletprocesspsbt( complete_psbt["psbt"]) assert_equal(complete_psbt, double_processed_psbt) # We don't care about the decode result, but decoding must succeed. self.nodes[0].decodepsbt(double_processed_psbt["psbt"]) # BIP 174 Test Vectors # Check that unknown values are just passed through unknown_psbt = "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)['psbt'] assert_equal(unknown_psbt, unknown_out) # Open the data file with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_psbt.json'), encoding='utf-8') as f: d = json.load(f) invalids = d['invalid'] valids = d['valid'] creators = d['creator'] signers = d['signer'] combiners = d['combiner'] finalizers = d['finalizer'] extractors = d['extractor'] # Invalid PSBTs for invalid in invalids: assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decodepsbt, invalid) # Valid PSBTs for valid in valids: self.nodes[0].decodepsbt(valid) # Creator Tests for creator in creators: created_tx = self.nodes[0].createpsbt(creator['inputs'], creator['outputs']) assert_equal(created_tx, creator['result']) # Signer tests for i, signer in enumerate(signers): self.nodes[2].createwallet("wallet{}".format(i)) wrpc = self.nodes[2].get_wallet_rpc("wallet{}".format(i)) for key in signer['privkeys']: wrpc.importprivkey(key) signed_tx = wrpc.walletprocesspsbt(signer['psbt'])['psbt'] assert_equal(signed_tx, signer['result']) # Combiner test for combiner in combiners: combined = self.nodes[2].combinepsbt(combiner['combine']) assert_equal(combined, combiner['result']) # Empty combiner test assert_raises_rpc_error(-8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, []) # Finalizer test for finalizer in finalizers: finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt'] assert_equal(finalized, finalizer['result']) # Extractor test for extractor in extractors: extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex'] assert_equal(extracted, extractor['result']) # Test decoding error: invalid base64 assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;") # Test that psbts with p2pkh outputs are created properly p2pkh = self.nodes[0].getnewaddress() psbt = self.nodes[1].walletcreatefundedpsbt([], [{ p2pkh: 1 }], 0, {"includeWatching": True}, True) self.nodes[0].decodepsbt(psbt['psbt']) # Send to all types of addresses addr1 = self.nodes[1].getnewaddress("") # originally bech32 txid1 = self.nodes[0].sendtoaddress(addr1, 11) vout1 = find_output(self.nodes[0], txid1, 11) addr2 = self.nodes[1].getnewaddress("") # originally legacy txid2 = self.nodes[0].sendtoaddress(addr2, 11) vout2 = find_output(self.nodes[0], txid2, 11) addr3 = self.nodes[1].getnewaddress("") # originally p2sh-segwit txid3 = self.nodes[0].sendtoaddress(addr3, 11) vout3 = find_output(self.nodes[0], txid3, 11) self.sync_all() def test_psbt_input_keys(psbt_input, keys): """Check that the psbt input has only the expected keys.""" assert_equal(set(keys), set(psbt_input.keys())) # Create a PSBT. None of the inputs are filled initially psbt = self.nodes[1].createpsbt([{ "txid": txid1, "vout": vout1 }, { "txid": txid2, "vout": vout2 }, { "txid": txid3, "vout": vout3 }], {self.nodes[0].getnewaddress(): 32.999}) decoded = self.nodes[1].decodepsbt(psbt) test_psbt_input_keys(decoded['inputs'][0], []) test_psbt_input_keys(decoded['inputs'][1], []) test_psbt_input_keys(decoded['inputs'][2], []) # Update a PSBT with UTXOs from the node updated = self.nodes[1].utxoupdatepsbt(psbt) decoded = self.nodes[1].decodepsbt(updated) test_psbt_input_keys(decoded['inputs'][1], []) test_psbt_input_keys(decoded['inputs'][2], []) # Try again, now while providing descriptors descs = [ self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1, addr2, addr3] ] updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs) decoded = self.nodes[1].decodepsbt(updated) test_psbt_input_keys(decoded['inputs'][1], []) # Two PSBTs with a common input should not be joinable psbt1 = self.nodes[1].createpsbt( [{ "txid": txid1, "vout": vout1 }], {self.nodes[0].getnewaddress(): Decimal('10.999')}) assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated]) # Join two distinct PSBTs addr4 = self.nodes[1].getnewaddress("") txid4 = self.nodes[0].sendtoaddress(addr4, 5) vout4 = find_output(self.nodes[0], txid4, 5) self.nodes[0].generate(6) self.sync_all() psbt2 = self.nodes[1].createpsbt( [{ "txid": txid4, "vout": vout4 }], {self.nodes[0].getnewaddress(): Decimal('4.999')}) psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt'] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) assert "final_scriptSig" in psbt2_decoded['inputs'][0] joined = self.nodes[0].joinpsbts([psbt, psbt2]) joined_decoded = self.nodes[0].decodepsbt(joined) assert len(joined_decoded['inputs']) == 4 and len( joined_decoded['outputs'] ) == 2 and "final_scriptSig" not in joined_decoded['inputs'][3] # Fail when trying to join less than two PSBTs assert_raises_rpc_error( -8, "At least two PSBTs are required to join PSBTs.", self.nodes[1].joinpsbts, []) assert_raises_rpc_error( -8, "At least two PSBTs are required to join PSBTs.", self.nodes[1].joinpsbts, [psbt2]) # Check that joining shuffles the inputs and outputs # 10 attempts should be enough to get a shuffled join shuffled = False for i in range(0, 10): shuffled_joined = self.nodes[0].joinpsbts([psbt, psbt2]) shuffled |= joined != shuffled_joined if shuffled: break assert shuffled # Newly created PSBT needs UTXOs and updating addr = self.nodes[1].getnewaddress("") txid = self.nodes[0].sendtoaddress(addr, 7) blockhash = self.nodes[0].generate(6)[0] self.sync_all() vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash) psbt = self.nodes[1].createpsbt( [{ "txid": txid, "vout": vout }], {self.nodes[0].getnewaddress(""): Decimal('6.999')}) analyzed = self.nodes[0].analyzepsbt(psbt) assert not analyzed['inputs'][0]['has_utxo'] and not analyzed[ 'inputs'][0]['is_final'] and analyzed['inputs'][0][ 'next'] == 'updater' and analyzed['next'] == 'updater' # After update with wallet, only needs signing updated = self.nodes[1].walletprocesspsbt(psbt, False, 'ALL|FORKID', True)['psbt'] analyzed = self.nodes[0].analyzepsbt(updated) assert analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0][ 'is_final'] and analyzed['inputs'][0][ 'next'] == 'signer' and analyzed['next'] == 'signer' # Check fee and size things assert analyzed['fee'] == Decimal( '0.001') and analyzed['estimated_vsize'] == 191 and analyzed[ 'estimated_feerate'] == Decimal('0.00523560') # After signing and finalizing, needs extracting signed = self.nodes[1].walletprocesspsbt(updated)['psbt'] analyzed = self.nodes[0].analyzepsbt(signed) assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0][ 'is_final'] and analyzed['next'] == 'extractor' self.log.info( "PSBT spending unspendable outputs should have error message and Creator as next" ) analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 spends unspendable output') self.log.info( "PSBT with invalid values should have error message and Creator as next" ) analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8AgIFq49AHABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 has invalid value') analysis = self.nodes[0].analyzepsbt( 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA' ) assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Output amount invalid')
def run_test(self): # Mine some coins self.nodes[0].generate(110) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for i in range(3)] addr2 = [self.nodes[2].getnewaddress() for i in range(3)] addrs = addr1 + addr2 # Send 1 + 0.5 coin to each address [self.nodes[0].sendtoaddress(addr, 1000000) for addr in addrs] [self.nodes[0].sendtoaddress(addr, 500000) for addr in addrs] self.nodes[0].generate(1) self.sync_all() # For each node, send 0.2 coins back to 0; # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 200000) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs assert_equal(1, len(tx1["vin"])) assert_equal(2, len(tx1["vout"])) # one output should be 0.2, the other should be ~0.3 v = sorted([vout["value"] for vout in tx1["vout"]]) assert_approx(v[0], 200000) assert_approx(v[1], 300000, 100) txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 200000) tx2 = self.nodes[2].getrawtransaction(txid2, True) # txid2 should have 2 inputs and 2 outputs assert_equal(2, len(tx2["vin"])) assert_equal(2, len(tx2["vout"])) # one output should be 0.2, the other should be ~1.3 v = sorted([vout["value"] for vout in tx2["vout"]]) assert_approx(v[0], 200000) assert_approx(v[1], 1300000, 100) # Test 'avoid partial if warranted, even if disabled' self.sync_all() self.nodes[0].generate(1) # Nodes 1-2 now have confirmed UTXOs (letters denote destinations): # Node #1: Node #2: # - A 1.0 - D0 1.0 # - B0 1.0 - D1 0.5 # - B1 0.5 - E0 1.0 # - C0 1.0 - E1 0.5 # - C1 0.5 - F ~1.3 # - D ~0.3 assert_approx(self.nodes[1].getbalance(), 4300000, 100) assert_approx(self.nodes[2].getbalance(), 4300000, 100) # Sending 1.4 btc should pick one 1.0 + one more. For node #1, # this could be (A / B0 / C0) + (B1 / C1 / D). We ensure that it is # B0 + B1 or C0 + C1, because this avoids partial spends while not being # detrimental to transaction cost txid3 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1400000) tx3 = self.nodes[1].getrawtransaction(txid3, True) # tx3 should have 2 inputs and 2 outputs assert_equal(2, len(tx3["vin"])) assert_equal(2, len(tx3["vout"])) # the accumulated value should be 1.5, so the outputs should be # ~0.1 and 1.4 and should come from the same destination values = sorted([vout["value"] for vout in tx3["vout"]]) assert_approx(values[0], 100000, 100) assert_approx(values[1], 1400000) input_txids = [vin["txid"] for vin in tx3["vin"]] input_addrs = [ self.nodes[1].gettransaction(txid)['details'][0]['address'] for txid in input_txids ] assert_equal(input_addrs[0], input_addrs[1]) # Node 2 enforces avoidpartialspends so needs no checking here # Test wallet option maxapsfee with Node 3 addr_aps = self.nodes[3].getnewaddress() self.nodes[0].sendtoaddress(addr_aps, 1000000) self.nodes[0].sendtoaddress(addr_aps, 1000000) self.nodes[0].generate(1) self.sync_all() txid4 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 100000) tx4 = self.nodes[3].getrawtransaction(txid4, True) # tx4 should have 2 inputs and 2 outputs although one output would # have been enough and the transaction caused higher fees assert_equal(2, len(tx4["vin"])) assert_equal(2, len(tx4["vout"])) # Empty out node2's wallet self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance(), subtractfeefromamount=True) self.sync_all() self.nodes[0].generate(1) # Fill node2's wallet with 10000 outputs corresponding to the same # scriptPubKey for i in range(5): raw_tx = self.nodes[0].createrawtransaction([{ "txid": "0" * 64, "vout": 0 }], [{ addr2[0]: 50000 }]) tx = FromHex(CTransaction(), raw_tx) tx.vin = [] tx.vout = [tx.vout[0]] * 2000 funded_tx = self.nodes[0].fundrawtransaction(ToHex(tx)) signed_tx = self.nodes[0].signrawtransactionwithwallet( funded_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].generate(1) self.sync_all() # Check that we can create a transaction that only requires ~100 of our # utxos, without pulling in all outputs and creating a transaction that # is way too big. assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5000000)
def test_fund_send_fund_senddirty(self): ''' Test the same as test_fund_send_fund_send, except send the 10 AUR with the avoid_reuse flag set to false. This means the 10 AUR send should succeed, where it fails in test_fund_send_fund_send. ''' fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) # node 0 should not show a used entry, as it does not enable avoid_reuse assert("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 aur), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5} self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False) # listunspent should show 1 total outputs (5 aur), unused assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # node 1 should now have about 5 aur left (for both cases) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001) def test_fund_send_fund_send(self): ''' Test the simple case where [1] generates a new address A, then [0] sends 10 AUR to A. [1] spends 5 AUR from A. (leaving roughly 5 BTC useable) [0] sends 10 AUR to A again. [1] tries to spend 10 AUR (fails; dirty). [1] tries to spend 4 AUR (succeeds; change address sufficient) ''' fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 aur output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 aur), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) self.nodes[1].sendtoaddress(retaddr, 4) # listunspent should show 2 total outputs (1, 10 aur), one unused (1), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) # getbalances should show 10 used, 1 aur trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 aur left (no dirty) and 11 (including dirty) assert_approx(self.nodes[1].getbalance(), 1, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001) if __name__ == '__main__': AvoidReuseTest().main()
def run_test(self): # Mine some coins self.nodes[0].generate(110) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for _ in range(3)] addr2 = [self.nodes[2].getnewaddress() for _ in range(3)] addrs = addr1 + addr2 # Send 1 + 0.5 coin to each address [self.nodes[0].sendtoaddress(addr, 1000000) for addr in addrs] [self.nodes[0].sendtoaddress(addr, 500000) for addr in addrs] self.nodes[0].generate(1) self.sync_all() # For each node, send 0.2 coins back to 0; # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest txid1 = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 200000) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs assert_equal(1, len(tx1["vin"])) assert_equal(2, len(tx1["vout"])) # one output should be 0.2, the other should be ~0.3 v = sorted([vout["value"] for vout in tx1["vout"]]) assert_approx(v[0], vexp=200_000, vspan=100) assert_approx(v[1], vexp=300_000, vspan=100) txid2 = self.nodes[2].sendtoaddress( self.nodes[0].getnewaddress(), 200000) tx2 = self.nodes[2].getrawtransaction(txid2, True) # txid2 should have 2 inputs and 2 outputs assert_equal(2, len(tx2["vin"])) assert_equal(2, len(tx2["vout"])) # one output should be 0.2, the other should be ~1.3 v = sorted([vout["value"] for vout in tx2["vout"]]) assert_approx(v[0], vexp=200_000, vspan=100) assert_approx(v[1], vexp=1_300_000, vspan=100) # Test 'avoid partial if warranted, even if disabled' self.sync_all() self.nodes[0].generate(1) # Nodes 1-2 now have confirmed UTXOs (letters denote destinations): # Node #1: Node #2: # - A 1.0 - D0 1.0 # - B0 1.0 - D1 0.5 # - B1 0.5 - E0 1.0 # - C0 1.0 - E1 0.5 # - C1 0.5 - F ~1.3 # - D ~0.3 assert_approx(self.nodes[1].getbalance(), vexp=4_300_000, vspan=100) assert_approx(self.nodes[2].getbalance(), vexp=4_300_000, vspan=100) # Sending 1.4 btc should pick one 1.0 + one more. For node #1, # this could be (A / B0 / C0) + (B1 / C1 / D). We ensure that it is # B0 + B1 or C0 + C1, because this avoids partial spends while not being # detrimental to transaction cost txid3 = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1400000) tx3 = self.nodes[1].getrawtransaction(txid3, True) # tx3 should have 2 inputs and 2 outputs assert_equal(2, len(tx3["vin"])) assert_equal(2, len(tx3["vout"])) # the accumulated value should be 1.5, so the outputs should be # ~0.1 and 1.4 and should come from the same destination values = sorted([vout["value"] for vout in tx3["vout"]]) assert_approx(values[0], vexp=100_000, vspan=100) assert_approx(values[1], vexp=1_400_000, vspan=100) input_txids = [vin["txid"] for vin in tx3["vin"]] input_addrs = [self.nodes[1].gettransaction( txid)['details'][0]['address'] for txid in input_txids] assert_equal(input_addrs[0], input_addrs[1]) # Node 2 enforces avoidpartialspends so needs no checking here # Test wallet option maxapsfee with Node 3 addr_aps = self.nodes[3].getnewaddress() self.nodes[0].sendtoaddress(addr_aps, 1000000) self.nodes[0].sendtoaddress(addr_aps, 1000000) self.nodes[0].generate(1) self.sync_all() with self.nodes[3].assert_debug_log([ 'Fee non-grouped = 225, grouped = 372, using grouped']): txid4 = self.nodes[3].sendtoaddress( self.nodes[0].getnewaddress(), 100_000) tx4 = self.nodes[3].getrawtransaction(txid4, True) # tx4 should have 2 inputs and 2 outputs although one output would # have been enough and the transaction caused higher fees assert_equal(2, len(tx4["vin"])) assert_equal(2, len(tx4["vout"])) addr_aps2 = self.nodes[3].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps2, 1_000_000) for _ in range(5)]
def test_fund_send_fund_senddirty(self): ''' Test the same as test_fund_send_fund_send, except send the 10 BTC with the avoid_reuse flag set to false. This means the 10 BTC send should succeed, where it fails in test_fund_send_fund_send. ''' fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 btc output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) # node 0 should not show a used entry, as it does not enable avoid_reuse assert ("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 btc output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # For the second send, we transmute it to a related single-key address # to make sure it's also detected as re-use fund_spk = self.nodes[0].getaddressinfo(fundaddr)["scriptPubKey"] fund_decoded = self.nodes[0].decodescript(fund_spk) if second_addr_type == "p2sh-segwit": new_fundaddr = fund_decoded["segwit"]["p2sh-segwit"] elif second_addr_type == "bech32": new_fundaddr = fund_decoded["segwit"]["addresses"][0] else: new_fundaddr = fundaddr assert_equal(second_addr_type, "legacy") self.nodes[0].sendtoaddress(new_fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False) # listunspent should show 1 total outputs (5 btc), unused assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0) # getbalances should show no used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # node 1 should now have about 5 btc left (for both cases) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001)
def test_sending_from_reused_address_fails(self, second_addr_type): ''' Test the simple case where [1] generates a new address A, then [0] sends 10 ECC to A. [1] spends 5 ECC from A. (leaving roughly 5 ECC useable) [0] sends 10 ECC to A again. [1] tries to spend 10 ECC (fails; dirty). [1] tries to spend 4 ECC (succeeds; change address sufficient) ''' self.log.info("Test sending from reused {} address fails".format(second_addr_type)) fundaddr = self.nodes[1].getnewaddress(label="", address_type="legacy") retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 ecc output assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0) # getbalances should show no used, 10 ecc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 ecc output assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0) # getbalances should show no used, 5 ecc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) if not self.options.descriptors: # For the second send, we transmute it to a related single-key address # to make sure it's also detected as re-use fund_spk = self.nodes[0].getaddressinfo(fundaddr)["scriptPubKey"] fund_decoded = self.nodes[0].decodescript(fund_spk) if second_addr_type == "p2sh-segwit": new_fundaddr = fund_decoded["segwit"]["p2sh-segwit"] elif second_addr_type == "bech32": new_fundaddr = fund_decoded["segwit"]["addresses"][0] else: new_fundaddr = fundaddr assert_equal(second_addr_type, "legacy") self.nodes[0].sendtoaddress(new_fundaddr, 10) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 ecc), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) # getbalances should show 10 used, 5 ecc trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) assert_approx(self.nodes[1].getbalance(), 5, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) self.nodes[1].sendtoaddress(retaddr, 4) # listunspent should show 2 total outputs (1, 10 ecc), one unused (1), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) # getbalances should show 10 used, 1 ecc trusted assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 ecc left (no dirty) and 11 (including dirty) assert_approx(self.nodes[1].getbalance(), 1, 0.001) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
def run_test(self): self.M = 2 self.N = self.num_nodes self.name = f"{self.M}_of_{self.N}_multisig" self.log.info(f"Testing {self.name}...") participants = { # Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet. # This wallet will be the participant's `signer` for the resulting multisig. Avoid reusing this wallet for any other purpose (for privacy reasons). "signers": [ node.get_wallet_rpc( node.createwallet( wallet_name=f"participant_{self.nodes.index(node)}", descriptors=True)["name"]) for node in self.nodes ], # After participants generate and exchange their xpubs they will each create their own watch-only multisig. # Note: these multisigs are all the same, this just highlights that each participant can independently verify everything on their own node. "multisigs": [] } self.log.info("Generate and exchange xpubs...") xpubs = [self._get_xpub(signer) for signer in participants["signers"]] self.log.info( "Every participant imports the following descriptors to create the watch-only multisig..." ) participants["multisigs"] = list( self.participants_create_multisigs(xpubs)) self.log.info( "Check that every participant's multisig generates the same addresses..." ) for _ in range( 10 ): # we check that the first 10 generated addresses are the same for all participant's multisigs receive_addresses = [ multisig.getnewaddress() for multisig in participants["multisigs"] ] all(address == receive_addresses[0] for address in receive_addresses) change_addresses = [ multisig.getrawchangeaddress() for multisig in participants["multisigs"] ] all(address == change_addresses[0] for address in change_addresses) self.log.info("Get a mature utxo to send to the multisig...") coordinator_wallet = participants["signers"][0] self.generatetoaddress(self.nodes[0], 101, coordinator_wallet.getnewaddress()) deposit_amount = 6.15 multisig_receiving_address = participants["multisigs"][ 0].getnewaddress() self.log.info( "Send funds to the resulting multisig receiving address...") coordinator_wallet.sendtoaddress(multisig_receiving_address, deposit_amount) self.generate(self.nodes[0], 1) for participant in participants["multisigs"]: assert_approx(participant.getbalance(), deposit_amount, vspan=0.001) self.log.info("Send a transaction from the multisig!") to = participants["signers"][self.N - 1].getnewaddress() value = 1 self.log.info( "First, make a sending transaction, created using `walletcreatefundedpsbt` (anyone can initiate this)..." ) psbt = participants["multisigs"][0].walletcreatefundedpsbt( inputs=[], outputs={to: value}, options={"feeRate": 0.00010}) psbts = [] self.log.info( "Now at least M users check the psbt with decodepsbt and (if OK) signs it with walletprocesspsbt..." ) for m in range(self.M): signers_multisig = participants["multisigs"][m] self._check_psbt(psbt["psbt"], to, value, signers_multisig) signing_wallet = participants["signers"][m] partially_signed_psbt = signing_wallet.walletprocesspsbt( psbt["psbt"]) psbts.append(partially_signed_psbt["psbt"]) self.log.info( "Finally, collect the signed PSBTs with combinepsbt, finalizepsbt, then broadcast the resulting transaction..." ) combined = coordinator_wallet.combinepsbt(psbts) finalized = coordinator_wallet.finalizepsbt(combined) coordinator_wallet.sendrawtransaction(finalized["hex"]) self.log.info( "Check that balances are correct after the transaction has been included in a block." ) self.generate(self.nodes[0], 1) assert_approx(participants["multisigs"][0].getbalance(), deposit_amount - value, vspan=0.001) assert_equal(participants["signers"][self.N - 1].getbalance(), value) self.log.info( "Send another transaction from the multisig, this time with a daisy chained signing flow (one after another in series)!" ) psbt = participants["multisigs"][0].walletcreatefundedpsbt( inputs=[], outputs={to: value}, options={"feeRate": 0.00010}) for m in range(self.M): signers_multisig = participants["multisigs"][m] self._check_psbt(psbt["psbt"], to, value, signers_multisig) signing_wallet = participants["signers"][m] psbt = signing_wallet.walletprocesspsbt(psbt["psbt"]) assert_equal(psbt["complete"], m == self.M - 1) finalized = coordinator_wallet.finalizepsbt(psbt["psbt"]) coordinator_wallet.sendrawtransaction(finalized["hex"]) self.log.info( "Check that balances are correct after the transaction has been included in a block." ) self.generate(self.nodes[0], 1) assert_approx(participants["multisigs"][0].getbalance(), deposit_amount - (value * 2), vspan=0.001) assert_equal(participants["signers"][self.N - 1].getbalance(), value * 2)
def assert_balances(node, mine): '''Make assertions about a node's getbalances output''' got = node.getbalances()["mine"] for k,v in mine.items(): assert_approx(got[k], v, 0.001)
def test_fund_send_fund_senddirty(self): ''' Test the same as test_fund_send_fund_send, except send the 10 BTC with the avoid_reuse flag set to false. This means the 10 BTC send should succeed, where it fails in test_fund_send_fund_send. ''' self.log.info("Test fund send fund send dirty") fundaddr = self.nodes[1].getnewaddress() retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 0.2) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 10 btc output assert_unspent(self.nodes[1], total_count=1, total_sum=0.2, reused_supported=True, reused_count=0) # getbalances should show no used, 10 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 0.2}) # node 0 should not show a used entry, as it does not enable avoid_reuse assert ("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 0.1) self.nodes[0].generate(1) self.sync_all() # listunspent should show 1 single, unused 5 btc output assert_unspent(self.nodes[1], total_count=1, total_sum=0.1, reused_supported=True, reused_count=0) # getbalances should show no used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 0.1}) self.nodes[0].sendtoaddress(fundaddr, 0.2) self.nodes[0].generate(1) self.sync_all() # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10) assert_unspent(self.nodes[1], total_count=2, total_sum=0.3, reused_count=1, reused_sum=0.2) # getbalances should show 10 used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0.2, "trusted": 0.1}) self.nodes[1].sendtoaddress(address=retaddr, amount=0.09, avoid_reuse=False) # listunspent should show 1 total outputs (5 btc), unused assert_unspent(self.nodes[1], total_count=2, total_sum=0.21, reused_count=1) # getbalances should show no used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0.2, "trusted": 0.009}) # node 1 should now have about 5 btc left (for both cases) assert_approx(self.nodes[1].getbalance(), 0.01, 0.01) assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 0.2, 0.1)