Esempio n. 1
0
    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 run_test(self):
     test = TestManager(self, self.options.tmpdir)
     test.add_all_connections(self.nodes)
     self.tip = None
     self.block_time = None
     network_thread_start()
     test.run()
Esempio n. 3
0
    def bootstrap_p2p(self, *, num_connections=1):
        """Add a P2P connection to the node.

        Helper to connect and wait for version handshake."""
        for _ in range(num_connections):
            self.nodes[0].add_p2p_connection(P2PDataStore())
        network_thread_start()
        self.nodes[0].p2p.wait_for_verack()
Esempio n. 4
0
    def run_test(self):
        self.address = self.nodes[0].getnewaddress()
        self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])['address']
        self.wit_address = self.nodes[0].addwitnessaddress(self.address)
        self.wit_ms_address = self.nodes[0].addmultisigaddress(1, [self.address], '', 'p2sh-segwit')['address']

        network_thread_start()
        self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
        coinbase_txid = []
        for i in self.coinbase_blocks:
            coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
        self.nodes[0].generate(427) # Block 429
        self.lastblockhash = self.nodes[0].getbestblockhash()
        self.tip = int("0x" + self.lastblockhash, 0)
        self.lastblockheight = 429
        self.lastblocktime = int(time.time()) + 429

        self.log.info("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
        test1txs = [self.create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, 49)]
        txid1 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[0].serialize_with_witness()), True)
        test1txs.append(self.create_transaction(self.nodes[0], txid1, self.ms_address, 48))
        txid2 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[1].serialize_with_witness()), True)
        test1txs.append(self.create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, 49))
        txid3 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[2].serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test1txs, False, True)

        self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
        test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 47)
        trueDummy(test2tx)
        assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True)

        self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
        self.block_submit(self.nodes[0], [test2tx], False, True)

        self.log.info("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
        test4tx = self.create_transaction(self.nodes[0], test2tx.hash, self.address, 46)
        test6txs=[CTransaction(test4tx)]
        trueDummy(test4tx)
        assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test4tx])

        self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
        test5tx = self.create_transaction(self.nodes[0], txid3, self.wit_address, 48)
        test6txs.append(CTransaction(test5tx))
        test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
        assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test5tx], True)

        self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
        for i in test6txs:
            self.nodes[0].sendrawtransaction(bytes_to_hex_str(i.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test6txs, True, True, VB_TOP_BITS)
    def run_test(self):
        # Handy alias
        node = self.nodes[0]
        node.add_p2p_connection(P2PInterface())
        network_thread_start()
        node.p2p.wait_for_verack()

        # Mine one period worth of blocks
        node.generate(VB_PERIOD)

        self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.")
        # Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit
        self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
        node.generate(VB_PERIOD - VB_THRESHOLD + 1)

        # Check that we're not getting any versionbit-related errors in get*info()
        assert(not VB_PATTERN.match(node.getmininginfo()["warnings"]))
        assert(not VB_PATTERN.match(node.getnetworkinfo()["warnings"]))

        self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version")
        # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
        self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
        node.generate(VB_PERIOD - VB_THRESHOLD)

        # Check that get*info() shows the 51/100 unknown block version error.
        assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"])
        assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"])

        self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
        # Mine a period worth of expected blocks so the generic block-version warning
        # is cleared. This will move the versionbit state to ACTIVE.
        node.generate(VB_PERIOD)

        # Stop-start the node. This is required because groincoind will only warn once about unknown versions or unknown rules activating.
        self.restart_node(0)

        # Generating one block guarantees that we'll get out of IBD
        node.generate(1)
        wait_until(lambda: not node.getblockchaininfo()['initialblockdownload'], timeout=10, lock=mininode_lock)
        # Generating one more block will be enough to generate an error.
        node.generate(1)
        # Check that get*info() shows the versionbits unknown rules warning
        assert(WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"])
        assert(WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"])
        # Check that the alert file shows the versionbits unknown rules warning
        wait_until(lambda: self.versionbits_in_alert_file(), timeout=60)
Esempio n. 6
0
    def run_test(self):
        # Setup the p2p connections and start up the network thread.
        inv_node = self.nodes[0].add_p2p_connection(BaseNode())
        # Make sure NODE_NETWORK is not set for test_node, so no block download
        # will occur outside of direct fetching
        test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS)

        network_thread_start()

        # 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()

        self.test_null_locators(test_node, inv_node)
        self.test_nonnull_locators(test_node, inv_node)
Esempio n. 7
0
    def _test_waitforblockheight(self):
        self.log.info("Test waitforblockheight")

        node = self.nodes[0]

        # Start a P2P connection since we'll need to create some blocks.
        node.add_p2p_connection(P2PInterface())
        network_thread_start()
        node.p2p.wait_for_verack()

        current_height = node.getblock(node.getbestblockhash())['height']

        # Create a fork somewhere below our current height, invalidate the tip
        # of that fork, and then ensure that waitforblockheight still
        # works as expected.
        #
        # (Previously this was broken based on setting
        # `rpc/blockchain.cpp:latestblock` incorrectly.)
        #
        b20hash = node.getblockhash(20)
        b20 = node.getblock(b20hash)

        def solve_and_send_block(prevhash, height, time):
            b = create_block(prevhash, create_coinbase(height), time)
            b.solve()
            node.p2p.send_message(msg_block(b))
            node.p2p.sync_with_ping()
            return b

        b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
        b22f = solve_and_send_block(b21f.sha256, 22, b21f.nTime + 1)

        node.invalidateblock(b22f.hash)

        def assert_waitforheight(height, timeout=2):
            assert_equal(
                node.waitforblockheight(height, timeout)['height'],
                current_height)

        assert_waitforheight(0)
        assert_waitforheight(current_height - 1)
        assert_waitforheight(current_height)
        assert_waitforheight(current_height + 1)
    def run_test(self):
        self.address = self.nodes[0].getnewaddress()
        self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])

        network_thread_start()
        self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
        coinbase_txid = []
        for i in self.coinbase_blocks:
            coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
        self.nodes[0].generate(427) # Block 429
        self.lastblockhash = self.nodes[0].getbestblockhash()
        self.tip = int("0x" + self.lastblockhash, 0)
        self.lastblockheight = 429
        self.lastblocktime = int(time.time()) + 429

        self.log.info("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
        test1txs = [self.create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, 49)]
        txid1 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[0].serialize_without_witness()), True)
        test1txs.append(self.create_transaction(self.nodes[0], txid1, self.ms_address, 48))
        txid2 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[1].serialize_without_witness()), True)
        self.block_submit(self.nodes[0], test1txs, True)

        self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
        test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 48)
        trueDummy(test2tx)
        txid4 = self.tx_submit(self.nodes[0], test2tx, NULLDUMMY_ERROR)

        self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
        self.block_submit(self.nodes[0], [test2tx], True)

        self.log.info("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
        test4tx = self.create_transaction(self.nodes[0], txid4, self.address, 47)
        test6txs=[CTransaction(test4tx)]
        trueDummy(test4tx)
        self.tx_submit(self.nodes[0], test4tx, NULLDUMMY_ERROR)
        self.block_submit(self.nodes[0], [test4tx])

        self.log.info("Test 6: NULLDUMMY compliant transactions should be accepted to mempool and in block after activation [432]")
        for i in test6txs:
            self.nodes[0].sendrawtransaction(bytes_to_hex_str(i.serialize_without_witness()), True)
        self.block_submit(self.nodes[0], test6txs, True)
Esempio n. 9
0
    def init_test(self):
        ''' Initializes test parameters
        :param:
        :return:
        '''
        self.log.info("\n\n*** Starting %s ***\n------------------------\n%s\n", self.__class__.__name__, self.description)
        # Global Test parameters (override in run_test)
        self.DEFAULT_FEE = 0.1
        # Spam blocks to send in current test
        self.NUM_BLOCKS = 30

        # Setup the p2p connections and start up the network thread.
        self.test_nodes = []
        for i in range(self.num_nodes):
            self.test_nodes.append(TestNode())
            self.test_nodes[i].peer_connect('127.0.0.1', p2p_port(i))

        network_thread_start()  # Start up network handling in another thread
        self.node = self.nodes[0]

        # Let the test nodes get in sync
        for i in range(self.num_nodes):
            self.test_nodes[i].wait_for_verack()
Esempio n. 10
0
    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 8400 deep (BiblePay needs 4x as much blocks to allow -assumevalid to work)
        for i in range(8400):
            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=self.extra_args +
                        ["-assumevalid=" + hex(block102.sha256)])
        self.start_node(2,
                        extra_args=self.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()

        # Make sure nodes actually accept the many headers
        self.mocktime = self.block_time
        set_node_times(self.nodes, self.mocktime)

        # send header lists to all three nodes.
        # node0 does not need to receive all headers
        # node1 must receive all headers as otherwise assumevalid is ignored in ConnectBlock
        # node2 should NOT receive all headers to force skipping of the assumevalid check in ConnectBlock
        p2p0.send_header_for_blocks(self.blocks[0:2000])
        p2p1.send_header_for_blocks(self.blocks[0:2000])
        p2p1.send_header_for_blocks(self.blocks[2000:4000])
        p2p1.send_header_for_blocks(self.blocks[4000:6000])
        p2p1.send_header_for_blocks(self.blocks[6000:8000])
        p2p1.send_header_for_blocks(self.blocks[8000:])
        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 200 blocks to node1. All blocks, including block 102, will be accepted.
        for i in range(200):
            p2p1.send_message(msg_block(self.blocks[i]))
        # Syncing so many blocks can take a while on slow systems. Give it plenty of time to sync.
        p2p1.sync_with_ping(300)
        assert_equal(
            self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'],
            200)

        # Send blocks to node2. Block 102 will be rejected.
        self.send_blocks_until_disconnected(p2p2)
        self.assert_blockchain_height(self.nodes[2], 101)
Esempio n. 11
0
    def run_test(self):
        self.nodes[0].add_p2p_connection(P2PDataStore())
        network_thread_start()
        self.nodes[0].p2p.wait_for_verack()

        self.log.info("Generate blocks in the past for coinbase outputs.")
        long_past_time = int(time.time()) - 600 * 1000  # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
        self.nodes[0].setmocktime(long_past_time - 100)  # enough so that the generated blocks will still all be before long_past_time
        self.coinbase_blocks = self.nodes[0].generate(1 + 16 + 2 * 32 + 1)  # 82 blocks generated for inputs
        self.nodes[0].setmocktime(0)  # set time back to present so yielded blocks aren't in the future as we advance last_block_time
        self.tipheight = 82  # height of the next block to build
        self.last_block_time = long_past_time
        self.tip = int(self.nodes[0].getbestblockhash(), 16)
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info("Test that the csv softfork is DEFINED")
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
        test_blocks = self.generate_blocks(61, 4)
        self.sync_blocks(test_blocks)

        self.log.info("Advance from DEFINED to STARTED, height = 143")
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        self.log.info("Fail to achieve LOCKED_IN")
        # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks

        test_blocks = self.generate_blocks(50, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(20, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(24, 536936448, test_blocks)  # 0x20010000 (signalling not)
        self.sync_blocks(test_blocks)

        self.log.info("Failed to advance past STARTED, height = 287")
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        self.log.info("Generate blocks to achieve LOCK-IN")
        # 108 out of 144 signal bit 0 to achieve lock-in
        # using a variety of bits to simulate multiple parallel softforks
        test_blocks = self.generate_blocks(58, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(26, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(10, 536936448, test_blocks)  # 0x20010000 (signalling not)
        self.sync_blocks(test_blocks)

        self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        # Generate 140 more version 4 blocks
        test_blocks = self.generate_blocks(140, 4)
        self.sync_blocks(test_blocks)

        # Inputs at height = 572
        #
        # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
        # Note we reuse inputs for v1 and v2 txs so must test these separately
        # 16 normal inputs
        bip68inputs = []
        for i in range(16):
            bip68inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))

        # 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112basicinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
            bip112basicinputs.append(inputs)

        # 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112diverseinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
            bip112diverseinputs.append(inputs)

        # 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)

        # 1 normal input
        bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)

        self.nodes[0].setmocktime(self.last_block_time + 600)
        inputblockhash = self.nodes[0].generate(1)[0]  # 1 block generated for inputs to be in chain at height 572
        self.nodes[0].setmocktime(0)
        self.tip = int(inputblockhash, 16)
        self.tipheight += 1
        self.last_block_time += 600
        assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)

        # 2 more version 4 blocks
        test_blocks = self.generate_blocks(2, 4)
        self.sync_blocks(test_blocks)

        self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)")
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        # Test both version 1 and version 2 transactions for all tests
        # BIP113 test transaction will be modified before each use to put in appropriate block time
        bip113tx_v1 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
        bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v1.nVersion = 1
        bip113tx_v2 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
        bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v2.nVersion = 2

        # For BIP68 test all 16 relative sequence locktimes
        bip68txs_v1 = create_bip68txs(self.nodes[0], bip68inputs, 1, self.nodeaddress)
        bip68txs_v2 = create_bip68txs(self.nodes[0], bip68inputs, 2, self.nodeaddress)

        # For BIP112 test:
        # 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 1, self.nodeaddress)
        bip112txs_vary_nSequence_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 2, self.nodeaddress)
        # 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_9_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 1, self.nodeaddress, -1)
        bip112txs_vary_nSequence_9_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 2, self.nodeaddress, -1)
        # sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 1, self.nodeaddress)
        bip112txs_vary_OP_CSV_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 2, self.nodeaddress)
        # sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_9_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 1, self.nodeaddress, -1)
        bip112txs_vary_OP_CSV_9_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 2, self.nodeaddress, -1)
        # -1 OP_CSV OP_DROP input
        bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
        bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)

        self.log.info("TESTING")

        self.log.info("Pre-Soft Fork Tests. All txs should pass.")
        self.log.info("Test version 1 txs")

        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        success_txs.append(bip113signed1)
        success_txs.append(bip112tx_special_v1)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v1))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v1))
        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("Test version 2 txs")

        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        success_txs.append(bip113signed2)
        success_txs.append(bip112tx_special_v2)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v2))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v2))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v2))
        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
        test_blocks = self.generate_blocks(1, 4)
        self.sync_blocks(test_blocks)
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')

        self.log.info("Post-Soft Fork Tests.")

        self.log.info("BIP 113 tests")
        # BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            self.sync_blocks([self.create_test_block([bip113tx])], success=False)
        # BIP 113 tests should now pass if the locktime is < MTP
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            self.sync_blocks([self.create_test_block([bip113tx])])
            self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # Next block height = 580 after 4 blocks of random version
        test_blocks = self.generate_blocks(4, 1234)
        self.sync_blocks(test_blocks)

        self.log.info("BIP 68 tests")
        self.log.info("Test version 1 txs - all should still pass")

        success_txs = []
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("Test version 2 txs")

        # All txs with SEQUENCE_LOCKTIME_DISABLE_FLAG set pass
        bip68success_txs = [tx['tx'] for tx in bip68txs_v2 if tx['sdf']]
        self.sync_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
        bip68timetxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and tx['stf']]
        for tx in bip68timetxs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        bip68heighttxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and not tx['stf']]
        for tx in bip68heighttxs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        # Advance one block to 581
        test_blocks = self.generate_blocks(1, 1234)
        self.sync_blocks(test_blocks)

        # Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
        bip68success_txs.extend(bip68timetxs)
        self.sync_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        for tx in bip68heighttxs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        # Advance one block to 582
        test_blocks = self.generate_blocks(1, 1234)
        self.sync_blocks(test_blocks)

        # All BIP 68 txs should pass
        bip68success_txs.extend(bip68heighttxs)
        self.sync_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("BIP 112 tests")
        self.log.info("Test version 1 txs")

        # -1 OP_CSV tx should fail
        self.sync_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass

        success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']]
        success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if tx['sdf']]
        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
        fail_txs = all_rlt_txs(bip112txs_vary_nSequence_v1)
        fail_txs += all_rlt_txs(bip112txs_vary_nSequence_9_v1)
        fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
        fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
        for tx in fail_txs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        self.log.info("Test version 2 txs")

        # -1 OP_CSV tx should fail
        self.sync_blocks([self.create_test_block([bip112tx_special_v2])], success=False)

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
        success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']]
        success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if tx['sdf']]

        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##

        # All txs with nSequence 9 should fail either due to earlier mismatch or failing the CSV check
        fail_txs = all_rlt_txs(bip112txs_vary_nSequence_9_v2)
        fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']]
        for tx in fail_txs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
        fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']]
        for tx in fail_txs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        # If sequencelock types mismatch, tx should fail
        fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and tx['stf']]
        fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]
        for tx in fail_txs:
            self.sync_blocks([self.create_test_block([tx])], success=False)

        # Remaining txs should pass, just test masking works properly
        success_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and not tx['stf']]
        success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and not tx['stf']]
        self.sync_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # Additional test, of checking that comparison of two time types works properly
        time_txs = []
        for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]:
            tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
            signtx = sign_transaction(self.nodes[0], tx)
            time_txs.append(signtx)

        self.sync_blocks([self.create_test_block(time_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
Esempio n. 12
0
    def test_p2p_schema(self):
        """
        This test creates the following nodes:
        1. serving_node - full node that has the the snapshot
        2. syncing_p2p - mini node that downloads snapshot from serving_node and tests the protocol
        3. syncing_node - the node which starts with fast sync
        4. serving_p2p - mini node that sends snapshot to syncing_node and tests the protocol
        """
        serving_node = self.nodes[0]
        syncing_node = self.nodes[1]

        self.start_node(serving_node.index)
        self.start_node(syncing_node.index)

        self.setup_stake_coins(serving_node)

        # generate 2 epochs + 1 block to create the first finalized snapshot
        serving_node.generatetoaddress(
            5 + 5 + 1, serving_node.getnewaddress('', 'bech32'))
        assert_equal(serving_node.getblockcount(), 11)
        wait_until(lambda: has_valid_snapshot(serving_node, 4), timeout=10)

        syncing_p2p = serving_node.add_p2p_connection(BaseNode())
        serving_p2p = syncing_node.add_p2p_connection(
            BaseNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)

        # configure serving_p2p to have snapshot header and parent block
        serving_p2p.update_snapshot_from(serving_node)
        serving_p2p.update_headers_and_blocks_from(serving_node)

        network_thread_start()
        syncing_p2p.wait_for_verack()

        # test snapshot downloading in chunks
        syncing_p2p.send_message(msg_getsnaphead())
        wait_until(lambda: syncing_p2p.snapshot_header.total_utxo_subsets > 0,
                   timeout=10)
        chunks = math.ceil(syncing_p2p.snapshot_header.total_utxo_subsets / 2)
        for i in range(1, chunks + 1):
            getsnapshot = GetSnapshot(
                syncing_p2p.snapshot_header.snapshot_hash,
                len(syncing_p2p.snapshot_data), 2)
            syncing_p2p.send_message(msg_getsnapshot(getsnapshot))

            snapshot_size = min(i * 2,
                                syncing_p2p.snapshot_header.total_utxo_subsets)
            wait_until(lambda: len(syncing_p2p.snapshot_data) == snapshot_size,
                       timeout=10)
        assert_equal(len(syncing_p2p.snapshot_data),
                     syncing_p2p.snapshot_header.total_utxo_subsets)

        self.log.info('Snapshot was downloaded successfully')

        # validate the snapshot hash
        utxos = []
        for subset in syncing_p2p.snapshot_data:
            for n in subset.outputs:
                out = COutPoint(subset.tx_id, n)
                utxo = UTXO(subset.height, subset.tx_type, out,
                            subset.outputs[n])
                utxos.append(utxo)
        inputs = bytes_to_hex_str(ser_vector([]))
        outputs = bytes_to_hex_str(ser_vector(utxos))
        stake_modifier = "%064x" % syncing_p2p.snapshot_header.stake_modifier
        chain_work = bytes_to_hex_str(
            ser_uint256(syncing_p2p.snapshot_header.chain_work))
        res = self.nodes[0].calcsnapshothash(inputs, outputs, stake_modifier,
                                             chain_work)
        snapshot_hash = uint256_from_hex(res['hash'])
        assert_equal(snapshot_hash, syncing_p2p.snapshot_header.snapshot_hash)

        self.log.info('Snapshot was validated successfully')

        # test snapshot serving
        wait_until(lambda: serving_p2p.snapshot_requested, timeout=10)
        snapshot = Snapshot(
            snapshot_hash=serving_p2p.snapshot_header.snapshot_hash,
            utxo_subset_index=0,
            utxo_subsets=syncing_p2p.snapshot_data,
        )
        serving_p2p.send_message(msg_snapshot(snapshot))
        wait_until(lambda: syncing_node.getblockcount() == 11, timeout=10)
        assert_equal(serving_node.gettxoutsetinfo(),
                     syncing_node.gettxoutsetinfo())

        self.log.info('Snapshot was sent successfully')

        # clean up test
        serving_node.disconnect_p2ps()
        syncing_node.disconnect_p2ps()
        network_thread_join()
        self.stop_node(serving_node.index)
        self.stop_node(syncing_node.index)
        self.log.info('test_p2p_schema passed')
Esempio n. 13
0
    def run_test(self):
        self.address = self.nodes[0].getnewaddress()
        self.ms_address = self.nodes[0].addmultisigaddress(
            1, [self.address])['address']
        self.wit_address = self.nodes[0].addwitnessaddress(self.address)
        self.wit_ms_address = self.nodes[0].addmultisigaddress(
            1, [self.address], '', 'p2sh-segwit')['address']

        network_thread_start()
        self.coinbase_blocks = self.nodes[0].generate(2)  # Block 2
        coinbase_txid = []
        for i in self.coinbase_blocks:
            coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])

        for i in range(COINBASE_MATURITY):
            block = create_block(
                int(self.nodes[0].getbestblockhash(), 16),
                create_coinbase(self.nodes[0].getblockcount() + 1),
                self.nodes[0].getblockcount() + 1,
                int(time.time()) + 2 + i)
            block.nVersion = 4
            block.hashMerkleRoot = block.calc_merkle_root()
            block.rehash()
            block.solve()
            self.nodes[0].submitblock(bytes_to_hex_str(block.serialize()))

        # Generate the number blocks signalling  that the continuation of the test case expects
        # for 800  maturity  144*8=1152 144*6=864 144*3=432
        self.nodes[0].generate((1152 - 1) - COINBASE_MATURITY - 2 - 2)
        self.lastblockhash = self.nodes[0].getbestblockhash()
        self.tip = int("0x" + self.lastblockhash, 0)
        self.lastblockheight = self.nodes[0].getblockcount()
        self.lastblocktime = int(time.time()) + self.lastblockheight + 1

        self.log.info(
            "Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]"
        )
        test1txs = [
            self.create_transaction(self.nodes[0], coinbase_txid[0],
                                    self.ms_address, INITIAL_BLOCK_REWARD - 1)
        ]
        txid1 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[0].serialize_with_witness()), True)
        test1txs.append(
            self.create_transaction(self.nodes[0], txid1, self.ms_address,
                                    INITIAL_BLOCK_REWARD - 2))
        txid2 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[1].serialize_with_witness()), True)
        test1txs.append(
            self.create_transaction(self.nodes[0], coinbase_txid[1],
                                    self.wit_ms_address,
                                    INITIAL_BLOCK_REWARD - 1))
        txid3 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[2].serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test1txs, False, True)

        self.log.info(
            "Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation"
        )
        test2tx = self.create_transaction(self.nodes[0], txid2,
                                          self.ms_address,
                                          INITIAL_BLOCK_REWARD - 3)
        trueDummy(test2tx)
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test2tx.serialize_with_witness()), True)

        self.log.info(
            "Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]"
        )
        self.block_submit(self.nodes[0], [test2tx], False, True)

        self.log.info(
            "Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation"
        )
        test4tx = self.create_transaction(self.nodes[0], test2tx.hash,
                                          self.address,
                                          INITIAL_BLOCK_REWARD - 4)
        test6txs = [CTransaction(test4tx)]
        trueDummy(test4tx)
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test4tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test4tx])

        self.log.info(
            "Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation"
        )
        test5tx = self.create_transaction(self.nodes[0], txid3,
                                          self.wit_address,
                                          INITIAL_BLOCK_REWARD - 2)
        test6txs.append(CTransaction(test5tx))
        test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test5tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test5tx], True)

        self.log.info(
            "Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]"
        )
        for i in test6txs:
            self.nodes[0].sendrawtransaction(
                bytes_to_hex_str(i.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test6txs, True, True)
    def run_test(self):
        self.log.info("Wait for DIP3 to activate")
        while get_bip9_status(self.nodes[0], 'dip0003')['status'] != 'active':
            self.bump_mocktime(10)
            self.nodes[0].generate(10)

        self.nodes[0].add_p2p_connection(P2PDataStore())
        network_thread_start()
        self.nodes[0].p2p.wait_for_verack()

        self.log.info("Mine all but one remaining block in the window")
        bi = self.nodes[0].getblockchaininfo()
        for i in range(498 - bi['blocks']):
            self.bump_mocktime(1)
            self.nodes[0].generate(1)

        self.log.info("Initial state is DEFINED")
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 498)
        assert_equal(bi['bip9_softforks']['realloc']['status'], 'defined')

        self.log.info("Advance from DEFINED to STARTED at height = 499")
        self.nodes[0].generate(1)
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 499)
        assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
        assert_equal(
            bi['bip9_softforks']['realloc']['statistics']['threshold'],
            self.threshold(0))

        self.signal(399, False)  # 1 block short

        self.log.info(
            "Still STARTED but new threshold should be lower at height = 999")
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 999)
        assert_equal(
            bi['bip9_softforks']['realloc']['statistics']['threshold'],
            self.threshold(1))

        self.signal(398, False)  # 1 block short again

        self.log.info(
            "Still STARTED but new threshold should be even lower at height = 1499"
        )
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 1499)
        assert_equal(
            bi['bip9_softforks']['realloc']['statistics']['threshold'],
            self.threshold(2))
        pre_locked_in_blockhash = bi['bestblockhash']

        self.signal(396, True)  # just enough to lock in
        self.log.info("Advanced to LOCKED_IN at height = 1999")

        for i in range(49):
            self.bump_mocktime(10)
            self.nodes[0].generate(10)
        self.nodes[0].generate(9)

        self.log.info("Still LOCKED_IN at height = 2498")
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 2498)
        assert_equal(bi['bip9_softforks']['realloc']['status'], 'locked_in')

        self.log.info("Advance from LOCKED_IN to ACTIVE at height = 2499")
        self.nodes[0].generate(1)  # activation
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 2499)
        assert_equal(bi['bip9_softforks']['realloc']['status'], 'active')
        assert_equal(bi['bip9_softforks']['realloc']['since'], 2500)

        self.log.info(
            "Reward split should stay ~50/50 before the first superblock after activation"
        )
        # This applies even if reallocation was activated right at superblock height like it does here
        bt = self.nodes[0].getblocktemplate()
        assert_equal(bt['height'], 2500)
        assert_equal(
            bt['masternode'][0]['amount'],
            get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
        self.nodes[0].generate(9)
        bt = self.nodes[0].getblocktemplate()
        assert_equal(
            bt['masternode'][0]['amount'],
            get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
        assert_equal(bt['coinbasevalue'], 13748571607)
        assert_equal(bt['masternode'][0]['amount'], 6874285801)  # 0.4999999998

        self.log.info(
            "Reallocation should kick-in with the superblock mined at height = 2010"
        )
        for period in range(
                19):  # there will be 19 adjustments, 3 superblocks long each
            for i in range(3):
                self.bump_mocktime(10)
                self.nodes[0].generate(10)
                bt = self.nodes[0].getblocktemplate()
                assert_equal(
                    bt['masternode'][0]['amount'],
                    get_masternode_payment(bt['height'], bt['coinbasevalue'],
                                           2500))

        self.log.info(
            "Reward split should reach ~60/40 after reallocation is done")
        assert_equal(bt['coinbasevalue'], 10221599170)
        assert_equal(bt['masternode'][0]['amount'], 6132959502)  # 0.6

        self.log.info(
            "Reward split should stay ~60/40 after reallocation is done")
        for period in range(10):  # check 10 next superblocks
            self.bump_mocktime(10)
            self.nodes[0].generate(10)
            bt = self.nodes[0].getblocktemplate()
            assert_equal(
                bt['masternode'][0]['amount'],
                get_masternode_payment(bt['height'], bt['coinbasevalue'],
                                       2500))
        assert_equal(bt['coinbasevalue'], 9491484944)
        assert_equal(bt['masternode'][0]['amount'], 5694890966)  # 0.6

        # make sure all nodes are still synced
        self.sync_all()

        self.log.info("Rollback the chain back to the STARTED state")
        self.mocktime = self.nodes[0].getblock(pre_locked_in_blockhash,
                                               1)['time']
        for node in self.nodes:
            node.invalidateblock(pre_locked_in_blockhash)
        # create and send non-signalling block
        test_block = self.create_test_block()
        self.nodes[0].submitblock(ToHex(test_block))
        bi = self.nodes[0].getblockchaininfo()
        assert_equal(bi['blocks'], 1499)
        assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
        assert_equal(
            bi['bip9_softforks']['realloc']['statistics']['threshold'],
            self.threshold(2))

        self.log.info("Check thresholds reach min level and stay there")
        for i in range(
                8
        ):  # 7 to reach min level and 1 more to check it doesn't go lower than that
            self.signal(0, False)  # no need to signal
            bi = self.nodes[0].getblockchaininfo()
            assert_equal(bi['blocks'], 1999 + i * 500)
            assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
            assert_equal(
                bi['bip9_softforks']['realloc']['statistics']['threshold'],
                self.threshold(i + 3))
        assert_equal(
            bi['bip9_softforks']['realloc']['statistics']['threshold'], 300)
Esempio n. 15
0
    def run_test(self):
        node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
        network_thread_start()
        node.wait_for_verack()

        expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED

        self.log.info("Check that node has signalled expected services.")
        assert_equal(node.nServices, expected_services)

        self.log.info("Check that the localservices is as expected.")
        assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services)

        self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
        connect_nodes_bi(self.nodes, 0, 1)
        blocks = self.nodes[1].generate(292)
        sync_blocks([self.nodes[0], self.nodes[1]])

        self.log.info("Make sure we can max retrieve block at tip-288.")
        node.send_getdata_for_block(blocks[1])  # last block in valid range
        node.wait_for_block(int(blocks[1], 16), timeout=3)

        self.log.info("Requesting block at height 2 (tip-289) must fail (ignored).")
        node.send_getdata_for_block(blocks[0])  # first block outside of the 288+2 limit
        node.wait_for_disconnect(5)

        self.log.info("Check local address relay, do a fresh connection.")
        self.nodes[0].disconnect_p2ps()
        network_thread_join()
        node1 = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
        network_thread_start()
        node1.wait_for_verack()
        node1.send_message(msg_verack())

        node1.wait_for_addr()
        #must relay address with NODE_NETWORK_LIMITED
        assert_equal(node1.firstAddrnServices, 1036)

        self.nodes[0].disconnect_p2ps()
        node1.wait_for_disconnect()

        # connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer
        # because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible
        connect_nodes_bi(self.nodes, 0, 2)
        try:
            sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
        except:
            pass
        # node2 must remain at heigh 0
        assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0)

        # now connect also to node 1 (non pruned)
        connect_nodes_bi(self.nodes, 1, 2)

        # sync must be possible
        sync_blocks(self.nodes)

        # disconnect all peers
        self.disconnect_all()

        # mine 10 blocks on node 0 (pruned node)
        self.nodes[0].generate(10)

        # connect node1 (non pruned) with node0 (pruned) and check if the can sync
        connect_nodes_bi(self.nodes, 0, 1)

        # sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
        sync_blocks([self.nodes[0], self.nodes[1]])
