def run_test(self):
        """Main test logic"""
        from pypoptools.pypopminer import MockMiner
        self.apm = MockMiner()

        node = self.nodes[0]
        addr = node.getnewaddress()

        # stop 1 block behind activation
        self.log.info("Mining blocks until activation -5 blocks")
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
        mine_until_pop_active(node, addr, delta=-5)
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
        # check that getblocktemplate does NOT have pop-related fields before POP activation

        self.log.info("Check that getblocktemplate does not have POP fields")
        resp = self.getblocktemplate()
        assert_no_field(resp, 'pop_data')
        assert_no_field(resp, 'pop_data_root')
        assert_no_field(resp, 'pop_first_previous_keystone')
        assert_no_field(resp, 'pop_second_previous_keystone')
        assert_no_field(resp, 'pop_rewards')

        self.log.info("Mining blocks until activation +5 blocks")
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
        mine_until_pop_active(node, addr, delta=+5)
        self.log.info("Current node[0] height {}".format(node.getblockcount()))

        self.log.info("Mine chain of {} consecutive endorsed blocks".format(
            POP_PAYOUT_DELAY))
        create_endorsed_chain(node, self.apm, POP_PAYOUT_DELAY, addr)
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
        endorse_block(self.nodes[0], self.apm, node.getblockcount() - 5, addr)
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
        self.log.info("Check that getblocktemplate does have POP fields")
        resp = self.getblocktemplate()

        is_dict = lambda x: isinstance(x, dict)
        is_list = lambda x: isinstance(x, list)
        is_int = lambda x: isinstance(x, int)
        is_hex = lambda x: bytes.fromhex(x)
        is_payload = lambda x: is_dict(x) and "id" in x and "serialized" in x
        is_payload_list = lambda x: is_list(x) and all(
            is_payload(p) for p in x)

        assert_field_exists(resp, 'pop_data', type=is_dict)
        assert_field_exists(resp['pop_data'], 'atvs', type=is_payload_list)
        assert_field_exists(resp['pop_data'], 'vtbs', type=is_payload_list)
        assert_field_exists(resp['pop_data'],
                            'vbkblocks',
                            type=is_payload_list)
        assert_field_exists(resp['pop_data'], 'version', type=is_int)
        assert_field_exists(resp, 'pop_data_root', type=is_hex)
        assert_field_exists(resp, 'pop_first_previous_keystone', type=is_hex)
        assert_field_exists(resp, 'pop_second_previous_keystone', type=is_hex)
        assert_field_exists(resp, 'pop_rewards', type=is_list)
        for reward in resp['pop_rewards']:
            assert_field_exists(reward, 'amount', type=is_int)
            assert_field_exists(reward, 'payout_info', type=is_hex)
        self.log.info("Current node[0] height {}".format(node.getblockcount()))
示例#2
0
    def _one_by_one(self):
        for node in self.nodes:
            # VBK block
            self.log.info("Submitting VBK")
            block = self.apm.mineVbkBlocks(1)
            response = node.submitpopvbk(block.toVbkEncodingHex())
            assert response['accepted'], response
            self.log.info("VBK accepted to mempool")
            sync_pop_mempools(self.nodes, timeout=30)

            # VTB
            self.log.info("Submitting VTB")
            lastBtc = node.getbtcbestblockhash()
            vtb = self.apm.endorseVbkBlock(
                block,  # endorsed vbk block
                lastBtc)
            response = node.submitpopvtb(vtb.toVbkEncodingHex())
            assert response['accepted'], response
            self.log.info("VTB accepted to mempool")
            sync_pop_mempools(self.nodes, timeout=100)

            # ATV
            self.log.info("Submitting ATV")
            tip = self.nodes[0].getbestblockhash()
            altblock = self.nodes[0].getblock(tip)
            endorse_block(self.nodes[0], self.apm, altblock['height'],
                          self.nodes[0].getnewaddress())
            self.log.info("ATV accepted to mempool")
            sync_pop_mempools(self.nodes, timeout=100)

            self.nodes[2].generate(nblocks=1)
            self.sync_all()
