Exemplo n.º 1
0
class TestAuthenticator(TestCase):
    def setUp(self):
        self.ctx = zmq.asyncio.Context()
        self.w = Wallet()

        masternodes = [
            Wallet().verifying_key().hex(),
            Wallet().verifying_key().hex(),
            Wallet().verifying_key().hex(),
        ]
        delegates = [
            Wallet().verifying_key().hex(),
            Wallet().verifying_key().hex(),
            Wallet().verifying_key().hex(),
        ]

        self.c = ContractingClient()
        self.c.flush()

        sync.submit_from_genesis_json_file(cilantro_ee.contracts.__path__[0] +
                                           '/genesis.json',
                                           client=self.c)
        sync.submit_node_election_contracts(initial_masternodes=masternodes,
                                            boot_mns=1,
                                            initial_delegates=delegates,
                                            boot_dels=1,
                                            client=self.c)

        self.s = SocketAuthenticator(ctx=self.ctx)

    def tearDown(self):
        self.ctx.destroy()

        self.c.flush()

    def test_double_init(self):
        w = Wallet()

        with self.assertRaises(Exception):
            b = SocketAuthenticator(ctx=self.ctx)

    def test_add_verifying_key_as_bytes(self):
        sk = SigningKey.generate()

        self.s.add_verifying_key(sk.verify_key.encode())

        self.assertTrue(
            os.path.exists(
                os.path.join(self.s.cert_dir,
                             f'{sk.verify_key.encode().hex()}.key')))

    def test_sync_certs_creates_files(self):
        self.s.sync_certs()

        for m in self.s.contacts.masternodes:
            self.assertTrue(
                os.path.exists(os.path.join(self.s.cert_dir, f'{m}.key')))

        for d in self.s.contacts.delegates:
            self.assertTrue(
                os.path.exists(os.path.join(self.s.cert_dir, f'{d}.key')))

    def test_add_governance_sockets_all_creates_files(self):
        fake_mns = [
            Wallet().verifying_key(),
            Wallet().verifying_key(),
            Wallet().verifying_key()
        ]
        fake_od_m = Wallet().verifying_key()

        fake_dels = [Wallet().verifying_key(), Wallet().verifying_key()]
        fake_od_d = Wallet().verifying_key()

        self.s.add_governance_sockets(masternode_list=fake_mns,
                                      on_deck_masternode=fake_od_m,
                                      delegate_list=fake_dels,
                                      on_deck_delegate=fake_od_d)

        for m in fake_mns:
            self.assertTrue(
                os.path.exists(os.path.join(self.s.cert_dir,
                                            f'{m.hex()}.key')))

        for d in fake_dels:
            self.assertTrue(
                os.path.exists(os.path.join(self.s.cert_dir,
                                            f'{d.hex()}.key')))

        self.assertTrue(
            os.path.exists(
                os.path.join(self.s.cert_dir, f'{fake_od_m.hex()}.key')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.s.cert_dir, f'{fake_od_d.hex()}.key')))