Esempio n. 16
0
    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)
        network_thread_start()
        self.test.p2p_connections[0].wait_for_verack()
 def run_test(self):
     self.test = TestManager(self, self.options.tmpdir)
     self.test.add_all_connections(self.nodes)
     network_thread_start()
     self.nodes[0].setmocktime(REPLAY_PROTECTION_START_TIME)
     self.test.run()
Esempio n. 18
0
    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 = 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_transaction(block1.vtx[0], 0, b'\x51', 50 * COIN)
        tx2 = create_transaction(tx1, 0, b'\x51', 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='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, False, False, 16,
                                      b'bad-cb-amount')
Esempio n. 19
0
    def run_test(self):
        # Setup the p2p connections and start up the network thread.
        self.test_node = self.nodes[0].add_p2p_connection(TestNode())
        self.ex_softfork_node = self.nodes[1].add_p2p_connection(
            TestNode(), services=NODE_NETWORK)
        self.old_node = self.nodes[1].add_p2p_connection(TestNode(),
                                                         services=NODE_NETWORK)

        network_thread_start()

        self.test_node.wait_for_verack()

        # We will need UTXOs to construct transactions in later tests.
        self.make_utxos()

        self.log.info("Running tests:")

        self.log.info("\tTesting SENDCMPCT p2p message... ")
        self.test_sendcmpct(self.nodes[0], self.test_node, 1)
        sync_blocks(self.nodes)
        self.test_sendcmpct(self.nodes[1],
                            self.ex_softfork_node,
                            1,
                            old_node=self.old_node)
        sync_blocks(self.nodes)

        self.log.info("\tTesting compactblock construction...")
        self.test_compactblock_construction(self.nodes[0], self.test_node)
        sync_blocks(self.nodes)
        self.test_compactblock_construction(self.nodes[1],
                                            self.ex_softfork_node)
        sync_blocks(self.nodes)

        self.log.info("\tTesting compactblock requests... ")
        self.test_compactblock_requests(self.nodes[0], self.test_node, 1)
        sync_blocks(self.nodes)
        self.test_compactblock_requests(self.nodes[1], self.ex_softfork_node,
                                        2)
        sync_blocks(self.nodes)

        self.log.info("\tTesting getblocktxn requests...")
        self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
        sync_blocks(self.nodes)
        self.test_getblocktxn_requests(self.nodes[1], self.ex_softfork_node, 2)
        sync_blocks(self.nodes)

        self.log.info("\tTesting getblocktxn handler...")
        self.test_getblocktxn_handler(self.nodes[0], self.test_node, 1)
        sync_blocks(self.nodes)
        self.test_getblocktxn_handler(self.nodes[1], self.ex_softfork_node, 2)
        self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
        sync_blocks(self.nodes)

        self.log.info(
            "\tTesting compactblock requests/announcements not at chain tip..."
        )
        self.test_compactblocks_not_at_tip(self.nodes[0], self.test_node)
        sync_blocks(self.nodes)
        self.test_compactblocks_not_at_tip(self.nodes[1],
                                           self.ex_softfork_node)
        self.test_compactblocks_not_at_tip(self.nodes[1], self.old_node)
        sync_blocks(self.nodes)

        self.log.info("\tTesting handling of incorrect blocktxn responses...")
        self.test_incorrect_blocktxn_response(self.nodes[0], self.test_node, 1)
        sync_blocks(self.nodes)
        self.test_incorrect_blocktxn_response(self.nodes[1],
                                              self.ex_softfork_node, 2)
        sync_blocks(self.nodes)

        # End-to-end block relay tests
        self.log.info("\tTesting end-to-end block relay...")
        self.request_cb_announcements(self.test_node, self.nodes[0])
        self.request_cb_announcements(self.old_node, self.nodes[1])
        self.request_cb_announcements(self.ex_softfork_node,
                                      self.nodes[1],
                                      version=2)
        self.test_end_to_end_block_relay(
            self.nodes[0],
            [self.ex_softfork_node, self.test_node, self.old_node])
        self.test_end_to_end_block_relay(
            self.nodes[1],
            [self.ex_softfork_node, self.test_node, self.old_node])

        self.log.info("\tTesting handling of invalid compact blocks...")
        self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node)
        self.test_invalid_tx_in_compactblock(self.nodes[1],
                                             self.ex_softfork_node)
        self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node)

        self.log.info(
            "\tTesting reconstructing compact blocks from all peers...")
        self.test_compactblock_reconstruction_multiple_peers(
            self.nodes[1], self.ex_softfork_node, self.old_node)
        sync_blocks(self.nodes)

        self.log.info("\tTesting invalid index in cmpctblock message...")
        self.test_invalid_cmpctblock_message()