示例#3
0
    def _check_pop_sync(self):
        self.log.info("running _check_pop_sync()")
        height = self.nodes[0].getblockcount()
        topheight = height + 52
        addr0 = self.nodes[0].getnewaddress()
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()
        keystoneInterval = get_keystone_interval(self.nodes[0])

        while height < topheight:
            self.nodes[0].generate(nblocks=1)
            # endorse every block
            self.nodes[2].waitforblockheight(height)
            node2_txid = endorse_block(self.nodes[2], self.apm, height, addr2)
            self.log.info("node2 endorsing block {} by miner {}: {}".format(
                height, addr2, node2_txid))

            # endorse each keystone
            if height % keystoneInterval == 0:
                self.nodes[0].waitforblockheight(height)
                node0_txid = endorse_block(self.nodes[0], self.apm, height,
                                           addr0)
                self.log.info(
                    "node0 endorsing block {} by miner {}: {}".format(
                        height, addr0, node0_txid))

                self.nodes[1].waitforblockheight(height)
                node1_txid = endorse_block(self.nodes[1], self.apm, height,
                                           addr1)
                self.log.info(
                    "node1 endorsing block {} by miner {}: {}".format(
                        height, addr1, node1_txid))

                # wait until node[1] gets relayed pop tx
                sync_pop_mempools_atvs(self.nodes, timeout=100)
                self.log.info("transactions relayed")

                # mine a block on node[1] with this pop tx
                containingblockhash = self.nodes[1].generate(nblocks=1)[0]
                containingblock = self.nodes[1].getblock(containingblockhash)
                self.log.info("node1 mined containing block={}".format(
                    containingblock['hash']))
                self.nodes[0].waitforblockheight(containingblock['height'])
                self.nodes[2].waitforblockheight(containingblock['height'])
                self.log.info("node0 and node2 got containing block over p2p")

                # assert that all txids exist in this block
                for node in self.nodes:
                    self.assert_atvs_in_node(
                        node, containingblockhash,
                        [node0_txid, node1_txid, node2_txid])

                # assert that node height matches
                assert self.nodes[0].getblockcount() == self.nodes[
                    1].getblockcount() == self.nodes[2].getblockcount()

            height += 1
        self.log.info("success! _check_pop_sync()")
    def run_test(self):
        """Main test logic"""

        self.sync_all(self.nodes)

        from pypopminer import MockMiner
        self.apm = MockMiner()

        addr0 = self.nodes[0].getnewaddress()
        self.log.info("node0 endorses block 5")
        self.nodes[0].generate(nblocks=10)
        atvid = endorse_block(self.nodes[0], self.apm, 5, addr0)

        self.sync_pop_mempools(self.nodes, timeout=20)
        self.log.info("nodes[0,1] have syncd pop mempools")

        rawpopmempool1 = self.nodes[1].getrawpopmempool()
        assert atvid in rawpopmempool1['atvs']
        self.log.info("node1 contains atv1 in its pop mempool")

        self.restart_node(1)
        self.log.info("node1 has been restarted")
        rawpopmempool1 = self.nodes[1].getrawpopmempool()
        assert atvid not in rawpopmempool1['atvs']
        self.log.info("node1 does not contain atv1 in its pop mempool after restart")

        connect_nodes(self.nodes[0], 1)
        self.log.info("node1 connect to node0")

        self.sync_pop_mempools(self.nodes, timeout=20)
        self.log.info("nodes[0,1] have syncd pop mempools")

        rawpopmempool1 = self.nodes[1].getrawpopmempool()
        assert atvid in rawpopmempool1['atvs']
示例#5
0
    def _run_case3(self):
        bn = BaseNode()
        self.nodes[0].add_p2p_connection(bn)
        self.log.warning("running case3 for pop data sending")

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        atv_id = endorse_block(self.nodes[0], self.apm, 5, addr)
        raw_atv = self.nodes[0].getrawatv(atv_id)

        assert len(self.nodes[0].getpeerinfo()) == 1
        peerinfo = self.nodes[0].getpeerinfo()[0]
        assert (not 'noban' in peerinfo['permissions'])
        assert peerinfo['banscore'] == 0

        # spaming node1
        self.log.info("send to node0 that it has not requested...")
        msg = msg_atv(raw_atv)
        # Send message is used to send a P2P message to the node over our P2PInterface
        self.nodes[0].p2p.send_message(msg)

        time.sleep(1)

        peerinfo = self.nodes[0].getpeerinfo()[0]
        assert peerinfo['banscore'] == 20