Exemplo n.º 2
0
class Node:
    def __init__(self,
                 socket_base,
                 ctx: zmq.asyncio.Context,
                 wallet,
                 constitution: dict,
                 overwrite=False,
                 bootnodes=[],
                 network_parameters=NetworkParameters(),
                 driver=BlockchainDriver(),
                 mn_seed=None,
                 debug=True,
                 store=False):

        self.driver = driver
        self.store = store

        self.blocks = None
        if self.store:
            self.blocks = MasterStorage()

        self.waiting_for_confirmation = False

        self.log = get_logger('NODE')
        self.log.propagate = debug
        self.log.info(constitution)
        self.socket_base = socket_base
        self.wallet = wallet
        self.ctx = ctx
        self.ctx.max_sockets = 50_000

        self.client = ContractingClient(
            driver=self.driver,
            submission_filename=cilantro_ee.contracts.__path__[0] +
            '/submission.s.py')

        # Sync contracts

        sync.submit_from_genesis_json_file(cilantro_ee.contracts.__path__[0] +
                                           '/genesis.json',
                                           client=self.client)
        sync.submit_node_election_contracts(
            initial_masternodes=constitution['masternodes'],
            boot_mns=constitution['masternode_min_quorum'],
            initial_delegates=constitution['delegates'],
            boot_dels=constitution['delegate_min_quorum'],
            client=self.client)

        self.driver.commit()
        self.driver.clear_pending_state()

        self.contacts = VKBook(
            client=self.client,
            boot_mn=constitution['masternode_min_quorum'],
            boot_del=constitution['delegate_min_quorum'],
        )

        self.current_masters = deepcopy(self.contacts.masternodes)
        self.current_delegates = deepcopy(self.contacts.delegates)

        self.parameters = Parameters(socket_base,
                                     ctx,
                                     wallet,
                                     contacts=self.contacts)

        self.socket_authenticator = SocketAuthenticator(ctx=self.ctx)

        self.elect_masternodes = self.client.get_contract('elect_masternodes')
        self.elect_delegates = self.client.get_contract('elect_delegates')

        self.masternode_contract = self.client.get_contract('masternodes')
        self.delegate_contract = self.client.get_contract('delegates')

        self.update_sockets()

        # Cilantro version / upgrade

        self.version_state = self.client.get_contract('upgrade')
        self.active_upgrade = self.version_state.quick_read('upg_lock')

        self.tol_mn = self.version_state.quick_read('tol_mn')
        self.tot_dl = self.version_state.quick_read('tot_dl')

        if self.tol_mn is None:
            self.tol_mn = 0

        if self.tot_dl is None:
            self.tot_dl = 0

        self.all_votes = self.tol_mn + self.tot_dl
        self.mn_votes = self.version_state.quick_read('mn_vote')
        self.dl_votes = self.version_state.quick_read('dl_vote')
        # self.pending_cnt = self.all_votes - self.vote_cnt
        # stuff

        self.network_parameters = network_parameters

        self.bootnodes = bootnodes
        self.constitution = constitution
        self.overwrite = overwrite

        # Should have a function to get the current NBN
        self.block_fetcher = BlockFetcher(
            wallet=self.wallet,
            ctx=self.ctx,
            parameters=self.parameters,
        )

        self.network = Network(
            wallet=self.wallet,
            ctx=self.ctx,
            socket_base=socket_base,
            bootnodes=self.bootnodes,
            params=self.network_parameters,
            initial_del_quorum=deepcopy(self.contacts.delegate_quorum_min),
            initial_mn_quorum=deepcopy(self.contacts.masternode_quorum_min),
            mn_to_find=deepcopy(self.contacts.masternodes),
            del_to_find=deepcopy(self.contacts.delegates),
            mn_seed=mn_seed)

        self.nbn_inbox = NBNInbox(socket_id=self.network_parameters.resolve(
            self.socket_base,
            service_type=ServiceType.BLOCK_NOTIFICATIONS,
            bind=True),
                                  ctx=self.ctx,
                                  driver=self.driver,
                                  contacts=self.contacts,
                                  wallet=wallet)

        self.reward_manager = RewardManager(driver=self.driver, debug=True)

        self.running = False

    async def catchup(self, mn_seed):
        current = self.driver.get_latest_block_num()
        latest = await self.block_fetcher.get_latest_block_height(mn_seed)

        if current == 0:
            current = 1

        for i in range(current, latest):
            block = await self.block_fetcher.get_block_from_master(i, mn_seed)
            block = block.to_dict()
            self.process_block(block)

        while len(self.nbn_inbox.q) > 0:
            block = self.nbn_inbox.q.pop(0)
            self.process_block(block)

    def should_process(self, block):
        if self.waiting_for_confirmation:
            return self.driver.latest_block_num <= block['blockNum'] and block[
                'hash'] != 'f' * 64
        else:
            return self.driver.latest_block_num < block['blockNum'] and block[
                'hash'] != 'f' * 64

    def process_block(self, block):
        # self.driver.reads.clear()
        # self.driver.cache.clear()
        #
        # self.log.info(f'PENDING WRITES :{self.driver.pending_writes}')
        # self.driver.pending_writes.clear()

        if self.should_process(block):
            self.log.info('Processing new block...')
            self.driver.update_with_block(block)
            self.reward_manager.issue_rewards(block=block)
            self.update_sockets()

            if self.store:
                self.blocks.store_block(block)
                #self.reward_manager.issue_rewards(block=block)
                #self.update_sockets()
        else:
            self.log.error('Could not store block...')
            if self.driver.latest_block_num >= block['blockNum']:
                self.log.error(
                    f'Latest block num = {self.driver.latest_block_num}')
                self.log.error(f'New block num = {block["blockNum"]}')
            if block['hash'] == 'f' * 64:
                self.log.error(f'Block hash = {block["hash"]}')
            self.driver.delete_pending_nonces()

        self.driver.cache.clear()
        self.nbn_inbox.clean()
        self.nbn_inbox.update_signers()

        self.version_check()

    async def start(self):
        await self.network.start()

        # Start block server
        asyncio.ensure_future(self.nbn_inbox.serve())

        # Catchup when joining the network
        if self.network.mn_seed is not None:
            await self.catchup(
                self.network_parameters.resolve(self.network.mn_seed,
                                                ServiceType.BLOCK_SERVER))

            self.log.info(self.network.peers())

            self.parameters.sockets.update(self.network.peers())

        # Start block server
        #asyncio.ensure_future(self.nbn_inbox.serve())

        self.running = True

    def stop(self):
        self.network.stop()
        self.nbn_inbox.stop()
        self.running = False

    def update_sockets(self):
        od_mn = self.elect_masternodes.quick_read('top_candidate')
        od_dl = self.elect_delegates.quick_read('top_candidate')

        masternodes = self.masternode_contract.quick_read('S', 'members')
        delegates = self.delegate_contract.quick_read('S', 'members')

        self.socket_authenticator.add_governance_sockets(
            masternode_list=masternodes,
            delegate_list=delegates,
            on_deck_masternode=od_mn,
            on_deck_delegate=od_dl)

    def version_check(self):

        # check for trigger
        self.version_state = self.client.get_contract('upgrade')
        self.mn_votes = self.version_state.quick_read('mn_vote')
        self.dl_votes = self.version_state.quick_read('dl_vote')

        if self.version_state:
            self.log.info('Waiting for Consensys on vote')
            self.log.info('num masters voted -> {}'.format(self.mn_votes))
            self.log.info('num delegates voted -> {}'.format(self.dl_votes))

            # Given node is either master or delegate default assumes it to be valid
            # delegate

            node_type = False

            for key in self.contacts.masternodes:
                if self.wallet.verifying_key().hex() == key:
                    self.log.info("Node Type : Master")
                    node_type = True

            # check for vote consensys
            vote_consensus = self.version_state.quick_read('upg_consensus')
            if vote_consensus:
                self.log.error('Rebooting Node with new version')
                version_reboot(bn=self.bootnodes, is_master=node_type)
            else:
                self.log.info(
                    '{} waiting for vote on upgrade'.format(node_type))

    def get_update_state(self):
        self.active_upgrade = self.version_state.quick_read('upg_lock')
        start_time = self.version_state.quick_read('upg_init_time')
        window = self.version_state.quick_read('upg_window')
        pepper = self.version_state.quick_read('upg_pepper')
        self.mn_votes = self.version_state.quick_read('mn_vote')
        self.dl_votes = self.version_state.quick_read('dl_vote')
        consensus = self.version_state.quick_read('upg_consensus')

        print("Upgrade -> {} Cil Pepper   -> {}\n"
              "Init time -> {}, Time Window -> {}\n"
              "Masters      -> {}\n"
              "Delegates    -> {}\n"
              "Votes        -> {}\n "
              "MN-Votes     -> {}\n "
              "DL-Votes     -> {}\n "
              "Consensus    -> {}\n".format(self.active_upgrade, pepper,
                                            start_time, window, self.tol_mn,
                                            self.tot_dl, self.all_votes,
                                            self.mn_votes, self.dl_votes,
                                            consensus))