Esempio n. 20
0
 def run_test(self):
     self.test = TestManager(self, self.options.tmpdir)
     self.test.add_all_connections(self.nodes)
     network_thread_start()
     # Set the blocksize to 2MB as initial condition
     self.test.run()
Esempio n. 21
0
    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 {} blocks".format(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()

        # 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':
            '16: mandatory-script-verify-flag-failed (Non-canonical DER signature)'
        }], self.nodes[0].testmempoolaccept(rawtxs=[ToHex(spendtx)],
                                            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(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)
Esempio n. 22
0
    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 = 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.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 (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_transaction(block1.vtx[0], 0, b'\x51', 50 * COIN)
        tx2 = create_transaction(tx1, 0, b'\x51', 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 = 251 * 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-blk-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_transaction(tx2, 0, b'\x51', 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')

        self.log.info("Test output value > input value out of range")
        # Can be removed when 'feature_block.py' is added to the suite.
        tx4 = create_transaction(tx2, 0, b'\x51', 260 * COIN)
        block4 = create_block(tip, create_coinbase(height), block_time)
        block4.vtx.extend([tx4])
        block4.hashMerkleRoot = block4.calc_merkle_root()
        block4.rehash()
        block4.solve()
        node.p2p.send_blocks_and_test([block4],
                                      node,
                                      success=False,
                                      reject_reason='bad-txns-in-belowout')
    def run_test(self):
        # Setup the p2p connections and start up the network thread.
        # test_node connects to node0 (not whitelisted)
        test_node = self.nodes[0].add_p2p_connection(P2PInterface())
        # min_work_node connects to node1 (whitelisted)
        min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())

        network_thread_start()

        # 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
        self.nodes[0].disconnect_p2ps()
        self.nodes[1].disconnect_p2ps()
        network_thread_join()

        test_node = self.nodes[0].add_p2p_connection(P2PInterface())
        network_thread_start()
        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()

            self.nodes[0].disconnect_p2ps()
            test_node = self.nodes[0].add_p2p_connection(P2PInterface())

            network_thread_start()
            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)
        # FIXME: Uncomment this line once Core backport 015a525 is completed.
        # Current behavior does not ban peers that give us headers on invalid chains.
        # test_node.wait_for_disconnect()

        # 9. Connect node1 to node0 and ensure it is able to sync
        connect_nodes(self.nodes[0], self.nodes[1])
        sync_blocks([self.nodes[0], self.nodes[1]])
        self.log.info("Successfully synced nodes 1 and 0")
Esempio n. 24
0
    def run_test(self):
        node0 = self.nodes[0].add_p2p_connection(P2PInterface())

        network_thread_start()
        node0.wait_for_verack()

        # Set node time to 60 days ago
        self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)

        # Generating a chain of 10 blocks
        block_hashes = self.nodes[0].generate(10)

        # Create longer chain starting 2 blocks before current tip
        height = len(block_hashes) - 2
        block_hash = block_hashes[height - 1]
        block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
        new_blocks = self.build_chain(5, block_hash, height, block_time)

        # Force reorg to a longer chain
        node0.send_message(msg_headers(new_blocks))
        node0.wait_for_getdata()
        for block in new_blocks:
            node0.send_and_ping(msg_block(block))

        # Check that reorg succeeded
        assert_equal(self.nodes[0].getblockcount(), 13)

        stale_hash = int(block_hashes[-1], 16)

        # Check that getdata request for stale block succeeds
        self.send_block_request(stale_hash, node0)
        test_function = lambda: self.last_block_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Check that getheader request for stale block header succeeds
        self.send_header_request(stale_hash, node0)
        test_function = lambda: self.last_header_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Longest chain is extended so stale is much older than chain tip
        self.nodes[0].setmocktime(0)
        tip = self.nodes[0].generate(nblocks=1)[0]
        assert_equal(self.nodes[0].getblockcount(), 14)

        # Send getdata & getheaders to refresh last received getheader message
        block_hash = int(tip, 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        # Request for very old stale block should now fail
        self.send_block_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_block_equals(stale_hash, node0)

        # Request for very old stale block header should now fail
        self.send_header_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_header_equals(stale_hash, node0)

        # Verify we can fetch very old blocks and headers on the active chain
        block_hash = int(block_hashes[2], 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        self.send_block_request(block_hash, node0)
        test_function = lambda: self.last_block_equals(block_hash, node0)
        wait_until(test_function, timeout=3)

        self.send_header_request(block_hash, node0)
        test_function = lambda: self.last_header_equals(block_hash, node0)
        wait_until(test_function, timeout=3)
    def run_test(self):
        node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
        network_thread_start()
        node.wait_for_verack()

        expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED

        self.log.info("Check that node has signalled expected services.")
        assert_equal(node.nServices, expected_services)

        self.log.info("Check that the localservices is as expected.")
        assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services)

        self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
        connect_nodes_bi(self.nodes, 0, 1)
        blocks = self.nodes[1].generate(292)
        sync_blocks([self.nodes[0], self.nodes[1]])

        self.log.info("Make sure we can max retrieve block at tip-288.")
        node.send_getdata_for_block(blocks[1])  # last block in valid range
        node.wait_for_block(int(blocks[1], 16), timeout=3)

        self.log.info("Requesting block at height 2 (tip-289) must fail (ignored).")
        node.send_getdata_for_block(blocks[0])  # first block outside of the 288+2 limit
        node.wait_for_disconnect(5)

        self.log.info("Check local address relay, do a fresh connection.")
        self.nodes[0].disconnect_p2ps()
        network_thread_join()
        node1 = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
        network_thread_start()
        node1.wait_for_verack()
        node1.send_message(msg_verack())

        node1.wait_for_addr()
        #must relay address with NODE_NETWORK_LIMITED
        assert_equal(node1.firstAddrnServices, 1036)

        self.nodes[0].disconnect_p2ps()
        node1.wait_for_disconnect()

        # connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer
        # because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible
        connect_nodes_bi(self.nodes, 0, 2)
        try:
            sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
        except:
            pass
        # node2 must remain at heigh 0
        assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0)

        # now connect also to node 1 (non pruned)
        connect_nodes_bi(self.nodes, 1, 2)

        # sync must be possible
        sync_blocks(self.nodes)

        # disconnect all peers
        self.disconnect_all()

        # mine 10 blocks on node 0 (pruned node)
        self.nodes[0].generate(10)

        # connect node1 (non pruned) with node0 (pruned) and check if the can sync
        connect_nodes_bi(self.nodes, 0, 1)

        # sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
        sync_blocks([self.nodes[0], self.nodes[1]])
Esempio n. 26
0
    def run_test(self):
        """Main test logic"""

        # Create P2P connections to two of the nodes
        self.nodes[0].add_p2p_connection(BaseNode())

        # Start up network handling in another thread. This needs to be called
        # after the P2P connections have been created.
        network_thread_start()
        # wait_for_verack ensures that the P2P connection is fully up.
        self.nodes[0].p2p.wait_for_verack()

        # Generating a block on one of the nodes will get us out of IBD
        blocks = [int(self.nodes[0].generate(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 P2PInterface
            self.nodes[0].p2p.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")
        # We can't add additional P2P connections once the network thread has started. Disconnect the connection
        # to node0, wait for the network thread to terminate, then connect to node2. This is specific to
        # the current implementation of the network thread and may be improved in future.
        self.nodes[0].disconnect_p2ps()
        network_thread_join()

        self.nodes[2].add_p2p_connection(BaseNode())
        network_thread_start()
        self.nodes[2].p2p.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))
        self.nodes[2].p2p.send_message(getdata_request)

        # wait_until() will loop until a predicate condition is met. Use it to test properties of the
        # P2PInterface objects.
        wait_until(lambda: sorted(blocks) == sorted(
            list(self.nodes[2].p2p.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 P2PConnection objects when sending and receiving
        # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
        # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
        with mininode_lock:
            for block in self.nodes[2].p2p.block_receive_map.values():
                assert_equal(block, 1)
Esempio n. 27
0
    def run_test(self):
        node = self.nodes[0]
        node.add_p2p_connection(P2PDataStore())
        network_thread_start()
        node.p2p.wait_for_verack()

        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # shorthand for functions
        block = self.next_block

        # Reference for blocks mined in this test:
        #
        #       11  21   -- 221 - 222
        #      /   /    /
        # 0 - 1 - 2  - 22 - 23 - 24 - 25
        #     \
        #      -- 12 - 13 - 14

        # Generate some valid blocks
        node.p2p.send_blocks_and_test([block(0), block(1), block(2)], node)

        # Explicitly invalidate blocks 1 and 2
        # See below for why we do this
        node.invalidateblock(self.blocks[1].hash)
        assert_equal(self.blocks[0].hash, node.getbestblockhash())
        node.invalidateblock(self.blocks[2].hash)
        assert_equal(self.blocks[0].hash, node.getbestblockhash())

        # Mining on top of blocks 1 or 2 is rejected
        tip(1)
        node.p2p.send_blocks_and_test([block(11)],
                                      node,
                                      success=False,
                                      reject_reason='bad-prevblk',
                                      request_block=False)

        tip(2)
        node.p2p.send_blocks_and_test([block(21)],
                                      node,
                                      success=False,
                                      reject_reason='bad-prevblk',
                                      request_block=False)

        # Reconsider block 2 to remove invalid status from *both* 1 and 2
        # The goal is to test that block 1 is not retaining any internal state
        # that prevents us from accepting blocks building on top of block 1
        node.reconsiderblock(self.blocks[2].hash)
        assert_equal(self.blocks[2].hash, node.getbestblockhash())

        # Mining on the block 1 chain should be accepted
        # (needs to mine two blocks because less-work chains are not processed)
        tip(1)
        node.p2p.send_blocks_and_test([block(12), block(13)], node)

        # Mining on the block 2 chain should still be accepted
        # (needs to mine two blocks because less-work chains are not processed)
        tip(2)
        node.p2p.send_blocks_and_test([block(22), block(221)], node)

        # Mine more blocks from block 22 to be longest chain
        tip(22)
        node.p2p.send_blocks_and_test([block(23), block(24)], node)

        # Sanity checks
        assert_equal(self.blocks[24].hash, node.getbestblockhash())
        assert any(self.blocks[221].hash == chaintip["hash"]
                   for chaintip in node.getchaintips())

        # Invalidating the block 2 chain should reject new blocks on that chain
        node.invalidateblock(self.blocks[2].hash)
        assert_equal(self.blocks[13].hash, node.getbestblockhash())

        # Mining on the block 2 chain should be rejected
        tip(24)
        node.p2p.send_blocks_and_test([block(25)],
                                      node,
                                      success=False,
                                      reject_reason='bad-prevblk',
                                      request_block=False)

        # Continued mining on the block 1 chain is still ok
        tip(13)
        node.p2p.send_blocks_and_test([block(14)], node)

        # Mining on a once-valid chain forking from block 2's longest chain,
        # which is now invalid, should also be rejected.
        tip(221)
        node.p2p.send_blocks_and_test([block(222)],
                                      node,
                                      success=False,
                                      reject_reason='bad-prevblk',
                                      request_block=False)
Esempio n. 28
0
 def run_test(self):
     self.test = TestManager(self, self.options.tmpdir)
     self.test.add_all_connections(self.nodes)
     network_thread_start()
     self.test.run()
Esempio n. 29
0
    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)
Esempio n. 30
0
    def run_test(self):
        self.address = self.nodes[0].getnewaddress()
        self.ms_address = self.nodes[0].addmultisigaddress(
            1, [self.address])['address']
        self.wit_address = self.nodes[0].addwitnessaddress(self.address)
        self.wit_ms_address = self.nodes[0].addmultisigaddress(
            1, [self.address], '', 'p2sh-segwit')['address']

        network_thread_start()
        self.coinbase_blocks = self.nodes[0].generate(2)  # Block 2
        coinbase_txid = []
        for i in self.coinbase_blocks:
            coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
        self.nodes[0].generate(427)  # Block 429
        self.lastblockhash = self.nodes[0].getbestblockhash()
        self.tip = int("0x" + self.lastblockhash, 0)
        self.lastblockheight = 429
        self.lastblocktime = int(time.time()) + 429

        self.log.info(
            "Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]"
        )
        test1txs = [
            self.create_transaction(self.nodes[0], coinbase_txid[0],
                                    self.ms_address, 49)
        ]
        txid1 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[0].serialize_with_witness()), True)
        test1txs.append(
            self.create_transaction(self.nodes[0], txid1, self.ms_address, 48))
        txid2 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[1].serialize_with_witness()), True)
        test1txs.append(
            self.create_transaction(self.nodes[0], coinbase_txid[1],
                                    self.wit_ms_address, 49))
        txid3 = self.nodes[0].sendrawtransaction(
            bytes_to_hex_str(test1txs[2].serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test1txs, False, True)

        self.log.info(
            "Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation"
        )
        test2tx = self.create_transaction(self.nodes[0], txid2,
                                          self.ms_address, 47)
        trueDummy(test2tx)
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test2tx.serialize_with_witness()), True)

        self.log.info(
            "Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]"
        )
        self.block_submit(self.nodes[0], [test2tx], False, True)

        self.log.info(
            "Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation"
        )
        test4tx = self.create_transaction(self.nodes[0], test2tx.hash,
                                          self.address, 46)
        test6txs = [CTransaction(test4tx)]
        trueDummy(test4tx)
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test4tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test4tx])

        self.log.info(
            "Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation"
        )
        test5tx = self.create_transaction(self.nodes[0], txid3,
                                          self.wit_address, 48)
        test6txs.append(CTransaction(test5tx))
        test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
        assert_raises_rpc_error(
            -26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction,
            bytes_to_hex_str(test5tx.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], [test5tx], True)

        self.log.info(
            "Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]"
        )
        for i in test6txs:
            self.nodes[0].sendrawtransaction(
                bytes_to_hex_str(i.serialize_with_witness()), True)
        self.block_submit(self.nodes[0], test6txs, True, True)