示例#6
0
    def _run_case2(self):
        bn = BaseNode()
        self.nodes[0].add_p2p_connection(bn)
        self.log.warning("running case for pop data sending")

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        atv_id = endorse_block(self.nodes[0], self.apm, 5, addr)
        raw_atv = self.nodes[0].getrawatv(atv_id)

        assert len(self.nodes[0].getpeerinfo()) == 1
        peerinfo = self.nodes[0].getpeerinfo()[0]
        assert (not 'noban' in peerinfo['permissions'])

        # spaming node1
        self.log.info("spaming node0 ...")
        disconnected = False
        for i in range(10000):
            try:
                msg = msg_atv(raw_atv)
                # Send message is used to send a P2P message to the node over our P2PInterface
                self.nodes[0].p2p.send_message(msg)
            except IOError:
                disconnected = True

        self.log.info("node0 banned our peer")
        assert disconnected

        assert_equal(len(self.nodes[0].getpeerinfo()), 0)
示例#7
0
    def _can_endorse(self):
        self.log.warning("starting _can_endorse()")
        connect_nodes(self.nodes[0], 1)
        self.sync_all()
        disconnect_nodes(self.nodes[1], 0)

        mine_until_pop_enabled(self.nodes[0])
        lastblock = self.nodes[0].getblockcount()

        # endorse block 200 (fork A tip)
        addr0 = self.nodes[0].getnewaddress()
        txid = endorse_block(self.nodes[0], self.apm, lastblock, addr0)
        self.log.info("node0 endorsed block {} (fork A tip)".format(lastblock))
        # mine pop tx on node0
        self.nodes[0].generate(nblocks=1)
        tip = self.get_best_block(self.nodes[0])
        self.log.info("node0 tip is {}".format(tip['height']))

        self.nodes[1].generate(nblocks=250)
        tip2 = self.get_best_block(self.nodes[1])
        self.log.info("node1 tip is {}".format(tip2['height']))

        connect_nodes(self.nodes[0], 1)
        self.sync_all()
        bestblocks = [self.get_best_block(x) for x in self.nodes]
        assert_equal(bestblocks[0]['hash'], bestblocks[1]['hash'])
        self.log.info("all nodes switched to common block")

        for i in range(len(bestblocks)):
            assert bestblocks[i]['height'] == tip['height'], \
                "node[{}] expected to select shorter chain ({}) with higher pop score\n" \
                "but selected longer chain ({})".format(i, tip['height'], bestblocks[i]['height'])

        self.log.info("all nodes selected fork A as best chain")
        self.log.warning("_can_endorse() succeeded!")
示例#8
0
    def _run_sync_case(self):
        self.log.info("running _run_sync_case")

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        atv_id = endorse_block(self.nodes[0], self.apm, 5, addr)

        bn = BaseNode(self.log)

        self.nodes[0].add_p2p_connection(bn)
        time.sleep(2)

        assert bn.executed_msg_atv == 0
        assert bn.executed_msg_offer_atv == 1
        assert bn.executed_msg_offer_vbk == 1

        msg = msg_get_atv([atv_id])
        self.nodes[0].p2p.send_message(msg)
        self.nodes[0].p2p.send_message(msg)
        self.nodes[0].p2p.send_message(msg)

        time.sleep(2)

        assert bn.executed_msg_atv == 3

        self.log.info("_run_sync_case successful")
