def get_tests(self): # shorthand for functions block = self.chain.next_block node = get_rpc_proxy(self.nodes[0].url, 1, timeout=6000, coveragedir=self.nodes[0].coverage_dir) self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) block(0) yield self.accepted() test, out, _ = prepare_init_chain(self.chain, 200, 200) yield test txHashes = [] for i in range(18): txLarge = create_transaction( out[i].tx, out[i].n, b"", ONE_MEGABYTE * 256, CScript([ OP_FALSE, OP_RETURN, bytearray([42] * (ONE_MEGABYTE * 256)) ])) self.test.connections[0].send_message(msg_tx(txLarge)) self.check_mempool(node, [txLarge], timeout=6000) txHashes.append([txLarge.hash, txLarge.sha256]) txOverflow = create_transaction( out[18].tx, out[18].n, b"", ONE_MEGABYTE * 305, CScript( [OP_FALSE, OP_RETURN, bytearray([42] * (ONE_MEGABYTE * 305))])) self.test.connections[0].send_message(msg_tx(txOverflow)) self.check_mempool(node, [txOverflow], timeout=6000) txHashes.append([txOverflow.hash, txOverflow.sha256]) txOverflow = create_transaction( out[19].tx, out[19].n, b"", ONE_MEGABYTE, CScript([OP_FALSE, OP_RETURN, bytearray([42] * ONE_MEGABYTE)])) self.test.connections[0].send_message(msg_tx(txOverflow)) self.check_mempool(node, [txOverflow], timeout=6000) txHashes.append([txOverflow.hash, txOverflow.sha256]) # Mine block with new transactions. self.log.info("BLOCK 2 - mining") minedBlock2 = node.generate(1) self.log.info("BLOCK 2 - mined") for txHash in txHashes: tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txHash[0])) tx.rehash() assert_equal(tx.sha256, txHash[1])
def run_test(self): for node in self.nodes: self.consolidation_factor = int( node.getnetworkinfo()['minconsolidationfactor']) self.minConfirmations = int( node.getnetworkinfo()['minconsolidationinputmaturity']) self.log.info("consolidation factor: {}".format( self.consolidation_factor)) self.log.info("minimum input confirmations: {}".format( self.minConfirmations)) node.generate(300) # test ratio between size of input script and size of output script tx_hex = self.create_and_sign_tx(node, 1, min_confirmations=1) tx = FromHex(CTransaction(), tx_hex) tx.rehash() sin = len(getInputScriptPubKey(node, tx.vin[0], 0)) sout = len(tx.vout[0].scriptPubKey) enough_inputs = sout * self.consolidation_factor // sin enough_inputs = max(enough_inputs, 2) enough_confirmations = self.minConfirmations # FAILING CONDITION: input_sizes <= consolidation_factor * output_size # We assume scriptSig ~ 4 * scriptPubKey tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs - 1, min_confirmations=enough_confirmations) assert_raises_rpc_error(-26, "66: insufficient priority", node.sendrawtransaction, tx_hex) self.log.info("test 1: PASS") # FAILING CONDITION: not enough input confirmations tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs, min_confirmations=enough_confirmations - 1) assert_raises_rpc_error(-26, "66: insufficient priority", node.sendrawtransaction, tx_hex) self.log.info("test 2: PASS") # ALL CONDITIONS MET: must succeed tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs, min_confirmations=enough_confirmations) txid = node.sendrawtransaction(tx_hex) node.generate(1) tx = node.getrawtransaction(txid, 1) confirmations = tx.get('confirmations', 0) assert_equal(confirmations, 1) self.log.info("test 3: PASS")
def get_tests(self): # shorthand for functions block = self.chain.next_block node = get_rpc_proxy(self.nodes[0].url, 1, timeout=6000, coveragedir=self.nodes[0].coverage_dir) self.chain.set_genesis_hash( int(node.getbestblockhash(), 16) ) # Create a new block block(0) self.chain.save_spendable_output() yield self.accepted() # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(200): block(5000 + i) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # Collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(200): out.append(self.chain.get_spendable_output()) txHashes = [] for i in range(18): txLarge = create_transaction(out[i].tx, out[i].n, b"", ONE_MEGABYTE * 256, CScript([OP_FALSE, OP_RETURN, bytearray([42] * (ONE_MEGABYTE * 256))])) self.test.connections[0].send_message(msg_tx(txLarge)) self.check_mempool(node, [txLarge]) txHashes.append([txLarge.hash, txLarge.sha256]) txOverflow = create_transaction(out[18].tx, out[18].n, b"", ONE_MEGABYTE * 305, CScript([OP_FALSE, OP_RETURN, bytearray([42] * (ONE_MEGABYTE * 305))])) self.test.connections[0].send_message(msg_tx(txOverflow)) self.check_mempool(node, [txOverflow]) txHashes.append([txOverflow.hash, txOverflow.sha256]) txOverflow = create_transaction(out[19].tx, out[19].n, b"", ONE_MEGABYTE, CScript([OP_FALSE, OP_RETURN, bytearray([42] * ONE_MEGABYTE)])) self.test.connections[0].send_message(msg_tx(txOverflow)) self.check_mempool(node, [txOverflow]) txHashes.append([txOverflow.hash, txOverflow.sha256]) # Mine block with new transactions. self.log.info("BLOCK 2 - mining") minedBlock2 = node.generate(1) self.log.info("BLOCK 2 - mined") for txHash in txHashes: tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txHash[0])) tx.rehash() assert_equal(tx.sha256, txHash[1])
def create_utxos_value10000(self, node, utxo_count, min_confirmations): utxos = [] addr = node.getnewaddress() for i in range(utxo_count): txid = node.sendtoaddress(addr, self.utxo_test_bsvs) tx = FromHex(CTransaction(), node.getrawtransaction(txid)) tx.rehash() utxos.append(tx) if min_confirmations > 0: node.generate(min_confirmations) return utxos
def test_basics(self, xt_node, test_node): self.log.info("bip64: basic checks") txid = xt_node.sendtoaddress(xt_node.getnewaddress(), 0.1) tx = FromHex(CTransaction(), xt_node.getrawtransaction(txid)) tx.rehash() new_outpoint = COutPoint(tx.sha256, 0) prev_outpoint = tx.vin[0].prevout # Test that the utxo doesn't exist in the chain. self.get_utxos([new_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('0', 2)) assert_equal(len(test_node.utxos.result), 0) # It does exist in the mempool. self.get_utxos([new_outpoint], checkmempool=True) assert_equal(test_node.utxos.bitmap[0], int('1', 2)) assert_equal(len(test_node.utxos.result), 1) magic_inmempool_height = 2147483647 assert_equal(test_node.utxos.result[0].height, magic_inmempool_height) # The prevout exists in the chain self.get_utxos([prev_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('1', 2)) # The prevout is spent in the mempool. self.get_utxos([prev_outpoint], checkmempool=True) assert_equal(test_node.utxos.bitmap[0], int('0', 2)) # Mine tx creating new_outpoint, now it should exist in the chain. xt_node.generate(1) self.get_utxos([new_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('1', 2)) assert_equal(test_node.utxos.result[0].height, xt_node.getblockcount()) # .. same result when including mempool self.get_utxos([new_outpoint], checkmempool=True) assert_equal(test_node.utxos.bitmap[0], int('1', 2)) # Check that we can fetch multiple outpoints self.get_utxos([new_outpoint, prev_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('01', 2)) self.get_utxos([prev_outpoint, new_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('10', 2)) self.get_utxos([new_outpoint, prev_outpoint, new_outpoint], checkmempool=False) assert_equal(test_node.utxos.bitmap[0], int('101', 2))
def create_utxos_value100000(self, node, utxo_count, utxo_size, min_confirmations): utxos = [] addr = node.getnewaddress() # create some confirmed UTXOs for i in range(utxo_count): txid = node.sendtoaddress(addr, self.utxo_test_bsvs) tx = FromHex(CTransaction(), node.getrawtransaction(txid)) tx.rehash() utxos.append(tx) node.generate(1) # Convert those utxos to new UTXO's in one single transaction that anyone can spend tx = None fee = 0 for _ in range(2): # firs iteration is to calculate the fee tx = CTransaction() amount = self.utxo_test_sats check_size = 0 for u in utxos: # Each UTXO will have two outputs, one for change and another one # amounting to roughly 10000 satoshis for i in range(len(u.vout)): uu = u.vout[i] if uu.nValue <= self.utxo_test_sats and uu.nValue > self.utxo_test_sats // 2: tx.vin.append( CTxIn( COutPoint( uint256_from_str( hex_str_to_bytes(u.hash)[::-1]), i), b'')) break adjust = 2 scriptPubKey = CScript([OP_DROP] + ([OP_NOP] * ((utxo_size // utxo_count) - adjust)) + [OP_TRUE]) if len(tx.vin) == utxo_count: amount = amount - fee while True: scriptPubKey = CScript([OP_DROP] + ([OP_NOP] * ( (utxo_size // utxo_count) - adjust)) + [OP_TRUE]) if check_size + len(scriptPubKey) > utxo_size: adjust = adjust + 1 continue elif check_size + len(scriptPubKey) < utxo_size: adjust = adjust - 1 continue break check_size = check_size + len(scriptPubKey) tx.vout.append(CTxOut(amount, scriptPubKey)) assert (len(tx.vout) == utxo_count) assert (check_size == utxo_size) # sign and send transaction txHex = node.signrawtransaction(ToHex(tx))['hex'] tx = FromHex(CTransaction(), txHex) tx.rehash() tx_size = len(ToHex(tx)) fee = int(self.blockmintxfee_sats * tx_size / 1000) node.sendrawtransaction(ToHex(tx)) if min_confirmations > 0: node.generate(min_confirmations) return tx
def run_test(self): # Turn on a webhook server self.start_webhook_server() # Create a P2P connection node = self.nodes[0] peer = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), node, peer) peer.add_connection(connection) NetworkThread().start() peer.wait_for_verack() # Create an initial block with a coinbase we will split into multiple utxos initialBlock, _ = make_block(connection) coinbaseTx = initialBlock.vtx[0] send_by_headers(connection, [initialBlock], do_send_blocks=True) wait_for_tip(connection, initialBlock.hash) node.generate(101) block101hex = node.getblock(node.getbestblockhash(), False) block101dict = node.getblock(node.getbestblockhash(), 2) block101 = FromHex(CBlock(), block101hex) block101.height = block101dict['height'] block101.rehash() # Create a block with a transaction spending coinbaseTx of a previous block and making multiple outputs for future transactions to spend utxoBlock, _ = make_block(connection, parent_block=block101) utxoTx = create_tx(coinbaseTx, 0, 1 * COIN) # Create additional 48 outputs (we let 1 COIN as fee) for _ in range(48): utxoTx.vout.append(CTxOut(1 * COIN, CScript([OP_TRUE]))) # Add to block utxoTx.rehash() utxoBlock.vtx.append(utxoTx) utxoBlock.hashMerkleRoot = utxoBlock.calc_merkle_root() utxoBlock.solve() send_by_headers(connection, [utxoBlock], do_send_blocks=True) wait_for_tip(connection, utxoBlock.hash) # Make sure serialization/deserialization works as expected # Create dsdetected message. The content is not important here. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(utxoBlock), CBlockHeader(initialBlock)], DSMerkleProof(1, utxoTx, utxoBlock.hashMerkleRoot, [MerkleProofNode(utxoBlock.vtx[0].sha256)])) ]) dsdBytes = dsdMessage.serialize() dsdMessageDeserialized = msg_dsdetected() dsdMessageDeserialized.deserialize(BytesIO(dsdBytes)) assert_equal(str(dsdMessage), str(dsdMessageDeserialized)) # Send a message containing random bytes. Webhook should not receive the notification. peer.send_and_ping(fake_msg_dsdetected()) assert_equal(self.get_JSON_notification(), None) # Create two blocks with transactions spending the same utxo blockA, _ = make_block(connection, parent_block=utxoBlock) blockB, _ = make_block(connection, parent_block=utxoBlock) blockF, _ = make_block(connection, parent_block=utxoBlock) txA = create_tx(utxoBlock.vtx[1], 0, int(0.8 * COIN)) txB = create_tx(utxoBlock.vtx[1], 0, int(0.9 * COIN)) txF = create_tx(utxoBlock.vtx[1], 0, int(0.7 * COIN)) txA.rehash() txB.rehash() txF.rehash() blockA.vtx.append(txA) blockB.vtx.append(txB) blockF.vtx.append(txF) blockA.hashMerkleRoot = blockA.calc_merkle_root() blockB.hashMerkleRoot = blockB.calc_merkle_root() blockF.hashMerkleRoot = blockF.calc_merkle_root() blockA.calc_sha256() blockB.calc_sha256() blockF.calc_sha256() blockA.solve() blockB.solve() blockF.solve() start_banscore = node.getpeerinfo()[0]['banscore'] # Webhook should not receive the notification if we send dsdetected message with only one block detail. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with two block details and one is containing no headers. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message where last headers in block details do not have a common previous block hash. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(utxoBlock)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message where block details does not have headers in proper order. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(utxoBlock), CBlockHeader(blockB)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the empty merkle proof. dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails([CBlockHeader(blockB)], DSMerkleProof()) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the wrong index in the merkle proof (merkle root validation should fail) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(0, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the wrong transaction in the merkle proof (merkle root validation should fail) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(1, txA, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the wrong merkle root (merkle root validation should fail) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(1, txB, blockA.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the wrong merkle proof (merkle root validation should fail) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockA.hashMerkleRoot)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the merkle proof having an additional unexpected node (merkle root validation should fail) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails([CBlockHeader(blockB)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [ MerkleProofNode(blockB.vtx[0].sha256), MerkleProofNode(blockA.hashMerkleRoot) ])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with the valid proof, but transaction is a coinbase transaction dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(0, blockB.vtx[0], blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[1].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if we send dsdetected message with transactions that are not double spending # Create a block similar as before, but with a transaction spending a different utxo blockC, _ = make_block(connection, parent_block=utxoBlock) txC = create_tx(utxoBlock.vtx[1], 1, int(0.7 * COIN)) blockC.vtx.append(txC) blockC.hashMerkleRoot = blockC.calc_merkle_root() blockC.solve() dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockC)], DSMerkleProof(1, txC, blockC.hashMerkleRoot, [MerkleProofNode(blockC.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if the two double spending transactions are actually the same transaction (having same txid) # Create a block similar as before, but with a transaction spending a different utxo blockD, _ = make_block(connection, parent_block=utxoBlock) blockD.vtx.append(txA) blockD.hashMerkleRoot = blockD.calc_merkle_root() blockD.solve() dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockD)], DSMerkleProof(1, txA, blockD.hashMerkleRoot, [MerkleProofNode(blockD.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Webhook should not receive the notification if header cannot pow # note hat pow is so easy in regtest that nonce can often be hence we have to select the nonce carefully blockE, _ = make_block(connection, parent_block=utxoBlock) blockE.vtx.append(txB) blockE.hashMerkleRoot = blockE.calc_merkle_root() nonce = blockE.nNonce while True: blockE.solve() if blockE.nNonce > nonce: blockE.nNonce = nonce break nonce += 1 blockE.nNonce = nonce dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockE)], DSMerkleProof(1, txB, blockE.hashMerkleRoot, [MerkleProofNode(blockE.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) end_banscore = node.getpeerinfo()[0]['banscore'] assert ((end_banscore - start_banscore) / 10 == 13 ) # because we have 13 negative tests so far # Finally, webhook should receive the notification if we send a proper dsdetected message dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) json_notification = self.get_JSON_notification() # remove diverentBlockHash so we can compare with the ds-message assert (json_notification != None) for e in json_notification['blocks']: del e['divergentBlockHash'] assert_equal(str(dsdMessage), str(msg_dsdetected(json_notification=json_notification))) # Repeat previous test but change the order of the BlockDetails, the node should identify this as a duplicate dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockB)], DSMerkleProof(1, txB, blockB.hashMerkleRoot, [MerkleProofNode(blockB.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # repeat previous test but generate many blocks in the node to age the notificatoin message. # very old notification messages shall be ignored. We use the same thresholds as safe mode. # We will hardcode this threshold for now until branch we depend on is merged node.generate(289) dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails( [CBlockHeader(blockA)], DSMerkleProof(1, txA, blockA.hashMerkleRoot, [MerkleProofNode(blockA.vtx[0].sha256)])), BlockDetails( [CBlockHeader(blockF)], DSMerkleProof(1, txF, blockF.hashMerkleRoot, [MerkleProofNode(blockF.vtx[0].sha256)])) ]) peer.send_and_ping(dsdMessage) assert_equal(self.get_JSON_notification(), None) # Create number of random valid block trees and send dsdetected P2P message for each maxNumberOfBranches = 10 maxNumberOfBlocksPerBranch = 30 for _ in range(10): blockTree = self.createRandomBlockTree(maxNumberOfBranches, maxNumberOfBlocksPerBranch, utxoBlock, [utxoBlock.vtx[1]]) dsdMessage = self.createDsDetectedMessageFromBlockTree(blockTree) peer.send_and_ping(dsdMessage) # Notification should be received as generated dsdetected message is valid json_notification = self.get_JSON_notification() # remove diverentBlockHash so we can compare with the ds-message assert (json_notification != None) for e in json_notification['blocks']: del e['divergentBlockHash'] assert_equal( str(dsdMessage), str(msg_dsdetected(json_notification=json_notification))) self.stop_webhook_server()
def run_test(self): for node in self.nodes: self.consolidation_factor = int( node.getnetworkinfo()['minconsolidationfactor']) self.minConfirmations = int( node.getnetworkinfo()['minconfconsolidationinput']) self.log.info("consolidation factor: {}".format( self.consolidation_factor)) self.log.info("minimum input confirmations: {}".format( self.minConfirmations)) # Disconnect nodes before each generate RPC. On a busy environment generate # RPC might not create the provided number of blocks. While nodes are communicating # P2P messages can cause generateBlocks function to skip a block. Check the comment # in generateBlocks function for details. disconnect_nodes_bi(self.nodes, 0, 1) node.generate(300) connect_nodes_bi(self.nodes, 0, 1) # test ratio between size of input script and size of output script tx_hex = self.create_and_sign_tx(node, 1, min_confirmations=1) tx = FromHex(CTransaction(), tx_hex) tx.rehash() sin = len(getInputScriptPubKey(node, tx.vin[0], 0)) sout = len(tx.vout[0].scriptPubKey) enough_inputs = sout * self.consolidation_factor // sin enough_inputs = max(enough_inputs, 2) enough_confirmations = self.minConfirmations # FAILING CONDITION: input_sizes <= consolidation_factor * output_size # We assume scriptSig ~ 4 * scriptPubKey tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs - 1, min_confirmations=enough_confirmations) assert_raises_rpc_error(-26, "66: insufficient priority", node.sendrawtransaction, tx_hex) self.log.info("test 1: PASS") # FAILING CONDITION: not enough input confirmations tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs, min_confirmations=enough_confirmations - 1) assert_raises_rpc_error(-26, "66: insufficient priority", node.sendrawtransaction, tx_hex) self.log.info("test 2: PASS") # ALL CONDITIONS MET: must succeed tx_hex = self.create_and_sign_tx( node, in_count=enough_inputs, min_confirmations=enough_confirmations) txid = node.sendrawtransaction(tx_hex) node.generate(1) tx = node.getrawtransaction(txid, 1) confirmations = tx.get('confirmations', 0) assert_equal(confirmations, 1) self.log.info("test 3: PASS") # Blocks must be synced because we do not want to start generating new blocks on node1 in the next loop iteration # before node1 has received all blocks generated on node0 and all pending P2P block requests have completed. sync_blocks(self.nodes) # Verify deprecated -minconsolidationinputmaturity is an alias to -minconfconsolidationinput self.log.info("Restarting nodes to test config options...") self.stop_nodes() self.extra_args[0].append("-minconsolidationinputmaturity=99") self.start_nodes(self.extra_args) sync_blocks(self.nodes) assert_equal( 99, self.nodes[0].getnetworkinfo()['minconfconsolidationinput']) assert_equal( 99, self.nodes[0].getnetworkinfo()['minconsolidationinputmaturity']) # Verify deprecation warning is logged self.stop_nodes() deprecation_log = False for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if f"Option -minconsolidationinputmaturity is deprecated, use -minconfconsolidationinput instead" in line: deprecation_log = True #self.log.info("Found line: %s", line.strip()) break assert (deprecation_log) # Verify init error when deprecated and new option are used together self.extra_args[0].append("-minconfconsolidationinput=99") self.assert_start_raises_init_error( 0, self.extra_args[0], 'Cannot use both -minconfconsolidationinput and -minconsolidationinputmaturity (deprecated) at the same time' )
def getInputScriptPubKey(node, input, index): txid = hashToHex(input.prevout.hash) raw = node.getrawtransaction(txid) tx = FromHex(CTransaction(), raw) tx.rehash() return tx.vout[index].scriptPubKey
def CheckForDoubleSpends(self, nodes): spent_inputs = [] seen_transactions = [] ds_counter = 0 for node in nodes: for height in range(node.getblockcount() + 1): blockhash = node.getblockhash(height) block = node.getblock(blockhash, 2) blockHex = node.getblock(blockhash, False) for txraw in block['tx'][1:]: # exclude coinbase # skip the identical transactions in the two chains, they are no double spends if txraw['txid'] in seen_transactions: continue else: seen_transactions.append(txraw['txid']) for i in txraw['vin']: utxoA = (i['txid'], i['vout']) blockA = FromHex(CBlock(), blockHex) txA = FromHex(CTransaction(), txraw['hex']) foundB = [ j for j in spent_inputs if j['utxo'] == utxoA ] if foundB: ds_counter += 1 foundB = foundB[0] blockB = foundB['block'] txB = foundB['tx'] txA.rehash() txB.rehash() blockA.vtx[0].rehash() blockB.vtx[0].rehash() sha256_A = blockA.vtx[0].sha256 sha256_B = blockB.vtx[0].sha256 dsdMessage = msg_dsdetected(blocksDetails=[ BlockDetails([CBlockHeader(blockA)], DSMerkleProof( 1, txA, blockA.hashMerkleRoot, [MerkleProofNode(sha256_A)])), BlockDetails([CBlockHeader(blockB)], DSMerkleProof( 1, txB, blockB.hashMerkleRoot, [MerkleProofNode(sha256_B)])) ]) self.message = dsdMessage dsdBytes = dsdMessage.serialize() dsdMessageDeserialized = msg_dsdetected() dsdMessageDeserialized.deserialize( BytesIO(dsdBytes)) assert_equal(str(dsdMessage), str(dsdMessageDeserialized)) break else: spent_inputs.append({ 'txid': txraw['txid'], 'tx': txA, 'utxo': utxoA, 'block': blockA }) return ds_counter