Esempio n. 31
0
    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 {} blocks".format(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 = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[0],
                                      self.nodeaddress, 50.0)
        spendtx = cltv_lock_to_height(self.nodes[0], spendtx)

        # Make sure the tx is valid
        self.nodes[0].sendrawtransaction(ToHex(spendtx))

        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 = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[1],
                                      self.nodeaddress, 49.99)
        spendtx = cltv_lock_to_height(self.nodes[0], spendtx)

        # 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()

        # Mine a block containing the funding transaction
        block.vtx.append(spendtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        # But a block containing a transaction spending this utxo is not
        rawspendtx = self.nodes[0].decoderawtransaction(ToHex(spendtx))
        inputs = [{
            "txid": rawspendtx['txid'],
            "vout": rawspendtx['vout'][0]['n']
        }]
        output = {self.nodeaddress: 49.98}

        rejectedtx_raw = self.nodes[0].createrawtransaction(inputs, output)
        rejectedtx_signed = self.nodes[0].signrawtransaction(rejectedtx_raw)

        # Couldn't complete signature due to CLTV
        assert (rejectedtx_signed['errors'][0]['error'] == 'Negative locktime')

        rejectedtx = FromHex(CTransaction(), rejectedtx_signed['hex'])
        pad_tx(rejectedtx)
        rejectedtx.rehash()

        tip = block.hash
        block_time += 1
        block = create_block(block.sha256, create_coinbase(CLTV_HEIGHT + 1),
                             block_time)
        block.nVersion = 4
        block.vtx.append(rejectedtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is invalid
        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 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'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 = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[2],
                                      self.nodeaddress, 49.99)
        spendtx = cltv_lock_to_height(self.nodes[0], spendtx, CLTV_HEIGHT - 1)

        # Modify the transaction in the block to be valid against CLTV
        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))
        # This block is now valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        # A block containing a transaction spending this utxo is also valid
        # Build this transaction
        rawspendtx = self.nodes[0].decoderawtransaction(ToHex(spendtx))
        inputs = [{
            "txid": rawspendtx['txid'],
            "vout": rawspendtx['vout'][0]['n'],
            "sequence": 0
        }]
        output = {self.nodeaddress: 49.98}

        validtx_raw = self.nodes[0].createrawtransaction(
            inputs, output, CLTV_HEIGHT)

        validtx = FromHex(CTransaction(), validtx_raw)

        # Signrawtransaction won't sign a non standard tx.
        # But the prevout being anyone can spend, scriptsig can be left empty
        validtx.vin[0].scriptSig = CScript()
        pad_tx(validtx)
        validtx.rehash()

        tip = block.sha256
        block_time += 1
        block = create_block(tip, create_coinbase(CLTV_HEIGHT + 3), block_time)
        block.nVersion = 4
        block.vtx.append(validtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)
Esempio n. 32
0
    def run_test(self):
        # Before we connect anything, we first set the time on the node
        # to be in the past, otherwise things break because the CNode
        # time counters can't be reset backward after initialization
        old_time = int(time.time() - 2 * 60 * 60 * 24 * 7)
        self.nodes[0].setmocktime(old_time)

        # Generate some old blocks
        self.nodes[0].generate(130)

        # p2p_conns[0] will only request old blocks
        # p2p_conns[1] will only request new blocks
        # p2p_conns[2] will test resetting the counters
        p2p_conns = []

        for _ in range(3):
            p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode()))

        network_thread_start()
        for p2pc in p2p_conns:
            p2pc.wait_for_verack()

        # Test logic begins here

        # Now mine a big block
        mine_big_block(self.nodes[0], self.utxo_cache)

        # Store the hash; we'll request this later
        big_old_block = self.nodes[0].getbestblockhash()
        old_block_size = self.nodes[0].getblock(big_old_block, True)['size']
        big_old_block = int(big_old_block, 16)

        # Advance to two days ago
        self.nodes[0].setmocktime(int(time.time()) - 2 * 60 * 60 * 24)

        # Mine one more block, so that the prior block looks old
        mine_big_block(self.nodes[0], self.utxo_cache)

        # We'll be requesting this new block too
        big_new_block = self.nodes[0].getbestblockhash()
        big_new_block = int(big_new_block, 16)

        # p2p_conns[0] will test what happens if we just keep requesting the
        # the same big old block too many times (expect: disconnect)

        getdata_request = msg_getdata()
        getdata_request.inv.append(CInv(2, big_old_block))

        max_bytes_per_day = 200 * 1024 * 1024
        daily_buffer = 144 * LEGACY_MAX_BLOCK_SIZE
        max_bytes_available = max_bytes_per_day - daily_buffer
        success_count = max_bytes_available // old_block_size

        # 144MB will be reserved for relaying new blocks, so expect this to
        # succeed for ~70 tries.
        for i in range(success_count):
            p2p_conns[0].send_message(getdata_request)
            p2p_conns[0].sync_with_ping()
            assert_equal(p2p_conns[0].block_receive_map[big_old_block], i + 1)

        assert_equal(len(self.nodes[0].getpeerinfo()), 3)
        # At most a couple more tries should succeed (depending on how long
        # the test has been running so far).
        for i in range(3):
            p2p_conns[0].send_message(getdata_request)
        p2p_conns[0].wait_for_disconnect()
        assert_equal(len(self.nodes[0].getpeerinfo()), 2)
        self.log.info(
            "Peer 0 disconnected after downloading old block too many times")

        # Requesting the current block on p2p_conns[1] should succeed indefinitely,
        # even when over the max upload target.
        # We'll try 200 times
        getdata_request.inv = [CInv(2, big_new_block)]
        for i in range(200):
            p2p_conns[1].send_message(getdata_request)
            p2p_conns[1].sync_with_ping()
            assert_equal(p2p_conns[1].block_receive_map[big_new_block], i + 1)

        self.log.info("Peer 1 able to repeatedly download new block")

        # But if p2p_conns[1] tries for an old block, it gets disconnected
        # too.
        getdata_request.inv = [CInv(2, big_old_block)]
        p2p_conns[1].send_message(getdata_request)
        p2p_conns[1].wait_for_disconnect()
        assert_equal(len(self.nodes[0].getpeerinfo()), 1)

        self.log.info("Peer 1 disconnected after trying to download old block")

        self.log.info("Advancing system time on node to clear counters...")

        # If we advance the time by 24 hours, then the counters should reset,
        # and p2p_conns[2] should be able to retrieve the old block.
        self.nodes[0].setmocktime(int(time.time()))
        p2p_conns[2].sync_with_ping()
        p2p_conns[2].send_message(getdata_request)
        p2p_conns[2].sync_with_ping()
        assert_equal(p2p_conns[2].block_receive_map[big_old_block], 1)

        self.log.info("Peer 2 able to download old block")

        self.nodes[0].disconnect_p2ps()

        # stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
        self.log.info("Restarting nodes with -whitelist=127.0.0.1")
        self.stop_node(0)
        self.start_node(0, [
            "-whitelist=127.0.0.1", "-maxuploadtarget=1",
            "-blockmaxsize=999000"
        ])

        # Reconnect to self.nodes[0]
        self.nodes[0].add_p2p_connection(TestNode())

        network_thread_start()
        self.nodes[0].p2p.wait_for_verack()

        # retrieve 20 blocks which should be enough to break the 1MB limit
        getdata_request.inv = [CInv(2, big_new_block)]
        for i in range(20):
            self.nodes[0].p2p.send_message(getdata_request)
            self.nodes[0].p2p.sync_with_ping()
            assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block],
                         i + 1)

        getdata_request.inv = [CInv(2, big_old_block)]
        self.nodes[0].p2p.send_and_ping(getdata_request)
        # node is still connected because of the whitelist
        assert_equal(len(self.nodes[0].getpeerinfo()), 1)

        self.log.info(
            "Peer still connected after trying to download old block (whitelisted)"
        )