示例#9
0
    def _test_case_atv(self, payloads_amount):
        self.log.info("running _test_case_vbk()")

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        for i in range(payloads_amount):
            self.log.info(
                "endorsing block 5 on node0 by miner {}".format(addr))
            endorse_block(self.nodes[0], self.apm, 5, addr)

        # mine a block on node[1] with this pop tx
        containingblockhash = self.nodes[0].generate(nblocks=1)[0]
        containingblock = self.nodes[0].getblock(containingblockhash)

        assert len(containingblock['pop']['data']['atvs']) == payloads_amount

        self.log.info("success! _test_case_atv()")
    def _invalidate_works(self):
        self.log.warning("starting _invalidate_works()")
        self.lastblock = self.nodes[0].getblockcount()

        # start with lastblock + 100 blocks
        self.chainAtiphash = self.nodes[0].generate(nblocks=100)[-1]
        self.log.info("node mined 100 blocks")

        self.assert_tip(self.chainAtiphash)

        # endorse block 300 (fork A tip)
        addr0 = self.nodes[0].getnewaddress()
        txid = endorse_block(self.nodes[0], self.apm, self.lastblock + 100,
                             addr0)
        self.log.info("node endorsed block %d (fork A tip)",
                      self.lastblock + 100)
        # mine pop tx on node0
        self.chainAtiphash = self.nodes[0].generate(nblocks=1)[-1]
        self.assert_tip(self.chainAtiphash)

        containingblock = self.nodes[0].getblock(self.chainAtiphash)

        tip = self.get_best_block(self.nodes[0])
        assert txid in containingblock['pop']['data'][
            'atvs'], "pop tx is not in containing block"
        self.log.info("node tip is {}".format(tip['height']))

        self.chainAtiphash = self.nodes[0].generate(nblocks=19)[-1]
        self.chainAtipheight = self.nodes[0].getblock(
            self.chainAtiphash)['height']
        self.forkheight = self.lastblock + 50
        self.forkhash = self.nodes[0].getblockhash(self.forkheight)

        self.log.info("tip={}:{}, fork={}:{}".format(self.chainAtipheight,
                                                     self.chainAtiphash,
                                                     self.forkheight,
                                                     self.forkhash))

        self.invalidatedheight = self.forkheight + 1
        self.invalidated = self.nodes[0].getblockhash(self.invalidatedheight)
        self.log.info("invalidating block next to fork block {}:{}".format(
            self.invalidatedheight, self.invalidated))
        self.nodes[0].invalidateblock(self.invalidated)

        tip = self.get_best_block(self.nodes[0])
        assert tip['height'] == self.forkheight
        assert tip['hash'] == self.forkhash

        tip = self.nodes[0].getblock(tip['hash'])
        assert not tip['pop']['state'][
            'endorsedBy'], "block should not be endorsed after invalidation"

        # rewrite invalid block with generatetoaddress
        # otherwise next block will be a duplicate
        addr1 = self.nodes[0].getnewaddress()
        self.chainBtiphash = self.nodes[0].generatetoaddress(nblocks=1,
                                                             address=addr1)[-1]
示例#11
0
    def _run_sync_after_generating(self):
        self.log.info("running _run_sync_after_generating")

        bn = BaseNode(self.log)
        self.nodes[0].add_p2p_connection(bn)

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        tipheight = self.nodes[0].getblock(
            self.nodes[0].getbestblockhash())['height']

        endorse_block(self.nodes[0], self.apm, tipheight - 5, addr)

        time.sleep(20)

        assert_equal(bn.executed_msg_offer_atv, 1)
        assert_equal(bn.executed_msg_offer_vbk, 1)

        self.log.info("_run_sync_after_generating successful")
    def run_test(self):
        from pypoptools.pypopminer import MockMiner
        apm = MockMiner()
        mock_address = "bcrt1quc5k7w4692g0t0sfxc9xgcc25rzngu55zsfwp0"

        self.nodes[0].generatetoaddress(nblocks=900, address=mock_address)
        assert self.nodes[0].getblockcount() == 900
        assert_raises_rpc_error(-1, 'POP protocol is not enabled. Current=900, bootstrap height=1000',
                                endorse_block, self.nodes[0], apm, 800, mock_address)

        connect_nodes(self.nodes[0], 1)
        self.sync_blocks()
        assert self.nodes[1].getblockcount() == 900
        assert_raises_rpc_error(-1, 'POP protocol is not enabled. Current=900, bootstrap height=1000',
                                endorse_block, self.nodes[1], apm, 800, mock_address)

        disconnect_nodes(self.nodes[0], 1)
        self.nodes[0].generatetoaddress(nblocks=200, address=mock_address)
        assert self.nodes[0].getblockcount() == 1100
        assert_raises_rpc_error(-1, 'POP protocol is not active. Current=1100, activation height=1200',
                                endorse_block, self.nodes[0], apm, 1100, mock_address)

        connect_nodes(self.nodes[0], 1)
        self.sync_blocks()
        assert self.nodes[1].getblockcount() == 1100
        assert_raises_rpc_error(-1, 'POP protocol is not active. Current=1100, activation height=1200',
                                endorse_block, self.nodes[1], apm, 1100, mock_address)

        disconnect_nodes(self.nodes[0], 1)
        self.nodes[0].generatetoaddress(nblocks=200, address=mock_address)
        assert self.nodes[0].getblockcount() == 1300
        endorse_block(self.nodes[0], apm, 1300, mock_address)

        connect_nodes(self.nodes[0], 1)
        self.sync_blocks()
        assert self.nodes[1].getblockcount() == 1300
        endorse_block(self.nodes[1], apm, 1300, mock_address)
