def generate_test_instance(self, pubkeystring, scriptsigstring): scriptpubkey = ParseScript(pubkeystring) scriptsig = ParseScript(scriptsigstring) test = TestInstance(sync_every_block=False) test_build = TestBuilder() test_build.create_credit_tx(scriptpubkey) test_build.create_spend_tx(scriptsig) test_build.rehash() block = create_block(self.tip, test_build.tx1, self.block_time) self.block_time += 1 block.solve() self.tip = block.sha256 test.blocks_and_transactions = [[block, True]] for i in xrange(100): block = create_block(self.tip, create_coinbase(), self.block_time) self.block_time += 1 block.solve() self.tip = block.sha256 test.blocks_and_transactions.append([block, True]) block = create_block(self.tip, create_coinbase(), self.block_time) self.block_time += 1 block.vtx.append(test_build.tx2) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() test.blocks_and_transactions.append([block, None]) return test
def run_test(self): # First, quick check that CSV is ACTIVE at genesis assert_equal(self.nodes[0].getblockcount(), 0) assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active') self.nodes[0].add_p2p_connection(P2PInterface()) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info("Test that blocks past the genesis block must be at least version 4") # Create a v3 block tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(1), block_time) block.nVersion = 3 block.solve() # The best block should not have changed, because... assert_equal(self.nodes[0].getbestblockhash(), tip) # ... we rejected it because it is v3 with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]): # Send it to the node self.nodes[0].p2p.send_and_ping(msg_block(block)) self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted") # Generate 100 blocks so that first coinbase matures generated_blocks = self.nodes[0].generate(100) spendable_coinbase_txid = self.nodes[0].getblock(generated_blocks[0])['tx'][0] coinbase_value = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(spendable_coinbase_txid)["hex"])["vout"][0]["value"] tip = generated_blocks[-1] # Construct a v4 block block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(len(generated_blocks) + 1), block_time) block.nVersion = 4 # Create a CLTV transaction spendtx = create_transaction(self.nodes[0], spendable_coinbase_txid, self.nodeaddress, amount=1.0, fee=coinbase_value-1) spendtx = cltv_validate(self.nodes[0], spendtx, 1) spendtx.rehash() # Add the CLTV transaction and prepare for sending block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Send block and check that it becomes new best block self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def get_tests(self): self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) self.block_time = 1333230000 # before the BIP16 switchover ''' Create a new block with an anyone-can-spend coinbase ''' block = create_block(self.tip, create_coinbase(), self.block_time) self.block_time += 1 block.solve() self.tip = block.sha256 yield TestInstance(objects=[[block, True]]) ''' Build out to 100 blocks total, maturing the coinbase. ''' test = TestInstance(objects=[], sync_every_block=False, sync_every_tx=False) for i in xrange(100): b = create_block(self.tip, create_coinbase(), self.block_time) b.solve() test.blocks_and_transactions.append([b, True]) self.tip = b.sha256 self.block_time += 1 yield test ''' Iterate through script tests. ''' counter = 0 for script_test in self.scripts.get_records(): ''' Reset the blockchain to genesis block + 100 blocks. ''' if self.nodes[0].getblockcount() > 101: self.nodes[0].invalidateblock(self.nodes[0].getblockhash(102)) self.nodes[1].invalidateblock(self.nodes[1].getblockhash(102)) self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) [scriptsig, scriptpubkey, flags] = script_test[0:3] flags = ParseScriptFlags(flags) # We can use block time to determine whether the nodes should be # enforcing BIP16. # # We intentionally let the block time grow by 1 each time. # This forces the block hashes to differ between tests, so that # a call to invalidateblock doesn't interfere with a later test. if (flags & SCRIPT_VERIFY_P2SH): self.block_time = 1333238400 + counter # Advance to enforcing BIP16 else: self.block_time = 1333230000 + counter # Before the BIP16 switchover print "Script test: [%s]" % script_test yield self.generate_test_instance(scriptpubkey, scriptsig) counter += 1
def build_block_on_tip(self): height = self.nodes[0].getblockcount() tip = self.nodes[0].getbestblockhash() mtp = self.nodes[0].getblockheader(tip)['mediantime'] block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1) block.solve() return block
def get_tests(self): self.coinbase_blocks = self.nodes[0].generate(1) self.nodes[0].generate(100) self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) self.nodeaddress = self.nodes[0].getnewaddress() '''Check that the rules are enforced.''' for valid in (True, False): spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) if not valid: self.invalidate_transaction(spendtx) spendtx.rehash() gbt = self.nodes[0].getblocktemplate() self.block_time = gbt["mintime"] + 1 self.block_bits = int("0x" + gbt["bits"], 0) block = create_block(self.tip, create_coinbase(101), self.block_time, self.block_bits) block.nVersion = 4 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.block_time += 1 self.tip = block.sha256 yield TestInstance([[block, valid]])
def run_test(self): node = self.nodes[0] # alias node.add_p2p_connection(P2PStoreTxInvs()) self.log.info("Create a new transaction and wait until it's broadcast") txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16) # Can take a few seconds due to transaction trickling wait_until(lambda: node.p2p.tx_invs_received[txid] >= 1, lock=mininode_lock) # Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown) node.add_p2p_connection(P2PStoreTxInvs()) self.log.info("Create a block") # Create and submit a block without the transaction. # Transactions are only rebroadcast if there has been a block at least five minutes # after the last time we tried to broadcast. Use mocktime and give an extra minute to be sure. block_time = int(time.time()) + 6 * 60 node.setmocktime(block_time) block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockchaininfo()['blocks']), block_time) block.nVersion = 3 block.rehash() block.solve() node.submitblock(ToHex(block)) # Transaction should not be rebroadcast node.p2ps[1].sync_with_ping() assert_equal(node.p2ps[1].tx_invs_received[txid], 0) self.log.info("Transaction should be rebroadcast after 30 minutes") # Use mocktime and give an extra 5 minutes to be sure. rebroadcast_time = int(time.time()) + 41 * 60 node.setmocktime(rebroadcast_time) wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock)
def solve_and_send_block(prevhash, height, time): b = create_block(prevhash, create_coinbase(height), time) b.nVersion = 0x20000000 b.solve() node.p2p.send_message(msg_block(b)) node.p2p.sync_with_ping() return b
def run_test(self): # Add p2p connection to node0 node = self.nodes[0] # convenience reference to the node node.add_p2p_connection(P2PDataStore()) network_thread_start() node.p2p.wait_for_verack() best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generate(100) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) node.p2p.send_txs_and_test([tx1], node, success=False, reject_code=16, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)') # Verify valid transaction tx1 = create_transaction(block1.vtx[0], 0, b'', 50 * COIN - 12000) node.p2p.send_txs_and_test([tx1], node, success=True)
def build_block_on_tip(self, node): height = node.getblockcount() tip = node.getbestblockhash() mtp = node.getblockheader(tip)["mediantime"] block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1) block.solve() return block
def create_credit_tx(self, scriptPubKey): # self.tx1 is a coinbase transaction, modeled after the one created by script_tests.cpp # This allows us to reuse signatures created in the unit test framework. self.tx1 = create_coinbase() # this has a bip34 scriptsig, self.tx1.vin[0].scriptSig = CScript([0, 0]) # but this matches the unit tests self.tx1.vout[0].nValue = 0 self.tx1.vout[0].scriptPubKey = scriptPubKey self.tx1.rehash()
def create_test_block(self, txs, version = 536870912): block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600) block.nVersion = version block.vtx.extend(txs) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() return block
def build_block_on_tip(self): height = self.nodes[0].getblockcount() tip = self.nodes[0].getbestblockhash() mtp = self.nodes[0].getblockheader(tip)['mediantime'] block = create_block(int(tip, 16), create_coinbase(absoluteHeight = height + 1), mtp + 1) if XT_TWEAKS: block.nVersion = 4 block.solve() return block
def build_block_on_tip(self, node, segwit=False): height = node.getblockcount() tip = node.getbestblockhash() mtp = node.getblockheader(tip)['mediantime'] block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1) block.nVersion = 4 if segwit: add_witness_commitment(block) block.solve() return block
def generate_blocks(self, number, version, error = None): for i in range(number): block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = version block.rehash() block.solve() assert_equal(self.nodes[0].submitblock(bytes_to_hex_str(block.serialize())), error) if (error == None): self.last_block_time += 1 self.tip = block.sha256 self.height += 1
def generate_blocks(self, number, version, test_blocks = []): for i in xrange(number): block = create_block(self.tip, create_coinbase(absoluteHeight=self.height), self.last_block_time + 1) block.nVersion = version block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 self.height += 1 return test_blocks
def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time): blocks = [] for _ in range(nblocks): coinbase = create_coinbase(prev_height + 1) block_time = prev_median_time + 1 block = create_block(int(prev_hash, 16), coinbase, block_time) block.solve() blocks.append(block) prev_hash = block.hash prev_height += 1 prev_median_time = block_time return blocks
def submit_block_with_tx(node, tx): ctx = CTransaction() ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx))) tip = node.getbestblockhash() height = node.getblockcount() + 1 block_time = node.getblockheader(tip)["mediantime"] + 1 block = blocktools.create_block(int(tip, 16), blocktools.create_coinbase(height), block_time) block.vtx.append(ctx) block.rehash() block.hashMerkleRoot = block.calc_merkle_root() block.solve() node.submitblock(bytes_to_hex_str(block.serialize(True)), '', True) return block
def send_blocks_with_version(self, peer, numblocks, nVersionToUse): tip = self.nodes[0].getbestblockhash() height = self.nodes[0].getblockcount() block_time = self.nodes[0].getblockheader(tip)["time"]+1 tip = int(tip, 16) for _ in range(numblocks): block = create_block(tip, create_coinbase(height+1), block_time) block.nVersion = nVersionToUse block.solve() peer.send_message(msg_block(block)) block_time += 1 height += 1 tip = block.sha256 peer.sync_with_ping()
def submit_block_with_tx(node, tx): ctx = CTransaction() ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx))) tip = node.getbestblockhash() height = node.getblockcount() + 1 block_time = node.getblockheader(tip)["mediantime"] + 1 block = create_block(int(tip, 16), create_coinbase(height), block_time, version=4) block.vtx.append(ctx) block.rehash() block.hashMerkleRoot = block.calc_merkle_root() add_witness_commitment(block) block.solve() node.submitblock(block.serialize(True).hex()) return block
def test_null_locators(self, test_node): tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip_hash = int(tip["hash"], 16) self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") test_node.clear_last_announcement() test_node.get_headers(locator=[], hashstop=tip_hash) assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True) self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block.solve() test_node.send_header_for_blocks([block]) test_node.clear_last_announcement() test_node.get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.sync_with_ping() assert_equal(test_node.block_announced, False) test_node.send_message(msg_block(block))
def block_submit(self, node, txs, accept = False): block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1) block.nVersion = 4 for tx in txs: tx.rehash() block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(bytes_to_hex_str(block.serialize())) if (accept): assert_equal(node.getbestblockhash(), block.hash) self.tip = block.sha256 self.lastblockhash = block.hash self.lastblocktime += 1 self.lastblockheight += 1 else: assert_equal(node.getbestblockhash(), self.lastblockhash)
def createBlock (self): """ Creates a new block that is valid for the current tip. It is marked as auxpow, but the auxpow is not yet filled in. """ bestHash = self.nodes[0].getbestblockhash () bestBlock = self.nodes[0].getblock (bestHash) tip = int (bestHash, 16) height = bestBlock["height"] + 1 time = bestBlock["time"] + 1 block = create_block (tip, create_coinbase (height), time) block.mark_auxpow () block.rehash () newHash = "%064x" % block.sha256 return block, newHash
def block_submit(self, node, txs, witness=False, accept=False): block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1) block.set_base_version(4) for tx in txs: tx.rehash() block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() witness and add_witness_commitment(block) block.rehash() block.solve() node.submitblock(block.serialize(True).hex()) if (accept): assert_equal(node.getbestblockhash(), block.hash) self.tip = block.sha256 self.lastblockhash = block.hash self.lastblocktime += 1 self.lastblockheight += 1 else: assert_equal(node.getbestblockhash(), self.lastblockhash)
def test_bip68_not_consensus(self): assert(get_bip9_status(self.nodes[0], 'csv')['status'] != 'active') txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # Make an anyone-can-spend transaction tx2 = CTransaction() tx2.nVersion = 1 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] # sign tx2 tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(ToHex(tx2)) # Now make an invalid spend of tx2 according to BIP68 sequence_value = 100 # 100 block relative locktime tx3 = CTransaction() tx3.nVersion = 2 tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)] tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), CScript([b'a' * 35]))] tx3.rehash() assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) # make a block that violates bip68; ensure that the tip updates tip = int(self.nodes[0].getbestblockhash(), 16) block = create_block(tip, create_coinbase(self.nodes[0].getblockcount()+1)) block.nVersion = 3 block.vtx.extend([tx1, tx2, tx3]) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() add_witness_commitment(block) block.solve() self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def mine_large_blocks(node, n): # Make a large scriptPubKey for the coinbase transaction. This is OP_RETURN # followed by 950k of OP_NOP. This would be non-standard in a non-coinbase # transaction but is consensus valid. # Set the nTime if this is the first time this function has been called. # A static variable ensures that time is monotonicly increasing and is therefore # different for each block created => blockhash is unique. if "nTimes" not in mine_large_blocks.__dict__: mine_large_blocks.nTime = 0 # Get the block parameters for the first block big_script = CScript([OP_RETURN] + [OP_NOP] * 950000) best_block = node.getblock(node.getbestblockhash()) height = int(best_block["height"]) + 1 mine_large_blocks.nTime = max(mine_large_blocks.nTime, int(best_block["time"])) + 1 previousblockhash = int(best_block["hash"], 16) for _ in range(n): # Build the coinbase transaction (with large scriptPubKey) coinbase_tx = create_coinbase(height) coinbase_tx.vin[0].nSequence = 2 ** 32 - 1 coinbase_tx.vout[0].scriptPubKey = big_script coinbase_tx.rehash() # Build the block block = CBlock() block.nVersion = best_block["version"] block.hashPrevBlock = previousblockhash block.nTime = mine_large_blocks.nTime block.nBits = int('207fffff', 16) block.nNonce = 0 block.vtx = [coinbase_tx] block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Submit to the node node.submitblock(ToHex(block)) previousblockhash = block.sha256 height += 1 mine_large_blocks.nTime += 1
def block_submit(self, node, txs, witness = False, accept = False): block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1) #Experiencecoin: old block verions not accepted after segwit activation block.nVersion = 0x83 for tx in txs: tx.rehash() block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() witness and add_witness_commitment(block) block.rehash() block.solve() node.submitblock(bytes_to_hex_str(block.serialize(True))) if (accept): assert_equal(node.getbestblockhash(), block.hash) self.tip = block.sha256 self.lastblockhash = block.hash self.lastblocktime += 1 self.lastblockheight += 1 else: assert_equal(node.getbestblockhash(), self.lastblockhash)
def tryUpdateInBlock (self, name, value, addr, withWitness): """ Tries to update the given name with a dummy witness directly in a block (to bypass any checks done on the mempool). """ txHex = self.buildDummySegwitNameUpdate (name, value, addr) tx = CTransaction () tx.deserialize (io.BytesIO (hex_str_to_bytes (txHex))) tip = self.node.getbestblockhash () height = self.node.getblockcount () + 1 nTime = self.node.getblockheader (tip)["mediantime"] + 1 block = create_block (int (tip, 16), create_coinbase (height), nTime, version=4) block.vtx.append (tx) add_witness_commitment (block, 0) block.solve () blkHex = block.serialize (withWitness).hex () return self.node.submitblock (blkHex)
def createBlock (self): """ Creates and mines a new block with auxpow. """ bestHash = self.nodes[0].getbestblockhash () bestBlock = self.nodes[0].getblock (bestHash) tip = int (bestHash, 16) height = bestBlock["height"] + 1 time = bestBlock["time"] + 1 block = create_block (tip, create_coinbase (height), time) block.mark_auxpow () block.rehash () newHash = "%064x" % block.sha256 target = b"%064x" % uint256_from_compact (block.nBits) auxpowHex = computeAuxpow (newHash, target, True) block.auxpow = CAuxPow () block.auxpow.deserialize (BytesIO (hex_str_to_bytes (auxpowHex))) return block, newHash
def test_null_locators(self, test_node): tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip_hash = int(tip["hash"], 16) # TODO this partly fixes the same thing that is fixed by https://github.com/bitcoin/bitcoin/pull/13192 # This will later conflict when backporting the actual fix. Just take everything from the Bitcoin fix as a # resolution assert_equal(test_node.check_last_announcement(headers=[], inv=[tip_hash]), True) self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") test_node.clear_last_announcement() test_node.get_headers(locator=[], hashstop=tip_hash) assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True) self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block.solve() test_node.send_header_for_blocks([block]) test_node.clear_last_announcement() test_node.get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.sync_with_ping() assert_equal(test_node.block_announced, False) test_node.send_message(msg_block(block))
def get_tests(self): self.coinbase_blocks = self.nodes[0].generate(2) height = 3 # height of the next block to build self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.nodeaddress = self.nodes[0].getnewaddress() self.last_block_time = int(time.time()) """ 98 more version 2 blocks """ test_blocks = [] for i in range(98): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(2) block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) """ Mine 749 version 3 blocks """ test_blocks = [] for i in range(749): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(3) block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) """ Check that the new DERSIG rules are not enforced in the 750th version 3 block. """ spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) unDERify(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(3) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) """ Check that the new DERSIG rules are enforced in the 751st version 3 block. """ spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) unDERify(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(3) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]]) """ Mine 199 new version blocks on last valid tip """ test_blocks = [] for i in range(199): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(3) block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) """ Mine 1 old version block """ block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(2) block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) """ Mine 1 new version block """ block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(3) block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) """ Mine 1 old version block, should be invalid """ block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.set_base_version(2) block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]])
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generatetoaddress( 100, self.nodes[0].get_deterministic_priv_key().address) # Iterate through a list of known invalid transaction types, ensuring each is # rejected. Some are consensus invalid and some just violate policy. for BadTxTemplate in invalid_txs.iter_all_templates(): self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__) template = BadTxTemplate(spend_block=block1) tx = template.get_tx() node.p2p.send_txs_and_test( [tx], node, success=False, expect_disconnect=template.expect_disconnect, reject_reason=template.reject_reason, ) if template.expect_disconnect: self.log.info("Reconnecting to peer") self.reconnect_p2p() # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependent transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout.append( CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append( CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [ CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test( [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool()))
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2) ] self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that a transaction with non-DER signature can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time) block.set_base_version(2) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time) block.set_base_version(2) block.rehash() block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ '{}, bad-version(0x00010002)'.format(block.hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() self.log.info( "Test that transactions with non-DER signatures cannot appear in a block" ) block.set_base_version(3) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() # First we show that this tx is valid except for DERSIG by getting it # rejected from the mempool for exactly that reason. assert_equal([{ 'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Non-canonical DER signature)' }], self.nodes[0].testmempoolaccept( rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True)) # Now we verify that a block with this transaction is also invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ 'CheckInputs on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)' .format(block.vtx[-1].hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert self.nodes[0].p2p.last_message["reject"].code in [ REJECT_INVALID, REJECT_NONSTANDARD ] assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) assert b'Non-canonical DER signature' in self.nodes[ 0].p2p.last_message["reject"].reason self.log.info( "Test that a version 3 block with a DERSIG-compliant transaction is accepted" ) block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() self.log.info("Mining %d blocks", DERSIG_HEIGHT - 1) self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 1) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info("Test that blocks must now be at least version 3") tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block( int(tip, 16), create_coinbase(DERSIG_HEIGHT), block_time) block.nVersion = 2 block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal( self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) assert_equal( self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)') assert_equal( self.nodes[0].p2p.last_message["reject"].data, block.sha256) del self.nodes[0].p2p.last_message["reject"] self.log.info( "Test that transactions with non-DER signatures cannot appear in a block") block.nVersion = 3 spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) unDERify(spendtx) spendtx.rehash() # Now we verify that a block with this transaction is invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: # We can receive different reject messages depending on whether # bitcoind is running with multiple script check threads. If script # check threads are not in use, then transaction script validation # happens sequentially, and bitcoind produces more specific reject # reasons. assert self.nodes[0].p2p.last_message["reject"].code in [ REJECT_INVALID, REJECT_NONSTANDARD] assert_equal( self.nodes[0].p2p.last_message["reject"].data, block.sha256) if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal( self.nodes[0].p2p.last_message["reject"].reason, b'blk-bad-inputs') else: assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason self.log.info( "Test that a version 3 block with a DERSIG-compliant transaction is accepted") block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): # Add p2p connection to node0 node = self.nodes[0] # convenience reference to the node node.add_p2p_connection(P2PDataStore()) best_block = node.getblock(node.getbestblockhash()) tip = int(node.getbestblockhash(), 16) height = best_block["height"] + 1 block_time = best_block["time"] + 1 self.log.info("Create a new block with an anyone-can-spend coinbase") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block1], node, True) self.log.info("Mature the block.") node.generate(100) best_block = node.getblock(node.getbestblockhash()) tip = int(node.getbestblockhash(), 16) height = best_block["height"] + 1 block_time = best_block["time"] + 1 # Use merkle-root malleability to generate an invalid block with # same blockheader. # Manufacture a block with 3 transactions (coinbase, spend of prior # coinbase, spend of that spend). Duplicate the 3rd transaction to # leave merkle root and blockheader unchanged but invalidate the block. self.log.info("Test merkle root malleability.") block2 = create_block(tip, create_coinbase(height), block_time) block_time += 1 # b'0x51' is OP_TRUE tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=b'\x51', amount=50 * COIN) tx2 = create_tx_with_script(tx1, 0, script_sig=b'\x51', amount=50 * COIN) block2.vtx.extend([tx1, tx2]) block2.hashMerkleRoot = block2.calc_merkle_root() block2.rehash() block2.solve() orig_hash = block2.sha256 block2_orig = copy.deepcopy(block2) # Mutate block 2 block2.vtx.append(tx2) assert_equal(block2.hashMerkleRoot, block2.calc_merkle_root()) assert_equal(orig_hash, block2.rehash()) assert(block2_orig.vtx != block2.vtx) node.p2p.send_blocks_and_test([block2], node, False, False, 16, b'bad-txns-duplicate') # Check transactions for duplicate inputs self.log.info("Test duplicate input block.") block2_orig.vtx[2].vin.append(block2_orig.vtx[2].vin[0]) block2_orig.vtx[2].rehash() block2_orig.hashMerkleRoot = block2_orig.calc_merkle_root() block2_orig.rehash() block2_orig.solve() node.p2p.send_blocks_and_test([block2_orig], node, success=False, request_block=False, reject_reason=b'bad-txns-inputs-duplicate') self.log.info("Test very broken block.") block3 = create_block(tip, create_coinbase(height), block_time) block_time += 1 block3.vtx[0].vout[0].nValue = 2200000000 * COIN # Too high! block3.vtx[0].sha256 = None block3.vtx[0].calc_sha256() block3.hashMerkleRoot = block3.calc_merkle_root() block3.rehash() block3.solve() node.p2p.send_blocks_and_test([block3], node, False, False, 16, b'bad-cb-amount')
def run_test(self): # Setup the p2p connections and start up the network thread. inv_node = InvNode() test_node = TestNode() self.p2p_connections = [inv_node, test_node] connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) # Set nServices to 0 for test_node, so no block download will occur outside of # direct fetching connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0)) inv_node.add_connection(connections[0]) test_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here inv_node.wait_for_verack() test_node.wait_for_verack() tip = int(self.nodes[0].getbestblockhash(), 16) # PART 1 # 1. Mine a block; expect inv announcements each time print "Part 1: headers don't start before sendheaders message..." for i in xrange(4): old_tip = tip tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) # Try a few different responses; none should affect next announcement if i == 0: # first request the block test_node.get_data([tip]) test_node.wait_for_block(tip, timeout=5) elif i == 1: # next try requesting header and block test_node.get_headers(locator=[old_tip], hashstop=tip) test_node.get_data([tip]) test_node.wait_for_block(tip) test_node.clear_last_announcement( ) # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 new_block = create_block(tip, create_coinbase(height + 1), block_time) new_block.solve() test_node.send_header_for_blocks([new_block]) test_node.wait_for_getdata([new_block.sha256], timeout=5) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed inv_node.clear_last_announcement() test_node.clear_last_announcement() print "Part 1: success!" print "Part 2: announce blocks with headers after sendheaders message..." # PART 2 # 2. Send a sendheaders message and test that headers announcements # commence and keep working. test_node.send_message(msg_sendheaders()) prev_tip = int(self.nodes[0].getbestblockhash(), 16) test_node.get_headers(locator=[prev_tip], hashstop=0L) test_node.sync_with_ping() # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead for i in xrange(10): # Mine i blocks, and alternate announcing either via # inv (of tip) or via headers. After each, new blocks # mined by the node should successfully be announced # with block header, even though the blocks are never requested for j in xrange(2): blocks = [] for b in xrange(i + 1): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 if j == 0: # Announce via inv test_node.send_block_inv(tip) test_node.wait_for_getdata([tip], timeout=5) # Test that duplicate inv's won't result in duplicate # getdata requests, or duplicate headers announcements inv_node.send_block_inv(tip) # Should have received a getheaders as well! test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata( [x.sha256 for x in blocks[0:-1]], timeout=5) [inv_node.send_block_inv(x.sha256) for x in blocks[0:-1]] inv_node.sync_with_ping() else: # Announce via headers test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) # Test that duplicate headers won't result in duplicate # getdata requests (the check is further down) inv_node.send_header_for_blocks(blocks) inv_node.sync_with_ping() [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() inv_node.sync_with_ping() # This block should not be announced to the inv node (since it also # broadcast it) assert_equal(inv_node.last_inv, None) assert_equal(inv_node.last_headers, None) tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height += 1 block_time += 1 print "Part 2: success!" print "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..." # PART 3. Headers announcements can stop after large reorg, and resume after # getheaders or inv from peer. for j in xrange(2): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal( test_node.check_last_announcement(headers=new_block_hashes), True) block_time += 8 # Mine a too-large reorg, which should be announced with a single inv new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) block_time += 9 fork_point = self.nodes[0].getblock( "%02x" % new_block_hashes[0])["previousblockhash"] fork_point = int(fork_point, 16) # Use getblocks/getdata test_node.send_getblocks(locator=[fork_point]) assert_equal( test_node.check_last_announcement(inv=new_block_hashes), True) test_node.get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in xrange(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) if i == 0: # Just get the data -- shouldn't cause headers announcements to resume test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # Send a getheaders message that shouldn't trigger headers announcements # to resume (best header sent will be too old) test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 2: test_node.get_data([tip]) test_node.wait_for_block(tip) # This time, try sending either a getheaders to trigger resumption # of headers announcements, or mine a new block and inv it, also # triggering resumption of headers announcements. if j == 0: test_node.get_headers(locator=[tip], hashstop=0L) test_node.sync_with_ping() else: test_node.send_block_inv(tip) test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) print "Part 3: success!" print "Part 4: Testing direct fetch behavior..." tip = self.mine_blocks(1) height = self.nodes[0].getblockcount() + 1 last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 # Create 2 blocks. Send the blocks, then send the headers. blocks = [] for b in xrange(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 inv_node.send_message(msg_block(blocks[-1])) inv_node.sync_with_ping() # Make sure blocks are processed test_node.last_getdata = None test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() # should not have received any getdata messages with mininode_lock: assert_equal(test_node.last_getdata, None) # This time, direct fetch should work blocks = [] for b in xrange(3): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=test_node.sleep_time) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Now announce a header that forks the last two blocks tip = blocks[0].sha256 height -= 1 blocks = [] # Create extra blocks for later for b in xrange(20): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Announcing one block on fork should not trigger direct fetch # (less work than tip) test_node.last_getdata = None test_node.send_header_for_blocks(blocks[0:1]) test_node.sync_with_ping() with mininode_lock: assert_equal(test_node.last_getdata, None) # Announcing one more block on fork should trigger direct fetch for # both blocks (same work as tip) test_node.send_header_for_blocks(blocks[1:2]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=test_node.sleep_time) # Announcing 16 more headers should trigger direct fetch for 14 more # blocks test_node.send_header_for_blocks(blocks[2:18]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=test_node.sleep_time) # Announcing 1 more header should not trigger any response test_node.last_getdata = None test_node.send_header_for_blocks(blocks[18:19]) test_node.sync_with_ping() with mininode_lock: assert_equal(test_node.last_getdata, None) print "Part 4: success!" # Finally, check that the inv node never received a getdata request, # throughout the test assert_equal(inv_node.last_getdata, None)
def run_test(self): # Setup the p2p connections and start up the network thread. test_node = NodeConnCB() # connects to node0 min_work_node = NodeConnCB() # connects to node1 connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append( NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], min_work_node)) test_node.add_connection(connections[0]) min_work_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() min_work_node.wait_for_verack() # 1. Have nodes mine a block (leave IBD) [n.generate(1) for n in self.nodes] tips = [int("0x" + n.getbestblockhash(), 0) for n in self.nodes] # 2. Send one block that builds on each tip. # This should be accepted by node0 blocks_h2 = [] # the height 2 blocks on each node's chain block_time = int(time.time()) + 1 for i in range(2): blocks_h2.append( create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) min_work_node.send_message(msg_block(blocks_h2[1])) for x in [test_node, min_work_node]: x.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 1) self.log.info( "First height 2 block accepted by node0; correctly rejected by node1" ) # 3. Send another block that builds on genesis. block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time) block_time += 1 block_h1f.solve() test_node.send_message(msg_block(block_h1f)) test_node.sync_with_ping() tip_entry_found = False for x in self.nodes[0].getchaintips(): if x['hash'] == block_h1f.hash: assert_equal(x['status'], "headers-only") tip_entry_found = True assert (tip_entry_found) assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash) # 4. Send another two block that build on the fork. block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time) block_time += 1 block_h2f.solve() test_node.send_message(msg_block(block_h2f)) test_node.sync_with_ping() # Since the earlier block was not processed by node, the new block # can't be fully validated. tip_entry_found = False for x in self.nodes[0].getchaintips(): if x['hash'] == block_h2f.hash: assert_equal(x['status'], "headers-only") tip_entry_found = True assert (tip_entry_found) # But this block should be accepted by node since it has equal work. self.nodes[0].getblock(block_h2f.hash) self.log.info("Second height 2 block accepted, but not reorg'ed to") # 4b. Now send another block that builds on the forking chain. block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime + 1) block_h3.solve() test_node.send_message(msg_block(block_h3)) test_node.sync_with_ping() # Since the earlier block was not processed by node, the new block # can't be fully validated. tip_entry_found = False for x in self.nodes[0].getchaintips(): if x['hash'] == block_h3.hash: assert_equal(x['status'], "headers-only") tip_entry_found = True assert (tip_entry_found) self.nodes[0].getblock(block_h3.hash) # But this block should be accepted by node since it has more work. self.nodes[0].getblock(block_h3.hash) self.log.info("Unrequested more-work block accepted") # 4c. Now mine 288 more blocks and deliver; all should be processed but # the last (height-too-high) on node (as long as its not missing any headers) tip = block_h3 all_blocks = [] for i in range(288): next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime + 1) next_block.solve() all_blocks.append(next_block) tip = next_block # Now send the block at height 5 and check that it wasn't accepted (missing header) test_node.send_message(msg_block(all_blocks[1])) test_node.sync_with_ping() assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash) assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash) # The block at height 5 should be accepted if we provide the missing header, though headers_message = msg_headers() headers_message.headers.append(CBlockHeader(all_blocks[0])) test_node.send_message(headers_message) test_node.send_message(msg_block(all_blocks[1])) test_node.sync_with_ping() self.nodes[0].getblock(all_blocks[1].hash) # Now send the blocks in all_blocks for i in range(288): test_node.send_message(msg_block(all_blocks[i])) test_node.sync_with_ping() # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead for x in all_blocks[:-1]: self.nodes[0].getblock(x.hash) assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). # The node should have requested the blocks at some point, so # disconnect/reconnect first connections[0].disconnect_node() test_node.wait_for_disconnect() test_node = NodeConnCB() # connects to node (not whitelisted) connections[0] = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node) test_node.add_connection(connections[0]) test_node.wait_for_verack() test_node.send_message(msg_block(block_h1f)) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) self.log.info( "Unrequested block that would complete more-work chain was ignored" ) # 6. Try to get node to request the missing block. # Poke the node with an inv for block at height 3 and see if that # triggers a getdata on block 2 (it should if block 2 is missing). with mininode_lock: # Clear state so we can check the getdata request test_node.last_message.pop("getdata", None) test_node.send_message(msg_inv([CInv(2, block_h3.sha256)])) test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_message["getdata"] # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, block_h1f.sha256) self.log.info("Inv at tip triggered getdata for unprocessed block") # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(block_h1f)) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 290) self.nodes[0].getblock(all_blocks[286].hash) assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash) assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash) self.log.info( "Successfully reorged to longer chain from non-whitelisted peer") # 8. Create a chain which is invalid at a height longer than the # current chain, but which has more blocks on top of that block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime + 1) block_289f.solve() block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime + 1) block_290f.solve() block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime + 1) # block_291 spends a coinbase below maturity! block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1)) block_291.hashMerkleRoot = block_291.calc_merkle_root() block_291.solve() block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime + 1) block_292.solve() # Now send all the headers on the chain and enough blocks to trigger reorg headers_message = msg_headers() headers_message.headers.append(CBlockHeader(block_289f)) headers_message.headers.append(CBlockHeader(block_290f)) headers_message.headers.append(CBlockHeader(block_291)) headers_message.headers.append(CBlockHeader(block_292)) test_node.send_message(headers_message) test_node.sync_with_ping() tip_entry_found = False for x in self.nodes[0].getchaintips(): if x['hash'] == block_292.hash: assert_equal(x['status'], "headers-only") tip_entry_found = True assert (tip_entry_found) assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash) test_node.send_message(msg_block(block_289f)) test_node.send_message(msg_block(block_290f)) test_node.sync_with_ping() self.nodes[0].getblock(block_289f.hash) self.nodes[0].getblock(block_290f.hash) test_node.send_message(msg_block(block_291)) # At this point we've sent an obviously-bogus block, wait for full processing # without assuming whether we will be disconnected or not try: # Only wait a short while so the test doesn't take forever if we do get # disconnected test_node.sync_with_ping(timeout=1) except AssertionError: test_node.wait_for_disconnect() test_node = NodeConnCB() # connects to node (not whitelisted) connections[0] = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node) test_node.add_connection(connections[0]) NetworkThread().start( ) # Start up network handling in another thread test_node.wait_for_verack() # We should have failed reorg and switched back to 290 (but have block 291) assert_equal(self.nodes[0].getblockcount(), 290) assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash) assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1) # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime + 1) block_293.solve() headers_message = msg_headers() headers_message.headers.append(CBlockHeader(block_293)) test_node.send_message(headers_message) test_node.wait_for_disconnect() # 9. Connect node1 to node0 and ensure it is able to sync connect_nodes(self.nodes[0], 1) sync_blocks([self.nodes[0], self.nodes[1]]) self.log.info("Successfully synced nodes 1 and 0") [c.disconnect_node() for c in connections]
def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno): assert_equal(self.get_bip9_status(bipName)['status'], 'defined') assert_equal(self.get_bip9_status(bipName)['since'], 0) # generate some coins for later self.coinbase_blocks = self.nodes[0].generate(2) self.height = 3 # height of the next block to build self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.nodeaddress = self.nodes[0].getnewaddress() self.last_block_time = int(time.time()) assert_equal(self.get_bip9_status(bipName)['status'], 'defined') assert_equal(self.get_bip9_status(bipName)['since'], 0) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert (bipName not in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert_equal(tmpl['version'], 0x20000000) # Test 1 # Advance from DEFINED to STARTED test_blocks = self.generate_blocks(141, 4) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'started') assert_equal(self.get_bip9_status(bipName)['since'], 144) assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert_equal(tmpl['vbavailable'][bipName], bitno) assert_equal(tmpl['vbrequired'], 0) assert (tmpl['version'] & activated_version) # Test 1-A # check stats after max number of "signalling not" blocks such that LOCKED_IN still possible this period test_blocks = self.generate_blocks( 36, 4, test_blocks) # 0x00000004 (signalling not) test_blocks = self.generate_blocks( 10, activated_version) # 0x20000001 (signalling ready) yield TestInstance(test_blocks, sync_every_block=False) assert_equal( self.get_bip9_status(bipName)['statistics']['elapsed'], 46) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10) assert_equal( self.get_bip9_status(bipName)['statistics']['possible'], True) # Test 1-B # check stats after one additional "signalling not" block -- LOCKED_IN no longer possible this period test_blocks = self.generate_blocks( 1, 4, test_blocks) # 0x00000004 (signalling not) yield TestInstance(test_blocks, sync_every_block=False) assert_equal( self.get_bip9_status(bipName)['statistics']['elapsed'], 47) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10) assert_equal( self.get_bip9_status(bipName)['statistics']['possible'], False) # Test 1-C # finish period with "ready" blocks, but soft fork will still fail to advance to LOCKED_IN test_blocks = self.generate_blocks( 97, activated_version) # 0x20000001 (signalling ready) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0) assert_equal( self.get_bip9_status(bipName)['statistics']['possible'], True) assert_equal(self.get_bip9_status(bipName)['status'], 'started') # Test 2 # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1 # using a variety of bits to simulate multiple parallel softforks test_blocks = self.generate_blocks( 50, activated_version) # 0x20000001 (signalling ready) test_blocks = self.generate_blocks( 20, 4, test_blocks) # 0x00000004 (signalling not) test_blocks = self.generate_blocks( 50, activated_version, test_blocks) # 0x20000101 (signalling ready) test_blocks = self.generate_blocks( 24, 4, test_blocks) # 0x20010000 (signalling not) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'started') assert_equal(self.get_bip9_status(bipName)['since'], 144) assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert_equal(tmpl['vbavailable'][bipName], bitno) assert_equal(tmpl['vbrequired'], 0) assert (tmpl['version'] & activated_version) # Test 3 # 108 out of 144 signal bit 1 to achieve LOCKED_IN # using a variety of bits to simulate multiple parallel softforks test_blocks = self.generate_blocks( 57, activated_version) # 0x20000001 (signalling ready) test_blocks = self.generate_blocks( 26, 4, test_blocks) # 0x00000004 (signalling not) test_blocks = self.generate_blocks( 50, activated_version, test_blocks) # 0x20000101 (signalling ready) test_blocks = self.generate_blocks( 10, 4, test_blocks) # 0x20010000 (signalling not) yield TestInstance(test_blocks, sync_every_block=False) # check counting stats and "possible" flag before last block of this period achieves LOCKED_IN... assert_equal( self.get_bip9_status(bipName)['statistics']['elapsed'], 143) assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 107) assert_equal( self.get_bip9_status(bipName)['statistics']['possible'], True) assert_equal(self.get_bip9_status(bipName)['status'], 'started') # ...continue with Test 3 test_blocks = self.generate_blocks( 1, activated_version) # 0x20000001 (signalling ready) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in') assert_equal(self.get_bip9_status(bipName)['since'], 576) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) # Test 4 # 143 more version 536870913 blocks (waiting period-1) test_blocks = self.generate_blocks(143, 4) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in') assert_equal(self.get_bip9_status(bipName)['since'], 576) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) # Test 5 # Check that the new rule is enforced spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) invalidate(spendtx) spendtx = self.sign_transaction(self.nodes[0], spendtx) spendtx.rehash() invalidatePostSignature(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = activated_version block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 self.height += 1 yield TestInstance([[block, True]]) assert_equal(self.get_bip9_status(bipName)['status'], 'active') assert_equal(self.get_bip9_status(bipName)['since'], 720) tmpl = self.nodes[0].getblocktemplate({}) assert (bipName in tmpl['rules']) assert (bipName not in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert (not (tmpl['version'] & (1 << bitno))) # Test 6 # Check that the new sequence lock rules are enforced spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) invalidate(spendtx) spendtx = self.sign_transaction(self.nodes[0], spendtx) spendtx.rehash() invalidatePostSignature(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 5 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]]) # Restart all self.test.clear_all_connections() self.stop_nodes() self.nodes = [] shutil.rmtree(self.options.tmpdir + "/node0") self.setup_chain() self.setup_network() self.test.add_all_connections(self.nodes) NetworkThread().start() self.test.p2p_connections[0].wait_for_verack()
def run_test(self): # Setup the p2p connections and start up the network thread. test_node = TestNode() # connects to node0 (not whitelisted) white_node = TestNode() # connects to node1 (whitelisted) connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) test_node.add_connection(connections[0]) white_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() white_node.wait_for_verack() # 1. Have both nodes mine a block (leave IBD) [ n.generate(1) for n in self.nodes ] tips = [ int ("0x" + n.getbestblockhash() + "L", 0) for n in self.nodes ] # 2. Send one block that builds on each tip. # This should be accepted. blocks_h2 = [] # the height 2 blocks on each node's chain block_time = int(time.time()) + 1 for i in xrange(2): blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) print "First height 2 block accepted by both nodes" # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain for i in xrange(2): blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1)) blocks_h2f[i].solve() test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") for x in self.nodes[1].getchaintips(): if x['hash'] == blocks_h2f[1].hash: assert_equal(x['status'], "valid-headers") print "Second height 2 block accepted only from whitelisted peer" # 4. Now send another block that builds on the forking chain. blocks_h3 = [] for i in xrange(2): blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1)) blocks_h3[i].solve() test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h3[0].hash: assert_equal(x['status'], "headers-only") # But this block should be accepted by node0 since it has more work. try: self.nodes[0].getblock(blocks_h3[0].hash) print "Unrequested more-work block accepted from non-whitelisted peer" except: raise AssertionError("Unrequested more work block was not processed") # Node1 should have accepted and reorged. assert_equal(self.nodes[1].getblockcount(), 3) print "Successfully reorged to length 3 chain from whitelisted peer" # 4b. Now mine 288 more blocks and deliver; all should be processed but # the last (height-too-high) on node0. Node1 should process the tip if # we give it the headers chain leading to the tip. tips = blocks_h3 headers_message = msg_headers() all_blocks = [] # node0's blocks for j in xrange(2): for i in xrange(288): next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1) next_block.solve() if j==0: test_node.send_message(msg_block(next_block)) all_blocks.append(next_block) else: headers_message.headers.append(CBlockHeader(next_block)) tips[j] = next_block time.sleep(2) for x in all_blocks: try: self.nodes[0].getblock(x.hash) if x == all_blocks[287]: raise AssertionError("Unrequested block too far-ahead should have been ignored") except: if x == all_blocks[287]: print "Unrequested block too far-ahead not processed" else: raise AssertionError("Unrequested block with more work should have been accepted") headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip try: white_node.sync_with_ping() self.nodes[1].getblock(tips[1].hash) print "Unrequested block far ahead of tip accepted from whitelisted peer" except: raise AssertionError("Unrequested block from whitelisted peer not accepted") # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). test_node.send_message(msg_block(blocks_h2f[0])) # Here, if the sleep is too short, the test could falsely succeed (if the # node hasn't processed the block by the time the sleep returns, and then # the node processes it and incorrectly advances the tip). # But this would be caught later on, when we verify that an inv triggers # a getdata request for this block. test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) print "Unrequested block that would complete more-work chain was ignored" # 6. Try to get node to request the missing block. # Poke the node with an inv for block at height 3 and see if that # triggers a getdata on block 2 (it should if block 2 is missing). with mininode_lock: # Clear state so we can check the getdata request test_node.last_getdata = None test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_getdata # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) print "Inv at tip triggered getdata for unprocessed block" # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(blocks_h2f[0])) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 290) print "Successfully reorged to longer chain from non-whitelisted peer" [ c.disconnect_node() for c in connections ]
def run_test(self): # Connect to node0 node0 = BaseNode() connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) node0.add_connection(connections[0]) NetworkThread().start() # Start up network handling in another thread node0.wait_for_verack() # Build the blockchain self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] + 1 self.blocks = [] # Get a pubkey for the coinbase TXO coinbase_key = CECKey() coinbase_key.set_secretbytes(b"horsebattery") coinbase_pubkey = coinbase_key.get_pubkey() # Create the first block with a coinbase output to our key height = 1 block = create_block(self.tip, create_coinbase(height, coinbase_pubkey), self.block_time) self.blocks.append(block) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 # Bury the block 100 deep so the coinbase output is spendable for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 # Create a transaction spending the coinbase output with an invalid (null) signature tx = CTransaction() tx.vin.append( CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE]))) tx.calc_sha256() block102 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block102.vtx.extend([tx]) block102.hashMerkleRoot = block102.calc_merkle_root() block102.rehash() block102.solve() self.blocks.append(block102) self.tip = block102.sha256 self.block_time += 1 height += 1 # Bury the assumed valid block 2100 deep for i in range(2100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.nVersion = 4 block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.nodes.append( start_node(1, self.options.tmpdir, ["-assumevalid=" + hex(block102.sha256)])) node1 = BaseNode() # connects to node1 connections.append( NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) node1.add_connection(connections[1]) node1.wait_for_verack() self.nodes.append( start_node(2, self.options.tmpdir, ["-assumevalid=" + hex(block102.sha256)])) node2 = BaseNode() # connects to node2 connections.append( NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) node2.add_connection(connections[2]) node2.wait_for_verack() # send header lists to all three nodes node0.send_header_for_blocks(self.blocks[0:2000]) node0.send_header_for_blocks(self.blocks[2000:]) node1.send_header_for_blocks(self.blocks[0:2000]) node1.send_header_for_blocks(self.blocks[2000:]) node2.send_header_for_blocks(self.blocks[0:200]) # Send 102 blocks to node0. Block 102 will be rejected. for i in range(101): node0.send_message(msg_block(self.blocks[i])) node0.sync_with_ping() # make sure the most recent block is synced node0.send_message(msg_block(self.blocks[101])) assert_equal( self.nodes[0].getblock(self.nodes[0].getbestblockhash())['height'], 101) # Send 3102 blocks to node1. All blocks will be accepted. for i in range(2202): node1.send_message(msg_block(self.blocks[i])) node1.sync_with_ping() # make sure the most recent block is synced assert_equal( self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) # Send 102 blocks to node2. Block 102 will be rejected. for i in range(101): node2.send_message(msg_block(self.blocks[i])) node2.sync_with_ping() # make sure the most recent block is synced node2.send_message(msg_block(self.blocks[101])) assert_equal( self.nodes[2].getblock(self.nodes[2].getbestblockhash())['height'], 101)
def run_test(self): # Setup the p2p connections and start up the network thread. inv_node = TestNode() test_node = TestNode() self.p2p_connections = [inv_node, test_node] connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) # Set nServices to 0 for test_node, so no block download will occur outside of # direct fetching connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0)) inv_node.add_connection(connections[0]) test_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here inv_node.wait_for_verack() test_node.wait_for_verack() # Ensure verack's have been processed by our peer inv_node.sync_with_ping() test_node.sync_with_ping() tip = int(self.nodes[0].getbestblockhash(), 16) # PART 1 # 1. Mine a block; expect inv announcements each time self.log.info( "Part 1: headers don't start before sendheaders message...") for i in range(4): old_tip = tip tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) # Try a few different responses; none should affect next announcement if i == 0: # first request the block test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # next try requesting header and block test_node.get_headers(locator=[old_tip], hashstop=tip) test_node.get_data([tip]) test_node.wait_for_block(tip) test_node.clear_last_announcement( ) # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 new_block = create_block(tip, create_coinbase(height + 1), block_time) new_block.solve() test_node.send_header_for_blocks([new_block]) test_node.wait_for_getdata([new_block.sha256]) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed inv_node.clear_last_announcement() test_node.clear_last_announcement() self.log.info("Part 1: success!") self.log.info( "Part 2: announce blocks with headers after sendheaders message..." ) # PART 2 # 2. Send a sendheaders message and test that headers announcements # commence and keep working. test_node.send_message(msg_sendheaders()) prev_tip = int(self.nodes[0].getbestblockhash(), 16) test_node.get_headers(locator=[prev_tip], hashstop=0) test_node.sync_with_ping() # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead for i in range(10): # Mine i blocks, and alternate announcing either via # inv (of tip) or via headers. After each, new blocks # mined by the node should successfully be announced # with block header, even though the blocks are never requested for j in range(2): blocks = [] for b in range(i + 1): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 if j == 0: # Announce via inv test_node.send_block_inv(tip) test_node.wait_for_getheaders() # Should have received a getheaders now test_node.send_header_for_blocks(blocks) # Test that duplicate inv's won't result in duplicate # getdata requests, or duplicate headers announcements [inv_node.send_block_inv(x.sha256) for x in blocks] test_node.wait_for_getdata([x.sha256 for x in blocks]) inv_node.sync_with_ping() else: # Announce via headers test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([x.sha256 for x in blocks]) # Test that duplicate headers won't result in duplicate # getdata requests (the check is further down) inv_node.send_header_for_blocks(blocks) inv_node.sync_with_ping() [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() inv_node.sync_with_ping() # This block should not be announced to the inv node (since it also # broadcast it) assert "inv" not in inv_node.last_message assert "headers" not in inv_node.last_message tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height += 1 block_time += 1 self.log.info("Part 2: success!") self.log.info( "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..." ) # PART 3. Headers announcements can stop after large reorg, and resume after # getheaders or inv from peer. for j in range(2): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal( test_node.check_last_announcement(headers=new_block_hashes), True) block_time += 8 # Mine a too-large reorg, which should be announced with a single inv new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) block_time += 9 fork_point = self.nodes[0].getblock( "%02x" % new_block_hashes[0])["previousblockhash"] fork_point = int(fork_point, 16) # Use getblocks/getdata test_node.send_getblocks(locator=[fork_point]) assert_equal( test_node.check_last_announcement(inv=new_block_hashes), True) test_node.get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) if i == 0: # Just get the data -- shouldn't cause headers announcements to resume test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # Send a getheaders message that shouldn't trigger headers announcements # to resume (best header sent will be too old) test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 2: test_node.get_data([tip]) test_node.wait_for_block(tip) # This time, try sending either a getheaders to trigger resumption # of headers announcements, or mine a new block and inv it, also # triggering resumption of headers announcements. if j == 0: test_node.get_headers(locator=[tip], hashstop=0) test_node.sync_with_ping() else: test_node.send_block_inv(tip) test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) self.log.info("Part 3: success!") self.log.info("Part 4: Testing direct fetch behavior...") tip = self.mine_blocks(1) height = self.nodes[0].getblockcount() + 1 last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 # Create 2 blocks. Send the blocks, then send the headers. blocks = [] for b in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 inv_node.send_message(msg_block(blocks[-1])) inv_node.sync_with_ping() # Make sure blocks are processed test_node.last_message.pop("getdata", None) test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() # should not have received any getdata messages with mininode_lock: assert "getdata" not in test_node.last_message # This time, direct fetch should work blocks = [] for b in range(3): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Now announce a header that forks the last two blocks tip = blocks[0].sha256 height -= 1 blocks = [] # Create extra blocks for later for b in range(20): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Announcing one block on fork should not trigger direct fetch # (less work than tip) test_node.last_message.pop("getdata", None) test_node.send_header_for_blocks(blocks[0:1]) test_node.sync_with_ping() with mininode_lock: assert "getdata" not in test_node.last_message # Announcing one more block on fork should trigger direct fetch for # both blocks (same work as tip) test_node.send_header_for_blocks(blocks[1:2]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time) # Announcing 16 more headers should trigger direct fetch for 14 more # blocks test_node.send_header_for_blocks(blocks[2:18]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time) # Announcing 1 more header should not trigger any response test_node.last_message.pop("getdata", None) test_node.send_header_for_blocks(blocks[18:19]) test_node.sync_with_ping() with mininode_lock: assert "getdata" not in test_node.last_message self.log.info("Part 4: success!") # Now deliver all those blocks we announced. [test_node.send_message(msg_block(x)) for x in blocks] self.log.info("Part 5: Testing handling of unconnecting headers") # First we test that receipt of an unconnecting header doesn't prevent # chain sync. for i in range(10): test_node.last_message.pop("getdata", None) blocks = [] # Create two more blocks. for j in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Send the header of the second block -> this won't connect. with mininode_lock: test_node.last_message.pop("getheaders", None) test_node.send_header_for_blocks([blocks[1]]) test_node.wait_for_getheaders() test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([x.sha256 for x in blocks]) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) blocks = [] # Now we test that if we repeatedly don't send connecting headers, we # don't go into an infinite loop trying to get them to connect. MAX_UNCONNECTING_HEADERS = 10 for j in range(MAX_UNCONNECTING_HEADERS + 1): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 for i in range(1, MAX_UNCONNECTING_HEADERS): # Send a header that doesn't connect, check that we get a getheaders. with mininode_lock: test_node.last_message.pop("getheaders", None) test_node.send_header_for_blocks([blocks[i]]) test_node.wait_for_getheaders() # Next header will connect, should re-set our count: test_node.send_header_for_blocks([blocks[0]]) # Remove the first two entries (blocks[1] would connect): blocks = blocks[2:] # Now try to see how many unconnecting headers we can send # before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS for i in range(5 * MAX_UNCONNECTING_HEADERS - 1): # Send a header that doesn't connect, check that we get a getheaders. with mininode_lock: test_node.last_message.pop("getheaders", None) test_node.send_header_for_blocks([blocks[i % len(blocks)]]) test_node.wait_for_getheaders() # Eventually this stops working. test_node.send_header_for_blocks([blocks[-1]]) # Should get disconnected test_node.wait_for_disconnect() self.log.info("Part 5: success!") # Finally, check that the inv node never received a getdata request, # throughout the test assert "getdata" not in inv_node.last_message
def create_spam_block(self, hashPrevBlock, stakingPrevOuts, height, fStakeDoubleSpent=False, fZPoS=False, spendingPrevOuts={}): ''' creates a block to spam the network with :param hashPrevBlock: (hex string) hash of previous block stakingPrevOuts: ({COutPoint --> (int, int, int, str)} dictionary) map outpoints (to be used as staking inputs) to amount, block_time, nStakeModifier, hashStake height: (int) block height fStakeDoubleSpent: (bool) spend the coinstake input inside the block fZPoS: (bool) stake the block with zerocoin spendingPrevOuts: ({COutPoint --> (int, int, int, str)} dictionary) map outpoints (to be used as tx inputs) to amount, block_time, nStakeModifier, hashStake :return block: (CBlock) generated block ''' # If not given inputs to create spam txes, use a copy of the staking inputs if len(spendingPrevOuts) == 0: spendingPrevOuts = dict(stakingPrevOuts) # Get current time current_time = int(time.time()) nTime = current_time & 0xfffffff0 # Create coinbase TX # Even if PoS blocks have empty coinbase vout, the height is required for the vin script coinbase = create_coinbase(height) coinbase.vout[0].nValue = 0 coinbase.vout[0].scriptPubKey = b"" coinbase.nTime = nTime coinbase.rehash() # Create Block with coinbase block = create_block(int(hashPrevBlock, 16), coinbase, nTime) # Find valid kernel hash - Create a new private key used for block signing. if not block.solve_stake(stakingPrevOuts): raise Exception("Not able to solve for any prev_outpoint") # Sign coinstake TX and add it to the block signed_stake_tx = self.sign_stake_tx( block, stakingPrevOuts[block.prevoutStake][0], fZPoS) block.vtx.append(signed_stake_tx) # Remove coinstake input prevout unless we want to try double spending in the same block. # Skip for zPoS as the spendingPrevouts are just regular UTXOs if not fZPoS and not fStakeDoubleSpent: del spendingPrevOuts[block.prevoutStake] # remove a random prevout from the list # (to randomize block creation if the same height is picked two times) if len(spendingPrevOuts) > 0: del spendingPrevOuts[choice(list(spendingPrevOuts))] # Create spam for the block. Sign the spendingPrevouts for outPoint in spendingPrevOuts: value_out = int(spendingPrevOuts[outPoint][0] - self.DEFAULT_FEE * COIN) tx = create_transaction(outPoint, b"", value_out, nTime, scriptPubKey=CScript([ self.block_sig_key.get_pubkey(), OP_CHECKSIG ])) # sign txes signed_tx_hex = self.node.signrawtransaction( bytes_to_hex_str(tx.serialize()))['hex'] signed_tx = CTransaction() signed_tx.deserialize(BytesIO(hex_str_to_bytes(signed_tx_hex))) block.vtx.append(signed_tx) # Get correct MerkleRoot and rehash block block.hashMerkleRoot = block.calc_merkle_root() block.rehash() # Sign block with coinstake key and return it block.sign_block(self.block_sig_key) return block
def run_test(self): # Add p2p connection to node0 node = self.nodes[0] # convenience reference to the node node.add_p2p_connection(P2PDataStore()) best_block = node.getblock(node.getbestblockhash()) tip = int(node.getbestblockhash(), 16) height = best_block["height"] + 1 block_time = best_block["time"] + 1 self.log.info("Create a new block with an anyone-can-spend coinbase") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block1], node, success=True) self.log.info("Mature the block.") node.generatetoaddress(100, node.get_deterministic_priv_key().address) best_block = node.getblock(node.getbestblockhash()) tip = int(node.getbestblockhash(), 16) height = best_block["height"] + 1 block_time = best_block["time"] + 1 # Use merkle-root malleability to generate an invalid block with # same blockheader (CVE-2012-2459). # Manufacture a block with 3 transactions (coinbase, spend of prior # coinbase, spend of that spend). Duplicate the 3rd transaction to # leave merkle root and blockheader unchanged but invalidate the block. # For more information on merkle-root malleability see src/consensus/merkle.cpp. self.log.info("Test merkle root malleability.") block2 = create_block(tip, create_coinbase(height), block_time) block_time += 1 # b'0x51' is OP_TRUE tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=b'\x51', amount=50 * COIN) tx2 = create_tx_with_script(tx1, 0, script_sig=b'\x51', amount=50 * COIN) block2.vtx.extend([tx1, tx2]) block2.hashMerkleRoot = block2.calc_merkle_root() block2.rehash() block2.solve() orig_hash = block2.sha256 block2_orig = copy.deepcopy(block2) # Mutate block 2 block2.vtx.append(tx2) assert_equal(block2.hashMerkleRoot, block2.calc_merkle_root()) assert_equal(orig_hash, block2.rehash()) assert block2_orig.vtx != block2.vtx node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate') # Check transactions for duplicate inputs (CVE-2018-17144) self.log.info("Test duplicate input block.") block2_dup = copy.deepcopy(block2_orig) block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0]) block2_dup.vtx[2].rehash() block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root() block2_dup.rehash() block2_dup.solve() node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate') self.log.info("Test very broken block.") block3 = create_block(tip, create_coinbase(height), block_time) block_time += 1 block3.vtx[0].vout[0].nValue = 100 * COIN # Too high! block3.vtx[0].sha256 = None block3.vtx[0].calc_sha256() block3.hashMerkleRoot = block3.calc_merkle_root() block3.rehash() block3.solve() node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount') # Complete testing of CVE-2012-2459 by sending the original block. # It should be accepted even though it has the same hash as the mutated one. self.log.info("Test accepting original block after rejecting its mutated version.") node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5) # Update tip info height += 1 block_time += 1 tip = int(block2_orig.hash, 16) # Complete testing of CVE-2018-17144, by checking for the inflation bug. # Create a block that spends the output of a tx in a previous block. block4 = create_block(tip, create_coinbase(height), block_time) tx3 = create_tx_with_script(tx2, 0, script_sig=b'\x51', amount=50 * COIN) # Duplicates input tx3.vin.append(tx3.vin[0]) tx3.rehash() block4.vtx.append(tx3) block4.hashMerkleRoot = block4.calc_merkle_root() block4.rehash() block4.solve() self.log.info("Test inflation by duplicating input") node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
def get_tests(self): self.coinbase_blocks = self.nodes[0].generate(2) height = 3 # height of the next block to build self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.nodeaddress = self.nodes[0].getnewaddress() self.last_block_time = int(time.time()) ''' 398 more version 3 blocks ''' test_blocks = [] for i in range(398): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 3 block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) ''' Mine 749 version 4 blocks ''' test_blocks = [] for i in range(749): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 4 block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) ''' Check that the new CLTV rules are not enforced in the 750th version 3 block. ''' spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 4 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Mine 199 new version blocks on last valid tip ''' test_blocks = [] for i in range(199): block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 4 block.rehash() block.solve() test_blocks.append([block, True]) self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance(test_blocks, sync_every_block=False) ''' Mine 1 old version block ''' block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 3 block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Mine 1 new version block ''' block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 4 block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Check that the new CLTV rules are enforced in the 951st version 4 block. ''' spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 4 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]]) ''' Mine 1 old version block, should be invalid ''' block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) block.nVersion = 3 block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]])
def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno): # generate some coins for later self.coinbase_blocks = self.nodes[0].generate(2) self.height = 3 # height of the next block to build self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.nodeaddress = self.nodes[0].getnewaddress() self.last_block_time = int(time.time()) assert_equal(self.get_bip9_status(bipName)['status'], 'defined') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert (bipName not in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert_equal(tmpl['version'], 0x20000000) # Test 1 # Advance from DEFINED to STARTED test_blocks = self.generate_blocks(141, 4) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'started') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert_equal(tmpl['vbavailable'][bipName], bitno) assert_equal(tmpl['vbrequired'], 0) assert (tmpl['version'] & activated_version) # Test 2 # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1 # using a variety of bits to simulate multiple parallel softforks test_blocks = self.generate_blocks( 50, activated_version) # 0x20000001 (signalling ready) test_blocks = self.generate_blocks( 20, 4, test_blocks) # 0x00000004 (signalling not) test_blocks = self.generate_blocks( 50, activated_version, test_blocks) # 0x20000101 (signalling ready) test_blocks = self.generate_blocks( 24, 4, test_blocks) # 0x20010000 (signalling not) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'started') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) assert_equal(tmpl['vbavailable'][bipName], bitno) assert_equal(tmpl['vbrequired'], 0) assert (tmpl['version'] & activated_version) # Test 3 # 108 out of 144 signal bit 1 to achieve LOCKED_IN # using a variety of bits to simulate multiple parallel softforks test_blocks = self.generate_blocks( 58, activated_version) # 0x20000001 (signalling ready) test_blocks = self.generate_blocks( 26, 4, test_blocks) # 0x00000004 (signalling not) test_blocks = self.generate_blocks( 50, activated_version, test_blocks) # 0x20000101 (signalling ready) test_blocks = self.generate_blocks( 10, 4, test_blocks) # 0x20010000 (signalling not) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) # Test 4 # 143 more version 536870913 blocks (waiting period-1) test_blocks = self.generate_blocks(143, 4) yield TestInstance(test_blocks, sync_every_block=False) assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName not in tmpl['rules']) # Test 5 # Check that the new rule is enforced spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) invalidate(spendtx) spendtx = self.sign_transaction(self.nodes[0], spendtx) spendtx.rehash() invalidatePostSignature(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = activated_version block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 self.tip = block.sha256 self.height += 1 yield TestInstance([[block, True]]) assert_equal(self.get_bip9_status(bipName)['status'], 'active') tmpl = self.nodes[0].getblocktemplate({}) assert (bipName in tmpl['rules']) assert (bipName not in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert (not (tmpl['version'] & (1 << bitno))) # Test 6 # Check that the new sequence lock rules are enforced spendtx = self.create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) invalidate(spendtx) spendtx = self.sign_transaction(self.nodes[0], spendtx) spendtx.rehash() invalidatePostSignature(spendtx) spendtx.rehash() block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 5 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.last_block_time += 1 yield TestInstance([[block, False]]) # Restart all self.test.block_store.close() stop_nodes(self.nodes) wait_BKSds() shutil.rmtree(self.options.tmpdir) self.setup_chain() self.setup_network() self.test.block_store = BlockStore(self.options.tmpdir) self.test.clear_all_connections() self.test.add_all_connections(self.nodes) NetworkThread().start() # Start up network handling in another thread
def run_test(self): # Set the forktime to be far into the future. Running the sript after forktime will cause a failure since # we will be expecting a > 1MB block to begin with. self.nodes[0].set("mining.forkTime=1901590000") self.nodes[1].set("mining.forkTime=1901590000") # Setup the p2p connections and start up the network thread. inv_node = InvNode() test_node = TestNode() self.p2p_connections = [inv_node, test_node] connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) # Set nServices to 0 for test_node, so no block download will occur outside of # direct fetching connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0)) inv_node.add_connection(connections[0]) test_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here inv_node.wait_for_verack() test_node.wait_for_verack() tip = int(self.nodes[0].getbestblockhash(), 16) # PART 1 # 1. Mine a block; expect inv announcements each time print("Part 1: headers don't start before sendheaders message...") for i in range(4): old_tip = tip tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) # Try a few different responses; none should affect next announcement if i == 0: # first request the block test_node.get_data([tip]) test_node.wait_for_block(tip, timeout=5) elif i == 1: # next try requesting header and block test_node.get_headers(locator=[old_tip], hashstop=tip) test_node.get_data([tip]) test_node.wait_for_block(tip) test_node.clear_last_announcement( ) # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 new_block = create_block(tip, create_coinbase(height + 1), block_time) new_block.solve() test_node.send_header_for_blocks([new_block]) test_node.wait_for_getdata([new_block.sha256], timeout=5) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed inv_node.clear_last_announcement() test_node.clear_last_announcement() print("Part 1: success!") print( "Part 2: announce blocks with headers after sendheaders message..." ) # PART 2 # 2. Send a sendheaders message and test that headers announcements # commence and keep working. test_node.send_message(msg_sendheaders()) prev_tip = int(self.nodes[0].getbestblockhash(), 16) test_node.get_headers(locator=[prev_tip], hashstop=0) test_node.sync_with_ping() # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead for i in range(10): # Mine i blocks, and alternate announcing either via # inv (of tip) or via headers. After each, new blocks # mined by the node should successfully be announced # with block header, even though the blocks are never requested for j in range(2): blocks = [] for b in range(1): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 if j == 0: # Announce via inv test_node.send_block_inv(tip) test_node.wait_for_getheaders() test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([tip], timeout=5) # Test that duplicate inv's won't result in duplicate # getdata requests, or duplicate headers announcements inv_node.send_block_inv(tip) # Should have received a getheaders as well! test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() test_node.wait_for_getdata( [x.sha256 for x in blocks[0:-1]], timeout=5) [inv_node.send_block_inv(x.sha256) for x in blocks[0:-1]] inv_node.sync_with_ping() else: # Announce via headers test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) # Test that duplicate headers won't result in duplicate # getdata requests (the check is further down) inv_node.send_header_for_blocks(blocks) inv_node.sync_with_ping() [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() inv_node.sync_with_ping() # This block should not be announced to the inv node (since it also # broadcast it) assert_equal(inv_node.last_inv, []) assert_equal(inv_node.last_headers, None) tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) height += 1 block_time += 1 print("Part 2: success!") print( "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..." ) # PART 3. Headers announcements can stop after large reorg, and resume after # getheaders or inv from peer. for j in range(2): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) assert_equal( inv_node.check_last_announcement(inv=new_block_hashes), True) assert_equal( test_node.check_last_announcement(headers=new_block_hashes), True) block_time += 8 # Mine a too-large reorg - we will receive only the first 8 inv's for the 9 block hashes mined. # which represents the MAX_BLOCKS_TO_ANNOUNCE=8 new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] assert_equal( inv_node.check_last_announcement(inv=new_block_hashes), True) assert_equal( test_node.check_last_announcement(inv=new_block_hashes), True) block_time += 9 fork_point = self.nodes[0].getblock( "%02x" % new_block_hashes[0])["previousblockhash"] fork_point = int(fork_point, 16) # Use getblocks/getdata test_node.send_getblocks(locator=[fork_point]) assert_equal( test_node.check_last_announcement(inv=new_block_hashes), True) test_node.get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) if i == 0: # Just get the data -- shouldn't cause headers announcements to resume test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # Send a getheaders message that shouldn't trigger headers announcements # to resume (best header sent will be too old) test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.get_data([tip]) test_node.wait_for_block(tip) elif i == 2: test_node.get_data([tip]) test_node.wait_for_block(tip) # This time, try sending either a getheaders to trigger resumption # of headers announcements, or mine a new block and inv it, also # triggering resumption of headers announcements. if j == 0: test_node.get_headers(locator=[tip], hashstop=0) test_node.sync_with_ping() else: test_node.send_block_inv(tip) test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) print("Part 3: success!") print("Part 4: Testing direct fetch behavior...") tip = self.mine_blocks(1) height = self.nodes[0].getblockcount() + 1 last_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 # Create 2 blocks. Send the blocks, then send the headers. blocks = [] for b in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 inv_node.send_message(msg_block(blocks[-1])) inv_node.sync_with_ping() # Make sure blocks are processed test_node.last_getdata = [] test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() # should not have received any getdata messages with mininode_lock: assert_equal(test_node.last_getdata, []) # This time, direct fetch should work blocks = [] for b in range(3): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Now announce a header that forks the last two blocks tip = blocks[0].sha256 height -= 1 blocks = [] # Create extra blocks for later for b in range(20): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Announcing one block on fork should not trigger direct fetch # (less work than tip) test_node.last_getdata = [] test_node.send_header_for_blocks(blocks[0:1]) test_node.sync_with_ping() with mininode_lock: assert_equal(test_node.last_getdata, []) # Announcing one more block on fork should trigger direct fetch for # both blocks (same work as tip) test_node.send_header_for_blocks(blocks[1:2]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=5) # Announcing 16 more headers should trigger direct fetch for 14 more # blocks self.nodes[0].set("net.maxBlocksInTransitPerPeer=16") self.nodes[1].set("net.maxBlocksInTransitPerPeer=16") test_node.send_header_for_blocks(blocks[2:18]) test_node.sync_with_ping() test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=5) with mininode_lock: assert_equal(test_node.last_getdata, []) # Announcing 1 more header should not trigger any response because we # already have the maximumum blocks in flight test_node.last_getdata = [] test_node.send_header_for_blocks(blocks[18:19]) test_node.sync_with_ping() with mininode_lock: assert_equal(test_node.last_getdata, []) print("Part 4: success!") # Now deliver all those blocks we announced. [test_node.send_message(msg_block(x)) for x in blocks] print("Part 5: Testing handling of unconnecting headers") # Test1: We test that receipt of a single unconnecting header doesn't cause a problem # Send an out of order header. Then send both headers in the correct order. # Result: Block chain updates correctly. for i in range(2): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks([blocks[1]]) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Now send them in the right order test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Test2: We test that receipt of a single unconnecting header doesn't cause a problem # Send an out of order header. Then send the first header. # Result: Block chain updates correctly. for i in range(2): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks([blocks[1]]) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Now send the first header only test_node.send_header_for_blocks([blocks[0]]) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Test3: We test that receipt of a multiple unconnecting header doesn't cause a problem # Send several out of order headers. Then send ALL the missing headers in order. # Result: Block chain updates correctly. for i in range(2): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(5): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Reverse order of one of the blocks blocks_reverse = [] if i == 0: blocks_reverse.append(blocks[1]) blocks_reverse.append(blocks[0]) blocks_reverse.append(blocks[2]) blocks_reverse.append(blocks[3]) blocks_reverse.append(blocks[4]) if i == 1: blocks_reverse.append(blocks[0]) blocks_reverse.append(blocks[1]) blocks_reverse.append(blocks[2]) blocks_reverse.append(blocks[4]) blocks_reverse.append(blocks[3]) # Send the header of the second block out of order-> this won't connect. test_node.send_header_for_blocks(blocks_reverse) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[4].sha256) # Now send them in the right order test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[4].sha256) # Test4: We test that receipt of a multiple unconnecting header doesn't cause a problem # Send several out of order headers. Then send only the missing header. # Result: Block chain updates correctly. for i in range(1): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(5): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Reverse order of one of the blocks blocks_reverse = [] if i == 0: blocks_reverse.append(blocks[1]) blocks_reverse.append(blocks[0]) blocks_reverse.append(blocks[2]) blocks_reverse.append(blocks[3]) blocks_reverse.append(blocks[4]) # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks(blocks_reverse) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Now send them in the right order test_node.send_header_for_blocks([blocks[0]]) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[4].sha256) # Test5: test that old unconnected headers will get deleted from the cache # 1) Send and unconnecting header. # Advance the time beyond the timeout. # Send the first header. # Result: both headers should connect. # 2) Send an unconnecting header that is at height 3. # Advance the time beyond the timeout. # Send a second unconnecting header at height 2. # Send the first header. # Result: The first two headers should connected with the 3rd having been deleted and the # chain will have only the first two blocks connected. for i in range(2): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks([blocks[1]]) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Advance the time beyond the timeout value cur_time = int(time.time()) self.nodes[0].setmocktime(cur_time + 120) self.nodes[1].setmocktime(cur_time + 120) # Now send them in the right order test_node.send_header_for_blocks([blocks[0]]) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) for i in range(2): test_node.last_getdata = [] blocks = [] # Create two more blocks. for j in range(3): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 self.nodes[0].setmocktime(block_time) self.nodes[1].setmocktime(block_time) # Send the header of the third block -> this won't connect. test_node.send_header_for_blocks([blocks[2]]) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[2].sha256) #setting time to 120 seconds in the future will cause the unconnecting header to be deleted self.nodes[0].setmocktime(block_time + 120) self.nodes[1].setmocktime(block_time + 120) # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks([blocks[1]]) test_node.sync_with_ping() assert_not_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Now send the first header test_node.send_header_for_blocks([blocks[0]]) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata( [x.sha256 for x in [blocks[0], blocks[1]]], timeout=5) [ test_node.send_message(msg_block(x)) for x in [blocks[0], blocks[1]] ] test_node.sync_with_ping() # Block chain should have updated correctly and all blocks connected to the second block assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) # Send the header of the third block again -> this will connect. test_node.send_header_for_blocks([blocks[2]]) test_node.sync_with_ping() # Wait for getdata and send blocks test_node.wait_for_getdata([x.sha256 for x in [blocks[2]]], timeout=5) [test_node.send_message(msg_block(x)) for x in [blocks[2]]] test_node.sync_with_ping() assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[2].sha256) # Send one more out of order header which should not cause any problems test_node.last_getdata = [] blocks = [] # Create two more blocks for j in range(2): blocks.append( create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 block_time += 1 height += 1 # Send the header of the second block -> this won't connect. test_node.send_header_for_blocks([blocks[1]]) # Finally, check that the inv node never received a getdata request, # throughout the test assert_equal(inv_node.last_getdata, []) print("Part 5: success!")
def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] tip_block = node.getblock(tip_hash) coinbasevalue = bt['coinbasevalue'] if miner_address is None: miner_address = node.getnewaddress() if mn_payee is None: if isinstance(bt['masternode'], list): mn_payee = bt['masternode'][0]['payee'] else: mn_payee = bt['masternode']['payee'] # we can't take the masternode payee amount from the template here as we might have additional fees in vtx # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees if mn_amount is None: mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) if use_mnmerkleroot_from_tip: if 'cbTx' in tip_block: cbtx.merkleRootMNList = int(tip_block['cbTx']['merkleRootMNList'], 16) else: cbtx.merkleRootMNList = 0 coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() result = node.submitblock(ToHex(block)) if expected_error is not None and result != expected_error: raise AssertionError('mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) elif expected_error is None and result is not None: raise AssertionError('submitblock returned %s' % (result))
def test_sequence(self): """ Sequence zmq notifications give every blockhash and txhash in order of processing, regardless of IBD, re-orgs, etc. Format of messages: <32-byte hash>C : Blockhash connected <32-byte hash>D : Blockhash disconnected <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool """ self.log.info("Testing 'sequence' publisher") address = 'tcp://127.0.0.1:28333' socket = self.ctx.socket(zmq.SUB) socket.set(zmq.RCVTIMEO, 60000) seq = ZMQSubscriber(socket, b'sequence') self.restart_node(0, [ '-zmqpub%s=%s' % (seq.topic.decode(), address), '-mempoolreplacement=1' ]) socket.connect(address) # Relax so that the subscriber is ready before publishing zmq messages sleep(0.2) # Mempool sequence number starts at 1 seq_num = 1 # Generate 1 block in nodes[0] and receive all notifications dc_block = self.nodes[0].generatetoaddress( 1, ADDRESS_BCRT1_UNSPENDABLE)[0] # Note: We are not notified of any block transactions, coinbase or mined assert_equal((self.nodes[0].getbestblockhash(), "C", None), seq.receive_sequence()) # Generate 2 blocks in nodes[1] to a different address to ensure a chain split self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE) # nodes[0] will reorg chain after connecting back nodes[1] self.connect_nodes(0, 1) # Then we receive all block (dis)connect notifications for the 2 block reorg assert_equal((dc_block, "D", None), seq.receive_sequence()) block_count = self.nodes[1].getblockcount() assert_equal((self.nodes[1].getblockhash(block_count - 1), "C", None), seq.receive_sequence()) assert_equal((self.nodes[1].getblockhash(block_count), "C", None), seq.receive_sequence()) # Rest of test requires wallet functionality if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") payment_txid = self.nodes[1].sendtoaddress( address=self.nodes[0].getnewaddress(), amount=5.0, replaceable=True) self.sync_all() self.log.info( "Testing sequence notifications with mempool sequence values") # Should receive the broadcasted txid. assert_equal((payment_txid, "A", seq_num), seq.receive_sequence()) seq_num += 1 self.log.info("Testing RBF notification") # Replace it to test eviction/addition notification rbf_info = self.nodes[1].bumpfee(payment_txid) self.sync_all() assert_equal((payment_txid, "R", seq_num), seq.receive_sequence()) seq_num += 1 assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence()) seq_num += 1 # Doesn't get published when mined, make a block and tx to "flush" the possibility # though the mempool sequence number does go up by the number of transactions # removed from the mempool by the block mining it. mempool_size = len(self.nodes[0].getrawmempool()) c_block = self.nodes[0].generatetoaddress( 1, ADDRESS_BCRT1_UNSPENDABLE)[0] self.sync_all() # Make sure the number of mined transactions matches the number of txs out of mempool mempool_size_delta = mempool_size - len( self.nodes[0].getrawmempool()) assert_equal( len(self.nodes[0].getblock(c_block)["tx"]) - 1, mempool_size_delta) seq_num += mempool_size_delta payment_txid_2 = self.nodes[1].sendtoaddress( self.nodes[0].getnewaddress(), 1.0) self.sync_all() assert_equal((c_block, "C", None), seq.receive_sequence()) assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence()) seq_num += 1 # Spot check getrawmempool results that they only show up when asked for assert type(self.nodes[0].getrawmempool()) is list assert type( self.nodes[0].getrawmempool(mempool_sequence=False)) is list assert "mempool_sequence" not in self.nodes[0].getrawmempool( verbose=True) assert_raises_rpc_error( -8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True) assert_equal( self.nodes[0].getrawmempool( mempool_sequence=True)["mempool_sequence"], seq_num) self.log.info("Testing reorg notifications") # Manually invalidate the last block to test mempool re-entry # N.B. This part could be made more lenient in exact ordering # since it greatly depends on inner-workings of blocks/mempool # during "deep" re-orgs. Probably should "re-construct" # blockchain/mempool state from notifications instead. block_count = self.nodes[0].getblockcount() best_hash = self.nodes[0].getbestblockhash() self.nodes[0].invalidateblock(best_hash) sleep(2) # Bit of room to make sure transaction things happened # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective # of the time they were gathered. assert self.nodes[0].getrawmempool( mempool_sequence=True)["mempool_sequence"] > seq_num assert_equal((best_hash, "D", None), seq.receive_sequence()) assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence()) seq_num += 1 # Other things may happen but aren't wallet-deterministic so we don't test for them currently self.nodes[0].reconsiderblock(best_hash) self.nodes[1].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all() self.log.info("Evict mempool transaction by block conflict") orig_txid = self.nodes[0].sendtoaddress( address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True) # More to be simply mined more_tx = [] for _ in range(5): more_tx.append(self.nodes[0].sendtoaddress( self.nodes[0].getnewaddress(), 0.1)) raw_tx = self.nodes[0].getrawtransaction(orig_txid) bump_info = self.nodes[0].bumpfee(orig_txid) # Mine the pre-bump tx block = create_block( int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount() + 1), version=0x20000000) tx = FromHex(CTransaction(), raw_tx) block.vtx.append(tx) for txid in more_tx: tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) block.vtx.append(tx) add_witness_commitment(block) block.solve() assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None) tip = self.nodes[0].getbestblockhash() assert_equal(int(tip, 16), block.sha256) orig_txid_2 = self.nodes[0].sendtoaddress( address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True) # Flush old notifications until evicted tx original entry (hash_str, label, mempool_seq) = seq.receive_sequence() while hash_str != orig_txid: (hash_str, label, mempool_seq) = seq.receive_sequence() mempool_seq += 1 # Added original tx assert_equal(label, "A") # More transactions to be simply mined for i in range(len(more_tx)): assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence()) mempool_seq += 1 # Bumped by rbf assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence()) mempool_seq += 1 assert_equal((bump_info["txid"], "A", mempool_seq), seq.receive_sequence()) mempool_seq += 1 # Conflict announced first, then block assert_equal((bump_info["txid"], "R", mempool_seq), seq.receive_sequence()) mempool_seq += 1 assert_equal((tip, "C", None), seq.receive_sequence()) mempool_seq += len(more_tx) # Last tx assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence()) mempool_seq += 1 self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) self.sync_all( ) # want to make sure we didn't break "consensus" for other tests
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2) ] self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that an invalid-according-to-CLTV transaction can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=1.0) cltv_invalidate(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) block.nVersion = 3 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) block.nVersion = 3 block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ '{}, bad-version(0x00000003)'.format(block.hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() self.log.info( "Test that invalid-according-to-cltv transactions cannot appear in a block" ) block.nVersion = 4 spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) cltv_invalidate(spendtx) spendtx.rehash() # First we show that this tx is valid except for CLTV by getting it # rejected from the mempool for exactly that reason. assert_equal([{ 'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Negative locktime)' }], self.nodes[0].testmempoolaccept( rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True)) # Now we verify that a block with this transaction is also invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ 'CheckInputs on {} failed with non-mandatory-script-verify-flag (Negative locktime)' .format(block.vtx[-1].hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() self.log.info( "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted" ) spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) spendtx.rehash() block.vtx.pop(1) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)] self.nodeaddress = self.nodes[0].getnewaddress() self.log.info("Test that a transaction with non-DER signature can still appear in a block") spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time) block.nVersion = 2 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time) block.nVersion = 2 block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)') assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) del self.nodes[0].p2p.last_message["reject"] self.log.info("Test that transactions with non-DER signatures cannot appear in a block") block.nVersion = 3 spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() # First we show that this tx is valid except for DERSIG by getting it # rejected from the mempool for exactly that reason. assert_equal( [{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Non-canonical DER signature)'}], self.nodes[0].testmempoolaccept(rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True) ) # Now we verify that a block with this transaction is also invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: # We can receive different reject messages depending on whether # sthcoind is running with multiple script check threads. If script # check threads are not in use, then transaction script validation # happens sequentially, and sthcoind produces more specific reject # reasons. assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') else: assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted") block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): # Connect to node0 p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) network_thread_start() self.nodes[0].p2p.wait_for_verack() # Build the blockchain self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 self.blocks = [] # Get a pubkey for the coinbase TXO coinbase_key = CECKey() coinbase_key.set_secretbytes(b"horsebattery") coinbase_pubkey = coinbase_key.get_pubkey() # Create the first block with a coinbase output to our key height = 1 block = create_block(self.tip, create_coinbase(height, coinbase_pubkey), self.block_time) self.blocks.append(block) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 # Bury the block 100 deep so the coinbase output is spendable for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 # Create a transaction spending the coinbase output with an invalid (null) signature tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE]))) tx.calc_sha256() block102 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block102.vtx.extend([tx]) block102.hashMerkleRoot = block102.calc_merkle_root() block102.rehash() block102.solve() self.blocks.append(block102) self.tip = block102.sha256 self.block_time += 1 height += 1 # Bury the assumed valid block 2100 deep for i in range(2100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.nVersion = 4 block.solve() self.blocks.append(block) self.tip = block.sha256 self.block_time += 1 height += 1 # We're adding new connections so terminate the network thread self.nodes[0].disconnect_p2ps() network_thread_join() # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) network_thread_start() p2p0.wait_for_verack() p2p1.wait_for_verack() p2p2.wait_for_verack() # send header lists to all three nodes p2p0.send_header_for_blocks(self.blocks[0:2000]) p2p0.send_header_for_blocks(self.blocks[2000:]) p2p1.send_header_for_blocks(self.blocks[0:2000]) p2p1.send_header_for_blocks(self.blocks[2000:]) p2p2.send_header_for_blocks(self.blocks[0:200]) # Send blocks to node0. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p0) self.assert_blockchain_height(self.nodes[0], 101) # Send all blocks to node1. All blocks will be accepted. for i in range(2202): p2p1.send_message(msg_block(self.blocks[i])) # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync. p2p1.sync_with_ping(120) assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) # Send blocks to node2. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p2) self.assert_blockchain_height(self.nodes[2], 101)
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2) ] self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that an invalid-according-to-CLTV transaction can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=1.0) cltv_invalidate(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) block.nVersion = 3 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) block.nVersion = 3 block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)') assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) del self.nodes[0].p2p.last_message["reject"] self.log.info( "Test that invalid-according-to-cltv transactions cannot appear in a block" ) block.nVersion = 4 spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) cltv_invalidate(spendtx) spendtx.rehash() # First we show that this tx is valid except for CLTV by getting it # rejected from the mempool for exactly that reason. assert_equal([{ 'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Negative locktime)' }], self.nodes[0].testmempoolaccept( rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True)) # Now we verify that a block with this transaction is also invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert self.nodes[0].p2p.last_message["reject"].code in [ REJECT_INVALID, REJECT_NONSTANDARD ] assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') else: assert b'Negative locktime' in self.nodes[0].p2p.last_message[ "reject"].reason self.log.info( "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted" ) spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) spendtx.rehash() block.vtx.pop(1) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def test_sequence_lock_unconfirmed_inputs(self): # Store height so we can easily reset the chain at the end of the test cur_height = self.nodes[0].getblockcount() # Create a mempool tx. txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # Anyone-can-spend mempool tx. # Sequence lock of 0 should pass. tx2 = CTransaction() tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [ CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), CScript([b'a'])) ] tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. # orig_tx.vout[0] must be an anyone-can-spend output def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): sequence_value = 1 if not use_height_lock: sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx = CTransaction() tx.nVersion = 2 tx.vin = [ CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value) ] tx.vout = [ CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), CScript([b'a' * 35])) ] tx.rehash() if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) else: # sendrawtransaction should succeed if the tx is not in the mempool node.sendrawtransaction(ToHex(tx)) return tx test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Now mine some blocks, but make sure tx2 doesn't get mined. # Use prioritisetransaction to lower the effective feerate to 0 self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(-self.relayfee * COIN)) cur_time = int(time.time()) for i in range(10): self.nodes[0].setmocktime(cur_time + 600) self.nodes[0].generate(1) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Mine tx2, and then try again self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(self.relayfee * COIN)) # Advance the time on the node so that we can test timelocks self.nodes[0].setmocktime(cur_time + 600) self.nodes[0].generate(1) assert tx2.hash not in self.nodes[0].getrawmempool() # Now that tx2 is not in the mempool, a sequence locked spend should # succeed tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) assert tx3.hash in self.nodes[0].getrawmempool() self.nodes[0].generate(1) assert tx3.hash not in self.nodes[0].getrawmempool() # One more test, this time using height locks tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True) assert tx4.hash in self.nodes[0].getrawmempool() # Now try combining confirmed and unconfirmed inputs tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() utxos = self.nodes[0].listunspent() tx5.vin.append( CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) tx5.vout[0].nValue += int(utxos[0]["amount"] * COIN) raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"] assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # # State of the transactions in the last blocks: # ... -> [ tx2 ] -> [ tx3 ] # tip-1 tip # And currently tx4 is in the mempool. # # If we invalidate the tip, tx3 should get added to the mempool, causing # tx4 to be removed (fails sequence-lock). self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) assert tx4.hash not in self.nodes[0].getrawmempool() assert tx3.hash in self.nodes[0].getrawmempool() # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in # diagram above). # This would cause tx2 to be added back to the mempool, which in turn causes # tx3 to be removed. tip = int( self.nodes[0].getblockhash(self.nodes[0].getblockcount() - 1), 16) height = self.nodes[0].getblockcount() for i in range(2): block = create_block(tip, create_coinbase(height), cur_time) block.nVersion = 3 block.rehash() block.solve() tip = block.sha256 height += 1 self.nodes[0].submitblock(ToHex(block)) cur_time += 1 mempool = self.nodes[0].getrawmempool() assert tx3.hash not in mempool assert tx2.hash in mempool # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height + 1)) self.nodes[0].generate(10)
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.nVersion = 0x20000000 block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generate(100) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) # and we get disconnected immediately self.log.info('Test a transaction that is rejected') tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=b'\x64' * 35, amount=50 * COIN - 12000) node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependend transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout.append( CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append( CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [ CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test( [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) # restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message self.log.info( 'Test a transaction that is rejected, with BIP61 disabled') self.restart_node(0, ['-enablebip61=0', '-persistmempool=0']) self.reconnect_p2p(num_connections=1) with node.assert_debug_log(expected_msgs=[ "{} from peer=0 was not accepted: mandatory-script-verify-flag-failed (Invalid OP_IF construction) (code 16)" .format(tx1.hash), "disconnecting peer=0", ]): node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) # send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received assert_equal(node.p2p.reject_code_received, None)
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info("Test that an invalid-according-to-CLTV transaction can still appear in a block") spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) block.nVersion = 3 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) block.nVersion = 3 block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)') assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) del self.nodes[0].p2p.last_message["reject"] self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block") block.nVersion = 4 spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) cltv_invalidate(spendtx) spendtx.rehash() # First we show that this tx is valid except for CLTV by getting it # accepted to the mempool (which we can achieve with # -promiscuousmempoolflags). self.nodes[0].p2p.send_and_ping(msg_tx(spendtx)) assert spendtx.hash in self.nodes[0].getrawmempool() # Now we verify that a block with this transaction is invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') else: assert b'Negative locktime' in self.nodes[0].p2p.last_message["reject"].reason self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted") spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) spendtx.rehash() block.vtx.pop(1) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) self.test_dersig_info(is_active=False) self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2) ] self.nodeaddress = self.nodes[0].getnewaddress() self.log.info( "Test that a transaction with non-DER signature can still appear in a block" ) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time) block.nVersion = 2 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.test_dersig_info( is_active=False ) # Not active as of current tip and next block does not need to obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_dersig_info( is_active=True ) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time) block.nVersion = 2 block.rehash() block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ '{}, bad-version(0x00000002)'.format(block.hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() self.log.info( "Test that transactions with non-DER signatures cannot appear in a block" ) block.nVersion = 3 spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) unDERify(spendtx) spendtx.rehash() # First we show that this tx is valid except for DERSIG by getting it # rejected from the mempool for exactly that reason. assert_equal([{ 'txid': spendtx.hash, 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Non-canonical DER signature)' }], self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0)) # Now we verify that a block with this transaction is also invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() with self.nodes[0].assert_debug_log(expected_msgs=[ 'CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)' .format(block.vtx[-1].hash) ]): self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) self.nodes[0].p2p.sync_with_ping() self.log.info( "Test that a version 3 block with a DERSIG-compliant transaction is accepted" ) block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() self.test_dersig_info( is_active=True ) # Not active as of current tip, but next block must obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_dersig_info(is_active=True) # Active as of current tip assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): node0 = NodeConnCB() connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) node0.add_connection(connections[0]) NetworkThread().start() # Start up network handling in another thread # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2) self.nodeaddress = self.nodes[0].getnewaddress() self.log.info("Test that a transaction with non-DER signature can still appear in a block") spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0], self.nodeaddress, 1.0) unDERify(spendtx) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time) block.nVersion = 2 block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") tip = block.sha256 block_time += 1 block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time) block.nVersion = 2 block.rehash() block.solve() node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)') assert_equal(node0.last_message["reject"].data, block.sha256) del node0.last_message["reject"] self.log.info("Test that transactions with non-DER signatures cannot appear in a block") block.nVersion = 3 spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) unDERify(spendtx) spendtx.rehash() # First we show that this tx is valid except for DERSIG by getting it # accepted to the mempool (which we can achieve with # -promiscuousmempoolflags). node0.send_and_ping(msg_tx(spendtx)) assert spendtx.hash in self.nodes[0].getrawmempool() # Now we verify that a block with this transaction is invalid. block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: # We can receive different reject messages depending on whether # BitcoinSubsidiumd is running with multiple script check threads. If script # check threads are not in use, then transaction script validation # happens sequentially, and BitcoinSubsidiumd produces more specific reject # reasons. assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(node0.last_message["reject"].data, block.sha256) if node0.last_message["reject"].code == REJECT_INVALID: # Generic rejection when a block is invalid assert_equal(node0.last_message["reject"].reason, b'block-validation-failed') else: assert b'Non-canonical DER signature' in node0.last_message["reject"].reason self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted") block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_blocks[1], self.nodeaddress, 1.0) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
def run_test(self): self.mine_chain() node = self.nodes[0] def assert_submitblock(block, result_str_1, result_str_2=None): block.solve() result_str_2 = result_str_2 or 'duplicate-invalid' assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex())) assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex())) self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], self.chain) assert 'currentblocktx' not in mining_info assert 'currentblockweight' not in mining_info assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generatetoaddress(1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl next_height = int(tmpl["height"]) coinbase_tx = create_coinbase(height=next_height) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] self.log.info("getblocktemplate: segwit rule must be set") assert_raises_rpc_error( -8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate) self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex()) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, bad_block.serialize().hex()) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error( -22, "Block decode failed", node.getblocktemplate, { 'data': block.serialize()[:-1].hex(), 'mode': 'proposal', 'rules': ['segwit'], }) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1) bad_block_sn[BLOCK_HEADER_SIZE] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': bad_block_sn.hex(), 'mode': 'proposal', 'rules': ['segwit'], }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE)) assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2))) assert_raises_rpc_error( -25, 'Must submit previous header', lambda: node.submitheader( hexdata=super(CBlock, bad_block).serialize().hex())) block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): return { 'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status } assert chain_tip(block.hash) not in node.getchaintips() node.submitheader(hexdata=block.serialize().hex()) assert chain_tip(block.hash) in node.getchaintips() node.submitheader( hexdata=CBlockHeader(block).serialize().hex()) # Noop assert chain_tip(block.hash) in node.getchaintips() bad_block_root = copy.deepcopy(block) bad_block_root.hashMerkleRoot += 2 bad_block_root.solve() assert chain_tip(bad_block_root.hash) not in node.getchaintips() node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return early without error: node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() bad_block_lock = copy.deepcopy(block) bad_block_lock.vtx[0].nLockTime = 2**32 - 1 bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal') assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 bad_block2.solve() assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) # Should reject invalid header right away bad_block_time = copy.deepcopy(block) bad_block_time.nTime = 1 bad_block_time.solve() assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) # Should ask for the block from a p2p node, if they announce the header as well: peer = node.add_p2p_connection(P2PDataStore()) peer.wait_for_getheaders(timeout=5) # Drop the first getheaders peer.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results node.generatetoaddress(10, node.get_deterministic_priv_key().address) assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
def run_test(self): node = self.nodes[0] self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], 'regtest') assert_equal(mining_info['currentblocktx'], 0) assert_equal(mining_info['currentblockweight'], 0) assert_equal(mining_info['difficulty'], Decimal('1.999969720836845')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generate(1) tmpl = node.getblocktemplate() self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl coinbase_tx = create_coinbase(height=int(tmpl["height"]) + 1) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] block.major_version = 10 block.merkle_root = uint256_from_str( bytes.fromhex( "3cf6c3b6da3f4058853ee70369ee43d473aca91ae8fc8f44a645beb21c392d80" )) block.calc_sha256() self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': b2x(block.serialize()[:-1]), 'mode': 'proposal' }) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) # Kevacoin does not have proposal mode. The bad-txns-inputs-missingorspent # error will only be thrown in proposal mode. # assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header TX_COUNT_OFFSET = 80 + 78 # Kevacoin: 78 is the size of the cnHeader. bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) bad_block_sn[TX_COUNT_OFFSET] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': b2x(bad_block_sn), 'mode': 'proposal' }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
def run_test(self): """Main test logic""" # Create a P2P connection to one of the nodes node0 = BaseNode() connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) node0.add_connection(connections[0]) # Start up network handling in another thread. This needs to be called # after the P2P connections have been created. NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() # Generating a block on one of the nodes will get us out of IBD blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)] self.sync_all([self.nodes[0:1]]) # Notice above how we called an RPC by calling a method with the same # name on the node object. Notice also how we used a keyword argument # to specify a named RPC argument. Neither of those are defined on the # node object. Instead there's some __getattr__() magic going on under # the covers to dispatch unrecognised attribute calls to the RPC # interface. # Logs are nice. Do plenty of them. They can be used in place of comments for # breaking the test into sub-sections. self.log.info("Starting test!") self.log.info("Calling a custom function") custom_function() self.log.info("Calling a custom method") self.custom_method() self.log.info("Create some blocks") self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock( self.nodes[0].getbestblockhash())['time'] + 1 height = 1 for i in range(10): # Use the mininode and blocktools functionality to manually build a block # Calling the generate() rpc is easier, but this allows us to exactly # control the blocks and transactions. block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() block_message = msg_block(block) # Send message is used to send a P2P message to the node over our NodeConn connection node0.send_message(block_message) self.tip = block.sha256 blocks.append(self.tip) self.block_time += 1 height += 1 self.log.info( "Wait for node1 to reach current tip (height 11) using RPC") self.nodes[1].waitforblockheight(11) self.log.info("Connect node2 and node1") connect_nodes(self.nodes[1], 2) self.log.info("Add P2P connection to node2") node2 = BaseNode() connections.append( NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) node2.add_connection(connections[1]) node2.wait_for_verack() self.log.info( "Wait for node2 reach current tip. Test that it has propagated all the blocks to us" ) getdata_request = msg_getdata() for block in blocks: getdata_request.inv.append(CInv(2, block)) node2.send_message(getdata_request) # wait_until() will loop until a predicate condition is met. Use it to test properties of the # NodeConnCB objects. wait_until(lambda: sorted(blocks) == sorted( list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock) self.log.info("Check that each block was received only once") # The network thread uses a global lock on data access to the NodeConn objects when sending and receiving # messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate. with mininode_lock: for block in node2.block_receive_map.values(): assert_equal(block, 1)
def create_block(self, node, vtx=[]): bt = node.getblocktemplate() height = bt['height'] tip_hash = bt['previousblockhash'] coinbasevalue = bt['coinbasevalue'] miner_address = node.getnewaddress() mn_payee = bt['masternode'][0]['payee'] # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't # include the template's transactions bt_fees = 0 for tx in bt['transactions']: bt_fees += tx['fee'] new_fees = 0 for tx in vtx: in_value = 0 out_value = 0 for txin in tx.vin: txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) in_value += int(txout['value'] * COIN) for txout in tx.vout: out_value += txout.nValue new_fees += in_value - out_value # fix fees coinbasevalue -= bt_fees coinbasevalue += new_fees mn_amount = get_masternode_payment(height, coinbasevalue) miner_amount = coinbasevalue - mn_amount outputs = {miner_address: str(Decimal(miner_amount) / COIN)} if mn_amount > 0: outputs[mn_payee] = str(Decimal(mn_amount) / COIN) coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) coinbase.vin = create_coinbase(height).vin # We can't really use this one as it would result in invalid merkle roots for masternode lists if len(bt['coinbase_payload']) != 0: cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) coinbase.nVersion = 3 coinbase.nType = 5 # CbTx coinbase.vExtraPayload = cbtx.serialize() coinbase.calc_sha256() block = create_block(int(tip_hash, 16), coinbase, nTime=bt['curtime']) block.vtx += vtx # Add quorum commitments from template for tx in bt['transactions']: tx2 = FromHex(CTransaction(), tx['data']) if tx2.nType == 6: block.vtx.append(tx2) block.hashMerkleRoot = block.calc_merkle_root() block.solve() return block