def _test_getblockheader(self): node = self.nodes[0] assert_raises( JSONRPCException, lambda: node.getblockheader('nonsense')) besthash = node.getbestblockhash() secondbesthash = node.getblockhash(199) header = node.getblockheader(besthash) assert_equal(header['hash'], besthash) assert_equal(header['height'], 200) assert_equal(header['confirmations'], 1) assert_equal(header['previousblockhash'], secondbesthash) assert_is_hex_string(header['chainwork']) assert_is_hash_string(header['hash']) assert_is_hash_string(header['previousblockhash']) assert_is_hash_string(header['merkleroot']) assert_is_hash_string(header['bits'], length=None) assert isinstance(header['time'], int) assert isinstance(header['mediantime'], int) assert isinstance(header['nonce'], int) assert isinstance(header['version'], int) assert isinstance(int(header['versionHex'], 16), int) assert isinstance(header['difficulty'], Decimal)
def run_test(self): print "Mining blocks..." self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) tx1 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1])), [txid1]) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2], blockhash)), txlist) txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 10}) self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # ...or if the first tx is not fully-spent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) try: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist) except JSONRPCException: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1])), txlist) # ...or if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent]) # Quick test of getblock using blockhash and different levels of verbosity result = self.nodes[0].getblock(blockhash, 2) coinbase_txid = result["tx"][0]["txid"] result = self.nodes[0].getblock(blockhash, 1) assert_equal(coinbase_txid, result["tx"][0]) # verbosity 1 only lists txids result = self.nodes[0].getblock(blockhash, 0) assert(c in string.hexdigits for c in result) # verbosity 0 returns raw hex
def run_test (self): print("Mining blocks...") rpc = self.nodes[0] # utxos from block 1 become mature in block 101 rpc.generate(101) self.sync_all() rpc.getinfo() rpc.getwalletinfo() taddr = rpc.getnewaddress() print "Sending to " + taddr # sending to arbitrary non-notary transparent address is not allowed assert_raises(JSONRPCException, rpc.sendtoaddress, taddr,1)
def run_test(self): print "Mining blocks..." self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) tx1 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1])), [txid1]) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2], blockhash)), txlist) txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 10}) self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # ...or if the first tx is not fully-spent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) try: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist) except JSONRPCException: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1])), txlist) # ...or if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent])
def _test_stopatheight(self): assert_equal(self.nodes[0].getblockcount(), 200) self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') self.nodes[0].wait_until_stopped() self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207)
def _test_stopatheight(self): assert_equal(self.nodes[0].getblockcount(), 200) self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: self.bitcoind_processes[0].wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) self.nodes[0] = self.start_node(0, self.options.tmpdir) assert_equal(self.nodes[0].getblockcount(), 207)
def run_test (self): # Test the check_node_log utility function string_to_find = "Pastel version" check_node_log(self, 1, string_to_find) # Node 1 was stopped to check the logs, need to be restarted self.nodes[1] = self.start_node_with(1, []) connect_nodes(self.nodes[1], 0) assert_raises(AssertionError, check_node_log, self, 1, "Will not be found") # Need to start node 1 before leaving the test self.nodes[1] = self.start_node_with(1, []) connect_nodes(self.nodes[1], 0)
def _test_stopatheight(self): assert_equal(self.nodes[0].getblockcount(), 200) self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') self.nodes[0].wait_until_stopped() self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207)
def _test_stopatheight(self): self.log.info("Test stopping at height") assert_equal(self.nodes[0].getblockcount(), HEIGHT) self.nodes[0].generatetoaddress(6, ADDRESS_BCRT1_P2WSH_OP_TRUE) assert_equal(self.nodes[0].getblockcount(), HEIGHT + 6) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_P2WSH_OP_TRUE) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') self.nodes[0].wait_until_stopped() self.start_node(0) assert_equal(self.nodes[0].getblockcount(), HEIGHT + 7)
def _test_stopatheight(self): assert_equal(self.nodes[0].getblockcount(), 200) self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: bitcoind_processes[0].wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) self.nodes[0] = self.start_node(0, self.options.tmpdir) assert_equal(self.nodes[0].getblockcount(), 207)
def run_test(self): # self.nodes[0].generate(200) chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) node0_address = self.nodes[0].getnewaddress() # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is # is too immature to spend. b = [self.nodes[0].getblockhash(n) for n in range(101, 103)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends_raw = [ self.create_tx(txid, node0_address, 50.00) for txid in coinbase_txids ] spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0]) # coinbase at height 102 should be too immature to spend assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, spends_raw[1]) # mempool should have just spend_101: mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 1) assert_equal(self.nodes[0].getrawmempool(), [spend_101_id]) # the size of the memory pool should be greater than 1x ~100 bytes assert_greater_than(mempoolinfo['bytes'], 100) # the actual memory usage should be strictly greater than the size # of the memory pool assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes']) # mine a block, spend_101 should get confirmed self.nodes[0].generate(1) mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 0) assert_equal(mempoolinfo['bytes'], 0) assert_equal(mempoolinfo['usage'], 0) assert_equal(set(self.nodes[0].getrawmempool()), set()) # ... and now height 102 can be spent: spend_102_id = self.nodes[0].sendrawtransaction(spends_raw[1]) mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 1) assert_equal(self.nodes[0].getrawmempool(), [spend_102_id]) assert_greater_than(mempoolinfo['bytes'], 100) assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes'])
def run_test(self): chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) node0_address = self.nodes[0].getnewaddress() # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is # is too immature to spend. b = [ self.nodes[0].getblockhash(n) for n in range(101, 103) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] spends_raw = [ self.create_tx(txid, node0_address, 10) for txid in coinbase_txids ] spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0]) # coinbase at height 102 should be too immature to spend assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, spends_raw[1]) # mempool should have just spend_101: mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 1) assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ]) # the size of the memory pool should be greater than 1x ~100 bytes assert_greater_than(mempoolinfo['bytes'], 100) # the actual memory usage should be strictly greater than the size # of the memory pool assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes']) # mine a block, spend_101 should get confirmed self.nodes[0].generate(1) mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 0) assert_equal(mempoolinfo['bytes'], 0) assert_equal(mempoolinfo['usage'], 0) assert_equal(set(self.nodes[0].getrawmempool()), set()) # ... and now height 102 can be spent: spend_102_id = self.nodes[0].sendrawtransaction(spends_raw[1]) mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['size'], 1) assert_equal(self.nodes[0].getrawmempool(), [ spend_102_id ]) assert_greater_than(mempoolinfo['bytes'], 100) assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes'])
def _test_getblockheader(self): node = self.nodes[0] assert_raises(JSONRPCException, lambda: node.getblockheader('nonsense')) besthash = node.getbestblockhash() secondbesthash = node.getblockhash(199) header = node.getblockheader(besthash) assert_equal(header['hash'], besthash) assert_equal(header['height'], 200) assert_equal(header['confirmations'], 1) assert_equal(header['previousblockhash'], secondbesthash) assert_is_hex_string(header['chainwork']) assert_is_hash_string(header['hash']) assert_is_hash_string(header['previousblockhash']) assert_is_hash_string(header['merkleroot']) assert_is_hash_string(header['bits'], length=None) assert isinstance(header['time'], int) assert isinstance(header['mediantime'], int) assert isinstance(header['nonce'], int) assert isinstance(header['version'], int) assert isinstance(header['difficulty'], Decimal)
def run_test(self): """Main test logic""" self.log.info("Starting test!") [n0, n1, n2] = self.nodes # n2 is the erasing node self.log.info("Generate a few blocks upfront to make sure pruning kicks in.") # On pruning, we must have a chain longer than PruneAfterHeight and bigger than 550 MiB. mine_large_blocks(n0, nblocks=200) self.nodes[0].generate(nblocks=200) self.log.info("Build a \"bad\" transaction.") bad_data = 'n42MaFLwantedToTestThisKYP112MM9jE' tx_bad = n1.createrawtransaction([], {bad_data: 0.001}) (tx_bad, txid_bad) = fund_sign_send(n1, tx_bad) # also adds inputs and change output tx_bad_vouts = n1.decoderawtransaction(tx_bad)['vout'] self.log.info("Add tx to a block, mine a few big blocks on top.") self.sync_all() block_hash_bad = n0.generate(nblocks=1)[0] # significantly lower nblocks might cause pruning not to work (needs changes to bitcoind pruning logic) mine_large_blocks(n0, nblocks=300) self.nodes[0].generate(nblocks=300) self.sync_all() erase_target = {block_hash_bad: {txid_bad: list(range(len(tx_bad_vouts)))}} self.log.info("Assert that node 2 serves the tx via RPC.") assert_equal(bytes_to_hex_str(hash256(hex_str_to_bytes(n2.getrawtransaction(txid_bad)))), txid_bad) self.log.info("Assert that node 2 serves the block with the tx via P2P.") n2.add_p2p_connection(P2PInterface()) n2.p2p.send_message(msg_getdata(inv=[CInv(2, int(block_hash_bad, 16))])) n2.p2p.wait_for_block(int(block_hash_bad, 16), timeout=1) self.log.info("Stopping node 2.") self.stop_node(2) self.log.info("Assert that UTXOs not erased according to tool.") assert_equal(tool.check(erase_target, n2.datadir, 'regtest'), False) def react_to_ui_request(request): assert("enable pruning" in request) self.log.info("Configuring node 2 to enable pruning.") append_config(n2.datadir, ["prune=1"]) assert("start your node" in request) self.log.info("Starting node 2.") self.start_node(2) connect_nodes_bi(self.nodes, 0, 2) self.log.info("Erasing using tool.") tool.interactive_erase(erase_target, n2.datadir, 'regtest', self.log.info, react_to_ui_request) self.log.info("Assert that the tx's block can't be obtained from node 2 via P2P anymore.") n2.add_p2p_connection(P2PInterface()) n2.p2p.send_message(msg_getdata(inv=[CInv(2, int(block_hash_bad, 16))])) assert_raises(AssertionError, n2.p2p.wait_for_block, int(block_hash_bad, 16), timeout=1) self.log.info("Assert that tx is different now when obtained from node 2 via RPC.") assert_raises_rpc_error(-5, None, n2.getrawtransaction, txid_bad) self.log.info("Assert that node 2 accepts new blocks.") n0.generate(nblocks=1) sync_blocks(self.nodes, timeout=1) self.log.info("Spend one output of the bad tx, include that in block.") tx_bad_vout = [x for x in n1.listunspent() if x['txid'] == txid_bad][0] tx_ok = n1.createrawtransaction([tx_bad_vout], {n0.getnewaddress(): 0.5}) (tx_ok, txid_ok) = fund_sign_send(n1, tx_ok) sync_mempools([n0, n1]) n0.generate(nblocks=1) self.log.info("Assert that node 2 accepts the resulting transaction and block.") sync_blocks(self.nodes, timeout=1) assert_equal(bytes_to_hex_str(hash256(hex_str_to_bytes(n2.getrawtransaction(txid_ok)))), txid_ok) self.log.info("Wait for all nodes to sync again, just in case. Should complete immediately.") self.sync_all() self.log.info("Stopping node 2 (again).") self.stop_node(2) self.log.info("Assert that UTXOs are erased according to tool.") assert_equal(tool.check(erase_target, n2.datadir, 'regtest'), True)
def run_test(self): #prepare some coins for multiple *rawtransaction commands self.generate_and_sync_inc(1, 2) self.generate_and_sync_inc(101) 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.sync_all() self.generate_and_sync_inc(5) ######################################### # 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].signrawtransaction(rawtx) errorString = "" try: rawtx = self.nodes[2].sendrawtransaction(rawtx['hex']) except JSONRPCException as e: errorString = e.error['message'] assert_equal("Missing inputs" in errorString, True) ##################################### # 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.sync_all() # We should be able to get the raw transaction by providing the correct block gottx = self.nodes[0].getrawtransaction(tx, 1, 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, 1) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should have hex for the transaction from the getblock and getrawtransaction calls. blk = self.nodes[0].getblock(block1, 2) assert_equal(gottx['hex'], blk['tx'][1]['hex']) # We should not get the tx if we provide an unrelated block assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, block2) # An invalid block hash should raise errors assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, True) assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "foobar") assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "abcd1234") assert_raises( JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") ######################### # RAW TX MULTISIG TESTS # ######################### # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].validateaddress(addr1) addr2Obj = self.nodes[2].validateaddress(addr2) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) mSigObjValid = self.nodes[2].validateaddress(mSigObj) #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 BTC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.generate_and_sync_inc(1) assert_equal( self.nodes[2].getbalance(), bal + Decimal('1.20000') ) #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].validateaddress(addr1) addr2Obj = self.nodes[2].validateaddress(addr2) addr3Obj = self.nodes[2].validateaddress(addr3) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']]) mSigObjValid = self.nodes[2].validateaddress(mSigObj) assert_equal(mSigObjValid['isvalid'], True) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) sPK = rawTx['vout'][0]['scriptPubKey']['hex'] [sPK] # hush pyflakes self.sync_all() self.generate_and_sync_inc(1) # THIS IS A 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.20000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.199} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs) assert_equal(rawTxPartialSigned['complete'], False) # node1 only has one key, can't comp. sign the tx rawTxSigned = self.nodes[2].signrawtransaction(rawTx, inputs) 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() self.generate_and_sync_inc(1) assert_equal(self.nodes[0].getbalance(), bal + self._reward + Decimal('2.19900')) #block reward + tx 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)
def run_test(self): print("Mining blocks...") self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) tx1 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid1 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 10}) txid2 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1 ])), [txid1]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2], blockhash)), txlist) txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction( [txin_spent], {self.nodes[0].getnewaddress(): 10}) self.nodes[0].sendrawtransaction( self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_spent], blockhash)), [txid_spent]) # ...or if the first tx is not fully-spent assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_unspent])), [txid_unspent]) try: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) except JSONRPCException: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid2, txid1])), txlist) # ...or if we have a -txindex assert_equal( self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof( [txid_spent])), [txid_spent]) # Quick test of getblock using blockhash and different levels of verbosity result = self.nodes[0].getblock(blockhash, 2) coinbase_txid = result["tx"][0]["txid"] result = self.nodes[0].getblock(blockhash, 1) assert_equal(coinbase_txid, result["tx"][0]) # verbosity 1 only lists txids result = self.nodes[0].getblock(blockhash, 0) assert (c in string.hexdigits for c in result) # verbosity 0 returns raw hex
def run_test(self): print("---- Pastel ID tests STARTED ----") print(" -pastelid help") assert_shows_help(self.nodes[0].pastelid) coinbase_addr1 = self.nodes[1].getnewaddress() taddr1 = self.nodes[1].getnewaddress() print(" -pastelid newkey") assert_shows_help(self.nodes[0].pastelid, "newkey") self.pastelid1, self.id1_lrkey = self.create_pastelid(0) self.pastelid2 = self.create_pastelid()[0] self.pastelid3, self.id3_lrkey = self.create_pastelid(1) print(f"pastelid1: {self.pastelid1}") print(f"pastelid2: {self.pastelid2}") print(f"pastelid3: {self.pastelid3}") # fail if empty passphrase assert_raises_rpc(rpc.RPC_MISC_ERROR, "passphrase for new key cannot be empty", self.nodes[0].pastelid, "newkey", "") # List all internally stored PastelID and keys print(" -pastelid list") # check Pastel IDs on node0 id_list0 = self.nodes[0].pastelid("list") id_list0 = dict((key+str(i), val) for i, k in enumerate(id_list0) for key, val in k.items()) assert_true(self.pastelid1 in id_list0.values(), f"PastelID {self.pastelid1} not in the list") assert_true(self.pastelid2 in id_list0.values(), f"PastelID {self.pastelid2} not in the list") # check Pastel IDs on node1 id_list1 = self.nodes[1].pastelid("list") id_list1 = dict((key+str(i), val) for i, k in enumerate(id_list1) for key, val in k.items()) assert_true(self.pastelid3 in id_list1.values(), f"PastelID {self.pastelid3} not in the list") print(" -pastelid sign & verify ed448") text_to_sign = "my text to sign" # Sign "text" with the internally stored private key associated with the PastelID # check that signing with existing passphrase works, default algorithm - EdDSA448 signature = self.nodes[0].pastelid("sign", text_to_sign, self.pastelid1, self.passphrase)["signature"] assert_true(signature, "Cannot sign text using existing passphrase. No ed448 signature was created") assert_equal(len(base64.b64decode(signature)), 114) # Verify text"'s "signature" (EdDSA448) with the PastelID result = self.nodes[0].pastelid("verify", text_to_sign, signature, self.pastelid1)["verification"] assert_equal(result, "OK") # Fail to verify EdDSA448 signature with the different key (PastelID2) result = self.nodes[0].pastelid("verify", text_to_sign, signature, self.pastelid2)["verification"] assert_equal(result, "Failed") # Fail to verify modified text (ed448 signature) text_to_sign_modified = 'X' + text_to_sign[1:] result = self.nodes[0].pastelid("verify", text_to_sign_modified, signature, self.pastelid1)["verification"] assert_equal(result, "Failed") # try to sign using PastelID with invalid passphrase assert_raises_rpc(rpc.RPC_MISC_ERROR, self.ERR_READ_PASTELID_FILE, self.nodes[1].pastelid, "sign", text_to_sign, self.pastelid1, self.new_passphrase) print(" -pastelid sign & verify legroast") # Sign with no errors using encoded LegRoast public key # returns base64 encoded signature lr_signature = self.nodes[0].pastelid("sign", text_to_sign, self.pastelid1, self.passphrase, "legroast")["signature"] assert_true(lr_signature, "Cannot sign text using existing passphrase. No LegRoast signature was created") assert_equal(len(base64.b64decode(lr_signature)), 14272) # Verify text"'s "signature" (LegRoast) with the PastelID result = self.nodes[0].pastelid("verify", text_to_sign, lr_signature, self.pastelid1, "legroast")["verification"] assert_equal(result, "OK") # Fail to verify LegRoast signature with the different key (PastelID2) result = self.nodes[0].pastelid("verify", text_to_sign, lr_signature, self.pastelid2, "legroast")["verification"] assert_equal(result, "Failed") # Fail to verify modified text (LegRoast signature) result = self.nodes[0].pastelid("verify", text_to_sign_modified, lr_signature, self.pastelid1, "legroast")["verification"] assert_equal(result, "Failed") # Sign message on node1 with the LegRoast key associated with pastelid3 lr_signature = self.nodes[1].pastelid("sign", text_to_sign, self.pastelid3, self.passphrase, "legroast")["signature"] assert_true(lr_signature, "Cannot sign text on node1 with LegRoast key associated with pastelid3. No LegRoast signature was created") # ... but verify it on node0 that does not have pastelid3 and we don't have any PastelID reg tickets assert_raises_rpc(rpc.RPC_MISC_ERROR, "is not stored locally and PastelID registration ticket was not found in the blockchain", self.nodes[0].pastelid, "verify", text_to_sign, lr_signature, self.pastelid3, "legroast") # now let's register pastelid3 self.generate_and_sync_inc(10) # send all utxos from node #3 to addr[0] to make empty balance self.nodes[0].sendtoaddress(taddr1, self.nodes[0].getbalance(), "empty node0", "test", True) self.generate_and_sync_inc(1) # register pastelid3 txid = self.nodes[1].tickets("register", "id", self.pastelid3, self.passphrase, taddr1) assert_true(txid, "pastelid3 registration failed") self.generate_and_sync_inc(1) # now we should be able to retrieve lr pubkey for pastelid3 on node0 # but first make sure pastelid3 is not stored locally on node0 assert_true(self.pastelid3 not in id_list0.values(), f"PastelID3 {self.pastelid3} should not be stored on node0") # Verify text"'s "signature" (LegRoast) with the PastelID3 result = self.nodes[0].pastelid("verify", text_to_sign, lr_signature, self.pastelid3, "legroast")["verification"] assert_equal(result, "OK") # Fail to verify LegRoast signature with the different key (PastelID1) result = self.nodes[0].pastelid("verify", text_to_sign, lr_signature, self.pastelid1, "legroast")["verification"] assert_equal(result, "Failed") # Fail to verify modified text (LegRoast signature) result = self.nodes[0].pastelid("verify", text_to_sign_modified, lr_signature, self.pastelid3, "legroast")["verification"] assert_equal(result, "Failed") print(" -pastelid passwd") assert_shows_help(self.nodes[0].pastelid, "passwd") # missing new passphrase assert_raises(JSONRPCException, self.nodes[0].pastelid, "passwd", self.pastelid1, self.passphrase) # empty new passphrase assert_raises_message(JSONRPCException, "cannot be empty", self.nodes[0].pastelid, "passwd", self.pastelid1, self.passphrase, "") # empty Pastel ID assert_raises_message(JSONRPCException, "cannot be empty", self.nodes[0].pastelid, "passwd", "", self.passphrase, self.new_passphrase) # empty passphrase assert_raises_message(JSONRPCException, "cannot be empty", self.nodes[0].pastelid, "passwd", self.pastelid1, "", self.new_passphrase) # change passphrase result = self.nodes[0].pastelid("passwd", self.pastelid1, self.passphrase, self.new_passphrase)["result"] assert_equal(result, "successful") # try to sign text using old passphrase assert_raises_message(JSONRPCException, "Failed to decrypt", self.nodes[0].pastelid, "sign", text_to_sign, self.pastelid1, self.passphrase) # signing using new passphrase should work signature = self.nodes[0].pastelid("sign", text_to_sign, self.pastelid1, self.new_passphrase)["signature"] assert_true(signature, "Cannot sign text using existing passphrase. No ed448 signature was created") # verify signature result = self.nodes[0].pastelid("verify", text_to_sign, signature, self.pastelid1)["verification"] assert_equal(result, "OK") print("----- Pastel ID tests FINISHED -----")
def run_test(self): # create a p2p receiver dspReceiver = P2PInterface() self.nodes[0].add_p2p_connection(dspReceiver) # workaround - nodes think they're in IBD unless one block is mined self.nodes[0].generate(1) self.sync_all() # Disconnect the third node, will be used later for triple-spend disconnect_nodes(self.nodes[1], self.nodes[2]) # Put fourth node (the non-dsproof-enabled node) with the connected group # (we will check its log at the end to ensure it ignored dsproof inv's) non_dsproof_node = self.nodes[3] disconnect_nodes(self.nodes[2], non_dsproof_node) connect_nodes(self.nodes[1], non_dsproof_node) # Create and mine a regular non-coinbase transaction for spending fundingtxid = self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0] fundingtx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(fundingtxid)) # Create three conflicting transactions. They are only signed, but not yet submitted to the mempool firstDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 49.95) secondDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 49.95) thirdDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 49.95) # Send the two conflicting transactions to the network # Submit to two different nodes, because a node would simply reject # a double spend submitted through RPC firstDSTxId = self.nodes[0].sendrawtransaction(firstDSTx) self.nodes[1].call_rpc('sendrawtransaction', secondDSTx, ignore_error='txn-mempool-conflict') wait_until( lambda: dspReceiver.message_count["dsproof-beta"] == 1, lock=mininode_lock, timeout=25 ) # 1. The DSP message is well-formed and contains all fields # If the message arrived and was deserialized successfully, then 1. is satisfied dsp = dspReceiver.last_message["dsproof-beta"].dsproof dsps = set() dsps.add(dsp.serialize()) # Check that it is valid, both spends are signed with the same key # NB: pushData is made of the sig + one last byte for hashtype pubkey = self.getpubkey() sighash1 = getSighashes(dsp.getPrevOutput(), dsp.spender1, fundingtx) sighash2 = getSighashes(dsp.getPrevOutput(), dsp.spender2, fundingtx) assert(pubkey.verify_ecdsa(dsp.spender1.pushData[0][:-1], sighash1)) assert(pubkey.verify_ecdsa(dsp.spender2.pushData[0][:-1], sighash2)) # 2. For p2pkh these is exactly one pushdata per spender assert_equal(1, len(dsp.spender1.pushData)) assert_equal(1, len(dsp.spender2.pushData)) # 3. The two spenders are different, specifically the signature (push data) has to be different. assert(dsp.spender1.pushData != dsp.spender2.pushData) # 4. The first & double spenders are sorted with two hashes as keys. assert(dsp.spender1.hashOutputs < dsp.spender2.hashOutputs) # 5. The double spent output is still available in the UTXO database, # implying no spending transaction has been mined. assert_equal(self.nodes[0].gettransaction(firstDSTxId)["confirmations"], 0) # The original fundingtx is the same as the transaction being spent reported by the DSP assert_equal(hex(dsp.prevTxId)[2:], fundingtxid) assert_equal(dsp.prevOutIndex, 0) # 6. No other valid proof is known. # IE if a valid proof is known, no new proofs will be constructed # We submit a _triple_ spend transaction to the third node connect_nodes(self.nodes[0], self.nodes[2]) self.nodes[2].call_rpc('sendrawtransaction', thirdDSTx, ignore_error='txn-mempool-conflict') # Await for a new dsp to be relayed to the node # if such a dsp (or the double or triple spending tx) arrives, the test fails assert_raises( AssertionError, wait_until, lambda: dspReceiver.message_count["dsproof-beta"] == 2 or dspReceiver.message_count["tx"] == 2, lock=mininode_lock, timeout=5 ) # Only P2PKH inputs are protected # Check that a non-P2PKH output is not protected self.nodes[0].generate(1) fundingtxid = self.nodes[0].getblock(self.nodes[0].getblockhash(2))['tx'][0] fundingtx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(fundingtxid)) fundingtx.rehash() nonP2PKHTx = create_tx_with_script(fundingtx, 0, b'', int(49.95 * COIN), CScript([OP_TRUE])) signedNonP2PKHTx = self.nodes[0].signrawtransactionwithwallet(ToHex(nonP2PKHTx)) self.nodes[0].sendrawtransaction(signedNonP2PKHTx['hex']) self.sync_all() tx = FromHex(CTransaction(), signedNonP2PKHTx['hex']) tx.rehash() firstDSTx = create_tx_with_script(tx, 0, b'', int(49.90 * COIN), CScript([OP_TRUE])) secondDSTx = create_tx_with_script(tx, 0, b'', int(49.90 * COIN), CScript([OP_FALSE])) self.nodes[0].sendrawtransaction(ToHex(firstDSTx)) self.nodes[1].call_rpc('sendrawtransaction', ToHex(secondDSTx), ignore_error='txn-mempool-conflict') assert_raises( AssertionError, wait_until, lambda: dspReceiver.message_count["dsproof-beta"] == 2, lock=mininode_lock, timeout=5 ) # Check that unconfirmed outputs are also protected self.nodes[0].generate(1) unconfirmedtx = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 25) self.sync_all() firstDSTx = create_raw_transaction(self.nodes[0], unconfirmedtx, self.nodes[0].getnewaddress(), 24.9) secondDSTx = create_raw_transaction(self.nodes[0], unconfirmedtx, self.nodes[0].getnewaddress(), 24.9) self.nodes[0].sendrawtransaction(firstDSTx) self.nodes[1].call_rpc('sendrawtransaction', secondDSTx, ignore_error='txn-mempool-conflict') wait_until( lambda: dspReceiver.message_count["dsproof-beta"] == 2, lock=mininode_lock, timeout=5 ) dsp2 = dspReceiver.last_message["dsproof-beta"].dsproof dsps.add(dsp2.serialize()) assert(len(dsps) == 2) # Check that a double spent tx, which has some non-P2PKH inputs # in its ancestor, still results in a dsproof being emitted. self.nodes[0].generate(1) # Create a 1-of-2 multisig address which will be an in-mempool # ancestor to a double-spent tx pubkey0 = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['pubkey'] pubkey1 = self.nodes[1].getaddressinfo( self.nodes[1].getnewaddress())['pubkey'] p2sh = self.nodes[0].addmultisigaddress(1, [pubkey0, pubkey1], "")['address'] # Fund the p2sh address fundingtxid = self.nodes[0].sendtoaddress(p2sh, 49) vout = find_output(self.nodes[0], fundingtxid, Decimal('49')) self.sync_all() # Spend from the P2SH to a P2PKH, which we will double spend from # in the next step. p2pkh1 = self.nodes[0].getnewaddress() rawtx1 = create_raw_transaction(self.nodes[0], fundingtxid, p2pkh1, 48.999, vout) signed_tx1 = self.nodes[0].signrawtransactionwithwallet(rawtx1) txid1 = self.nodes[0].sendrawtransaction(signed_tx1['hex']) vout1 = find_output(self.nodes[0], txid1, Decimal('48.999')) self.sync_all() # Now double spend the P2PKH which has a P2SH ancestor. firstDSTx = create_raw_transaction(self.nodes[0], txid1, self.nodes[0].getnewaddress(), 48.9, vout1) secondDSTx = create_raw_transaction(self.nodes[0], txid1, self.nodes[1].getnewaddress(), 48.9, vout1) self.nodes[0].sendrawtransaction(firstDSTx) self.nodes[1].call_rpc('sendrawtransaction', secondDSTx, ignore_error='txn-mempool-conflict') # We still get a dsproof, showing that not all ancestors have # to be P2PKH. wait_until( lambda: dspReceiver.message_count["dsproof-beta"] == 3, lock=mininode_lock, timeout=5 ) dsp3 = dspReceiver.last_message["dsproof-beta"].dsproof dsps.add(dsp3.serialize()) assert(len(dsps) == 3) # Check that a double spent tx, which has some unconfirmed ANYONECANPAY # transactions in its ancestry, still results in a dsproof being emitted. self.nodes[0].generate(1) fundingtxid = self.nodes[0].getblock(self.nodes[0].getblockhash(5))['tx'][0] vout1 = find_output(self.nodes[0], fundingtxid, Decimal('50')) addr = self.nodes[1].getnewaddress() pubkey = self.nodes[1].getaddressinfo(addr)['pubkey'] inputs = [ {'txid': fundingtxid, 'vout': vout1, 'amount': 49.99, 'scriptPubKey': pubkey} ] outputs = {addr: 49.99} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) signed = self.nodes[0].signrawtransactionwithwallet(rawtx, None, "NONE|FORKID|ANYONECANPAY") assert 'complete' in signed assert_equal(signed['complete'], True) assert 'errors' not in signed txid = self.nodes[0].sendrawtransaction(signed['hex']) self.sync_all() # The ANYONECANPAY is still unconfirmed, but let's create some # double spends from it. vout2 = find_output(self.nodes[0], txid, Decimal('49.99')) firstDSTx = create_raw_transaction(self.nodes[1], txid, self.nodes[0].getnewaddress(), 49.98, vout2) secondDSTx = create_raw_transaction(self.nodes[1], txid, self.nodes[1].getnewaddress(), 49.98, vout2) self.nodes[0].sendrawtransaction(firstDSTx) self.nodes[1].call_rpc('sendrawtransaction', secondDSTx, ignore_error='txn-mempool-conflict') # We get a dsproof. wait_until( lambda: dspReceiver.message_count["dsproof-beta"] == 4, lock=mininode_lock, timeout=5 ) dsp4 = dspReceiver.last_message["dsproof-beta"].dsproof dsps.add(dsp4.serialize()) assert(len(dsps) == 4) # Create a P2SH to double-spend directly (1-of-1 multisig) self.nodes[0].generate(1) self.sync_all() pubkey2 = self.nodes[0].getaddressinfo( self.nodes[0].getnewaddress())['pubkey'] p2sh = self.nodes[0].addmultisigaddress(1, [pubkey2,], "")['address'] fundingtxid = self.nodes[0].sendtoaddress(p2sh, 49) vout = find_output(self.nodes[0], fundingtxid, Decimal('49')) self.sync_all() # Now double spend it firstDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 48.9, vout) secondDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[1].getnewaddress(), 48.9, vout) self.nodes[0].sendrawtransaction(firstDSTx) self.nodes[1].call_rpc('sendrawtransaction', secondDSTx, ignore_error='txn-mempool-conflict') # No dsproof is generated. assert_raises( AssertionError, wait_until, lambda: dspReceiver.message_count["dsproof-beta"] == 5, lock=mininode_lock, timeout=5 ) # Check end conditions - still only 4 DSPs last_dsp = dspReceiver.last_message["dsproof-beta"].dsproof dsps.add(last_dsp.serialize()) assert(len(dsps) == 4) # Next, test that submitting a double-spend via the RPC interface also results in a broadcasted # dsproof self.nodes[0].generate(1) self.sync_all() fundingtxid = self.nodes[0].getblock(self.nodes[0].getblockhash(6))['tx'][0] # Create 2 new double-spends firstDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 49.95) secondDSTx = create_raw_transaction(self.nodes[0], fundingtxid, self.nodes[0].getnewaddress(), 49.95) # Send the two conflicting transactions to the same node via RPC assert_equal(dspReceiver.message_count["dsproof-beta"], 4) self.nodes[0].sendrawtransaction(firstDSTx) # send second tx to same node via RPC # -- it's normal for it to reject the tx, but it should still generate a dsproof broadcast assert_raises_rpc_error( -26, "txn-mempool-conflict (code 18)", self.nodes[0].sendrawtransaction, secondDSTx ) wait_until( lambda: dspReceiver.message_count["dsproof-beta"] == 5, lock=mininode_lock, timeout=5 ) # Finally, ensure that the non-dsproof node has the messages we expect in its log # (this checks that dsproof was disabled for this node) debug_log = os.path.join(non_dsproof_node.datadir, 'regtest', 'debug.log') dsp_inv_ctr = 0 with open(debug_log, encoding='utf-8') as dl: for line in dl.readlines(): if "Got DSProof INV" in line: # Ensure that if this node did see a dsproof inv, it explicitly ignored it assert "(ignored, -doublespendproof=0)" in line dsp_inv_ctr += 1 else: # Ensure this node is not processing dsproof messages and not requesting them via getdata assert ("received: dsproof-beta" not in line and "Good DSP" not in line and "DSP broadcasting" not in line and "bad-dsproof" not in line) # We expect it to have received at least some DSP inv broadcasts assert_greater_than(dsp_inv_ctr, 0)
def run_test(self): # Mine three blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) self.sync_all() node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() # 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. b = [self.nodes[0].getblockhash(n) for n in range(101, 105)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spend_101_raw = self.create_tx(coinbase_txids[1], node1_address, self._reward) spend_102_raw = self.create_tx(coinbase_txids[2], node0_address, self._reward) spend_103_raw = self.create_tx(coinbase_txids[3], node0_address, self._reward) # Create a block-height-locked transaction which will be invalid after reorg timelock_tx = self.nodes[0].createrawtransaction( [{ "txid": coinbase_txids[0], "vout": 0 }], {node0_address: self._reward}) # Set the time lock, ensuring we don't clobber the rest of the Sapling v4 tx format timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) timelock_tx = timelock_tx[:-38] + hex( self.nodes[0].getblockcount() + 2)[2:] + "000000" + timelock_tx[-30:] timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] assert_raises(JSONRPCException, 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) self.nodes[0].generate(1) assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = self.create_tx(spend_102_id, node1_address, self._reward) spend_103_1_raw = self.create_tx(spend_103_id, node1_address, self._reward) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1) 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()), set([spend_101_id, spend_102_1_id, timelock_tx_id])) for node in self.nodes: node.invalidateblock(last_block[0]) assert_equal(set(self.nodes[0].getrawmempool()), set([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 be empty. assert_equal(set(self.nodes[0].getrawmempool()), set())
def run_test(self): # Generate a Sapling address for node 1 node1_zaddr = self.nodes[1].z_getnewaddress('sapling') self.nodes[1].stop() bitcoind_processes[1].wait() self.nodes[1] = self.start_node_with(1, [ "-mineraddress=%s" % node1_zaddr, ]) connect_nodes(self.nodes[1], 0) # Node 0 can mine blocks, because it is targeting a transparent address print("Mining block with node 0") self.nodes[0].generate(1) self.sync_all() walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['immature_balance'], 3802400) assert_equal(walletinfo['balance'], 0) # Node 1 cannot mine blocks, because it is targeting a Sapling address # but Heartwood is not yet active print("Attempting to mine block with node 1") assert_raises(JSONRPCException, self.nodes[1].generate, 1) assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0) # Stop node 1 and check logs to verify the block was rejected correctly string_to_find = "CheckTransaction(): coinbase has output descriptions" check_node_log(self, 1, string_to_find) # Restart node 1 self.nodes[1] = self.start_node_with( 1, ["-mineraddress=%s" % node1_zaddr]) connect_nodes(self.nodes[1], 0) # Activate Heartwood print("Activating Heartwood") self.nodes[0].generate(8) self.sync_all() # Node 1 can now mine blocks! print("Mining block with node 1") self.nodes[1].generate(1) self.sync_all() # Transparent coinbase outputs are subject to coinbase maturity assert_equal(self.nodes[0].getbalance(), Decimal('0')) assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00') # Shielded coinbase outputs are not subject to coinbase maturity assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 97) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 97) assert_equal(self.nodes[1].z_gettotalbalance()['private'], '97.00') assert_equal(self.nodes[1].z_gettotalbalance()['total'], '97.00') # Send from Sapling coinbase to Sapling address and transparent address # (to check that a non-empty vout is allowed when spending shielded # coinbase) print("Sending Sapling coinbase to Sapling address") node0_zaddr = self.nodes[0].z_getnewaddress('sapling') node0_taddr = self.nodes[0].getnewaddress() recipients = [] recipients.append({"address": node0_zaddr, "amount": Decimal('2')}) recipients.append({"address": node0_taddr, "amount": Decimal('2')}) myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0) wait_and_assert_operationid_status(self.nodes[1], myopid) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2) assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 93)
def run_test(self): # Generate a Sapling address for node 1 node1_zaddr = self.nodes[1].z_getnewaddress('sapling') self.nodes[1].stop() bitcoind_processes[1].wait() self.nodes[1] = self.start_node_with(1, [ "-mineraddress=%s" % node1_zaddr, ]) connect_nodes(self.nodes[1], 0) # Node 0 can mine blocks, because it is targeting a transparent address print("Mining block with node 0") self.nodes[0].generate(1) self.sync_all() walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['immature_balance'], 5) assert_equal(walletinfo['balance'], 0) # Node 1 cannot mine blocks, because it is targeting a Sapling address # but Heartwood is not yet active print("Attempting to mine block with node 1") assert_raises(JSONRPCException, self.nodes[1].generate, 1) assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0) # Stop node 1 and check logs to verify the block was rejected correctly print("Checking node 1 logs") self.nodes[1].stop() bitcoind_processes[1].wait() logpath = self.options.tmpdir + "/node1/regtest/debug.log" foundErrorMsg = False with open(logpath, "r") as myfile: logdata = myfile.readlines() for logline in logdata: if "CheckTransaction(): coinbase has output descriptions" in logline: foundErrorMsg = True break assert (foundErrorMsg) # Restart node 1 self.nodes[1] = self.start_node_with(1, [ "-mineraddress=%s" % node1_zaddr, ]) connect_nodes(self.nodes[1], 0) # Activate Heartwood print("Activating Heartwood") self.nodes[0].generate(8) self.sync_all() # Node 1 can now mine blocks! print("Mining block with node 1") self.nodes[1].generate(1) self.sync_all() # Transparent coinbase outputs are subject to coinbase maturity assert_equal(self.nodes[0].getbalance(), Decimal('0')) assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00') # Shielded coinbase outputs are not subject to coinbase maturity assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 5) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 5) assert_equal(self.nodes[1].z_gettotalbalance()['private'], '5.00') assert_equal(self.nodes[1].z_gettotalbalance()['total'], '5.00') # Send from Sapling coinbase to Sapling address and transparent address # (to check that a non-empty vout is allowed when spending shielded # coinbase) print("Sending Sapling coinbase to Sapling address") node0_zaddr = self.nodes[0].z_getnewaddress('sapling') node0_taddr = self.nodes[0].getnewaddress() recipients = [] recipients.append({"address": node0_zaddr, "amount": Decimal('2')}) recipients.append({"address": node0_taddr, "amount": Decimal('2')}) myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0) wait_and_assert_operationid_status(self.nodes[1], myopid) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2) assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 1)
def run_test(self): print("Mining blocks...") self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) utxo1 = node0utxos.pop() tx1 = self.nodes[0].createrawtransaction([utxo1], {self.nodes[1].getnewaddress(): utxo1['amount']}) txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx1)["hex"]) utxo2 = node0utxos.pop() tx2 = self.nodes[0].createrawtransaction([utxo2], {self.nodes[1].getnewaddress(): utxo2['amount']}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) hashlist = [] resp = self.nodes[0].getrawtransaction(txid1, 1) txhash1 = resp['hash'] resp = self.nodes[0].getrawtransaction(txid2, 1) txhash2 = resp['hash'] hashlist.append(txhash1) hashlist.append(txhash2) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1])), [txhash1]) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), hashlist) assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2], blockhash)), hashlist) txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): txin_spent['amount']}) self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_spent_hash = txhash1 if txin_spent["txid"] == txid1 else txhash2 txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 txid_unspent_hash = txhash1 if txin_spent["txid"] != txid1 else txhash2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent_hash]) # ...or if the first tx is not fully-spent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent_hash]) try: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), hashlist) except JSONRPCException: assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1])), hashlist) # ...or if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent_hash]) # Quick test of getblock using blockhash and different levels of verbosity result = self.nodes[0].getblock(blockhash, 2) coinbase_txid = result["tx"][0]["txid"] result = self.nodes[0].getblock(blockhash, 1) assert_equal(coinbase_txid, result["tx"][0]) # verbosity 1 only lists txids result = self.nodes[0].getblock(blockhash, 0) assert(c in string.hexdigits for c in result) # verbosity 0 returns raw hex # Test getblock heights including negatives relative to the head assert_equal(self.nodes[0].getblock("0")["height"], 0) assert_raises(JSONRPCException, self.nodes[0].getblock, ["108"]) assert_equal(self.nodes[0].getblock("107")["height"], 107) assert_equal(self.nodes[0].getblock("-1")["height"], 107) assert_equal(self.nodes[0].getblock("-2")["height"], 106) assert_equal(self.nodes[0].getblock("-20")["height"], 88) assert_equal(self.nodes[0].getblock("-107")["height"], 1) assert_equal(self.nodes[0].getblock("-108")["height"], 0) assert_raises(JSONRPCException, self.nodes[0].getblock, ["-109"]) assert_raises(JSONRPCException, self.nodes[0].getblock, ["-0"]) # Test getblockhash negative heights assert_equal(self.nodes[0].getblockhash(-1), self.nodes[0].getblockhash(107)) assert_equal(self.nodes[0].getblockhash(-2), self.nodes[0].getblockhash(106))
def run_faucet_tests(self): rpc = self.rpc # basic sanity tests result = rpc.getwalletinfo() assert_greater_than(result['txcount'], 100) assert_greater_than(result['balance'], 0.0) balance = result['balance'] faucet = rpc.faucetaddress() assert_equal(faucet['result'], 'success') # verify all keys look like valid AC addrs, could be better for x in [ 'myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress' ]: assert_equal(faucet[x][0], 'R') result = rpc.faucetaddress(self.pubkey) assert_success(result) # test that additional CCaddress key is returned for x in [ 'myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress', 'CCaddress' ]: assert_equal(result[x][0], 'R') # no funds in the faucet yet result = rpc.faucetget() assert_error(result) result = rpc.faucetinfo() assert_success(result) result = rpc.faucetfund("0") assert_error(result) result = rpc.faucetfund("-1") assert_error(result) # we need at least 1 + txfee to get result = rpc.faucetfund("2") assert_success(result) assert result['hex'], "hex key found" # broadcast the xtn result = rpc.sendrawtransaction(result['hex']) txid = result[0] assert txid, "found txid" # we need the tx above to be confirmed in the next block rpc.generate(1) result = rpc.getwalletinfo() balance2 = result['balance'] # make sure our balance is less now assert_greater_than(balance, balance2) result = rpc.faucetinfo() assert_success(result) assert_greater_than(result['funding'], 0) result = rpc.faucetget() assert_success(result) assert result['hex'], "hex key found" # try to broadcast the xtn, but we will get 'faucet is only for brand new addresses' assert_raises(JSONRPCException, rpc.sendrawtransaction, [result['hex']]) newaddr = rpc.getnewaddress() assert newaddr, "got a new address" result = rpc.validateaddress(newaddr) newpubkey = result['pubkey'] assert newpubkey, "got a pubkey for new address"
def run_test(self): # Mine three blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) self.sync_all() node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() # 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. b = [ self.nodes[0].getblockhash(n) for n in range(101, 105) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] spend_101_raw = self.create_tx(coinbase_txids[1], node1_address, 10) spend_102_raw = self.create_tx(coinbase_txids[2], node0_address, 10) spend_103_raw = self.create_tx(coinbase_txids[3], node0_address, 10) # Create a block-height-locked transaction which will be invalid after reorg timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 10}) # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] assert_raises(JSONRPCException, 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) self.nodes[0].generate(1) assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = self.create_tx(spend_102_id, node1_address, 10) spend_103_1_raw = self.create_tx(spend_103_id, node1_address, 10) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1) 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()), set([ spend_101_id, spend_102_1_id, timelock_tx_id ])) for node in self.nodes: node.invalidateblock(last_block[0]) assert_equal(set(self.nodes[0].getrawmempool()), set([ 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 be empty. assert_equal(set(self.nodes[0].getrawmempool()), set())
def run_test(self): print "Mining blocks..." self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) tx1 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 11.4375}) txid1 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 11.4375}) txid2 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1 ])), [txid1]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2], blockhash)), txlist) txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction( [txin_spent], {self.nodes[0].getnewaddress(): 11.4375}) self.nodes[0].sendrawtransaction( self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_spent], blockhash)), [txid_spent]) # ...or if the first tx is not fully-spent assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_unspent])), [txid_unspent]) try: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) except JSONRPCException: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid2, txid1])), txlist) # ...or if we have a -txindex assert_equal( self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof( [txid_spent])), [txid_spent]) # send funds to node 2, it will use them for sending a certificate tx0 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[2].getnewaddress(): 11.4375}) self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx0)["hex"]) # reach sc fork and create a SC self.nodes[0].generate(MINIMAL_SC_HEIGHT - 105) self.sync_all() prev_epoch_hash = self.nodes[0].getbestblockhash() sc_address = "0000000000000000000000000000000000000000000000000000000000000abc" sc_epoch_len = EPOCH_LENGTH sc_cr_amount = Decimal('12.00000000') certMcTest = CertTestUtils(self.options.tmpdir, self.options.srcdir) cswMcTest = CSWTestUtils(self.options.tmpdir, self.options.srcdir) # generate wCertVk and constant certVk = certMcTest.generate_params("sc") cswVk = cswMcTest.generate_params("sc") constant1 = generate_random_field_element_hex() sc_cr = [] sc_cr.append({ "version": 0, "epoch_length": sc_epoch_len, "amount": sc_cr_amount, "address": sc_address, "wCertVk": certVk, "wCeasedVk": cswVk, "constant": constant1 }) rawtx = self.nodes[0].createrawtransaction([], {}, [], sc_cr) funded_tx = self.nodes[0].fundrawtransaction(rawtx) sigRawtx = self.nodes[0].signrawtransaction(funded_tx['hex']) finalRawtx = self.nodes[0].sendrawtransaction(sigRawtx['hex']) self.sync_all() decoded_tx = self.nodes[1].getrawtransaction(finalRawtx, 1) scid = decoded_tx['vsc_ccout'][0]['scid'] mark_logs("created SC id: {}".format(scid), self.nodes, DEBUG_MODE) # advance 1 epoch mark_logs("\nLet 1 epochs pass by...", self.nodes, DEBUG_MODE) q = 1 cert_fee = Decimal("0.0") cert, epoch_number = advance_epoch(certMcTest, self.nodes[2], self.sync_all, scid, "sc", constant1, sc_epoch_len, q, cert_fee) mark_logs( "\n==> certificate for SC epoch {} {}".format(epoch_number, cert), self.nodes, DEBUG_MODE) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [cert]) # send some coin for having a tx in the next block as well t_addr1 = self.nodes[1].getnewaddress() tx = self.nodes[0].sendtoaddress(t_addr1, 0.1) self.sync_all() # mine one block for having last cert and tx in chain mark_logs("\nNode0 generates 1 block confirming last cert and tx", self.nodes, DEBUG_MODE) bl_hash = self.nodes[0].generate(1)[-1] self.sync_all() proof1 = self.nodes[1].gettxoutproof([cert]) proof2 = self.nodes[2].gettxoutproof([tx, cert]) assert_equal(self.nodes[2].verifytxoutproof(proof1), [cert]) assert_equal(self.nodes[2].verifytxoutproof(proof2), [tx, cert]) # spend cert change: since there are no bwts in the cert, it will be fully spent tx = self.nodes[2].sendtoaddress(t_addr1, 0.1) self.sync_all() mark_logs("\nNode0 generates 1 block confirming last tx", self.nodes, DEBUG_MODE) self.nodes[0].generate(1) self.sync_all() # We cant find the block from a fully-spent cert assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [cert]) # ...but we can if we specify the block proof = self.nodes[0].gettxoutproof([cert], bl_hash) # ...or if we have a -txindex proof = self.nodes[3].gettxoutproof([cert]) assert_equal(self.nodes[0].verifytxoutproof(proof), [cert])
def run_test(self): print "Mining blocks..." self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) node0utxos = self.nodes[0].listunspent(1) # ZEN_MOD_START tx1 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 11.4375}) # ZEN_MOD_END txid1 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx1)["hex"]) # ZEN_MOD_START tx2 = self.nodes[0].createrawtransaction( [node0utxos.pop()], {self.nodes[1].getnewaddress(): 11.4375}) # ZEN_MOD_END txid2 = self.nodes[0].sendrawtransaction( self.nodes[0].signrawtransaction(tx2)["hex"]) assert_raises(JSONRPCException, self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) self.sync_all() txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1 ])), [txid1]) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2], blockhash)), txlist) txin_spent = self.nodes[1].listunspent(1).pop() # ZEN_MOD_START tx3 = self.nodes[1].createrawtransaction( [txin_spent], {self.nodes[0].getnewaddress(): 11.4375}) # ZEN_MOD_END self.nodes[0].sendrawtransaction( self.nodes[1].signrawtransaction(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We cant find the block from a fully-spent tx assert_raises(JSONRPCException, self.nodes[2].gettxoutproof, [txid_spent]) # ...but we can if we specify the block assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_spent], blockhash)), [txid_spent]) # ...or if the first tx is not fully-spent assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid_unspent])), [txid_unspent]) try: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid1, txid2])), txlist) except JSONRPCException: assert_equal( self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof( [txid2, txid1])), txlist) # ...or if we have a -txindex assert_equal( self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof( [txid_spent])), [txid_spent])
def run_test(self): # Generate a Sapling address for node 1 node1_zaddr = self.nodes[1].z_getnewaddress('sapling') self.nodes[1].stop() bitcoind_processes[1].wait() self.nodes[1] = self.start_node_with(1, [ "-mineraddress=%s" % node1_zaddr, ]) connect_nodes(self.nodes[1], 0) # Node 0 can mine blocks, because it is targeting a transparent address print("Mining block with node 0") self.nodes[0].generate(1) self.sync_all() walletinfo = self.nodes[0].getwalletinfo() assert_equal(walletinfo['immature_balance'], 5) assert_equal(walletinfo['balance'], 0) # Node 1 cannot mine blocks, because it is targeting a Sapling address # but Heartwood is not yet active print("Attempting to mine block with node 1") assert_raises(JSONRPCException, self.nodes[1].generate, 1) assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0) # Stop node 1 and check logs to verify the block was rejected correctly string_to_find = "CheckTransaction(): coinbase has output descriptions" check_node_log(self, 1, string_to_find) # Restart node 1 self.nodes[1] = self.start_node_with( 1, ["-mineraddress=%s" % node1_zaddr]) connect_nodes(self.nodes[1], 0) # Activate Heartwood print("Activating Heartwood") self.nodes[0].generate(8) self.sync_all() # Node 1 can now mine blocks! print("Mining block with node 1") self.nodes[1].generate(1) self.sync_all() # Transparent coinbase outputs are subject to coinbase maturity assert_equal(self.nodes[0].getbalance(), Decimal('0')) assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00') assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00') # Shielded coinbase outputs are not subject to coinbase maturity assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 5) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 5) assert_equal(self.nodes[1].z_gettotalbalance()['private'], '5.00') assert_equal(self.nodes[1].z_gettotalbalance()['total'], '5.00') # Send from Sapling coinbase to Sapling address and transparent address # (to check that a non-empty vout is allowed when spending shielded # coinbase) print("Sending Sapling coinbase to Sapling address") node0_zaddr = self.nodes[0].z_getnewaddress('sapling') node0_taddr = self.nodes[0].getnewaddress() recipients = [] recipients.append({"address": node0_zaddr, "amount": Decimal('2')}) recipients.append({"address": node0_taddr, "amount": Decimal('2')}) myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0) wait_and_assert_operationid_status(self.nodes[1], myopid) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2) assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2) assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 1) # Generate a Unified Address for node 1 self.nodes[1].z_getnewaccount() node1_addr0 = self.nodes[1].z_getaddressforaccount(0) assert_equal(node1_addr0['account'], 0) assert_equal(set(node1_addr0['receiver_types']), set(['p2pkh', 'sapling', 'orchard'])) node1_ua = node1_addr0['address'] # Set node 1's miner address to the UA self.nodes[1].stop() bitcoind_processes[1].wait() self.nodes[1] = self.start_node_with(1, [ "-mineraddress=%s" % node1_ua, ]) connect_nodes(self.nodes[1], 0) # The UA starts with zero balance. assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {}) # Node 1 can mine blocks because the miner selects the Sapling receiver # of its UA. print("Mining block with node 1") self.nodes[1].generate(1) self.sync_all() # The UA balance should show that Sapling funds were received. assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], { 'sapling': { 'valueZat': 5 * COIN }, }) # Activate NU5 print("Activating NU5") self.nodes[0].generate(7) self.sync_all() # Now any block mined by node 1 should use the Orchard receiver of its UA. print("Mining block with node 1") self.nodes[1].generate(1) self.sync_all() assert_equal( self.nodes[1].z_getbalanceforaccount(0)['pools'], { 'sapling': { 'valueZat': 5 * COIN }, # 6.25 ZEC because the FR always ends when Canopy activates, and # regtest has no defined funding streams. 'orchard': { 'valueZat': 6.25 * COIN }, })