示例#13
0
    def _case1_endorse_keystone_get_paid(self):
        self.log.warning("running _case1_endorse_keystone_get_paid()")

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        atv_id = endorse_block(self.nodes[0], self.apm, 5, addr)

        # wait until node[1] gets relayed pop tx
        self.sync_pop_mempools(self.nodes)
        self.log.info("node1 got relayed transaction")

        # mine a block on node[1] with this pop tx
        containingblockhash = self.nodes[1].generate(nblocks=1)[0]
        containingblock = self.nodes[1].getblock(containingblockhash)
        self.log.info("node1 mined containing block={}".format(containingblock['hash']))
        self.nodes[0].waitforblockheight(containingblock['height'])
        self.log.info("node0 got containing block over p2p")

        # assert that txid exists in this block
        block = self.nodes[0].getblock(containingblockhash)

        assert atv_id in block['pop']['data']['atvs']

        # target height is 5 + POP_PAYOUT_DELAY + 1
        n = POP_PAYOUT_DELAY + 6 - block['height']
        payoutblockhash = self.nodes[1].generate(nblocks=n)[-1]
        self.sync_blocks(self.nodes)
        self.log.info("pop rewards paid")

        # check that expected block pays for this endorsement
        block = self.nodes[0].getblock(payoutblockhash)
        coinbasetxhash = block['tx'][0]
        coinbasetx = self.nodes[0].getrawtransaction(coinbasetxhash, 1)
        outputs = coinbasetx['vout']
        assert len(outputs) > 3, "block with payout does not contain pop payout: {}".format(outputs)
        assert outputs[1]['n'] == 1
        assert outputs[1]['value'] > 0, "expected non-zero output at n=1, got: {}".format(outputs[1])


        # mine 100 blocks and check balance
        self.nodes[0].generate(nblocks=100)
        balance = self.nodes[0].getbalance()

        # node[0] has 11 mature coinbases and single pop payout
        assert balance == POW_PAYOUT * 10 + outputs[1]['value']
        self.log.warning("success! _case1_endorse_keystone_get_paid()")
示例#14
0
    def _cannot_endorse(self):
        self.log.warning("starting _cannot_endorse()")

        # node0 start with 100 blocks
        self.nodes[0].generate(nblocks=100)
        self.nodes[0].waitforblockheight(100)
        assert self.get_best_block(self.nodes[0])['height'] == 100
        self.log.info("node0 mined 100 blocks")

        # endorse block 100 (fork A tip)
        addr0 = self.nodes[0].getnewaddress()
        self.log.info(
            'Should not accept POP data before activation block height')
        assert_raises_rpc_error(
            -1,
            'POP protocol is not active. Current=100, activation height=200',
            lambda: endorse_block(self.nodes[0], self.apm, 100, addr0))

        self.log.warning("_cannot_endorse() succeeded!")
示例#15
0
    def _cannot_endorse(self):
        self.log.warning("starting _cannot_endorse()")

        # node0 start with 100 blocks
        self.nodes[0].generate(nblocks=100)
        self.nodes[0].waitforblockheight(100)
        assert self.get_best_block(self.nodes[0])['height'] == 100
        self.log.info("node0 mined 100 blocks")

        # endorse block 100 (fork A tip)
        addr0 = self.nodes[0].getnewaddress()
        txid = endorse_block(self.nodes[0], self.apm, 100, addr0)
        self.log.info("node0 endorsed block 100 (fork A tip)")
        # mine pop tx on node0
        self.log.info(
            'Should not accept POP data before activation block height')
        assert_raises_rpc_error(
            -1, 'block contains PopData before PopSecurity has been enabled',
            lambda: self.nodes[0].generate(nblocks=1))

        self.log.warning("_cannot_endorse() succeeded!")
