def generate_small_transactions(node, count, utxo_list): fee = 100000000 # TODO: replace this with node relay fee based calculation num_transactions = 0 random.shuffle(utxo_list) utxo = None while len(utxo_list) >= 2 and num_transactions < count: tx = CTransaction() input_amount = 0 for _ in range(2): utxo = utxo_list.pop() tx.vin.append( CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout']))) input_amount += int(utxo['amount'] * COIN) output_amount = (input_amount - fee) // 3 if output_amount <= 0: # Sanity check -- if we chose inputs that are too small, skip continue for _ in range(3): if utxo is not None: tx.vout.append( CTxOut(output_amount, hex_str_to_bytes(utxo['scriptPubKey']))) # Sign and send the transaction to get into the mempool tx_signed_hex = node.signrawtransaction(to_hex(tx))['hex'] node.sendrawtransaction(tx_signed_hex) num_transactions += 1
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(105): 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(105): out.append(self.chain.get_spendable_output()) assert_equal(node.getblock(node.getbestblockhash())['height'], 105) block(1) redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000]) spend_tx1 = CTransaction() spend_tx1.vin.append( CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(), 0xffffffff)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.calc_sha256() self.log.info(spend_tx1.hash) self.chain.update_block(1, [spend_tx1]) yield self.accepted() tx1 = CTransaction() tx1.vout = [CTxOut(499, CScript([OP_TRUE]))] tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff)) tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff)) tx1.calc_sha256() self.log.info(tx1.hash) yield TestInstance( [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) test, out, _ = prepare_init_chain(self.chain, 105, 105, block_0=False) yield test assert_equal(node.getblock(node.getbestblockhash())['height'], 105) block(1) redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000]) spend_tx1 = CTransaction() spend_tx1.vin.append( CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(), 0xffffffff)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.calc_sha256() self.log.info(spend_tx1.hash) self.chain.update_block(1, [spend_tx1]) yield self.accepted() tx1 = CTransaction() tx1.vout = [CTxOut(499, CScript([OP_TRUE]))] tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff)) tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff)) tx1.calc_sha256() self.log.info(tx1.hash) yield TestInstance( [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
def _zmq_test(self): block_hashes = self.nodes[0].generate(101) """Test case 1""" tx_hash1 = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) tx_hash2 = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) block_hash1 = self.nodes[0].generate(1)[0] # sync blocks so we are synchronized later in test sync_blocks(self.nodes) # receive notifications for txs to be included in block msg1 = self.zmqSubSocket.recv_multipart() assert_equal(msg1[0], b"removedfrommempoolblock") msg1_body = json.loads(msg1[1]) assert_equal(msg1_body["reason"], "included-in-block") msg2 = self.zmqSubSocket.recv_multipart() assert_equal(msg2[0], b"removedfrommempoolblock") msg2_body = json.loads(msg2[1]) assert_equal(msg2_body["reason"], "included-in-block") removed_tx = [msg1_body["txid"], msg2_body["txid"]] assert_equal(tx_hash1 in removed_tx and tx_hash2 in removed_tx, True) """Test case 2""" # bring txs back to mempool self.nodes[0].invalidateblock(block_hash1) # invalidate again so the coins that txs uses are immature self.nodes[0].invalidateblock(block_hashes[len(block_hashes) - 2]) # receive notifications for txs about reorg mempool removal reason msg1 = self.zmqSubSocket.recv_multipart() assert_equal(msg1[0], b"removedfrommempoolblock") msg1_body = json.loads(msg1[1]) assert_equal(msg1_body["reason"], "reorg") msg2 = self.zmqSubSocket.recv_multipart() assert_equal(msg2[0], b"removedfrommempoolblock") msg2_body = json.loads(msg2[1]) assert_equal(msg2_body["reason"], "reorg") removed_tx = [msg1_body["txid"], msg2_body["txid"]] assert_equal(tx_hash1 in removed_tx and tx_hash2 in removed_tx, True) """Test case 3""" # bring both nodes on same height self.nodes[1].invalidateblock(block_hashes[len(block_hashes) - 2]) self.nodes[0].generate(4) sync_blocks(self.nodes) unspent = self.nodes[0].listunspent()[0] # create tx with spendable output for both nodes to use tx_spendable_output = CTransaction() tx_outs = [CTxOut(4500000000, CScript([OP_TRUE]))] tx_spendable_output.vout = tx_outs tx_spendable_output.vin = [ CTxIn(COutPoint(int(unspent["txid"], 16), 0)) ] tx_hex = self.nodes[0].signrawtransaction( ToHex(tx_spendable_output))['hex'] self.nodes[0].sendrawtransaction(tx_hex, True) tx_spendable_output = FromHex(CTransaction(), tx_hex) tx_spendable_output.rehash() self.nodes[0].generate(1) # ignore included in block message _ = self.zmqSubSocket.recv_multipart() sync_blocks(self.nodes) # disconnect nodes and create transaction tx2 on node1 and mine a block # then create tx1 on node0 that use same output as tx2. disconnect_nodes_bi(self.nodes, 0, 1) tx2 = CTransaction() tx_outs = [CTxOut(4400000000, CScript([OP_TRUE]))] tx2.vout = tx_outs tx2.vin = [CTxIn(COutPoint(int(tx_spendable_output.hash, 16), 0))] tx_hex = self.nodes[1].signrawtransaction(ToHex(tx2))['hex'] tx2_size = len(tx_hex) / 2 tx2 = FromHex(CTransaction(), tx_hex) tx2.rehash() self.nodes[1].sendrawtransaction(tx_hex, True) blockhash = self.nodes[1].generate(1)[0] tx1 = CTransaction() tx_outs = [CTxOut(4300000000, CScript([OP_TRUE]))] tx1.vout = tx_outs tx1.vin = [CTxIn(COutPoint(int(tx_spendable_output.hash, 16), 0))] tx_hex = self.nodes[0].signrawtransaction(ToHex(tx1))['hex'] tx1 = FromHex(CTransaction(), tx_hex) tx1.rehash() self.nodes[0].sendrawtransaction(tx_hex, True) # connect nodes again and sync blocks, we now expect to get conflict for tx1 # because tx2 that uses same output as tx1 is already in block. connect_nodes_bi(self.nodes, 0, 1) sync_blocks(self.nodes) msg = self.zmqSubSocket.recv_multipart() assert_equal(msg[0], b"discardedfrommempool") body = json.loads(msg[1]) assert_equal(body["reason"], "collision-in-block-tx") assert_equal(body["txid"], tx1.hash) assert_equal(body["collidedWith"]["txid"], tx2.hash) assert_equal(body["collidedWith"]["size"], tx2_size) assert_equal(body["blockhash"], blockhash) """Test case 4""" # create tx with spendable output for both nodes to use unspent = self.nodes[0].listunspent()[0] tx_spendable_output = CTransaction() tx_outs = [CTxOut(4500000000, CScript([OP_TRUE]))] tx_spendable_output.vout = tx_outs tx_spendable_output.vin = [ CTxIn(COutPoint(int(unspent["txid"], 16), 0)) ] tx_hex = self.nodes[0].signrawtransaction( ToHex(tx_spendable_output))['hex'] self.nodes[0].sendrawtransaction(tx_hex, True) tx_spendable_output = FromHex(CTransaction(), tx_hex) tx_spendable_output.rehash() self.nodes[0].generate(5) # ignore included in block message _ = self.zmqSubSocket.recv_multipart() sync_blocks(self.nodes) # disconnect nodes; mine few blocks on n1; create transaction tx2 on node1 and mine a block # then create tx1 on node0 that use same output as tx2. disconnect_nodes_bi(self.nodes, 0, 1) self.nodes[1].generate(5) tx2 = CTransaction() tx_outs = [CTxOut(4400000000, CScript([OP_TRUE]))] tx2.vout = tx_outs tx2.vin = [CTxIn(COutPoint(int(tx_spendable_output.hash, 16), 0))] tx_hex = self.nodes[1].signrawtransaction(ToHex(tx2))['hex'] tx2_size = len(tx_hex) / 2 tx2 = FromHex(CTransaction(), tx_hex) tx2.rehash() self.nodes[1].sendrawtransaction(tx_hex, True) blockhash_tx2 = self.nodes[1].generate(1)[0] tx1 = CTransaction() tx_outs = [CTxOut(4300000000, CScript([OP_TRUE]))] tx1.vout = tx_outs tx1.vin = [CTxIn(COutPoint(int(tx_spendable_output.hash, 16), 0))] tx_hex = self.nodes[0].signrawtransaction(ToHex(tx1))['hex'] tx1 = FromHex(CTransaction(), tx_hex) tx1.rehash() self.nodes[0].sendrawtransaction(tx_hex, True) self.nodes[0].generate(1) # ignore included in block message _ = self.zmqSubSocket.recv_multipart() # connect nodes again to cause reorg to n1 chain, we now expect to # get conflict for tx1, because tx2 that uses same input as tx1 is already # in block on longer chain. connect_nodes_bi(self.nodes, 0, 1) sync_blocks(self.nodes) msg = self.zmqSubSocket.recv_multipart() assert_equal(msg[0], b"discardedfrommempool") body = json.loads(msg[1]) assert_equal(body["reason"], "collision-in-block-tx") assert_equal(body["txid"], tx1.hash) assert_equal(body["collidedWith"]["txid"], tx2.hash) assert_equal(body["collidedWith"]["size"], tx2_size) assert_equal(body["blockhash"], blockhash_tx2)