Esempio n. 33
0
    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 {} blocks".format(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"
        )

        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[0],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        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(fundtx)
        # include the -1 CLTV in block
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        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

        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[1],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        # The funding tx only has unexecuted bad CLTV, in scriptpubkey; this is valid.
        self.nodes[0].p2p.send_and_ping(msg_tx(fundtx))
        assert fundtx.hash in self.nodes[0].getrawmempool()

        # Mine a block containing the funding transaction
        block.vtx.append(fundtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        # We show that this tx is invalid due to 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=[spendtx.serialize().hex()],
                                            allowhighfees=True))

        rejectedtx_signed = self.nodes[0].signrawtransactionwithwallet(
            ToHex(spendtx))

        # Couldn't complete signature due to CLTV
        assert (rejectedtx_signed['errors'][0]['error'] == 'Negative locktime')

        tip = block.hash
        block_time += 1
        block = create_block(block.sha256, create_coinbase(CLTV_HEIGHT + 1),
                             block_time)
        block.nVersion = 4
        block.vtx.append(spendtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is invalid
        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 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'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"
        )
        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[2],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98,
                                              CLTV_HEIGHT)

        # make sure sequence is nonfinal and locktime is good
        spendtx.vin[0].nSequence = 0xfffffffe
        spendtx.nLockTime = CLTV_HEIGHT

        # both transactions are fully valid
        self.nodes[0].sendrawtransaction(ToHex(fundtx))
        self.nodes[0].sendrawtransaction(ToHex(spendtx))

        # Modify the transactions in the block to be valid against CLTV
        block.vtx.pop(1)
        block.vtx.append(fundtx)
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is now valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)
Esempio n. 34
0
    def test_sync_with_restarts(self):
        """
        This test creates the following nodes:
        1. snap_node - full node that has the the snapshot
        2. snap_p2p - mini node that is used as a helper to retrieve the snapshot content
        3. node - the node which syncs the snapshot
        4. p2p - mini node that sends snapshot in stages
        """
        snap_node = self.nodes[2]
        node = self.nodes[3]

        self.start_node(snap_node.index)
        self.start_node(node.index)

        self.setup_stake_coins(snap_node)

        # generate 2 epochs + 1 block to create the first finalized snapshot
        snap_node.generatetoaddress(5 + 5 + 1,
                                    snap_node.getnewaddress('', 'bech32'))
        assert_equal(snap_node.getblockcount(), 11)
        wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10)

        # configure p2p to have snapshot header and parent block
        p2p = node.add_p2p_connection(WaitNode(),
                                      services=SERVICE_FLAGS_WITH_SNAPSHOT)
        p2p.update_snapshot_from(snap_node)
        p2p.update_headers_and_blocks_from(snap_node)

        network_thread_start()

        # test 1. the node can be restarted after it discovered the snapshot
        wait_until(lambda: p2p.snapshot_chunk1_requested, timeout=10)
        node.disconnect_p2ps()
        network_thread_join()
        self.restart_node(node.index)
        self.log.info(
            'Node restarted successfully after it discovered the snapshot')

        # test 2. the node can be restarted after it downloaded half of the snapshot
        # and deletes it's partially downloaded snapshot
        p2p.return_snapshot_chunk1 = True
        node.add_p2p_connection(p2p, services=SERVICE_FLAGS_WITH_SNAPSHOT)
        network_thread_start()
        wait_until(lambda: p2p.snapshot_chunk2_requested, timeout=10)
        node.disconnect_p2ps()
        network_thread_join()
        assert_has_snapshot_on_disk(node, p2p.snapshot_header.snapshot_hash)
        self.restart_node(node.index)
        assert_no_snapshot_on_disk(node, p2p.snapshot_header.snapshot_hash)
        assert_equal(
            len(os.listdir(os.path.join(node.datadir, "regtest",
                                        "snapshots"))), 0)
        self.log.info(
            'Node restarted successfully after it downloaded half of the snapshot'
        )

        # test 3. the node can be restarted after it downloaded the full snapshot
        # and doesn't delete it
        p2p.return_snapshot_chunk2 = True
        node.add_p2p_connection(p2p, services=SERVICE_FLAGS_WITH_SNAPSHOT)
        network_thread_start()
        wait_until(lambda: p2p.parent_block_requested, timeout=10)
        node.disconnect_p2ps()
        network_thread_join()
        assert_has_snapshot_on_disk(node, p2p.snapshot_header.snapshot_hash)
        self.restart_node(node.index)
        assert_has_snapshot_on_disk(node, p2p.snapshot_header.snapshot_hash)
        self.log.info(
            'Node restarted successfully after it downloaded the full snapshot'
        )

        # test 4. the node can be restarted after it downloaded the parent block
        p2p.snapshot_header_requested = False
        p2p.snapshot_chunk1_requested = False
        p2p.snapshot_chunk2_requested = False
        p2p.return_parent_block = True
        node.add_p2p_connection(p2p, services=SERVICE_FLAGS_WITH_SNAPSHOT)
        network_thread_start()
        wait_until(lambda: node.getblockcount() == snap_node.getblockcount(),
                   timeout=10)
        assert_chainstate_equal(node, snap_node)

        # node didn't request a new snapshot as it already downloaded the one
        assert_equal(p2p.snapshot_header_requested, False)
        assert_equal(p2p.snapshot_chunk1_requested, False)
        assert_equal(p2p.snapshot_chunk2_requested, False)

        node.disconnect_p2ps()
        network_thread_join()
        self.restart_node(node.index)
        self.restart_node(snap_node.index)
        assert_chainstate_equal(node, snap_node)
        assert_equal(node.listsnapshots(), snap_node.listsnapshots())
        self.log.info(
            'Node restarted successfully after it downloaded the parent block')

        # clean up test
        self.stop_node(snap_node.index)
        self.stop_node(node.index)
        self.log.info('test_sync_with_restarts passed')
Esempio n. 35
0
    def run_test(self):
        """Main test logic"""

        # Create P2P connections to two of the nodes
        self.nodes[0].add_p2p_connection(BaseNode())

        # Start up network handling in another thread. This needs to be called
        # after the P2P connections have been created.
        network_thread_start()
        # wait_for_verack ensures that the P2P connection is fully up.
        self.nodes[0].p2p.wait_for_verack()

        # Generating a block on one of the nodes will get us out of IBD
        blocks = [int(self.nodes[0].generate(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 P2PInterface
            self.nodes[0].p2p.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")
        # We can't add additional P2P connections once the network thread has started. Disconnect the connection
        # to node0, wait for the network thread to terminate, then connect to node2. This is specific to
        # the current implementation of the network thread and may be improved in future.
        self.nodes[0].disconnect_p2ps()
        network_thread_join()

        self.nodes[2].add_p2p_connection(BaseNode())
        network_thread_start()
        self.nodes[2].p2p.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))
        self.nodes[2].p2p.send_message(getdata_request)

        # wait_until() will loop until a predicate condition is met. Use it to test properties of the
        # P2PInterface objects.
        wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.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 P2PConnection objects when sending and receiving
        # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
        # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
        with mininode_lock:
            for block in self.nodes[2].p2p.block_receive_map.values():
                assert_equal(block, 1)
    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)
        network_thread_start()
        self.test.p2p_connections[0].wait_for_verack()