示例#16
0
    def _run_sync_after_generating(self):
        self.log.info("running _run_sync_after_generating")

        bn = BaseNode(self.log)
        self.nodes[0].add_p2p_connection(bn)

        # endorse block 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block 5 on node0 by miner {}".format(addr))
        atv_id = endorse_block(self.nodes[0], self.apm, 5, addr)

        msg = msg_get_atv([atv_id])
        self.nodes[0].p2p.send_message(msg)

        time.sleep(5)

        assert_equal(bn.executed_msg_atv, 1)
        assert_equal(bn.executed_msg_offer_vbk, 2)


        self.log.info("_run_sync_after_generating successful")
示例#17
0
    def _shorter_endorsed_chain_wins(self):
        self.log.warning("starting _shorter_endorsed_chain_wins()")
        lastblock = self.nodes[3].getblockcount()

        # stop node3
        self.stop_node(3)
        self.log.info("node3 stopped with block height %d", lastblock)

        # all nodes start with lastblock + 103 blocks
        self.nodes[0].generate(nblocks=103)
        self.log.info("node0 mined 103 blocks")
        self.sync_blocks([self.nodes[0], self.nodes[1], self.nodes[2]],
                         timeout=20)
        assert self.get_best_block(self.nodes[0])['height'] == lastblock + 103
        assert self.get_best_block(self.nodes[1])['height'] == lastblock + 103
        assert self.get_best_block(self.nodes[2])['height'] == lastblock + 103
        self.log.info("nodes[0,1,2] synced are at block %d", lastblock + 103)

        # node2 is disconnected from others
        disconnect_nodes(self.nodes[2], 0)
        disconnect_nodes(self.nodes[2], 1)
        self.log.info("node2 is disconnected")

        # node2 mines another 97 blocks, so total height is lastblock + 200
        self.nodes[2].generate(nblocks=97)

        # fork A is at 303 (lastblock = 200)
        # fork B is at 400
        self.nodes[2].waitforblockheight(lastblock + 200)
        self.log.info("node2 mined 97 more blocks, total height is %d",
                      lastblock + 200)

        bestblocks = [self.get_best_block(x) for x in self.nodes[0:3]]

        assert bestblocks[0] != bestblocks[2], "node[0,2] have same best hashes"
        assert bestblocks[0] == bestblocks[
            1], "node[0,1] have different best hashes: {} vs {}".format(
                bestblocks[0], bestblocks[1])

        # mine a keystone interval of blocks to fork A
        self.nodes[0].generate(nblocks=self.keystoneInterval)
        self.sync_all(self.nodes[0:2])
        self.log.info(
            "nodes[0,1] are in sync and are at fork A (%d...%d blocks)",
            lastblock + 103, lastblock + 103 + self.keystoneInterval)

        # fork B is at 400
        assert bestblocks[2][
            'height'] == lastblock + 200, "unexpected tip: {}".format(
                bestblocks[2])
        self.log.info("node2 is at fork B (%d...%d blocks)", lastblock + 103,
                      lastblock + 200)

        assert 200 > 103 + self.keystoneInterval + 10, "keystone interval is set too high"

        # endorse block 303 + keystone interval (fork A tip)
        addr0 = self.nodes[0].getnewaddress()
        txid = endorse_block(self.nodes[0], self.apm,
                             lastblock + 103 + self.keystoneInterval, addr0)
        self.log.info("node0 endorsed block %d (fork A tip)",
                      lastblock + 103 + self.keystoneInterval)
        # mine pop tx on node0
        containinghash = self.nodes[0].generate(nblocks=10)
        self.log.info("node0 mines 10 more blocks")
        self.sync_all(self.nodes[0:2])
        containingblock = self.nodes[0].getblock(containinghash[0])

        assert_equal(self.nodes[1].getblock(containinghash[0])['hash'],
                     containingblock['hash'])

        tip = self.get_best_block(self.nodes[0])
        assert txid in containingblock['pop']['data'][
            'atvs'], "pop tx is not in containing block"
        self.sync_blocks(self.nodes[0:2])
        self.log.info(
            "nodes[0,1] are in sync, pop tx containing block is {}".format(
                containingblock['height']))
        self.log.info("node0 tip is {}".format(tip['height']))

        connect_nodes(self.nodes[0], 2)
        connect_nodes(self.nodes[1], 2)
        self.log.info("node2 connected to nodes[0,1]")

        self.start_node(3)
        connect_nodes(self.nodes[3], 0)
        connect_nodes(self.nodes[3], 2)
        self.log.info("node3 started with 0 blocks, connected to nodes[0,2]")

        self.sync_blocks(self.nodes, timeout=30)
        self.log.info("nodes[0,1,2,3] are in sync")

        # expected best block hash is fork A (has higher pop score)
        bestblocks = [self.get_best_block(x) for x in self.nodes]
        assert_equal(bestblocks[0]['hash'], bestblocks[1]['hash'])
        assert_equal(bestblocks[0]['hash'], bestblocks[2]['hash'])
        assert_equal(bestblocks[0]['hash'], bestblocks[3]['hash'])
        self.log.info("all nodes switched to common block")

        for i in range(len(bestblocks)):
            assert bestblocks[i]['height'] == tip['height'], \
                "node[{}] expected to select shorter chain ({}) with higher pop score\n" \
                "but selected longer chain ({})".format(i, tip['height'], bestblocks[i]['height'])

        # get best headers view
        blockchaininfo = [x.getblockchaininfo() for x in self.nodes]
        for n in blockchaininfo:
            assert_equal(n['blocks'], n['headers'])

        self.log.info("all nodes selected fork A as best chain")
        self.log.warning("_shorter_endorsed_chain_wins() succeeded!")
