def run_test(self): node0_address = self.nodes[0].getnewaddress() # Spend block 1/2/3's coinbase transactions # Mine a block. # Create three more transactions, spending the spends # Mine another block. # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again. b = [self.nodes[0].getblockhash(n) for n in range(1, 4)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends1_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.99) for txid in coinbase_txids] spends1_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends1_raw] blocks = [] blocks.extend(self.nodes[0].generate(1, self.signblockprivkey_wif)) spends2_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.98) for txid in spends1_id] spends2_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw] blocks.extend(self.nodes[0].generate(1, self.signblockprivkey_wif)) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id+spends2_id: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] > 0) # Use invalidateblock to re-org back for node in self.nodes: node.invalidateblock(blocks[0]) color_ids=[create_colored_transaction(2, 1000, self.nodes[0])['txid'], create_colored_transaction(3, 1, self.nodes[0])['txid']] blocks.extend(self.nodes[0].generate(1, self.signblockprivkey_wif)) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) # Use invalidate to re-org back for node in self.nodes: node.invalidateblock(blocks[2]) # All txns should be back in mempool with 0 confirmations assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id+spends2_id+color_ids)) for txid in spends1_id+spends2_id+color_ids: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] == 0) # Generate another block, they should all get mined self.nodes[0].generate(1, self.signblockprivkey_wif) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id+spends2_id+color_ids: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] > 0)
def run_test(self): self.log.info("Cresting blocks") self.nodes[0].generate(2, self.signblockprivkey_wif) self.sync_all() self.nodes[1].generate(2, self.signblockprivkey_wif) self.sync_all() self.nodes[2].generate(2, self.signblockprivkey_wif) self.sync_all() self.log.info("Testing token issue using create_colored_transaction ") new_color1 = create_colored_transaction(1, 1000, self.nodes[0])['color'] new_color2 = create_colored_transaction(2, 1000, self.nodes[1])['color'] new_color3 = create_colored_transaction(3, 1, self.nodes[2])['color'] self.sync_all() self.nodes[2].generate(1, self.signblockprivkey_wif) walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance'][new_color1], 1000) walletinfo = self.nodes[1].getwalletinfo() assert_equal(walletinfo['balance'][new_color2], 1000) walletinfo = self.nodes[2].getwalletinfo() assert_equal(walletinfo['balance'][new_color3], 1) self.log.info( "Testing token transfer using create_colored_transaction ") create_colored_transaction(1, 500, self.nodes[0], False, new_color1, self.nodes[1]) create_colored_transaction(2, 500, self.nodes[1], False, new_color2, self.nodes[2]) create_colored_transaction(3, 1, self.nodes[2], False, new_color3, self.nodes[0]) self.sync_all() self.nodes[2].generate(1, self.signblockprivkey_wif) walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance'][new_color1], 500) assert_equal(walletinfo['balance'][new_color3], 1) walletinfo = self.nodes[1].getwalletinfo() assert_equal(walletinfo['balance'][new_color2], 500) assert_equal(walletinfo['balance'][new_color1], 500) walletinfo = self.nodes[2].getwalletinfo() assert_equal(walletinfo['balance'][new_color3], 0) assert_equal(walletinfo['balance'][new_color2], 500)
def run_test(self): """Run the test using the labels API.""" node = self.nodes[0] # Check that there's no UTXO on any of the nodes assert_equal(len(node.listunspent()), 0) # Note each time we call generate, all generated coins go into # the same address, so we call twice to get two addresses w/50 each node.generate(1, self.signblockprivkey_wif) node.generate(1, self.signblockprivkey_wif) assert_equal(node.getbalance(), 100) assert_equal(len(node.listunspent()), 2) # there should be 2 address groups # each with 1 address with a balance of 50 TPC address_groups = node.listaddressgroupings() assert_equal(len(address_groups), 2) # the addresses aren't linked now, but will be after we send to the # common address linked_addresses = set() for address_group in address_groups: assert_equal(len(address_group), 1) assert_equal(len(address_group[0]), 2) assert_equal(address_group[0][1], 50) linked_addresses.add(address_group[0][0]) # send 50 from each address to a third address not in this wallet # There's some fee that will come back to us when the miner reward # matures. common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr" txid = node.sendmany(amounts={common_address: 100}, subtractfeefrom=[common_address]) tx_details = node.gettransaction(txid) fee = -tx_details['details'][0]['fee'] # there should be 1 address group, with the previously # unlinked addresses now linked (they both have 0 balance) address_groups = node.listaddressgroupings() assert_equal(len(address_groups), 1) assert_equal(len(address_groups[0]), 2) assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses) assert_equal([a[1] for a in address_groups[0]], [0, 0]) node.generate(1, self.signblockprivkey_wif) amount_to_send = 1.0 self.log.debug("Checking lables with TPC") # Create labels and make sure subsequent label API calls # recognize the label/address associations. labels = [Label(name) for name in ("a", "b", "c", "d", "e")] for label in labels: address = node.getnewaddress(label.name) label.add_address(address) label.verify(node) # Check all labels are returned by listlabels. assert_equal(node.listlabels(), [label.name for label in labels]) # Send a transaction to each label, and make sure this forces # getaccountaddress to generate a new receiving address. for label in labels: node.sendtoaddress(label.addresses[0], amount_to_send) label.verify(node) # Check the amounts received. node.generate(1, self.signblockprivkey_wif) for label in labels: assert_equal(node.getreceivedbyaddress(label.addresses[0]), amount_to_send) assert_equal(node.getreceivedbylabel(label.name), {'TPC': Decimal('1.00000000')}) # generate extra blocks so that they can be used in token issue. node.generate(6, self.signblockprivkey_wif) self.log.debug("Checking lables with tokens") colorId = create_colored_transaction(2, 10, node)['color'] for label in labels: address = node.getnewaddress(label.name, colorId) label.add_coloraddress(address) #separate calls to getaddressinfo to avoid race conditin in address book reading when getnewaddress and getaddressinfo are in the same loop'' for label in labels: label.verify(node, colorId) # Send a transaction to each label, and make sure this forces # getaccountaddress to generate a new receiving address. for label in labels: node.sendtoaddress(label.caddresses[0], 1) label.verify(node, colorId) # Check the amounts received. node.generate(1, self.signblockprivkey_wif) for label in labels: assert_equal(node.getreceivedbyaddress(label.caddresses[0]), 1) assert_equal(node.getreceivedbyaddress(label.addresses[0]), amount_to_send) assert_equal(node.getreceivedbylabel(label.name), { 'TPC': Decimal('1.00000000'), colorId: 1 }) self.log.debug( "Check that sendfrom label reduces listaccounts balances in TPC") for i, label in enumerate(labels): to_label = labels[(i + 1) % len(labels)] node.sendtoaddress(to_label.addresses[0], amount_to_send) node.generate(1, self.signblockprivkey_wif) for label in labels: address = node.getnewaddress(label.name) label.add_address(address) label.verify(node) assert_equal(node.getreceivedbylabel(label.name), { 'TPC': Decimal('2.00000000'), colorId: 1 }) label.verify(node) node.generate(3, self.signblockprivkey_wif) assert_equal(math.floor(node.getbalance()), 650) self.log.debug( "Check that sendfrom label reduces listaccounts balances in tokens" ) for i, label in enumerate(labels): to_label = labels[(i + 1) % len(labels)] node.sendtoaddress(to_label.caddresses[0], 1) node.generate(1, self.signblockprivkey_wif) for label in labels: address = node.getnewaddress(label.name, colorId) label.add_coloraddress(address) label.verify(node, colorId) assert_equal(node.getreceivedbylabel(label.name), { 'TPC': Decimal('2.0000000'), colorId: 2 }) label.verify(node, colorId) node.generate(3, self.signblockprivkey_wif) assert_equal(math.floor(node.getbalance()), 850) # Check that setlabel can assign a label to a new unused address. for label in labels: address = node.getnewaddress() node.setlabel(address, label.name) label.add_address(address) label.verify(node) assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "") # Check that addmultisigaddress can assign labels. for label in labels: addresses = [] for x in range(10): addresses.append(node.getnewaddress()) multisig_address = node.addmultisigaddress(5, addresses, label.name)['address'] label.add_address(multisig_address) label.purpose[multisig_address] = "send" label.verify(node) node.generate(1, self.signblockprivkey_wif) # Check that setlabel can change the label of an address from a # different label. change_label(node, labels[0].addresses[0], labels[0], labels[1]) # Check that setlabel can set the label of an address already # in the label. This is a no-op. change_label(node, labels[2].addresses[0], labels[2], labels[2])
def run_test(self): # Check that nodes don't own any UTXOs assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) self.log.info("Mining one block for each node") self.nodes[0].generate(1, self.signblockprivkey_wif) colorid1 = create_colored_transaction(2, 100, self.nodes[0])['color'] self.nodes[0].generatetoaddress(1, RANDOM_COINBASE_ADDRESS, self.signblockprivkey_wif) self.sync_all() self.nodes[1].generate(1, self.signblockprivkey_wif) colorid2 = create_colored_transaction(2, 100, self.nodes[1])['color'] self.nodes[1].generatetoaddress(1, RANDOM_COINBASE_ADDRESS, self.signblockprivkey_wif) self.sync_all() assert_equal(self.nodes[0].getbalance(), Decimal('49.99995520')) wallet_bal = self.nodes[0].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid1], 100) assert_equal(self.nodes[1].getbalance(), Decimal('49.99995520')) wallet_bal = self.nodes[1].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid2], 100) self.log.info("Test getbalance with different arguments") assert_equal(self.nodes[0].getbalance(True), Decimal('49.99995520')) assert_equal(self.nodes[1].getbalance(True), Decimal('49.99995520')) # Send 40 TPC from 0 to 1 and 60 BTC from 1 to 0. txs = create_transactions(self.nodes[0], 'TPC', self.nodes[1].getnewaddress(), 40, [Decimal('0.01')]) self.nodes[0].sendrawtransaction(txs[0]['hex']) self.nodes[1].sendrawtransaction( txs[0]['hex'] ) # sending on both nodes is faster than waiting for propagation self.sync_all() txs = create_transactions( self.nodes[1], 'TPC', self.nodes[0].getnewaddress(), 60, [Decimal('0.01'), Decimal('0.04')]) self.nodes[1].sendrawtransaction(txs[0]['hex']) self.nodes[0].sendrawtransaction(txs[0]['hex']) self.sync_all() self.log.info( "Test getbalance and getunconfirmedbalance with unconfirmed inputs" ) # getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions assert_equal(round(self.nodes[0].getbalance(), 2), Decimal('9.99')) # change from node 0's send assert_equal(round(self.nodes[1].getbalance(), 2), Decimal('29.99')) # change from node 1's send # getunconfirmedbalance assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) # output of node 1's spend assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal( '0')) # Doesn't include output of node 0's send since it was spent # Send 40 colorid1 from 0 to 1 and 60 colorid2 from 1 to 0. ctxs = create_transactions(self.nodes[0], colorid1, self.nodes[1].getnewaddress("", colorid1), 40, [Decimal('0.01')]) self.nodes[0].sendrawtransaction(ctxs[0]['hex']) self.nodes[1].sendrawtransaction(ctxs[0]['hex']) self.sync_all() ctxs = create_transactions(self.nodes[1], colorid2, self.nodes[0].getnewaddress("", colorid2), 60, [Decimal('0.01')]) self.nodes[1].sendrawtransaction(ctxs[0]['hex']) self.nodes[0].sendrawtransaction(ctxs[0]['hex']) self.sync_all() # getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions assert_equal(round(self.nodes[0].getbalance(), 2), Decimal('9.98')) assert_equal(round(self.nodes[1].getbalance(), 2), Decimal('29.98')) wallet_bal = self.nodes[0].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid1], 60) assert_equal(len(wallet_bal), 2) wallet_bal = self.nodes[1].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid2], 40) assert_equal(len(wallet_bal), 2) # getunconfirmedbalance assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) wallet_bal = self.nodes[0].getwalletinfo()['unconfirmed_balance'] assert_equal(len(wallet_bal), 1) wallet_bal = self.nodes[1].getwalletinfo()['unconfirmed_balance'] assert_equal(len(wallet_bal), 1) # Node 1 bumps the transaction fee and resends self.nodes[1].sendrawtransaction(txs[1]['hex']) self.sync_all() self.log.info( "Test getbalance and getunconfirmedbalance with conflicted unconfirmed inputs" ) assert_equal( self.nodes[0].getwalletinfo()["unconfirmed_balance"]['TPC'], Decimal('60')) # output of node 1's send assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) assert_equal( self.nodes[1].getwalletinfo()["unconfirmed_balance"]['TPC'], Decimal('0') ) # Doesn't include output of node 0's send since it was spent assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) self.nodes[1].generatetoaddress(1, RANDOM_COINBASE_ADDRESS, self.signblockprivkey_wif) self.sync_all() # balances are correct after the transactions are confirmed assert_equal( round(self.nodes[0].getbalance(), 2), Decimal('69.98')) # node 1's send plus change from node 0's send assert_equal(round(self.nodes[1].getbalance(), 2), Decimal('29.96')) # change from node 0's send wallet_bal = self.nodes[0].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid1], 60) wallet_bal = self.nodes[1].getwalletinfo()['balance'] assert_equal(wallet_bal[colorid2], 100) # Send total balance away from node 1 txs = create_transactions(self.nodes[1], 'TPC', self.nodes[0].getnewaddress(), Decimal('29.94995520'), [Decimal('0.01')]) self.nodes[1].sendrawtransaction(txs[0]['hex']) self.nodes[1].generatetoaddress(2, RANDOM_COINBASE_ADDRESS, self.signblockprivkey_wif) self.sync_all() assert_equal(self.nodes[1].getbalance(), Decimal('0'))
def run_test(self): # Generate block to get out of IBD self.nodes[0].generate(1, self.signblockprivkey_wif) sync_blocks(self.nodes) self.log.info("listreceivedbyaddress Test") # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() # Check not listed in listreceivedbyaddress because has 0 confirmations assert_array_result(self.nodes[1].listreceivedbyaddress(), {"address": addr}, {}, True) # Bury Tx under 10 block so it will be returned by listreceivedbyaddress self.nodes[1].generate(10, self.signblockprivkey_wif) self.sync_all() assert_array_result(self.nodes[1].listreceivedbyaddress(), {"address": addr}, {"address": addr, "label": "", "token": "TPC", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}) # Empty Tx empty_addr = self.nodes[1].getnewaddress() assert_array_result(self.nodes[1].listreceivedbyaddress(True), {"address": empty_addr}, {"address": empty_addr, "label": "", "token": "TPC", "amount": 0, "confirmations": 0, "txids": []}) # Test Address filtering # Only on addr expected = {"address": addr, "label": "", "token": "TPC", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(include_empty=True, include_watchonly=True, address_filter=addr) assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) # Error on invalid address assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, include_empty=True, include_watchonly=True, address_filter="bamboozling") # Another address receive money res = self.nodes[1].listreceivedbyaddress(True, True) assert_equal(len(res), 2) # Right now 2 entries other_addr = self.nodes[1].getnewaddress() txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() # Same test as above should still pass expected = {"address": addr, "label": "", "token": "TPC", "amount": Decimal("0.1"), "confirmations": 11, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(True, True, addr) assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) # Same test as above but with other_addr should still pass expected = {"address": other_addr, "label": "", "token": "TPC", "amount": Decimal("0.1"), "confirmations": 1, "txids": [txid2, ]} res = self.nodes[1].listreceivedbyaddress(True, True, other_addr) assert_array_result(res, {"address": other_addr}, expected) assert_equal(len(res), 1) # Should be two entries though without filter res = self.nodes[1].listreceivedbyaddress(True, True) assert_equal(len(res), 3) # Became 3 entries # Not on random addr other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr res = self.nodes[1].listreceivedbyaddress(True, True, other_addr) assert_equal(len(res), 0) self.log.info("getreceivedbyaddress Test") # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() # Check balance is 0 because of 0 confirmations balance = self.nodes[1].getreceivedbyaddress(addr) assert_equal(balance, Decimal("0.0")) # Check balance is 0.1 balance = self.nodes[1].getunconfirmedbalance() assert_equal(balance, Decimal("0.1")) # Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress self.nodes[1].generate(10, self.signblockprivkey_wif) self.sync_all() balance = self.nodes[1].getreceivedbyaddress(addr) assert_equal(balance, Decimal("0.1")) # Trying to getreceivedby for an address the wallet doesn't own should return an error assert_raises_rpc_error(-4, "Address not found in wallet", self.nodes[0].getreceivedbyaddress, addr) self.log.info("listreceivedbylabel + getreceivedbylabel Test") # set pre-state label = '' address = self.nodes[1].getnewaddress() assert_equal(self.nodes[1].getaddressinfo(address)['label'], label) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() # listreceivedbylabel should return received_by_label_json because of 0 confirmations assert_array_result(self.nodes[1].listreceivedbylabel(), {"label": label}, received_by_label_json) # getreceivedbyaddress should return same balance because of 0 confirmations balance = self.nodes[1].getreceivedbylabel(label) assert_equal(balance, balance_by_label) self.nodes[1].generate(10, self.signblockprivkey_wif) self.sync_all() # listreceivedbylabel should return updated received list assert_array_result(self.nodes[1].listreceivedbylabel(), {"label": label}, {"label": received_by_label_json["label"], "token": 'TPC', "amount": (received_by_label_json["amount"] + Decimal("0.1"))}) # getreceivedbylabel should return updated receive total balance = self.nodes[1].getreceivedbylabel(label) assert_equal(balance['TPC'], balance_by_label['TPC'] + Decimal("0.1")) # Create a new label named "mynewlabel" that has a 0 balance address = self.nodes[1].getnewaddress() self.nodes[1].setlabel(address, "mynewlabel") received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(True) if r["label"] == "mynewlabel"][0] # Test includeempty of listreceivedbylabel assert_equal(received_by_label_json["amount"], Decimal("0.0")) # Test getreceivedbylabel for 0 amount labels balance = self.nodes[1].getreceivedbylabel("mynewlabel") assert_equal(balance, {}) self.log.info("listreceivedbylabel + getreceivedbylabel Test with tokens") colorId = create_colored_transaction(1, 10, self.nodes[0])['color'] address = self.nodes[1].getnewaddress("", colorId) assert_equal(self.nodes[1].getaddressinfo(address)['label'], label) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) txid = self.nodes[0].sendtoaddress(address, 1) self.sync_all() # listreceivedbylabel should return received_by_label_json because of 0 confirmations assert_array_result(self.nodes[1].listreceivedbylabel(), {"label": label}, received_by_label_json) # getreceivedbyaddress should return same balance because of 0 confirmations balance = self.nodes[1].getreceivedbylabel(label) assert_equal(balance, balance_by_label) self.nodes[1].generate(10, self.signblockprivkey_wif) self.sync_all() # listreceivedbylabel should return updated received list assert_array_result(self.nodes[1].listreceivedbylabel(), {"token": colorId}, {"label": received_by_label_json["label"], "amount": 1}) # getreceivedbylabel should return updated receive total balance = self.nodes[1].getreceivedbylabel(label) assert_equal(balance[colorId], 1) # Create a new label named "mynewlabel" that has a 0 balance address = self.nodes[1].getnewaddress("", colorId) self.nodes[1].setlabel(address, "mynewcolorlabel") received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(True) if r["label"] == "mynewcolorlabel"][0] # Test includeempty of listreceivedbylabel assert_equal(received_by_label_json["amount"], Decimal("0.0")) # Test getreceivedbylabel for 0 amount labels balance = self.nodes[1].getreceivedbylabel("mynewcolorlabel") assert_equal(balance, {})
def run_test(self): node = self.nodes[0] # convenience reference to the node self.address = node.getnewaddress() node.add_p2p_connection(P2PDataStore()) node.p2p.wait_for_getheaders(timeout=5) self.log.info("Test starting...") #genesis block (B0) self.blocks = [self.genesisBlock.hash] block_time = self.genesisBlock.nTime # Create a new blocks B1 - B10 self.blocks += node.generate(1, self.aggprivkey_wif[0]) create_colored_transaction(2, 100, node) self.blocks += node.generate(9, self.aggprivkey_wif[0]) best_block = node.getblock(node.getbestblockhash()) self.tip = node.getbestblockhash() self.log.info("First federation block") # B11 - Create block - aggpubkey2 - sign with aggpubkey1 block_time = best_block["time"] + 1 blocknew = create_block(int(self.tip, 16), create_coinbase(11), block_time, self.aggpubkeys[1]) blocknew.solve(self.aggprivkey[0]) self.blocks += [blocknew.hash] node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = self.blocks[-1] assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B -- Create block with invalid aggpubkey2 - sign with aggpubkey1 -- failure - invalid aggpubkey aggpubkeyInv = self.aggpubkeys[-1][:-2] block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(12), block_time, aggpubkeyInv) blocknew.solve(self.aggprivkey[0]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) # B - Create block - sign with aggpubkey1 - failure - Proof verification failed block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(12), block_time) blocknew.solve(self.aggprivkey[0]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) # B12 - Create block - sign with aggpubkey2 blocknew.solve(self.aggprivkey[1]) self.blocks += [blocknew.hash] node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = self.blocks[-1] assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) # Create a new blocks B13 - B22 self.blocks += node.generate(10, self.aggprivkey_wif[1]) best_block = node.getblock(node.getbestblockhash()) self.tip = node.getbestblockhash() #B23 -- Create block with 1 valid transaction - sign with aggpubkey2 -- success block_time = best_block["time"] + 1 blocknew = create_block(int(self.tip, 16), create_coinbase(23), block_time) spendHash = node.getblock(self.blocks[2])['tx'][1] vout = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( spendHash, 1)["vout"]) if vout["value"] != 100) rawtx = node.createrawtransaction(inputs=[{ "txid": spendHash, "vout": vout }], outputs={self.address: 1.0}) signresult = node.signrawtransactionwithwallet(rawtx, [], "ALL", self.options.scheme) assert_equal(signresult["complete"], True) tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signresult['hex']))) blocknew.vtx += [tx] blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[1]) self.blocks.append(blocknew.hash) self.tip = blocknew.hash node.submitblock(bytes_to_hex_str(blocknew.serialize())) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #call invalidate block rpc on B23 -- success - B23 is removed from the blockchain. tip is B22 node.invalidateblock(self.tip) self.tip = self.blocks[22] assert_equal(self.tip, node.getbestblockhash()) #B23 -- Re Create a new block B23 -- success block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(23), block_time) spendHash = node.getblock(self.blocks[2])['tx'][0] blocknew.vtx += [ create_transaction(node, spendHash, self.address, amount=49.0) ] blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[1]) self.blocks[22] = blocknew.hash self.tip = blocknew.hash node.submitblock(bytes_to_hex_str(blocknew.serialize())) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B -- - Create block with 1 invalid transaction - sign with aggpubkey2 -- failure block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(23), block_time) spendHash = node.getblock(self.blocks[3])['tx'][0] blocknew.vtx += [ create_transaction(node, spendHash, self.address, amount=100.0) ] #invalid blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[1]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) #B -- - Create block with 1 invalid transaction and aggpubkey3 - sign with aggpubkey2 -- failure and aggpubkey3 is not added to the list blocknew = create_block(int(self.tip, 16), create_coinbase(23), block_time, self.aggpubkeys[2]) spendHash = node.getblock(self.blocks[4])['tx'][0] blocknew.vtx += [ create_transaction(node, spendHash, self.address, amount=100.0) ] #invalid blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[1]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) # verify aggpubkey3 is not added to the list : verify that block signed using aggprivkey3 is rejected blocknew = create_block(int(self.tip, 16), create_coinbase(24), block_time, self.aggpubkeys[2]) blocknew.solve(self.aggprivkey[2]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) self.log.info("Second federation block") #B24 -- Create block with 1 valid transaction and aggpubkey3- sign with aggpubkey2 -- success and aggpubkey3 is added to the list block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(24), block_time, self.aggpubkeys[2]) spendHash = node.getblock(self.blocks[4])['tx'][0] blocknew.vtx += [ create_transaction(node, spendHash, self.address, amount=10.0) ] blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[1]) self.blocks.append(blocknew.hash) self.tip = blocknew.hash node.submitblock(bytes_to_hex_str(blocknew.serialize())) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B25 -- Create block with 1 valid transaction - sign with aggpubkey3 -- success block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(25), block_time) spendHash = node.getblock(self.blocks[5])['tx'][0] blocknew.vtx += [ create_transaction(node, spendHash, self.address, amount=10.0) ] blocknew.hashMerkleRoot = blocknew.calc_merkle_root() blocknew.hashImMerkleRoot = blocknew.calc_immutable_merkle_root() blocknew.solve(self.aggprivkey[2]) self.blocks.append(blocknew.hash) self.tip = blocknew.hash b25 = blocknew.hash node.submitblock(bytes_to_hex_str(blocknew.serialize())) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) # Create a new blocks B26 - B30 self.blocks += node.generate(5, self.aggprivkey_wif[2]) best_block = node.getblock(node.getbestblockhash()) self.tip = node.getbestblockhash() self.log.info("Verifying getblockchaininfo") #getblockchaininfo expectedAggPubKeys = [{ self.aggpubkeys[0]: 0 }, { self.aggpubkeys[1]: 12 }, { self.aggpubkeys[2]: 25 }] blockchaininfo = node.getblockchaininfo() assert_equal(blockchaininfo["aggregatePubkeys"], expectedAggPubKeys) self.log.info( "Simulate Blockchain Reorg - After the last federation block") #B27 -- Create block with previous block hash = B26 - sign with aggpubkey3 -- success - block is accepted but there is no re-org block_time += 1 self.forkblocks = self.blocks blocknew = create_block(int(self.blocks[26], 16), create_coinbase(27), block_time) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks[27] = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B28 -- Create block with previous block hash = B27 - sign with aggpubkey3 -- success - block is accepted but there is no re-org block_time += 1 blocknew = create_block(int(self.forkblocks[27], 16), create_coinbase(28), block_time) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks[28] = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B29 -- Create block with previous block hash = B28 - sign with aggpubkey3 -- success - block is accepted but there is no re-org block_time += 1 blocknew = create_block(int(self.forkblocks[28], 16), create_coinbase(29), block_time) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks[29] = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B30 -- Create block with previous block hash = B29 - sign with aggpubkey3 -- success - block is accepted but there is no re-org block_time += 1 blocknew = create_block(int(self.forkblocks[29], 16), create_coinbase(30), block_time) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks[30] = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B31 -- Create block with previous block hash = B30 - sign with aggpubkey3 -- success - block is accepted and re-org happens block_time += 1 blocknew = create_block(int(self.forkblocks[30], 16), create_coinbase(31), block_time) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks.append(blocknew.hash) self.tip = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) assert_equal(blockchaininfo["aggregatePubkeys"], expectedAggPubKeys) self.log.info( "Simulate Blockchain Reorg - Before the last federation block") #B24 -- Create block with previous block hash = B23 - sign with aggpubkey2 -- failure - block is in invalid chain block_time += 1 blocknew = create_block(int(self.blocks[23], 16), create_coinbase(24), block_time) blocknew.solve(self.aggprivkey[1]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B25 -- Create block with previous block hash = B24 - sign with aggpubkey2 -- success - block is in invalid chain block_time += 1 blocknew = create_block(int(self.blocks[24], 16), create_coinbase(25), block_time) blocknew.solve(self.aggprivkey[1]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #there are 3 tips in the current blockchain chaintips = node.getchaintips() assert_equal(len(chaintips), 3) assert (node.getblock(self.blocks[12])) assert (node.getblock(self.blocks[25])) assert_raises_rpc_error(-5, "Block not found", node.getblock, blocknew.hash) self.log.info("Third Federation Block - active chain") #B32 -- Create block with aggpubkey4 - sign with aggpubkey3 -- success - aggpubkey4 is added to the list block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(32), block_time, self.aggpubkeys[3]) blocknew.solve(self.aggprivkey[2]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks.append(blocknew.hash) self.tip = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B -- Create block - sign with aggpubkey2 -- failure - proof verification failed block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(33), block_time) blocknew.solve(self.aggprivkey[1]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) #B -- Create block - sign with aggpubkey3 -- failure - proof verification failed blocknew.solve(self.aggprivkey[2]) assert_equal(node.submitblock(bytes_to_hex_str(blocknew.serialize())), "invalid") assert_equal(self.tip, node.getbestblockhash()) #B33 -- Create block - sign with aggpubkey4 -- success blocknew.solve(self.aggprivkey[3]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.forkblocks.append(blocknew.hash) self.tip = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B34 - B35 -- Generate 2 blocks - no aggpubkey -- chain becomes longer self.forkblocks += node.generate(2, self.aggprivkey_wif[3]) self.tip = self.forkblocks[35] assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) self.log.info("Fourth Federation Block") #B36 -- Create block with aggpubkey5 - sign using aggpubkey4 -- success - aggpubkey5 is added to the list block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(36), block_time, self.aggpubkeys[4]) blocknew.solve(self.aggprivkey[3]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = blocknew.hash self.forkblocks.append(blocknew.hash) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #call invalidate block rpc on B36 -- failure - B36 is a federation block assert_raises_rpc_error(-8, "Federation block found", node.invalidateblock, self.tip) assert_raises_rpc_error(-8, "Federation block found", node.invalidateblock, self.forkblocks[33]) assert_raises_rpc_error(-8, "Federation block found", node.invalidateblock, self.blocks[29]) assert_equal(self.tip, node.getbestblockhash()) #B37 - Create block - sign using aggpubkey5 -- success block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(37), block_time) blocknew.solve(self.aggprivkey[4]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = blocknew.hash self.forkblocks.append(blocknew.hash) assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) self.log.info("Verifying getblockchaininfo") #getblockchaininfo expectedAggPubKeys = [ { self.aggpubkeys[0]: 0 }, { self.aggpubkeys[1]: 12 }, { self.aggpubkeys[2]: 25 }, { self.aggpubkeys[3]: 33 }, { self.aggpubkeys[4]: 37 }, ] blockchaininfo = node.getblockchaininfo() assert_equal(blockchaininfo["aggregatePubkeys"], expectedAggPubKeys) #B38 - B40 -- Generate 2 blocks - no aggpubkey -- chain becomes longer self.forkblocks += node.generate(3, self.aggprivkey_wif[4]) self.tip = node.getbestblockhash() best_block = node.getblock(self.tip) self.log.info("Test Repeated aggpubkeys in Federation Block") #B41 -- Create block with aggpubkey0 - sign using aggpubkey5 -- success - aggpubkey0 is added to the list block_time = best_block["time"] + 1 blocknew = create_block(int(self.tip, 16), create_coinbase(41), block_time, self.aggpubkeys[0]) blocknew.solve(self.aggprivkey[4]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) #B42 -- Create block with aggpubkey1 - sign using aggpubkey0 -- success - aggpubkey1 is added to the list block_time += 1 blocknew = create_block(int(self.tip, 16), create_coinbase(42), block_time, self.aggpubkeys[1]) blocknew.solve(self.aggprivkey[0]) node.submitblock(bytes_to_hex_str(blocknew.serialize())) self.tip = blocknew.hash assert_equal(self.tip, node.getbestblockhash()) assert (node.getblock(self.tip)) self.log.info("Verifying getblockchaininfo") #getblockchaininfo expectedAggPubKeys = [ { self.aggpubkeys[0]: 0 }, { self.aggpubkeys[1]: 12 }, { self.aggpubkeys[2]: 25 }, { self.aggpubkeys[3]: 33 }, { self.aggpubkeys[4]: 37 }, { self.aggpubkeys[0]: 42 }, { self.aggpubkeys[1]: 43 }, ] blockchaininfo = node.getblockchaininfo() assert_equal(blockchaininfo["aggregatePubkeys"], expectedAggPubKeys) self.stop_node(0) self.log.info("Restarting node with '-reindex-chainstate'") self.start_node(0, extra_args=["-reindex-chainstate"]) self.sync_all() self.stop_node(0) self.log.info("Restarting node with '-loadblock'") shutil.copyfile( os.path.join(self.nodes[0].datadir, NetworkDirName(), 'blocks', 'blk00000.dat'), os.path.join(self.nodes[0].datadir, 'blk00000.dat')) os.remove( os.path.join(self.nodes[0].datadir, NetworkDirName(), 'blocks', 'blk00000.dat')) extra_args = [ "-loadblock=%s" % os.path.join(self.nodes[0].datadir, 'blk00000.dat'), "-reindex" ] self.start_node(0, extra_args)
def run_test(self): self.nodes[0].generate(1, self.signblockprivkey_wif) #create tokens colorId = create_colored_transaction(2, 100, self.nodes[0])['color'] self.sync_all() self.nodes[2].generate(1, self.signblockprivkey_wif) # Create and fund a raw tx for sending 10 TPC psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt'] # 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(psbtx1)['psbt'] final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] self.nodes[0].sendrawtransaction(final_tx, True) self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() # Create and fund a raw tx for sending colored coin psbtx2 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress("tokenpsbt", colorId):10, self.nodes[0].getnewaddress("tokenpsbt", colorId):90})['psbt'] psbtx = self.nodes[1].walletprocesspsbt(psbtx2)['psbt'] assert_equal(psbtx2, psbtx) signed_tx = self.nodes[0].walletprocesspsbt(psbtx2)['psbt'] final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] self.nodes[0].sendrawtransaction(final_tx, True) self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() # Create p2sh and 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("") cp2pkh = self.nodes[1].getnewaddress("", colorId) # fund those addresses rawtx = self.nodes[0].createrawtransaction([], {p2sh:10, p2pkh:10}) rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition":2}) rawtx = self.nodes[0].createrawtransaction([], {cp2pkh :10}) rawtx = self.nodes[0].fundrawtransaction(rawtx) rawtx = self.nodes[0].createrawtransaction([], {p2sh:30, p2pkh:30, cp2pkh : 10}) rawtx = self.nodes[0].fundrawtransaction(rawtx) signed_tx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'], [], "ALL", self.options.scheme)['hex'] txid = self.nodes[0].sendrawtransaction(signed_tx, True) self.nodes[0].generate(6, self.signblockprivkey_wif) 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():29.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'], True) # partially sign multisig things with node 1 psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2sh_pos}], {self.nodes[1].getnewaddress():29.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'], True) # 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 psbt with signatures cannot be converted signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'], [], "ALL", self.options.scheme) assert_raises_rpc_error(-22, "Inputs must not have scriptSigs", self.nodes[0].converttopsbt, signedtx['hex']) # 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) self.nodes[0].generate(6, self.signblockprivkey_wif) sync_blocks(self.nodes) #mempool is not synched. vout1 = find_output(self.nodes[1], txid1, 13) vout2 = find_output(self.nodes[2], txid2, 13) # 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)['psbt'] psbt1_decoded = self.nodes[0].decodepsbt(psbt1) assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1] psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt'] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1] # 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, True) self.nodes[0].generate(6, self.signblockprivkey_wif) sync_blocks(self.nodes) #mempool is not synched. # 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":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_equal(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 psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, 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 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([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}]) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) for tx_in in decoded_psbt["tx"]["vin"]: assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE assert_equal(decoded_psbt["tx"]["locktime"], 0) # 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']) # 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'])
def run_test(self): # Start with a 1 block chain self.nodes[0].generate(1, self.signblockprivkey_wif) assert_equal(self.nodes[0].getblockcount(), 1) # create a coloured coin transaction res = create_colored_transaction(2, 1000, self.nodes[0]) colorid = res['color'] ctxid = res['txid'] #new blocks to confirm the coin issue self.nodes[0].generate(10, self.signblockprivkey_wif) self.sync_all() #create multiple coiored coin outputs for use later txid_fee = self.nodes[0].getblock(self.nodes[0].getblockhash(2))['tx'][0] color_tx = self.nodes[0].createrawtransaction([{"txid": ctxid, "vout": 0}, {"txid": txid_fee, "vout": 0} ], [{self.nodes[0].getnewaddress("", colorid): 100}, {self.nodes[0].getnewaddress("", colorid): 100}, {self.nodes[0].getnewaddress("", colorid): 100}, {self.nodes[0].getnewaddress("", colorid): 100}, {self.nodes[1].getnewaddress("", colorid): 600}, {self.nodes[0].getnewaddress():49.99}]) color_txid = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(color_tx, [], "ALL", self.options.scheme)["hex"]) # Mine 3 blocks # blocks 3 to 15 are spend-able. self.nodes[0].generate(5, self.signblockprivkey_wif) self.sync_all() #collect coinbase tx ids whose outputs will be spent or used as fee later b = [ self.nodes[0].getblockhash(n) for n in range(3, 15) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() node0_caddress = self.nodes[0].getnewaddress("", colorid) node1_caddress = self.nodes[1].getnewaddress("", colorid) for (address_n0, address_n1, amt, height) in [(node0_address, node1_address, 49.99, 3), (node0_caddress, node1_caddress, 50, 7)]: # Three scenarios for re-orging coinbase spends in the memory pool: # 1. Direct coinbase spend : spend_101 # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1 # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. self.log.info("From address [%s]" % address_n0) #create transactions with colored coin and with TPC if amt == 50: spend_101_raw = self.nodes[0].createrawtransaction([{"txid": color_txid, "vout": 1}, {"txid": coinbase_txids[height], "vout": 0}], [{address_n1: amt}, {node0_address: 49.99}]) spend_101_raw = self.nodes[0].signrawtransactionwithwallet(spend_101_raw, [], "ALL", self.options.scheme)["hex"] spend_102_raw = self.nodes[0].createrawtransaction([{"txid": color_txid, "vout": 2}, {"txid": coinbase_txids[height+1], "vout": 0}], [{address_n0: amt}, {node0_address: 49.99}]) spend_102_raw = self.nodes[0].signrawtransactionwithwallet(spend_102_raw, [], "ALL", self.options.scheme)["hex"] spend_103_raw = self.nodes[0].createrawtransaction([{"txid": color_txid, "vout": 3}, {"txid": coinbase_txids[height+2], "vout": 0}], [{address_n0: amt}, {node0_address: 49.99}]) spend_103_raw = self.nodes[0].signrawtransactionwithwallet(spend_103_raw, [], "ALL", self.options.scheme)["hex"] # Create a transaction which is time-locked to two blocks in the future timelock_tx = self.nodes[0].createrawtransaction([{"txid": color_txid, "vout": 0}, {"txid": coinbase_txids[height+3], "vout": 0}], [{address_n0: amt}, {node0_address: 49.99}]) else: spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[height+1], address_n1, amount=amt) spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[height+2], address_n0, amount=amt) spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[height+3], address_n0, amount=amt) # Create a transaction which is time-locked to two blocks in the future timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[height], "vout": 0}], {address_n0: amt}) # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransactionwithwallet(timelock_tx, [], "ALL", self.options.scheme)["hex"] # This will raise an exception because the timelock transaction is too immature to spend assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) new_blocks = self.nodes[0].generate(1, self.signblockprivkey_wif) # Time-locked transaction is still too immature to spend assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: if amt == 50: spend_102_1_raw = self.nodes[0].createrawtransaction([{"txid": spend_102_id, "vout": 0}, {"txid": spend_102_id, "vout": 1}], [{address_n1: amt}, {node0_address: 49.98}]) spend_102_1_raw = self.nodes[0].signrawtransactionwithwallet(spend_102_1_raw, [], "ALL", self.options.scheme)["hex"] spend_103_1_raw = self.nodes[0].createrawtransaction([{"txid": spend_103_id, "vout": 0}, {"txid": spend_103_id, "vout": 1}], [{address_n1: amt}, {node0_address: 49.98}]) spend_103_1_raw = self.nodes[0].signrawtransactionwithwallet(spend_103_1_raw, [], "ALL", self.options.scheme)["hex"] else: spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, address_n1, amount=49.98) spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, address_n1, amount=49.98) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1, self.signblockprivkey_wif) # Time-locked transaction can now be spent timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw) self.sync_all() assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id}) for node in self.nodes: node.invalidateblock(last_block[0]) # Time-locked transaction is now too immature and has been removed from the mempool # spend_103_1 has been re-orged out of the chain and is back in the mempool assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id}) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: for node in self.nodes: node.invalidateblock(new_blocks[0]) self.sync_all() # mempool should have all trasnactions. assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_id, spend_102_1_id, spend_103_id, spend_103_1_id}) self.nodes[0].generate(1, self.signblockprivkey_wif) # finally mempool should be empty assert_equal(self.nodes[0].getrawmempool(), []) self.log.info("Done")
def run_test(self): node1 = self.nodes[1] node0 = self.nodes[0] # Get out of IBD node1.generate(1, self.signblockprivkey_wif) colorid = create_colored_transaction(2, 500, node1)['color'] node1.generate(1, self.signblockprivkey_wif) [ node1.sendtoaddress(node0.getnewaddress("", colorid), 10) for x in range(3) ] node1.generate(1, self.signblockprivkey_wif) sync_blocks(self.nodes) self.nodes[0].add_p2p_connection(TestP2PConn()) # Test that invs are received for all txs at feerate of 20 sat/byte node1.settxfee(Decimal("0.00020000")) txids = [ node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3) ] ctxids = [ node1.sendtoaddress(node1.getnewaddress("", colorid), 10) for x in range(3) ] assert (allInvsMatch(txids + ctxids, self.nodes[0].p2p)) self.nodes[0].p2p.clear_invs() # Set a filter of 15 sat/byte self.nodes[0].p2p.send_and_ping(msg_feefilter(15000)) # Test that txs are still being received (paying 20 sat/byte) txids = [ node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3) ] ctxids = [ node1.sendtoaddress(node1.getnewaddress("", colorid), 10) for x in range(3) ] assert (allInvsMatch(txids + ctxids, self.nodes[0].p2p)) self.nodes[0].p2p.clear_invs() # Change tx fee rate to 10 sat/byte and test they are no longer received node1.settxfee(Decimal("0.00010000")) [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)] [ node1.sendtoaddress(node1.getnewaddress("", colorid), 10) for x in range(3) ] sync_mempools(self.nodes) # must be sure node 0 has received all txs # Send one transaction from node0 that should be received, so that we # we can sync the test on receipt (if node1's txs were relayed, they'd # be received by the time this node0 tx is received). This is # unfortunately reliant on the current relay behavior where we batch up # to 35 entries in an inv, which means that when this next transaction # is eligible for relay, the prior transactions from node1 are eligible # as well. node0.settxfee(Decimal("0.00020000")) txids = [node0.sendtoaddress(node0.getnewaddress(), 1)] ctxids = [node0.sendtoaddress(node0.getnewaddress("", colorid), 10)] assert (allInvsMatch(txids + ctxids, self.nodes[0].p2p)) self.nodes[0].p2p.clear_invs() # Remove fee filter and check that txs are received again self.nodes[0].p2p.send_and_ping(msg_feefilter(0)) txids = [ node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3) ] ctxids = [ node1.sendtoaddress(node1.getnewaddress("", colorid), 10) for x in range(3) ] assert (allInvsMatch(txids + ctxids, self.nodes[0].p2p)) self.nodes[0].p2p.clear_invs()
def run_test(self): colorid = create_colored_transaction(2, 100, self.nodes[0])['color'] self.nodes[1].generate(1, self.signblockprivkey_wif) sync_blocks(self.nodes) balance = self.nodes[0].getbalance() txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10")) txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10")) txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10")) txD = self.nodes[0].sendtoaddress( self.nodes[0].getnewaddress("", colorid), 15) self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress("", colorid), 80) sync_mempools(self.nodes) self.nodes[1].generate(1, self.signblockprivkey_wif) # Can not abandon non-wallet transaction assert_raises_rpc_error( -5, 'Invalid or non-wallet transaction id', lambda: self.nodes[0].abandontransaction(txid='ff' * 32)) # Can not abandon confirmed transaction assert_raises_rpc_error( -5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA)) sync_blocks(self.nodes) newbalance = self.nodes[0].getbalance() assert (balance - newbalance < Decimal("0.001") ) #no more than fees lost balance = newbalance # Disconnect nodes so node0's transactions don't get into node1's mempool disconnect_nodes(self.nodes[0], 1) # Identify the 10tpc outputs nA = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( txA, 1)["vout"]) if vout["value"] == Decimal("10")) nB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( txB, 1)["vout"]) if vout["value"] == Decimal("10")) nC = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( txC, 1)["vout"]) if vout["value"] == Decimal("10")) nD = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( txD, 1)["vout"]) if vout["value"] == 15) inputs = [] # spend 10tpc outputs from txA and txB inputs.append({"txid": txA, "vout": nA}) inputs.append({"txid": txB, "vout": nB}) outputs = {} outputs[self.nodes[0].getnewaddress()] = Decimal("14.99998") outputs[self.nodes[1].getnewaddress()] = Decimal("5") signed = self.nodes[0].signrawtransactionwithwallet( self.nodes[0].createrawtransaction(inputs, outputs), [], "ALL", self.options.scheme) txAB1 = self.nodes[0].sendrawtransaction(signed["hex"]) # Identify the 14.99998tpc output nAB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction( txAB1, 1)["vout"]) if vout["value"] == Decimal("14.99998")) #Create a child tx spending AB1 and C inputs = [] inputs.append({"txid": txAB1, "vout": nAB}) inputs.append({"txid": txC, "vout": nC}) inputs.append({"txid": txD, "vout": nD}) outputs = {} outputs[self.nodes[0].getnewaddress()] = Decimal("24.9996") outputs[self.nodes[1].getnewaddress("", colorid)] = 7 outputs[self.nodes[0].getnewaddress("", colorid)] = 8 signed2 = self.nodes[0].signrawtransactionwithwallet( self.nodes[0].createrawtransaction(inputs, outputs), [], "ALL", self.options.scheme) txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"]) # Create a child tx spending ABC2 signed3_change = Decimal("24.999") inputs = [{"txid": txABC2, "vout": 0}, {"txid": txABC2, "vout": 2}] outputs = [{ self.nodes[0].getnewaddress(): signed3_change }, { self.nodes[0].getnewaddress("", colorid): 2 }, { self.nodes[1].getnewaddress("", colorid): 6 }] signed3 = self.nodes[0].signrawtransactionwithwallet( self.nodes[0].createrawtransaction(inputs, outputs), [], "ALL", self.options.scheme) # note tx is never directly referenced, only abandoned as a child of the above self.nodes[0].sendrawtransaction(signed3["hex"]) # In mempool txs from self should increase balance from change newbalance = self.nodes[0].getbalance() walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance'][colorid], 7) walletinfo = self.nodes[1].getwalletinfo() assert_equal(walletinfo['balance'][colorid], 80) assert_equal(newbalance, balance - Decimal("30") + signed3_change) balance = newbalance # Restart the node with a higher min relay fee so the parent tx is no longer in mempool # TODO: redo with eviction self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) # Verify txs no longer in either node's mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(len(self.nodes[1].getrawmempool()), 0) walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance'][colorid], 5) walletinfo = self.nodes[1].getwalletinfo() assert_equal(walletinfo['balance'][colorid], 80) # Not in mempool txs from self should only reduce balance # inputs are still spent, but change not received newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - signed3_change) # Unconfirmed received funds that are not in mempool, also shouldn't show # up in unconfirmed balance unconfbalance = self.nodes[0].getunconfirmedbalance( ) + self.nodes[0].getbalance() assert_equal(unconfbalance, newbalance) # Also shouldn't show up in listunspent assert (not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)]) balance = newbalance # Abandon original transaction and verify inputs are available again # including that the child tx was also abandoned self.nodes[0].abandontransaction(txAB1) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance + Decimal("30")) balance = newbalance # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(self.nodes[0].getbalance(), balance) # But if it is received again then it is unabandoned # And since now in mempool, the change is available # But its child tx remains abandoned self.nodes[0].sendrawtransaction(signed["hex"]) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998")) balance = newbalance # Send child tx again so it is unabandoned self.nodes[0].sendrawtransaction(signed2["hex"]) newbalance = self.nodes[0].getbalance() assert_equal( newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996")) balance = newbalance # Remove using high relay fee again self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("24.9996")) balance = newbalance # Create a double spend of AB1 by spending again from only A's 10 output # Mine double spend from node 1 inputs = [] inputs.append({"txid": txA, "vout": nA}) outputs = {} outputs[self.nodes[1].getnewaddress()] = Decimal("9.9999") tx = self.nodes[0].createrawtransaction(inputs, outputs) signed = self.nodes[0].signrawtransactionwithwallet( tx, [], "ALL", self.options.scheme) self.nodes[1].sendrawtransaction(signed["hex"]) self.nodes[1].generate(1, self.signblockprivkey_wif) connect_nodes(self.nodes[0], 1) sync_blocks(self.nodes) # Verify that B and C's 10 TPC outputs are available for spending again because AB1 is now conflicted newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance + Decimal("20")) balance = newbalance # There is currently a minor bug around this and so this test doesn't work. See Issue #7315 # Invalidate the block with the double spend and B's 10 TPC output should no longer be available # Don't think C's should either self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) newbalance = self.nodes[0].getbalance() #assert_equal(newbalance, balance - Decimal("10")) self.log.info( "If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer" ) self.log.info( "conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315" ) self.log.info(str(balance) + " -> " + str(newbalance) + " ?")
def run_test(self): self.log.info('prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1, self.signblockprivkey_wif) self.sync_all() #generate one block that matures immediately for spending self.nodes[0].generate(1, self.signblockprivkey_wif) colorid = create_colored_transaction(2, 500, self.nodes[0])['color'] self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),5.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress("", colorid), 5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress("", colorid), 10) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress("", colorid), 50) self.sync_all() self.log.info('Test getrawtransaction on genesis block coinbase returns an error') block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) self.log.info('Check parameter types and required parameters of createrawtransaction') # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo') # Test `createrawtransaction` invalid `inputs` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {}) assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {}) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() caddress = self.nodes[0].getnewaddress("", colorid) caddress2 = self.nodes[0].getnewaddress("", colorid) assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') self.nodes[0].createrawtransaction(inputs=[], outputs={}) # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid Tapyrus address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {caddress: 'foo'}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {caddress: '66ae'}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {caddress: 66.99}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {caddress: -1}) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % caddress, self.nodes[0].createrawtransaction, [], multidict([(caddress, 1), (caddress, 1)])) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}]) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % caddress, self.nodes[0].createrawtransaction, [], [{caddress: 1}, {caddress: 1}]) assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}]) assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) # Test `createrawtransaction` invalid `replaceable` assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') self.log.info('Check that createrawtransaction accepts an array and object as outputs') tx = CTransaction() # One output tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99})))) assert_equal(len(tx.vout), 1) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]), ) # One colored output tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={caddress: 99})))) assert_equal(len(tx.vout), 1) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{caddress: 99}]), ) # Two outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]), ) # Two colored outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(caddress, 99), (caddress2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{caddress: 99}, {caddress2: 99}]), ) # Two data outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([('data', '99'), ('data', '99')]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{'data': '99'}, {'data': '99'}]), ) # Multiple mixed outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (caddress, 99), ('data', '99'), ('data', '99')]))))) assert_equal(len(tx.vout), 4) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {caddress: 99}, {'data': '99'}, {'data': '99'}]), ) addr = self.nodes[0].getnewaddress("") addrinfo = self.nodes[0].getaddressinfo(addr) pubkey = addrinfo["scriptPubKey"] self.log.info('sendrawtransaction with missing prevtx info') # Test `signrawtransactionwithwallet` invalid `prevtxs` inputs = [ {'txid' : txid, 'vout' : 3, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) prevtx = dict(txid=txid, scriptPubKey=pubkey, vout=3, amount=1) succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx], "ALL", self.options.scheme) assert succ["complete"] del prevtx["amount"] succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx], "ALL", self.options.scheme) assert succ["complete"] assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "scriptPubKey": pubkey, "token": "TPC", "amount": 1, } ], "ALL", self.options.scheme) assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "scriptPubKey": pubkey, "token": "TPC", "vout": 3, "amount": 1, } ], "ALL", self.options.scheme) assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "token": "TPC", "vout": 3, "amount": 1 } ], "ALL", self.options.scheme) ######################################### # sendrawtransaction with missing input # ######################################### self.log.info('sendrawtransaction with missing input') inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1}] #won't exists outputs = { self.nodes[0].getnewaddress() : 4.998 } rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx, [], "ALL", self.options.scheme) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) block1, block2 = self.nodes[2].generate(2, self.signblockprivkey_wif) self.sync_all() # We should be able to get the raw transaction by providing the correct block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx['txid'], tx) assert_equal(gottx['in_active_chain'], True) # We should not have the 'in_active_chain' flag when we don't provide a block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True) assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar") assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234") assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx['in_active_chain'], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) ######################### # RAW TX MULTISIG TESTS # ######################### # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) self.nodes[0].createmultisig(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) # createmultisig can only take public keys assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1]) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here. mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr1])['address'] #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 TPC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() assert_equal(self.nodes[2].getbalance(), bal+Decimal('1.20000000')) #node2 has both keys of the 2of2 ms addr., tx should affect the balance # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "token": "TPC", "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet(rawTx, inputs, "ALL", self.options.scheme) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs, "ALL", self.options.scheme) assert_equal(rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() new_block = self.nodes[0].generate(1, self.signblockprivkey_wif)[0] self.sync_all() #get block reward blockData = self.nodes[0].getblock(new_block) blockReward = self.nodes[0].gettransaction(blockData['tx'][0])['amount'] assert_equal(self.nodes[0].getbalance(), bal+blockReward+Decimal('2.19000000')) #block reward + tx # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() assert_equal(self.nodes[2].getbalance(), bal) # the funds of a 2of2 multisig tx should not be marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx2['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "token": "TPC", "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet(rawTx2, inputs, "ALL", self.options.scheme) self.log.debug(rawTxPartialSigned1) assert_equal(rawTxPartialSigned1['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet(rawTx2, inputs, "ALL", self.options.scheme) self.log.debug(rawTxPartialSigned2) assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx rawTxComb = self.nodes[2].combinerawtransaction([rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() new_block = self.nodes[0].generate(1, self.signblockprivkey_wif)[0] self.sync_all() #get block reward blockData = self.nodes[0].getblock(new_block) blockReward = self.nodes[0].gettransaction(blockData['tx'][0])['amount'] assert_equal(self.nodes[0].getbalance(), bal+blockReward+Decimal('2.19000000')) #block reward + tx # decoderawtransaction tests # witness transaction encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000102616100000000" assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx) # non-witness transaction encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx) assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["txid"] assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex']) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex']) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex']) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to update this test every time the output format changes. assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) # 9. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}] outputs = { self.nodes[0].getnewaddress() : 1 } assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}] outputs = { self.nodes[0].getnewaddress() : 1 } assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) #################################### # TRANSACTION FEATURES NUMBER TESTS # #################################### # Test the minimum transaction feature number that fits in a signed 32-bit integer. tx = CTransaction() tx.nFeatures = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['features'], -0x80000000) # Test the maximum transaction feature number that fits in a signed 32-bit integer. tx = CTransaction() tx.nFeatures = 0x7fffffff rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['features'], 0x7fffffff)
def run_test(self): # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) assert_equal(len(self.nodes[2].listunspent()), 0) self.log.info("Mining blocks...") self.nodes[0].generate(1, self.signblockprivkey_wif) out = create_colored_transaction(2, 100, self.nodes[0]) colorid1 = out['color'] ctxid = out['txid'] self.nodes[0].generate(1, self.signblockprivkey_wif) walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance']['TPC'], 100) assert_equal(walletinfo['balance'][colorid1], 100) self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 100) assert_equal(math.floor(self.nodes[1].getbalance()), 50) assert_equal(self.nodes[2].getbalance(), 0) # Check that only first and second nodes have UTXOs utxos = self.nodes[0].listunspent() assert_equal(len(utxos), 3) assert_equal(len(self.nodes[1].listunspent()), 1) assert_equal(len(self.nodes[2].listunspent()), 0) self.log.info("test gettxout") utxo = [utxo for utxo in utxos if utxo["amount"] >= 50 and utxo["token"] == 'TPC'] confirmed_txid, confirmed_index = utxo[0]["txid"], utxo[0]["vout"] # First, outputs that are unspent both in the chain and in the # mempool should appear with or without include_mempool txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=False) assert_equal(math.floor(txout['value']), 50) txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True) assert_equal(math.floor(txout['value']), 50) # Send 21 TPC from 0 to 2 using sendtoaddress call. # Locked memory should use at least 32 bytes to sign each transaction self.log.info("test getmemoryinfo") memory_before = self.nodes[0].getmemoryinfo() mempool_txid1 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11) mempool_txid2 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10) memory_after = self.nodes[0].getmemoryinfo() assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used']) self.log.info("test gettxout (second part)") # utxo spent in mempool should be visible if you exclude mempool # but invisible if you include mempool txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) assert_equal(math.floor(txout['value']), 50) txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) assert txout is None # new utxo from mempool should be invisible if you exclude mempool # but visible if you include mempool txout = self.nodes[0].gettxout(mempool_txid1, 0, False) assert txout is None txout = self.nodes[0].gettxout(mempool_txid2, 0, False) assert txout is None txout1 = self.nodes[0].gettxout(mempool_txid1, 0, True) txout2 = self.nodes[0].gettxout(mempool_txid1, 1, True) txout3 = self.nodes[0].gettxout(mempool_txid2, 0, True) txout4 = self.nodes[0].gettxout(mempool_txid2, 1, True) # note the mempool tx will have randomly assigned indices # but 10 will go to node2 and the rest will go to node0 balance = self.nodes[0].getbalance() assert_equal([txout1['value'] + txout2['value'] + txout3['value'] + txout4['value']], [Decimal('10') + Decimal('11') + balance]) walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['balance']['TPC'], balance) assert_equal(walletinfo['balance'][colorid1], 100) # Have node0 mine a block, thus it will collect its own fee. self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) # Exercise locking of unspent outputs unspent_0 = self.nodes[2].listunspent()[0] unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} assert_raises_rpc_error(-8, "Invalid parameter, expected locked output", self.nodes[2].lockunspent, True, [unspent_0]) self.nodes[2].lockunspent(False, [unspent_0]) assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0]) assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", self.nodes[2].lockunspent, False, [{"txid": "0000000000000000000000000000000000", "vout": 0}]) assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}]) # An output should be unlocked when spent unspent_0 = self.nodes[1].listunspent()[0] self.nodes[1].lockunspent(False, [unspent_0]) tx = self.nodes[1].createrawtransaction([unspent_0], { self.nodes[1].getnewaddress() : 1 }) tx = self.nodes[1].fundrawtransaction(tx)['hex'] tx = self.nodes[1].signrawtransactionwithwallet(tx, [], "ALL", self.options.scheme)["hex"] self.nodes[1].sendrawtransaction(tx) assert_equal(len(self.nodes[1].listlockunspent()), 0) # Have node1 generate 1 block (so node0 can recover the fee) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) # node0 should end up with 100 TPC in block rewards plus fees, but # minus the 21 plus fees sent to node2 assert_equal(self.nodes[0].getbalance(), 150 - 21) assert_equal(self.nodes[2].getbalance(), 21) # Node0 should have two unspent outputs. # Create a couple of transactions to send them to node2, submit them through # node1, and make sure both node0 and node2 pick them up properly: node0utxos = self.nodes[0].listunspent(1) assert_equal(len(node0utxos), 4) # create both transactions txns_to_send = [] spend_i = 0 for i, utxo in enumerate(node0utxos): if utxo['token'] != 'TPC': continue spend_i = i inputs = [] outputs = {} inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx, [], "ALL", self.options.scheme)) # Have node 1 (miner) send the transactions self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True) self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True) self.nodes[1].sendrawtransaction(txns_to_send[2]["hex"], True) # Have node1 mine a block to confirm transactions: self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 141) # Verify that a spent output cannot be locked anymore spent_itx = {"txid": node0utxos[spend_i]["txid"], "vout": node0utxos[spend_i]["vout"]} assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output", self.nodes[0].lockunspent, False, [spent_itx]) # Send 10 TPC normal address = self.nodes[0].getnewaddress("test") fee_per_byte = Decimal('0.001') / 1000 self.nodes[2].settxfee(fee_per_byte * 1000) txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) # generate block on another node so that balance is not distorted by block reward self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(1, self.signblockprivkey_wif)[0] self.sync_all([self.nodes[0:3]]) node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('131'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 TPC with subtract fee from amount txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) # generate block on another node so that balance is not distorted by block reward self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Sendmany 10 TPC txid = self.nodes[2].sendmany({address: 10}, "", []) # generate block on another node so that balance is not distorted by block reward self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 10 TPC with subtract fee from amount txid = self.nodes[2].sendmany({address: 10}, "", [address]) # generate block on another node so that balance is not distorted by block reward self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Test ResendWalletTransactions: # Create a couple of transactions, then start up a fourth # node (nodes[3]) and ask nodes[0] to rebroadcast. # EXPECT: nodes[3] should have those transactions in its mempool. txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) sync_mempools(self.nodes[0:2]) self.stop_nodes() self.start_node(3, extra_args = ['-acceptnonstdtxn=1']) self.start_node(2, extra_args = ['-acceptnonstdtxn=1']) self.start_node(1, extra_args = ['-acceptnonstdtxn=1']) self.start_node(0, extra_args = ['-acceptnonstdtxn=1']) connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) connect_nodes_bi(self.nodes, 0, 3) sync_blocks(self.nodes) relayed = self.nodes[0].resendwallettransactions() assert_equal(set(relayed), {txid1, txid2}) sync_mempools(self.nodes) assert(txid1 in self.nodes[3].getrawmempool()) # Exercise balance rpcs assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"]['TPC'], 1) assert_equal(self.nodes[0].getunconfirmedbalance(), 1) # check if we can list zero value tx as available coins # 1. create raw_tx # 2. hex-changed one output to 0.0 # 3. sign and send # 4. check if recipient (node0) can list the zero value tx usp = self.nodes[1].listunspent(query_options={'minimumAmount': '49.998'})[0] inputs = [{"txid": usp['txid'], "vout": usp['vout']}] outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11} raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32) signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx, [], "ALL", self.options.scheme) decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex']) zero_value_txid = decoded_raw_tx['txid'] self.nodes[1].sendrawtransaction(signed_raw_tx['hex'], True) self.sync_all() self.nodes[1].generate(1, self.signblockprivkey_wif) # mine a block self.sync_all() unspent_txs = self.nodes[0].listunspent() # zero value tx must be in listunspents output found = False for uTx in unspent_txs: if uTx['txid'] == zero_value_txid: found = True assert_equal(uTx['amount'], Decimal('0')) assert(found) # do some -walletbroadcast tests self.stop_nodes() self.start_node(0, ["-walletbroadcast=0"]) self.start_node(1, ["-walletbroadcast=0"]) self.start_node(2, ["-walletbroadcast=0"]) connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) self.sync_all([self.nodes[0:3]]) txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) self.nodes[1].generate(1, self.signblockprivkey_wif) # mine a block, tx should not be in there self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted # now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) self.nodes[1].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) node_2_bal += 2 tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) assert_equal(self.nodes[2].getbalance(), node_2_bal) # create another tx self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) # restart the nodes with -walletbroadcast=1 self.stop_nodes() self.start_node(0) self.start_node(1) self.start_node(2) connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1, self.signblockprivkey_wif) sync_blocks(self.nodes[0:3]) node_2_bal += 2 # tx should be added to balance because after restarting the nodes tx should be broadcast assert_equal(self.nodes[2].getbalance(), node_2_bal) # send a tx with value in a string (PR#6380 +) txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") tx_obj = self.nodes[0].gettransaction(txid) assert_equal(tx_obj['amount'], Decimal('-2')) txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") tx_obj = self.nodes[0].gettransaction(txid) assert_equal(tx_obj['amount'], Decimal('-0.0001')) # check if JSON parser can handle scientific notation in strings txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") tx_obj = self.nodes[0].gettransaction(txid) assert_equal(tx_obj['amount'], Decimal('-0.0001')) # This will raise an exception because the amount type is wrong assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") # This will raise an exception since generate does not accept a string assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2", self.signblockprivkey) # Import address and private key to check correct behavior of spendable unspents # 1. Send some coins to generate new UTXO address_to_import = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(address_to_import, 1) self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) # 2. Import address from node2 to node1 self.nodes[1].importaddress(address_to_import) # 3. Validate that the imported address is watch-only on node1 assert(self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"]) # 4. Check that the unspents after import are not spendable assert_array_result(self.nodes[1].listunspent(), {"address": address_to_import}, {"spendable": False}) # 5. Import private key of the previously imported address on node1 priv_key = self.nodes[2].dumpprivkey(address_to_import) self.nodes[1].importprivkey(priv_key) # 6. Check that the unspents are now spendable on node1 assert_array_result(self.nodes[1].listunspent(), {"address": address_to_import}, {"spendable": True}) # Mine a block from node0 to an address from node1 coinbase_addr = self.nodes[1].getnewaddress() block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr, self.signblockprivkey_wif)[0] coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 self.nodes[1].gettransaction(coinbase_txid) # check if wallet or blockchain maintenance changes the balance self.sync_all([self.nodes[0:3]]) blocks = self.nodes[0].generate(2, self.signblockprivkey_wif) self.sync_all([self.nodes[0:3]]) balance_nodes = [self.nodes[i].getbalance() for i in range(3)] block_count = self.nodes[0].getblockcount() # Check modes: # - True: unicode escaped as \u.... # - False: unicode directly as UTF-8 for mode in [True, False]: self.nodes[0].rpc.ensure_ascii = mode # unicode check: Basic Multilingual Plane, Supplementary Plane respectively for label in [u'ббаБаА', u'№ Ё']: addr = self.nodes[0].getnewaddress() self.nodes[0].setlabel(addr, label) assert_equal(self.nodes[0].getaddressinfo(addr)['label'], label) assert(label in self.nodes[0].listlabels()) self.nodes[0].rpc.ensure_ascii = True # restore to default # maintenance tests maintenance = [ '-rescan', '-reindex', '-zapwallettxes=1', '-zapwallettxes=2', # disabled until issue is fixed: https://github.com/bitcoin/bitcoin/issues/7463 # '-salvagewallet', ] chainlimit = 6 for m in maintenance: self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)]) self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)]) self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)]) if m == '-reindex': # reindex will leave rpc warm up "early"; Wait for it to finish wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)]) assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)]) # Exercise listsinceblock with the last two blocks coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0]) assert_equal(coinbase_tx_1["lastblock"], blocks[1]) assert_equal(len(coinbase_tx_1["transactions"]), 1) assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0) # ==Check that wallet prefers to use coins that don't exceed mempool limits ===== # Get all non-zero utxos together chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()] singletxid = self.nodes[0].sendtoaddress(chain_addrs[0], self.nodes[0].getbalance(), "", "", True) node0_balance = self.nodes[0].getbalance() self.nodes[0].generate(1, self.signblockprivkey_wif) # Split into two chains rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')}) signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx, [], "ALL", self.options.scheme) singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"]) self.nodes[0].generate(1, self.signblockprivkey_wif) # Make a long chain of unconfirmed payments without hitting mempool limit # Each tx we make leaves only one output of change on a chain 1 longer # Since the amount to send is always much less than the outputs, we only ever need one output # So we should be able to generate exactly chainlimit txs for each original output sending_addr = self.nodes[1].getnewaddress() txid_list = [] for i in range(chainlimit * 4): #including 2 block rewards its 4 txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001'))) assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 4) assert_equal(len(txid_list), chainlimit * 4) # Without walletrejectlongchains, we will still generate a txid # The tx will be stored in the wallet but not accepted to the mempool extra_txid = self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001')) assert(extra_txid not in self.nodes[0].getrawmempool()) assert(extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()]) self.nodes[0].abandontransaction(extra_txid) total_txs = len(self.nodes[0].listtransactions(99999)) # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) # wait for loadmempool timeout = 10 while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit * 4): time.sleep(0.5) timeout -= 0.5 assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 4) node0_balance = self.nodes[0].getbalance() # With walletrejectlongchains we will not create the tx and store it in our wallet. assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) # Verify nothing new in wallet assert_equal(total_txs, len(self.nodes[0].listtransactions(99999))) # Test getaddressinfo. Note that these addresses are taken from disablewallet.py assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") address_info = self.nodes[0].getaddressinfo("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") assert_equal(address_info["scriptPubKey"], "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac") assert not address_info["ismine"] assert not address_info["iswatchonly"] assert not address_info["isscript"]
def run_test(self): # Mine some coins self.nodes[0].generate(5, self.signblockprivkey_wif) colorid1 = create_colored_transaction(2, 100, self.nodes[0])['color'] self.nodes[0].generate(1, self.signblockprivkey_wif) self.sync_all() # 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 caddr1 = [self.nodes[1].getnewaddress("", colorid1) for i in range(3)] caddr2 = [self.nodes[2].getnewaddress("", colorid1) for i in range(3)] caddrs = caddr1 + caddr2 # 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].sendtoaddress(caddr, 1) for caddr in caddrs] [self.nodes[0].sendtoaddress(caddr, 2) for caddr in caddrs] self.nodes[0].generate(1, self.signblockprivkey_wif) 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) ctxid1 = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress("", colorid1), 2) ctx1 = self.nodes[1].getrawtransaction(ctxid1, True) # txid1 should have 2 input and 2 outputs assert_equal(2, len(ctx1["vin"])) assert_equal(2, len(ctx1["vout"])) # one output should be 2, the other should be fee v = [vout["value"] for vout in ctx1["vout"]] v.sort() assert_approx(v[0], 0.5, 0.0001) assert_approx(v[1], 2) 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) ctxid2 = self.nodes[2].sendtoaddress( self.nodes[0].getnewaddress("", colorid1), 2) ctx2 = self.nodes[2].getrawtransaction(ctxid2, True) # txid2 should have 4 inputs and 3 outputs assert_equal(4, len(ctx2["vin"])) assert_equal(3, len(ctx2["vout"])) # one output should be 2, the other should be fee v = [vout["value"] for vout in ctx2["vout"]] v.sort() assert_approx(v[0], 1) assert_approx(v[1], 1.5, 0.0002) assert_approx(v[2], 2) # 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[2].generate(1, self.signblockprivkey_wif) amt = self.nodes[2].getwalletinfo()['balance'][colorid1] self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress( "", colorid1), amount=amt) self.sync_all() self.nodes[0].generate(1, self.signblockprivkey_wif) # 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'], [], "ALL", self.options.scheme) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].generate(1, self.signblockprivkey_wif) 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)