Esempio n. 37
0
    def test_invalid_snapshot(self):
        """
        This test creates the following nodes:
        1. snap_node - full node that has the the snapshot
        2. snap_p2p - mini node that is used as a helper to retrieve the snapshot content
        3. node - the node which syncs the snapshot
        4. broken_p2p - mini node that claims has the best snapshot but it's broken
        5. valid_p2p - mini node that sends a valid snapshot
        6. not_finalized_p2p - mini node that claims has the best snapshot but it's not finalized
        """

        snap_node = self.nodes[4]
        node = self.nodes[5]

        self.start_node(snap_node.index)
        self.start_node(node.index)

        self.setup_stake_coins(snap_node)

        # generate 2 epochs + 1 block to create the first finalized snapshot
        # and store it in valid_p2p
        snap_node.generatetoaddress(5 + 5 + 1,
                                    snap_node.getnewaddress('', 'bech32'))
        assert_equal(snap_node.getblockcount(), 11)
        wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10)

        valid_p2p = node.add_p2p_connection(
            WaitNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)
        valid_p2p.update_snapshot_from(snap_node)

        # create the second snapshot and store it in broken_p2p
        snap_node.generatetoaddress(5, snap_node.getnewaddress('', 'bech32'))
        assert_equal(snap_node.getblockcount(), 16)
        wait_until(lambda: has_valid_snapshot(snap_node, 9), timeout=10)

        broken_p2p = node.add_p2p_connection(
            WaitNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)
        broken_p2p.update_snapshot_from(snap_node)
        broken_p2p.snapshot_data[-1].outputs[0].nValue *= 2  # break snapshot
        broken_p2p.update_headers_and_blocks_from(snap_node)
        valid_p2p.update_headers_and_blocks_from(snap_node)

        not_finalized_p2p = node.add_p2p_connection(
            WaitNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)
        not_finalized_p2p.update_snapshot_from(snap_node, finalized=False)
        not_finalized_p2p.update_headers_and_blocks_from(snap_node)

        network_thread_start()

        # make sure that node knows about all the peers
        valid_p2p.wait_for_verack()
        broken_p2p.wait_for_verack()
        not_finalized_p2p.wait_for_verack()

        # node must pick the best snapshot
        wait_until(lambda: broken_p2p.snapshot_chunk1_requested, timeout=10)
        broken_p2p.return_snapshot_chunk1 = True
        broken_p2p.on_getsnapshot(broken_p2p.last_getsnapshot_message)
        wait_until(lambda: broken_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(node,
                                    broken_p2p.snapshot_header.snapshot_hash)
        assert_no_snapshot_on_disk(node,
                                   valid_p2p.snapshot_header.snapshot_hash)
        assert_equal(valid_p2p.snapshot_chunk1_requested, False)

        # node detects broken snapshot, removes it and switches to the second best
        broken_p2p.return_snapshot_chunk2 = True
        broken_p2p.on_getsnapshot(broken_p2p.last_getsnapshot_message)
        wait_until(lambda: valid_p2p.snapshot_chunk1_requested, timeout=10)
        assert_no_snapshot_on_disk(node,
                                   broken_p2p.snapshot_header.snapshot_hash)
        valid_p2p.return_snapshot_chunk1 = True
        valid_p2p.on_getsnapshot(valid_p2p.last_getsnapshot_message)
        wait_until(lambda: valid_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(node,
                                    valid_p2p.snapshot_header.snapshot_hash)
        valid_p2p.return_snapshot_chunk2 = True
        valid_p2p.return_parent_block = True
        valid_p2p.on_getsnapshot(valid_p2p.last_getsnapshot_message)

        # node doesn't request not finalized snapshot
        assert_equal(not_finalized_p2p.snapshot_header_requested, True)
        assert_equal(not_finalized_p2p.snapshot_chunk1_requested, False)

        # node requests parent block and finishes ISD
        wait_until(lambda: node.getblockcount() == 16, timeout=20)
        node.disconnect_p2ps()
        assert_chainstate_equal(snap_node, node)

        self.log.info('test_invalid_snapshot passed')
Esempio n. 38
0
    def run_test(self):
        self.nodes[0].add_p2p_connection(P2PDataStore())
        network_thread_start()
        self.nodes[0].p2p.wait_for_verack()

        self.log.info("Generate blocks in the past for coinbase outputs.")
        self.coinbase_blocks = self.nodes[0].generate(
            1 + 16 + 2 * 32 + 1)  # 82 blocks generated for inputs
        # set time so that there was enough time to build up to 1000 blocks 10 minutes apart on top of the last one
        # without worrying about getting into the future
        self.nodes[0].setmocktime(GENESISTIME + 600 * 1000 + 100)
        self.tipheight = 82  # height of the next block to build
        self.last_block_time = GENESISTIME
        self.tip = int(self.nodes[0].getbestblockhash(), 16)
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info("Test that the csv softfork is DEFINED")
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
        test_blocks = self.generate_blocks(61, 4)
        self.send_blocks(test_blocks)

        self.log.info("Advance from DEFINED to STARTED, height = 143")
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        self.log.info("Fail to achieve LOCKED_IN")
        # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks

        test_blocks = self.generate_blocks(
            50, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            20, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            24, 536936448, test_blocks)  # 0x20010000 (signalling not)
        self.send_blocks(test_blocks)

        self.log.info("Failed to advance past STARTED, height = 287")
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        self.log.info("Generate blocks to achieve LOCK-IN")
        # 108 out of 144 signal bit 0 to achieve lock-in
        # using a variety of bits to simulate multiple parallel softforks
        test_blocks = self.generate_blocks(
            58, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            26, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            10, 536936448, test_blocks)  # 0x20010000 (signalling not)
        self.send_blocks(test_blocks)

        self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        # Generate 140 more version 4 blocks
        test_blocks = self.generate_blocks(140, 4)
        self.send_blocks(test_blocks)

        # Inputs at height = 572
        #
        # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
        # Note we reuse inputs for v1 and v2 txs so must test these separately
        # 16 normal inputs
        bip68inputs = []
        for i in range(16):
            bip68inputs.append(
                send_generic_input_tx(self.nodes[0], self.coinbase_blocks,
                                      self.nodeaddress))

        # 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112basicinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(
                    send_generic_input_tx(self.nodes[0], self.coinbase_blocks,
                                          self.nodeaddress))
            bip112basicinputs.append(inputs)

        # 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112diverseinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(
                    send_generic_input_tx(self.nodes[0], self.coinbase_blocks,
                                          self.nodeaddress))
            bip112diverseinputs.append(inputs)

        # 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112specialinput = send_generic_input_tx(self.nodes[0],
                                                   self.coinbase_blocks,
                                                   self.nodeaddress)

        # 1 normal input
        bip113input = send_generic_input_tx(self.nodes[0],
                                            self.coinbase_blocks,
                                            self.nodeaddress)

        self.nodes[0].setmocktime(self.last_block_time + 600)
        inputblockhash = self.nodes[0].generate(1)[
            0]  # 1 block generated for inputs to be in chain at height 572
        self.nodes[0].setmocktime(GENESISTIME + 600 * 1000 + 100)
        self.tip = int(inputblockhash, 16)
        self.tipheight += 1
        self.last_block_time += 600
        assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]),
                     82 + 1)

        # 2 more version 4 blocks
        test_blocks = self.generate_blocks(2, 4)
        self.send_blocks(test_blocks)

        self.log.info(
            "Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)"
        )
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        # Test both version 1 and version 2 transactions for all tests
        # BIP113 test transaction will be modified before each use to put in appropriate block time
        bip113tx_v1 = create_transaction(self.nodes[0], bip113input,
                                         self.nodeaddress, Decimal("499.98"))
        bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v1.nVersion = 1
        bip113tx_v2 = create_transaction(self.nodes[0], bip113input,
                                         self.nodeaddress, Decimal("499.98"))
        bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v2.nVersion = 2

        # For BIP68 test all 16 relative sequence locktimes
        bip68txs_v1 = create_bip68txs(self.nodes[0], bip68inputs, 1,
                                      self.nodeaddress)
        bip68txs_v2 = create_bip68txs(self.nodes[0], bip68inputs, 2,
                                      self.nodeaddress)

        # For BIP112 test:
        # 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_v1 = create_bip112txs(self.nodes[0],
                                                       bip112basicinputs[0],
                                                       False, 1,
                                                       self.nodeaddress)
        bip112txs_vary_nSequence_v2 = create_bip112txs(self.nodes[0],
                                                       bip112basicinputs[0],
                                                       False, 2,
                                                       self.nodeaddress)
        # 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_9_v1 = create_bip112txs(
            self.nodes[0], bip112basicinputs[1], False, 1, self.nodeaddress,
            -1)
        bip112txs_vary_nSequence_9_v2 = create_bip112txs(
            self.nodes[0], bip112basicinputs[1], False, 2, self.nodeaddress,
            -1)
        # sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_v1 = create_bip112txs(self.nodes[0],
                                                    bip112diverseinputs[0],
                                                    True, 1, self.nodeaddress)
        bip112txs_vary_OP_CSV_v2 = create_bip112txs(self.nodes[0],
                                                    bip112diverseinputs[0],
                                                    True, 2, self.nodeaddress)
        # sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_9_v1 = create_bip112txs(self.nodes[0],
                                                      bip112diverseinputs[1],
                                                      True, 1,
                                                      self.nodeaddress, -1)
        bip112txs_vary_OP_CSV_9_v2 = create_bip112txs(self.nodes[0],
                                                      bip112diverseinputs[1],
                                                      True, 2,
                                                      self.nodeaddress, -1)
        # -1 OP_CSV OP_DROP input
        bip112tx_special_v1 = create_bip112special(self.nodes[0],
                                                   bip112specialinput, 1,
                                                   self.nodeaddress)
        bip112tx_special_v2 = create_bip112special(self.nodes[0],
                                                   bip112specialinput, 2,
                                                   self.nodeaddress)

        self.log.info("TESTING")

        self.log.info("Pre-Soft Fork Tests. All txs should pass.")
        self.log.info("Test version 1 txs")

        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        success_txs.append(bip113signed1)
        success_txs.append(bip112tx_special_v1)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v1))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v1))
        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("Test version 2 txs")

        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        success_txs.append(bip113signed2)
        success_txs.append(bip112tx_special_v2)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v2))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v2))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v2))
        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
        test_blocks = self.generate_blocks(1, 4)
        self.send_blocks(test_blocks)
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')

        self.log.info("Post-Soft Fork Tests.")

        self.log.info("BIP 113 tests")
        # BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            self.send_blocks([self.create_test_block([bip113tx])],
                             success=False)
        # BIP 113 tests should now pass if the locktime is < MTP
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            self.send_blocks([self.create_test_block([bip113tx])])
            self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # Next block height = 580 after 4 blocks of random version
        test_blocks = self.generate_blocks(4, 1234)
        self.send_blocks(test_blocks)

        self.log.info("BIP 68 tests")
        self.log.info("Test version 1 txs - all should still pass")

        success_txs = []
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("Test version 2 txs")

        # All txs with SEQUENCE_LOCKTIME_DISABLE_FLAG set pass
        bip68success_txs = [tx['tx'] for tx in bip68txs_v2 if tx['sdf']]
        self.send_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
        bip68timetxs = [
            tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and tx['stf']
        ]
        for tx in bip68timetxs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        bip68heighttxs = [
            tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and not tx['stf']
        ]
        for tx in bip68heighttxs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        # Advance one block to 581
        test_blocks = self.generate_blocks(1, 1234)
        self.send_blocks(test_blocks)

        # Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
        bip68success_txs.extend(bip68timetxs)
        self.send_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        for tx in bip68heighttxs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        # Advance one block to 582
        test_blocks = self.generate_blocks(1, 1234)
        self.send_blocks(test_blocks)

        # All BIP 68 txs should pass
        bip68success_txs.extend(bip68heighttxs)
        self.send_blocks([self.create_test_block(bip68success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        self.log.info("BIP 112 tests")
        self.log.info("Test version 1 txs")

        # -1 OP_CSV tx should fail
        self.send_blocks([self.create_test_block([bip112tx_special_v1])],
                         success=False)
        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass

        success_txs = [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']
        ]
        success_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if tx['sdf']
        ]
        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
        fail_txs = all_rlt_txs(bip112txs_vary_nSequence_v1)
        fail_txs += all_rlt_txs(bip112txs_vary_nSequence_9_v1)
        fail_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']
        ]
        fail_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']
        ]
        for tx in fail_txs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        self.log.info("Test version 2 txs")

        # -1 OP_CSV tx should fail
        self.send_blocks([self.create_test_block([bip112tx_special_v2])],
                         success=False)

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
        success_txs = [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']
        ]
        success_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if tx['sdf']
        ]

        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##

        # All txs with nSequence 9 should fail either due to earlier mismatch or failing the CSV check
        fail_txs = all_rlt_txs(bip112txs_vary_nSequence_9_v2)
        fail_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']
        ]
        for tx in fail_txs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
        fail_txs = [
            tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']
        ]
        for tx in fail_txs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        # If sequencelock types mismatch, tx should fail
        fail_txs = [
            tx['tx'] for tx in bip112txs_vary_nSequence_v2
            if not tx['sdf'] and tx['stf']
        ]
        fail_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_v2
            if not tx['sdf'] and tx['stf']
        ]
        for tx in fail_txs:
            self.send_blocks([self.create_test_block([tx])], success=False)

        # Remaining txs should pass, just test masking works properly
        success_txs = [
            tx['tx'] for tx in bip112txs_vary_nSequence_v2
            if not tx['sdf'] and not tx['stf']
        ]
        success_txs += [
            tx['tx'] for tx in bip112txs_vary_OP_CSV_v2
            if not tx['sdf'] and not tx['stf']
        ]
        self.send_blocks([self.create_test_block(success_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())

        # Additional test, of checking that comparison of two time types works properly
        time_txs = []
        for tx in [
                tx['tx'] for tx in bip112txs_vary_OP_CSV_v2
                if not tx['sdf'] and tx['stf']
        ]:
            tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
            signtx = sign_transaction(self.nodes[0], tx)
            time_txs.append(signtx)

        self.send_blocks([self.create_test_block(time_txs)])
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
Esempio n. 39
0
    def test_cannot_sync_with_snapshot(self):
        """
        This test creates the following nodes:
        1. snap_node - snapshot node that is used as a helper node to generate the snapshot
        2. helper_p2p - mini node that retrieves the content of the snapshot
        3. full_snap_p2p - mini node that has full 2nd best snapshot
        3. half_snap_p2p - mini node that has half of the best snapshot
        4. no_snap_p2p - mini node that doesn't have snapshot
        5. sync_node - the node which syncs with the snapshot
        """
        snap_node = self.nodes[6]
        sync_node = self.nodes[7]
        self.start_node(snap_node.index)
        self.start_node(sync_node.index)

        self.setup_stake_coins(snap_node)

        # add 2nd best snapshot to full_snap_p2p
        snap_node.generatetoaddress(5 + 5 + 1,
                                    snap_node.getnewaddress('', 'bech32'))
        assert_equal(snap_node.getblockcount(), 11)
        wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10)
        full_snap_p2p = sync_node.add_p2p_connection(
            WaitNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)
        no_snap_p2p = sync_node.add_p2p_connection(WaitNode())
        for p2p in [full_snap_p2p, no_snap_p2p]:
            p2p.update_snapshot_from(snap_node)

        # add the best snapshot to half_snap_p2p
        snap_node.generatetoaddress(5, snap_node.getnewaddress('', 'bech32'))
        assert_equal(snap_node.getblockcount(), 16)
        wait_until(lambda: has_valid_snapshot(snap_node, 9), timeout=10)
        half_snap_p2p = sync_node.add_p2p_connection(
            WaitNode(), services=SERVICE_FLAGS_WITH_SNAPSHOT)
        half_snap_p2p.update_snapshot_from(snap_node)
        for p2p in [half_snap_p2p, full_snap_p2p, no_snap_p2p]:
            p2p.update_headers_and_blocks_from(snap_node)

        self.stop_node(snap_node.index)

        network_thread_start()

        # test 1. the node requests snapshot from peers that have service flag set
        full_snap_p2p.wait_for_verack()
        half_snap_p2p.wait_for_verack()
        no_snap_p2p.wait_for_verack()

        wait_until(lambda: full_snap_p2p.snapshot_header_requested, timeout=10)
        wait_until(lambda: half_snap_p2p.snapshot_header_requested, timeout=10)
        wait_until(lambda: half_snap_p2p.snapshot_chunk1_requested, timeout=10)
        assert full_snap_p2p.snapshot_header_requested is True
        assert half_snap_p2p.snapshot_header_requested is True
        assert no_snap_p2p.snapshot_header_requested is False
        assert full_snap_p2p.snapshot_chunk1_requested is False  # didn't start asking for the 2nd best
        self.log.info('Service flag are correctly recognized')

        # test 2. the node can't receive the 2nd part of the snapshot
        half_snap_p2p.return_snapshot_chunk1 = True
        half_snap_p2p.on_getsnapshot(half_snap_p2p.last_getsnapshot_message)
        wait_until(lambda: half_snap_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(
            sync_node, half_snap_p2p.snapshot_header.snapshot_hash)
        wait_until(lambda: full_snap_p2p.snapshot_chunk1_requested,
                   timeout=10)  # fallback to 2nd best
        assert_no_snapshot_on_disk(sync_node,
                                   half_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info('Node cannot receive 2nd half of the snapshot')

        # test 3. the node can't receive the parent block
        full_snap_p2p.return_snapshot_chunk1 = True
        full_snap_p2p.return_snapshot_chunk2 = True
        full_snap_p2p.on_getsnapshot(full_snap_p2p.last_getsnapshot_message)
        wait_until(lambda: full_snap_p2p.parent_block_requested, timeout=10)
        wait_until(lambda: no_snap_p2p.parent_block_requested, timeout=10)
        assert_has_snapshot_on_disk(
            sync_node, full_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info(
            'Node cannot receive parent block from already connected peers')

        # test 4. the node can't receive the parent block from new peers
        sync_node.disconnect_p2ps()
        network_thread_join()

        for p2p in [full_snap_p2p, no_snap_p2p]:
            p2p.snapshot_chunk1_requested = False
            p2p.snapshot_chunk2_requested = False
            p2p.parent_block_requested = False

        sync_node.add_p2p_connection(full_snap_p2p)
        sync_node.add_p2p_connection(no_snap_p2p)
        network_thread_start()
        full_snap_p2p.wait_for_verack()
        no_snap_p2p.wait_for_verack()

        wait_until(lambda: full_snap_p2p.parent_block_requested, timeout=10)
        wait_until(lambda: no_snap_p2p.parent_block_requested, timeout=10)
        assert full_snap_p2p.snapshot_chunk1_requested is False
        assert no_snap_p2p.snapshot_chunk1_requested is False
        assert_has_snapshot_on_disk(
            sync_node, full_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info('Node cannot receive parent block from new peers')

        self.stop_node(sync_node.index)
        network_thread_join()

        self.log.info('test_cannot_sync_with_snapshot passed')
Esempio n. 40
0
    def run_test(self):
        def create_justification(fork, finalizer, after_blocks):
            fork.generatetoaddress(after_blocks - 1,
                                   fork.getnewaddress('', 'bech32'))
            self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork)
            fork.generatetoaddress(1, fork.getnewaddress('', 'bech32'))
            assert_equal(len(fork.getrawmempool()), 0)

        def sync_node_to_fork(node, fork):
            connect_nodes(node, fork.index)
            block_hash = fork.getblockhash(fork.getblockcount())
            node.waitforblock(block_hash, 5000)
            assert_equal(node.getblockhash(node.getblockcount()), block_hash)
            disconnect_nodes(node, fork.index)

        def wait_for_reject(p2p, err, block):
            wait_until(lambda: p2p.has_reject(err, block), timeout=5)

        # Two validators (but actually having the same key) produce parallel justifications
        # node must always follow the longest justified fork
        # finalizer1 -> fork1
        #             /
        #           node
        #             \
        # finalizer2 -> fork2
        node = self.nodes[0]
        fork1 = self.nodes[1]
        fork2 = self.nodes[2]
        finalizer1 = self.nodes[3]
        finalizer2 = self.nodes[4]

        node.importmasterkey(regtest_mnemonics[0]['mnemonics'])
        finalizer1.importmasterkey(regtest_mnemonics[1]['mnemonics'])
        finalizer2.importmasterkey(regtest_mnemonics[1]['mnemonics'])
        fork1.importmasterkey(regtest_mnemonics[2]['mnemonics'])
        fork2.importmasterkey(regtest_mnemonics[2]['mnemonics'])

        # create network topology
        connect_nodes(node, fork1.index)
        connect_nodes(node, fork2.index)
        connect_nodes(finalizer1, fork1.index)
        connect_nodes(finalizer2, fork2.index)

        # leave IBD
        node.generatetoaddress(2, node.getnewaddress('', 'bech32'))
        sync_blocks([node, fork1, fork2, finalizer1, finalizer2])

        # Do not let finalizer2 to see deposit from finalizer1
        disconnect_nodes(node, fork2.index)

        payto = finalizer1.getnewaddress('', 'legacy')
        txid1 = finalizer1.deposit(payto, 1500)
        finalizer2.setaccount(payto, '')
        txid2 = finalizer2.deposit(payto, 1500)
        if txid1 != txid2:  # improve log message
            tx1 = FromHex(CTransaction(), finalizer1.getrawtransaction(txid1))
            tx2 = FromHex(CTransaction(), finalizer2.getrawtransaction(txid2))
            print(tx1)
            print(tx2)
            assert_equal(txid1, txid2)

        # Connect back
        connect_nodes(node, fork2.index)

        self.wait_for_transaction(txid1, timeout=150)

        node.generatetoaddress(1, node.getnewaddress('', 'bech32'))
        sync_blocks([node, fork1, fork2])

        disconnect_nodes(node, fork1.index)
        disconnect_nodes(node, fork2.index)
        disconnect_nodes(finalizer1, fork1.index)
        disconnect_nodes(finalizer2, fork2.index)

        # create common 5 epochs to leave instant finalization
        #                             fork1
        # F    F    F    F    J      /
        # e0 - e1 - e2 - e3 - e4 - e5 node
        #                            \
        #                             fork2
        node.generatetoaddress(22, node.getnewaddress('', 'bech32'))
        assert_equal(node.getblockcount(), 25)
        assert_finalizationstate(
            node, {
                'currentDynasty': 2,
                'currentEpoch': 5,
                'lastJustifiedEpoch': 4,
                'lastFinalizedEpoch': 3,
                'validators': 0
            })

        connect_nodes(node, fork1.index)
        connect_nodes(node, fork2.index)
        sync_blocks([node, fork1, fork2])
        disconnect_nodes(node, fork1.index)
        disconnect_nodes(node, fork2.index)

        # create fist justified epoch on fork1
        # node must follow this fork
        #
        #                             - e6 fork1, node
        # F    F    F    F    J    * /
        # e0 - e1 - e2 - e3 - e4 - e5
        #                            \
        #                             fork2
        # e4 is finalized for fork1
        # e5 is justified for fork1
        create_justification(fork=fork1, finalizer=finalizer1, after_blocks=2)
        assert_equal(fork1.getblockcount(), 27)
        assert_finalizationstate(
            fork1, {
                'currentDynasty': 3,
                'currentEpoch': 6,
                'lastJustifiedEpoch': 5,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        sync_node_to_fork(node, fork1)

        assert_finalizationstate(
            node, {
                'currentDynasty': 3,
                'currentEpoch': 6,
                'lastJustifiedEpoch': 5,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        self.log.info('node successfully switched to the justified fork')

        # create longer justified epoch on fork2
        # node must switch ("zig") to this fork
        #
        #                             - e6 fork1
        # F    F    F    F    F    J /
        # e0 - e1 - e2 - e3 - e4 - e5
        #                            \       J
        #                             - e6 - e7 - e8 fork2, node
        create_justification(fork=fork2, finalizer=finalizer2, after_blocks=2)
        assert_equal(fork2.getblockcount(), 27)
        assert_finalizationstate(
            fork2, {
                'currentDynasty': 3,
                'currentEpoch': 6,
                'lastJustifiedEpoch': 5,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        create_justification(fork=fork2, finalizer=finalizer2, after_blocks=10)
        assert_equal(fork2.getblockcount(), 37)
        assert_finalizationstate(
            fork2, {
                'currentDynasty': 4,
                'currentEpoch': 8,
                'lastJustifiedEpoch': 7,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        sync_node_to_fork(node, fork2)

        assert_finalizationstate(
            node, {
                'currentDynasty': 4,
                'currentEpoch': 8,
                'lastJustifiedEpoch': 7,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        self.log.info(
            'node successfully switched to the longest justified fork')

        # create longer justified epoch on the previous fork1
        # node must switch ("zag") to this fork
        #                                         J
        #                             - e6 - e7 - e8 - e9 fork1, node
        # F    F    F    F    F    J /
        # e0 - e1 - e2 - e3 - e4 - e5
        #                            \       J
        #                             - e6 - e7 - e8 fork2
        create_justification(fork=fork1, finalizer=finalizer1, after_blocks=16)
        assert_equal(fork1.getblockcount(), 43)
        assert_finalizationstate(
            fork1, {
                'currentDynasty': 4,
                'currentEpoch': 9,
                'lastJustifiedEpoch': 8,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        sync_node_to_fork(node, fork1)

        assert_finalizationstate(
            node, {
                'currentDynasty': 4,
                'currentEpoch': 9,
                'lastJustifiedEpoch': 8,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        self.log.info(
            'node successfully switched back to the longest justified fork')

        # test that re-org before finalization is not possible
        #                                         J               J*
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork1
        # F    F    F    F    F    J /                                      |
        # e0 - e1 - e2 - e3 - e4 - e5                                       56] node
        #                            \       J
        #                             - e6 - e7 - e8 fork2
        # e11 is not justified for node
        known_fork1_height = fork1.getblockcount()
        assert_equal(node.getblockcount(), known_fork1_height)

        known_fork1_hash = fork1.getblockhash(known_fork1_height)
        assert_equal(node.getblockhash(known_fork1_height), known_fork1_hash)
        create_justification(fork=fork1, finalizer=finalizer1, after_blocks=14)

        assert_equal(fork1.getblockcount(), 57)
        assert_finalizationstate(
            fork1, {
                'currentDynasty': 4,
                'currentEpoch': 12,
                'lastJustifiedEpoch': 11,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        attacker = node.add_p2p_connection(BaseNode())
        network_thread_start()
        attacker.wait_for_verack()

        # send blocks without the last one that has a justified vote
        node_blocks = node.getblockcount()
        for h in range(known_fork1_height + 1, fork1.getblockcount()):
            block_hash = fork1.getblockhash(h)
            block = FromHex(CBlock(), fork1.getblock(block_hash, 0))
            attacker.send_message(msg_witness_block(block))
            node_blocks += 1
            wait_until(lambda: node.getblockcount() == node_blocks, timeout=15)

        assert_equal(node.getblockcount(), 56)
        assert_finalizationstate(
            node, {
                'currentDynasty': 4,
                'currentEpoch': 12,
                'lastJustifiedEpoch': 8,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        # create finalization
        #                                         J               J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork1
        # F    F    F    F    F    J /                                      |
        # e0 - e1 - e2 - e3 - e4 - e5                                       56] node
        #                            \       J         F    J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork2
        create_justification(fork=fork2, finalizer=finalizer2, after_blocks=11)
        assert_equal(fork2.getblockcount(), 48)
        assert_finalizationstate(
            fork2, {
                'currentDynasty': 4,
                'currentEpoch': 10,
                'lastJustifiedEpoch': 9,
                'lastFinalizedEpoch': 4,
                'validators': 1
            })

        create_justification(fork=fork2, finalizer=finalizer2, after_blocks=6)
        assert_equal(fork2.getblockcount(), 54)
        assert_finalizationstate(
            fork2, {
                'currentDynasty': 4,
                'currentEpoch': 11,
                'lastJustifiedEpoch': 10,
                'lastFinalizedEpoch': 9,
                'validators': 1
            })

        fork2.generatetoaddress(3, fork2.getnewaddress('', 'bech32'))
        assert_equal(fork2.getblockcount(), 57)
        assert_finalizationstate(
            fork2, {
                'currentDynasty': 5,
                'currentEpoch': 12,
                'lastJustifiedEpoch': 10,
                'lastFinalizedEpoch': 9,
                'validators': 1
            })

        # node follows longer finalization
        #                                         J               J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork1
        # F    F    F    F    F    J /
        # e0 - e1 - e2 - e3 - e4 - e5
        #                            \       J         F    J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork2, node
        tip = fork2.getblockhash(57)
        sync_node_to_fork(node, fork2)

        assert_equal(node.getblockcount(), 57)
        assert_finalizationstate(
            node, {
                'currentDynasty': 5,
                'currentEpoch': 12,
                'lastJustifiedEpoch': 10,
                'lastFinalizedEpoch': 9,
                'validators': 1
            })

        # send block with surrounded vote that justifies longer fork
        # node's view:
        #                                         J               J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork1
        # F    F    F    F    F    J /
        # e0 - e1 - e2 - e3 - e4 - e5
        #                            \       J         F    J
        #                             - e6 - e7 - e8 - e9 - e10 - e11 - e12[56, 57] fork2, node

        block_hash = fork1.getblockhash(fork1.getblockcount())
        block = FromHex(CBlock(), fork1.getblock(block_hash, 0))
        block.calc_sha256()
        attacker.send_message(msg_witness_block(block))

        # node should't re-org to malicious fork
        wait_for_reject(attacker, b'bad-fork-before-last-finalized-epoch',
                        block.sha256)
        assert_equal(node.getblockcount(), 57)
        assert_equal(node.getblockhash(57), tip)
        assert_finalizationstate(
            node, {
                'currentDynasty': 5,
                'currentEpoch': 12,
                'lastJustifiedEpoch': 10,
                'lastFinalizedEpoch': 9,
                'validators': 1
            })

        self.log.info('node did not re-org before finalization')
Esempio n. 41
0
 def test_instantsend_publishers(self):
     instantsend_publishers = [
         ZMQPublisher.hash_tx_lock, ZMQPublisher.raw_tx_lock,
         ZMQPublisher.raw_tx_lock_sig,
         ZMQPublisher.hash_instantsend_doublespend,
         ZMQPublisher.raw_instantsend_doublespend
     ]
     self.log.info("Testing %d InstantSend publishers" %
                   len(instantsend_publishers))
     # Subscribe to InstantSend messages
     self.subscribe(instantsend_publishers)
     # Initialize test node
     self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn())
     network_thread_start()
     self.nodes[0].p2p.wait_for_verack()
     # Make sure all nodes agree
     self.wait_for_chainlocked_block_all_nodes(
         self.nodes[0].getbestblockhash())
     # Create two raw TXs, they will conflict with each other
     rpc_raw_tx_1 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1,
                                       100)
     rpc_raw_tx_2 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1,
                                       100)
     # Send the first transaction and wait for the InstantLock
     rpc_raw_tx_1_hash = self.nodes[0].sendrawtransaction(
         rpc_raw_tx_1['hex'])
     self.wait_for_instantlock(rpc_raw_tx_1_hash, self.nodes[0])
     # Validate hashtxlock
     zmq_tx_lock_hash = bytes_to_hex_str(
         self.receive(ZMQPublisher.hash_tx_lock).read(32))
     assert_equal(zmq_tx_lock_hash, rpc_raw_tx_1['txid'])
     # Validate rawtxlock
     zmq_tx_lock_raw = CTransaction()
     zmq_tx_lock_raw.deserialize(self.receive(ZMQPublisher.raw_tx_lock))
     assert (zmq_tx_lock_raw.is_valid())
     assert_equal(zmq_tx_lock_raw.hash, rpc_raw_tx_1['txid'])
     # Validate rawtxlocksig
     zmq_tx_lock_sig_stream = self.receive(ZMQPublisher.raw_tx_lock_sig)
     zmq_tx_lock_tx = CTransaction()
     zmq_tx_lock_tx.deserialize(zmq_tx_lock_sig_stream)
     assert (zmq_tx_lock_tx.is_valid())
     assert_equal(zmq_tx_lock_tx.hash, rpc_raw_tx_1['txid'])
     zmq_tx_lock = msg_islock()
     zmq_tx_lock.deserialize(zmq_tx_lock_sig_stream)
     assert_equal(uint256_to_string(zmq_tx_lock.txid), rpc_raw_tx_1['txid'])
     # Try to send the second transaction. This must throw an RPC error because it conflicts with rpc_raw_tx_1
     # which already got the InstantSend lock.
     assert_raises_rpc_error(-26, "tx-txlock-conflict",
                             self.nodes[0].sendrawtransaction,
                             rpc_raw_tx_2['hex'])
     # Validate hashinstantsenddoublespend
     zmq_double_spend_hash2 = bytes_to_hex_str(
         self.receive(ZMQPublisher.hash_instantsend_doublespend).read(32))
     zmq_double_spend_hash1 = bytes_to_hex_str(
         self.receive(ZMQPublisher.hash_instantsend_doublespend).read(32))
     assert_equal(zmq_double_spend_hash2, rpc_raw_tx_2['txid'])
     assert_equal(zmq_double_spend_hash1, rpc_raw_tx_1['txid'])
     # Validate rawinstantsenddoublespend
     zmq_double_spend_tx_2 = CTransaction()
     zmq_double_spend_tx_2.deserialize(
         self.receive(ZMQPublisher.raw_instantsend_doublespend))
     assert (zmq_double_spend_tx_2.is_valid())
     assert_equal(zmq_double_spend_tx_2.hash, rpc_raw_tx_2['txid'])
     zmq_double_spend_tx_1 = CTransaction()
     zmq_double_spend_tx_1.deserialize(
         self.receive(ZMQPublisher.raw_instantsend_doublespend))
     assert (zmq_double_spend_tx_1.is_valid())
     assert_equal(zmq_double_spend_tx_1.hash, rpc_raw_tx_1['txid'])
     # No islock notifications when tx is not received yet
     self.nodes[0].generate(1)
     rpc_raw_tx_3 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1,
                                       100)
     islock = self.create_islock(rpc_raw_tx_3['hex'])
     self.test_node.send_islock(islock)
     # Validate NO hashtxlock
     time.sleep(1)
     try:
         self.receive(ZMQPublisher.hash_tx_lock, zmq.NOBLOCK)
         assert (False)
     except zmq.ZMQError:
         # this is expected
         pass
     # Now send the tx itself
     self.test_node.send_tx(FromHex(msg_tx(), rpc_raw_tx_3['hex']))
     self.wait_for_instantlock(rpc_raw_tx_3['txid'], self.nodes[0])
     # Validate hashtxlock
     zmq_tx_lock_hash = bytes_to_hex_str(
         self.receive(ZMQPublisher.hash_tx_lock).read(32))
     assert_equal(zmq_tx_lock_hash, rpc_raw_tx_3['txid'])
     # Drop test node connection
     self.nodes[0].disconnect_p2ps()
     # Unsubscribe from InstantSend messages
     self.unsubscribe(instantsend_publishers)
Esempio n. 42
0
    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 = 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_transaction(block1.vtx[0], 0, b'\x51', 50 * COIN)
        tx2 = create_transaction(tx1, 0, b'\x51', 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')

        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, False, False, 16, b'bad-cb-amount')
Esempio n. 43
0
        def test_basics():
            self.log.info("Testing basics of QGETDATA/QDATA")
            p2p_node0 = p2p_connection(node0)
            p2p_mn1 = p2p_connection(mn1.node)
            network_thread_start()
            p2p_node0.wait_for_verack()
            p2p_mn1.wait_for_verack()
            id_p2p_node0 = get_mininode_id(node0)
            id_p2p_mn1 = get_mininode_id(mn1.node)

            # Ensure that both nodes start with zero ban score
            wait_for_banscore(node0, id_p2p_node0, 0)
            wait_for_banscore(mn1.node, id_p2p_mn1, 0)

            self.log.info("Check that normal node doesn't respond to qgetdata "
                          "and does bump our score")
            p2p_node0.test_qgetdata(qgetdata_all, response_expected=False)
            wait_for_banscore(node0, id_p2p_node0, 10)
            # The masternode should not respond to qgetdata for non-masternode connections
            self.log.info("Check that masternode doesn't respond to "
                          "non-masternode connection. Doesn't bump score.")
            p2p_mn1.test_qgetdata(qgetdata_all, response_expected=False)
            wait_for_banscore(mn1.node, id_p2p_mn1, 10)
            # Open a fake MNAUTH authenticated P2P connection to the masternode to allow qgetdata
            node0.disconnect_p2ps()
            mn1.node.disconnect_p2ps()
            network_thread_join()
            p2p_mn1 = p2p_connection(mn1.node)
            network_thread_start()
            p2p_mn1.wait_for_verack()
            id_p2p_mn1 = get_mininode_id(mn1.node)
            mnauth(mn1.node, id_p2p_mn1, fake_mnauth_1[0], fake_mnauth_1[1])
            # The masternode should now respond to qgetdata requests
            self.log.info("Request verification vector")
            p2p_mn1.test_qgetdata(qgetdata_vvec, 0, self.llmq_threshold, 0)
            wait_for_banscore(mn1.node, id_p2p_mn1, 0)
            # Note: our banscore is bumped as we are requesting too rapidly,
            # however the node still returns the data
            self.log.info("Request encrypted contributions")
            p2p_mn1.test_qgetdata(qgetdata_contributions, 0, 0, self.llmq_size)
            wait_for_banscore(mn1.node, id_p2p_mn1, 25)
            # Request both
            # Note: our banscore is bumped as we are requesting too rapidly,
            # however the node still returns the data
            self.log.info("Request both")
            p2p_mn1.test_qgetdata(qgetdata_all, 0, self.llmq_threshold,
                                  self.llmq_size)
            wait_for_banscore(mn1.node, id_p2p_mn1, 50)
            mn1.node.disconnect_p2ps()
            network_thread_join()
            self.log.info(
                "Test ban score increase for invalid / unexpected QDATA")
            p2p_mn1 = p2p_connection(mn1.node)
            p2p_mn2 = p2p_connection(mn2.node)
            network_thread_start()
            p2p_mn1.wait_for_verack()
            p2p_mn2.wait_for_verack()
            id_p2p_mn1 = get_mininode_id(mn1.node)
            id_p2p_mn2 = get_mininode_id(mn2.node)
            mnauth(mn1.node, id_p2p_mn1, fake_mnauth_1[0], fake_mnauth_1[1])
            mnauth(mn2.node, id_p2p_mn2, fake_mnauth_2[0], fake_mnauth_2[1])
            wait_for_banscore(mn1.node, id_p2p_mn1, 0)
            p2p_mn2.test_qgetdata(qgetdata_all, 0, self.llmq_threshold,
                                  self.llmq_size)
            qdata_valid = p2p_mn2.get_qdata()
            # - Not requested
            p2p_mn1.send_message(qdata_valid)
            time.sleep(1)
            wait_for_banscore(mn1.node, id_p2p_mn1, 10)
            # - Already received
            force_request_expire()
            assert mn1.node.quorum("getdata", id_p2p_mn1, 100, quorum_hash,
                                   0x03, mn1.proTxHash)
            p2p_mn1.wait_for_qgetdata()
            p2p_mn1.send_message(qdata_valid)
            time.sleep(1)
            p2p_mn1.send_message(qdata_valid)
            wait_for_banscore(mn1.node, id_p2p_mn1, 20)
            # - Not like requested
            force_request_expire()
            assert mn1.node.quorum("getdata", id_p2p_mn1, 100, quorum_hash,
                                   0x03, mn1.proTxHash)
            p2p_mn1.wait_for_qgetdata()
            qdata_invalid_request = qdata_valid
            qdata_invalid_request.data_mask = 2
            p2p_mn1.send_message(qdata_invalid_request)
            wait_for_banscore(mn1.node, id_p2p_mn1, 30)
            # - Invalid verification vector
            force_request_expire()
            assert mn1.node.quorum("getdata", id_p2p_mn1, 100, quorum_hash,
                                   0x03, mn1.proTxHash)
            p2p_mn1.wait_for_qgetdata()
            qdata_invalid_vvec = qdata_valid
            qdata_invalid_vvec.quorum_vvec.pop()
            p2p_mn1.send_message(qdata_invalid_vvec)
            wait_for_banscore(mn1.node, id_p2p_mn1, 40)
            # - Invalid contributions
            force_request_expire()
            assert mn1.node.quorum("getdata", id_p2p_mn1, 100, quorum_hash,
                                   0x03, mn1.proTxHash)
            p2p_mn1.wait_for_qgetdata()
            qdata_invalid_contribution = qdata_valid
            qdata_invalid_contribution.enc_contributions.pop()
            p2p_mn1.send_message(qdata_invalid_contribution)
            wait_for_banscore(mn1.node, id_p2p_mn1, 50)
            mn1.node.disconnect_p2ps()
            mn2.node.disconnect_p2ps()
            network_thread_join()
            self.log.info("Test all available error codes")
            p2p_mn1 = p2p_connection(mn1.node)
            network_thread_start()
            p2p_mn1.wait_for_verack()
            id_p2p_mn1 = get_mininode_id(mn1.node)
            mnauth(mn1.node, id_p2p_mn1, fake_mnauth_1[0], fake_mnauth_1[1])
            qgetdata_invalid_type = msg_qgetdata(quorum_hash_int, 103, 0x01,
                                                 protx_hash_int)
            qgetdata_invalid_block = msg_qgetdata(protx_hash_int, 100, 0x01,
                                                  protx_hash_int)
            qgetdata_invalid_quorum = msg_qgetdata(
                int(mn1.node.getblockhash(0), 16), 100, 0x01, protx_hash_int)
            qgetdata_invalid_no_member = msg_qgetdata(quorum_hash_int, 100,
                                                      0x02, quorum_hash_int)
            p2p_mn1.test_qgetdata(qgetdata_invalid_type, QUORUM_TYPE_INVALID)
            p2p_mn1.test_qgetdata(qgetdata_invalid_block,
                                  QUORUM_BLOCK_NOT_FOUND)
            p2p_mn1.test_qgetdata(qgetdata_invalid_quorum, QUORUM_NOT_FOUND)
            p2p_mn1.test_qgetdata(qgetdata_invalid_no_member,
                                  MASTERNODE_IS_NO_MEMBER)
            # The last two error case require the node to miss its DKG data so we just reindex the node.
            mn1.node.disconnect_p2ps()
            network_thread_join()
            self.restart_mn(mn1, reindex=True)
            # Re-connect to the masternode
            p2p_mn1 = p2p_connection(mn1.node)
            p2p_mn2 = p2p_connection(mn2.node)
            network_thread_start()
            p2p_mn1.wait_for_verack()
            p2p_mn2.wait_for_verack()
            id_p2p_mn1 = get_mininode_id(mn1.node)
            id_p2p_mn2 = get_mininode_id(mn2.node)
            assert id_p2p_mn1 is not None
            assert id_p2p_mn2 is not None
            mnauth(mn1.node, id_p2p_mn1, fake_mnauth_1[0], fake_mnauth_1[1])
            mnauth(mn2.node, id_p2p_mn2, fake_mnauth_2[0], fake_mnauth_2[1])
            # Validate the DKG data is missing
            p2p_mn1.test_qgetdata(qgetdata_vvec,
                                  QUORUM_VERIFICATION_VECTOR_MISSING)
            p2p_mn1.test_qgetdata(qgetdata_contributions,
                                  ENCRYPTED_CONTRIBUTIONS_MISSING)
            self.log.info("Test DKG data recovery with QDATA")
            # Now that mn1 is missing its DKG data try to recover it by querying the data from mn2 and then sending it
            # to mn1 with a direct QDATA message.
            #
            # mininode - QGETDATA -> mn2 - QDATA -> mininode - QDATA -> mn1
            #
            # However, mn1 only accepts self requested QDATA messages, that's why we trigger mn1 - QGETDATA -> mininode
            # via the RPC command "quorum getdata".
            #
            # Get the required DKG data for mn1
            p2p_mn2.test_qgetdata(qgetdata_all, 0, self.llmq_threshold,
                                  self.llmq_size)
            # Trigger mn1 - QGETDATA -> p2p_mn1
            assert mn1.node.quorum("getdata", id_p2p_mn1, 100, quorum_hash,
                                   0x03, mn1.proTxHash)
            # Wait until mn1 sent the QGETDATA to p2p_mn1
            p2p_mn1.wait_for_qgetdata()
            # Send the QDATA received from mn2 to mn1
            p2p_mn1.send_message(p2p_mn2.get_qdata())
            # Now mn1 should have its data back!
            self.wait_for_quorum_data([mn1], 100, quorum_hash, recover=False)
            # Restart one more time and make sure data gets saved to db
            mn1.node.disconnect_p2ps()
            mn2.node.disconnect_p2ps()
            network_thread_join()
            self.restart_mn(mn1)
            self.wait_for_quorum_data([mn1], 100, quorum_hash, recover=False)
Esempio n. 44
0
    def run_test(self):
        node0 = self.nodes[0].add_p2p_connection(P2PInterface())

        network_thread_start()
        node0.wait_for_verack()

        # Set node time to 60 days ago
        self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)

        # Generating a chain of 10 blocks
        block_hashes = self.nodes[0].generate(nblocks=10)

        # Create longer chain starting 2 blocks before current tip
        height = len(block_hashes) - 2
        block_hash = block_hashes[height - 1]
        block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
        new_blocks = self.build_chain(5, block_hash, height, block_time)

        # Force reorg to a longer chain
        node0.send_message(msg_headers(new_blocks))
        node0.wait_for_getdata()
        for block in new_blocks:
            node0.send_and_ping(msg_block(block))

        # Check that reorg succeeded
        assert_equal(self.nodes[0].getblockcount(), 13)

        stale_hash = int(block_hashes[-1], 16)

        # Check that getdata request for stale block succeeds
        self.send_block_request(stale_hash, node0)
        test_function = lambda: self.last_block_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Check that getheader request for stale block header succeeds
        self.send_header_request(stale_hash, node0)
        test_function = lambda: self.last_header_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Longest chain is extended so stale is much older than chain tip
        self.nodes[0].setmocktime(0)
        tip = self.nodes[0].generate(nblocks=1)[0]
        assert_equal(self.nodes[0].getblockcount(), 14)

        # Send getdata & getheaders to refresh last received getheader message
        block_hash = int(tip, 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        # Request for very old stale block should now fail
        self.send_block_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_block_equals(stale_hash, node0)

        # Request for very old stale block header should now fail
        self.send_header_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_header_equals(stale_hash, node0)

        # Verify we can fetch very old blocks and headers on the active chain
        block_hash = int(block_hashes[2], 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        self.send_block_request(block_hash, node0)
        test_function = lambda: self.last_block_equals(block_hash, node0)
        wait_until(test_function, timeout=3)

        self.send_header_request(block_hash, node0)
        test_function = lambda: self.last_header_equals(block_hash, node0)
        wait_until(test_function, timeout=3)
Esempio n. 45
0
        def test_request_limit():
            def test_send_from_two_to_one(send_1,
                                          expected_score_1,
                                          send_2,
                                          expected_score_2,
                                          clear_requests=False):
                if clear_requests:
                    force_request_expire()
                if send_1:
                    p2p_mn3_1.test_qgetdata(qgetdata_vvec, 0,
                                            self.llmq_threshold, 0)
                if send_2:
                    p2p_mn3_2.test_qgetdata(qgetdata_vvec, 0,
                                            self.llmq_threshold, 0)
                wait_for_banscore(mn3.node, id_p2p_mn3_1, expected_score_1)
                wait_for_banscore(mn3.node, id_p2p_mn3_2, expected_score_2)

            self.log.info("Test request limiting / banscore increases")

            p2p_mn1 = p2p_connection(mn1.node)
            network_thread_start()
            p2p_mn1.wait_for_verack()
            id_p2p_mn1 = get_mininode_id(mn1.node)
            mnauth(mn1.node, id_p2p_mn1, fake_mnauth_1[0], fake_mnauth_1[1])
            p2p_mn1.test_qgetdata(qgetdata_vvec, 0, self.llmq_threshold, 0)
            wait_for_banscore(mn1.node, id_p2p_mn1, 0)
            force_request_expire(
                299
            )  # This shouldn't clear requests, next request should bump score
            p2p_mn1.test_qgetdata(qgetdata_vvec, 0, self.llmq_threshold, 0)
            wait_for_banscore(mn1.node, id_p2p_mn1, 25)
            force_request_expire(
                1
            )  # This should clear the requests now, next request should not bump score
            p2p_mn1.test_qgetdata(qgetdata_vvec, 0, self.llmq_threshold, 0)
            wait_for_banscore(mn1.node, id_p2p_mn1, 25)
            mn1.node.disconnect_p2ps()
            network_thread_join()
            # Requesting one QDATA with mn1 and mn2 from mn3 should not result
            # in banscore increase for either of both.
            p2p_mn3_1 = p2p_connection(mn3.node, uacomment_m3_1)
            p2p_mn3_2 = p2p_connection(mn3.node, uacomment_m3_2)
            network_thread_start()
            p2p_mn3_1.wait_for_verack()
            p2p_mn3_2.wait_for_verack()
            id_p2p_mn3_1 = get_mininode_id(mn3.node, uacomment_m3_1)
            id_p2p_mn3_2 = get_mininode_id(mn3.node, uacomment_m3_2)
            assert id_p2p_mn3_1 != id_p2p_mn3_2
            mnauth(mn3.node, id_p2p_mn3_1, fake_mnauth_1[0], fake_mnauth_1[1])
            mnauth(mn3.node, id_p2p_mn3_2, fake_mnauth_2[0], fake_mnauth_2[1])
            # Now try some {mn1, mn2} - QGETDATA -> mn3 combinations to make
            # sure request limit works connection based
            test_send_from_two_to_one(False, 0, True, 0, True)
            test_send_from_two_to_one(True, 0, True, 25)
            test_send_from_two_to_one(True, 25, False, 25)
            test_send_from_two_to_one(False, 25, True, 25, True)
            test_send_from_two_to_one(True, 25, True, 50)
            test_send_from_two_to_one(True, 50, True, 75)
            test_send_from_two_to_one(True, 50, True, 75, True)
            test_send_from_two_to_one(True, 75, False, 75)
            test_send_from_two_to_one(False, 75, True, None)
            # mn1 should still have a score of 75
            wait_for_banscore(mn3.node, id_p2p_mn3_1, 75)
            # mn2 should be "banned" now
            wait_until(lambda: not p2p_mn3_2.is_connected, timeout=10)
            mn3.node.disconnect_p2ps()
            network_thread_join()
Esempio n. 46
0
 def run_test(self):
     test = TestManager(self, self.options.tmpdir)
     test.add_all_connections(self.nodes)
     network_thread_start()
     test.run()
Esempio n. 47
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()

        # Get to one block of the May 15, 2018 HF activation
        for i in range(6):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])

        # Send it all to the node at once.
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # There can be only one network thread running at a time.
        # Adding a new P2P connection here will try to start the network thread
        # at init, which will throw an assertion because it's already running.
        # This requires a few steps to avoid this:
        #   1/ Disconnect all the TestManager nodes
        #   2/ Terminate the network thread
        #   3/ Add the new P2P connection
        #   4/ Reconnect all the TestManager nodes
        #   5/ Restart the network thread

        # Disconnect all the TestManager nodes
        [n.disconnect_node() for n in self.test.p2p_connections]
        self.test.wait_for_disconnections()
        self.test.clear_all_connections()

        # Wait for the network thread to terminate
        network_thread_join()

        # Add the new connection
        node = self.nodes[0]
        node.add_p2p_connection(TestNode())

        # Reconnect TestManager nodes
        self.test.add_all_connections(self.nodes)

        # Restart the network thread
        network_thread_start()

        # Wait for connection to be etablished
        peer = node.p2p
        peer.wait_for_verack()

        # Check that compact block also work for big blocks
        # Wait for SENDCMPCT
        def received_sendcmpct():
            return (peer.last_sendcmpct != None)

        wait_until(received_sendcmpct, timeout=30)

        sendcmpct = msg_sendcmpct()
        sendcmpct.version = 1
        sendcmpct.announce = True
        peer.send_and_ping(sendcmpct)

        # Exchange headers
        def received_getheaders():
            return (peer.last_getheaders != None)

        wait_until(received_getheaders, timeout=30)

        # Return the favor
        peer.send_message(peer.last_getheaders)

        # Wait for the header list
        def received_headers():
            return (peer.last_headers != None)

        wait_until(received_headers, timeout=30)

        # It's like we know about the same headers !
        peer.send_message(peer.last_headers)

        # Send a block
        b1 = block(1, spend=out[0], block_size=ONE_MEGABYTE + 1)
        yield accepted()

        # Checks the node to forward it via compact block
        def received_block():
            return (peer.last_cmpctblock != None)

        wait_until(received_block, timeout=30)

        # Was it our block ?
        cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header
        cmpctblk_header.calc_sha256()
        assert (cmpctblk_header.sha256 == b1.sha256)

        # Send a large block with numerous transactions.
        peer.clear_block_data()
        b2 = block(2,
                   spend=out[1],
                   extra_txns=70000,
                   block_size=self.excessive_block_size - 1000)
        yield accepted()

        # Checks the node forwards it via compact block
        wait_until(received_block, timeout=30)

        # Was it our block ?
        cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header
        cmpctblk_header.calc_sha256()
        assert (cmpctblk_header.sha256 == b2.sha256)

        # In order to avoid having to resend a ton of transactions, we invalidate
        # b2, which will send all its transactions in the mempool.
        node.invalidateblock(node.getbestblockhash())

        # Let's send a compact block and see if the node accepts it.
        # Let's modify b2 and use it so that we can reuse the mempool.
        tx = b2.vtx[0]
        tx.vout.append(CTxOut(0, CScript([random.randint(0, 256), OP_RETURN])))
        tx.rehash()
        b2.vtx[0] = tx
        b2.hashMerkleRoot = b2.calc_merkle_root()
        b2.solve()

        # Now we create the compact block and send it
        comp_block = HeaderAndShortIDs()
        comp_block.initialize_from_block(b2)
        peer.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))

        # Check that compact block is received properly
        assert (int(node.getbestblockhash(), 16) == b2.sha256)