示例#18
0
    def _case1_endorse_keystone_get_paid(self):
        self.log.warning("running _case1_endorse_keystone_get_paid()")
        lastblock = self.nodes[0].getblockcount()
        self.nodes[0].generate(nblocks=10)
        self.nodes[0].waitforblockheight(lastblock + 10)
        lastblock = self.nodes[0].getblockcount()
        self.log.info("node0 mined 10 more blocks")

        # endorse block 5
        assert lastblock >= 5
        addr = self.nodes[0].getnewaddress()
        self.log.info("endorsing block {} on node0 by miner {}".format(
            lastblock - 5, addr))
        atv_id = endorse_block(self.nodes[0], self.apm, lastblock - 5, addr)

        # wait until node[1] gets relayed pop tx
        self.sync_pop_mempools(self.nodes)
        self.log.info("node1 got relayed transaction")

        # mine a block on node[1] with this pop tx
        containingblockhash = self.nodes[1].generate(nblocks=1)[0]
        containingblock = self.nodes[1].getblock(containingblockhash)
        self.log.info("node1 mined containing block={}".format(
            containingblock['hash']))
        self.nodes[0].waitforblockheight(containingblock['height'])
        self.log.info("node0 got containing block over p2p")

        # assert that txid exists in this block
        block = self.nodes[0].getblock(containingblockhash)

        assert atv_id in block['pop']['data']['atvs']

        # target height is lastblock - 5 + POP_PAYOUT_DELAY + 1
        n = lastblock - 5 + POP_PAYOUT_DELAY + 1 - block['height']
        payoutblockhash = self.nodes[1].generate(nblocks=n)[-1]
        self.sync_blocks(self.nodes)
        self.log.info("pop rewards paid")

        # check that expected block pays for this endorsement
        block = self.nodes[0].getblock(payoutblockhash)
        coinbasetxhash = block['tx'][0]
        coinbasetx = self.nodes[0].getrawtransaction(coinbasetxhash, 1)
        outputs = coinbasetx['vout']
        assert len(
            outputs
        ) > 3, "block with payout does not contain pop payout: {}".format(
            outputs)
        assert outputs[1]['n'] == 1
        assert outputs[1][
            'value'] > 0, "expected non-zero output at n=1, got: {}".format(
                outputs[1])

        # mine 100 blocks and check balance
        self.nodes[0].generate(nblocks=100)
        balance = self.nodes[0].getbalance()

        # node[0] has 210 (lastblock) mature coinbases and a single pop payout
        assert lastblock == 210, "calculation below are only valid for POP activation height = 210"
        pop_payout = float(outputs[1]['value'])
        half_payout = POW_PAYOUT / 2
        assert balance == POW_PAYOUT * 149 + half_payout * 50 + half_payout * .6 * 11 + pop_payout
        self.log.warning("success! _case1_endorse_keystone_get_paid()")