Beispiel #1
0
    def new_block(self, block, peers):
        if len(peers) > 0:
            # start consensus for active peers
            self.start_consensus(block)
            return False

        if self.check_consensus(block):
            # at this point state PREPARED
            LOGGER.info('Passed consensus check in state PREPARED: %s ',
                        _short_id(block.block_id.hex()))
            if block.block_num == 3 and self._try_branch:
                # try make branch pause current block for main branch
                self._make_branch = True
                self._try_branch = False
                if self._freeze:
                    self._freeze_block = block
                else:
                    self.check_block(block.block_id)
            else:
                self.check_block(
                    block.block_id
                )  # this message send chain controller message for continue block validation
                # waiting block valid message
            return True
        else:
            LOGGER.info('Failed consensus blk=%s branch=%s',
                        _short_id(block.block_id.hex()), self._head_id[:8])
            self.reset_state()
            self.fail_block(block.block_id)
            return False
Beispiel #2
0
    def _handle_invalid_block(self, block_id):

        try:
            block = self._get_block(block_id)
            signer_id = block.signer_id.hex()
            bid = block_id.hex()
            LOGGER.info('=> INVALID_BLOCK:Received id=%s signer=%s\n',
                        _short_id(bid), signer_id[:8])
            if bid in self._new_heads:
                self._new_heads.pop(bid)
            if signer_id == self._validator_id:
                if block.previous_block_id in self._branches:
                    branch = self._branches[block.previous_block_id]
                    if bid in self._peers_branches:
                        del self._peers_branches[bid]
                    if block.block_num == 0:
                        branch.reset_state()
                    else:
                        LOGGER.info('=> INVALID_BLOCK: DONT DO reset \n')
            else:
                LOGGER.info(
                    '=> INVALID_BLOCK: external block=%s branches=%s \n',
                    bid[:8], [key[:8] for key in self._peers_branches.keys()])
                if bid in self._peers_branches:
                    branch = self._peers_branches[bid]
                    branch = self.reset_state()
                    del self._peers_branches[bid]
            LOGGER.info('=> INVALID_BLOCK: branches=%s \n',
                        [key[:8] for key in self._peers_branches.keys()])
        except:
            LOGGER.info('=> INVALID_BLOCK: undefined id=%s\n',
                        _short_id(block_id.hex()))
        self.reset_state()
Beispiel #3
0
 def __str__(self):
     return ("Block(" + ", ".join([
         "block_num: {}".format(self.block_num),
         "block_id: {}".format(_short_id(self.block_id.hex())),
         "previous_id: {}".format(_short_id(self.previous_id.hex())),
         "signer_id: {}".format(_short_id(self.signer_id.hex())),
         "payload: {}".format(self.payload),
         "summary: {}".format(self.summary.hex()),
     ]) + ")")
Beispiel #4
0
    def __init__(self, block):
        # fields that come with consensus blocks

        self.block_id = block.block_id
        self.previous_id = block.previous_id
        self.signer_id = block.signer_id
        self.block_num = block.block_num
        self.payload = block.payload
        self.summary = block.summary

        # fields that bgt requires
        identifier = block.block_id.hex()
        previous_block_id = block.previous_id.hex()
        signer_public_key = block.signer_id.hex()

        self.identifier = identifier
        self.header_signature = identifier
        self.previous_block_id = previous_block_id
        self.signer_public_key = signer_public_key
        LOGGER.debug('BgtBlock: block=%s.%s(%s)', self.block_num,
                     _short_id(identifier), signer_public_key[:8])
        self.header = _DummyHeader(consensus=block.payload,
                                   signer_public_key=signer_public_key,
                                   previous_block_id=previous_block_id)

        # this is a trick
        self.state_root_hash = block.block_id
Beispiel #5
0
 def resolve_fork(self, chain_head, block):
     LOGGER.info(
         'Branch[%s] Choosing between chain heads current:%s new:%s',
         self._head_id[:8], _short_id(chain_head.block_id.hex()),
         _short_id(block.block_id.hex()))
     if self.switch_forks(chain_head, block):
         LOGGER.info('Committing block=%s for BRANCH=%s',
                     _short_id(block.block_id.hex()), self._head_id[:8])
         self.commit_block(block.block_id)
         self._committing = True
         return True
     else:
         LOGGER.info('Ignoring block=%s for BRANCH=%s',
                     _short_id(block.block_id.hex()), self._head_id[:8])
         self.reset_state()
         self.ignore_block(block.block_id)
         return False
Beispiel #6
0
    def _handle_valid_block(self, block_id):
        LOGGER.info('=> VALID_BLOCK:Received %s', _short_id(block_id.hex()))
        block = self._get_block(block_id)

        self._pending_forks_to_resolve.push(block)
        self._committing = False
        self._process_pending_forks()
        LOGGER.info('VALID_BLOCK  pending_forks DONE.')
Beispiel #7
0
 def _broadcast(self, payload, msg_type, block_id):
     # broadcast message
     block_id = block_id.hex()
     #state.shift_sequence_number(block_id,self._consensus_state_store)
     mgs_type = CONSENSUS_MSG[msg_type]
     LOGGER.debug("BROADCAST =>> '%s' for block_id=%s", mgs_type,
                  _short_id(block_id))
     self._service.broadcast(mgs_type, payload.SerializeToString())
Beispiel #8
0
 def _handle_peer_connected(self, block):
     #block = BgtBlock(block)
     pinfo = ConsensusNotifyPeerConnected()
     #info = pinfo.ParseFromString(block)
     pid = block.peer_id.hex()
     if pid not in self._peers and pid != self._validator_id:
         self._peers[pid] = True
         LOGGER.info('Connected peer with ID=%s\n', _short_id(pid))
Beispiel #9
0
    def switch_forks(self, cur_fork_head, new_fork_head):
        '''"compare_forks" is not an intuitive name.'''
        LOGGER.debug('BgtOracle: switch_forks %s', cur_fork_head)
        if self._can_fail_block and new_fork_head.block_num == 3:
            self._can_fail_block = False
            LOGGER.debug('BgtOracle: MAKE forks FAIL FOR TEST ONLY %s\n',
                         cur_fork_head)
            return False  # FOR TESTING ONLY
        if new_fork_head.block_num > cur_fork_head.block_num or (
                new_fork_head.block_num == cur_fork_head.block_num
                and new_fork_head.block_id > cur_fork_head.block_id):
            LOGGER.debug(
                'BgtOracle: switch_forks new-num=%s cur-num=%s new-id=%s cur-id=%s',
                new_fork_head.block_num, cur_fork_head.block_num,
                _short_id(new_fork_head.block_id.hex()),
                _short_id(cur_fork_head.block_id.hex()))
            return True
        elif new_fork_head.block_num < cur_fork_head.block_num:
            #
            chain_block = cur_fork_head
            LOGGER.debug(
                'BgtOracle: new_fork_head.block_num < cur_fork_head.block_num')
            while (True):
                chain_block = BgtBlock(
                    self._service.get_blocks([chain_block.previous_id
                                              ])[chain_block.previous_id])
                if chain_block.block_num == new_fork_head.block_num:
                    LOGGER.debug('BgtOracle: found block')
                    break
            if new_fork_head.block_id > chain_block.block_id:
                LOGGER.debug('BgtOracle: switch to new forks')
                return True

        return False
        fork_resolver = BgtForkResolver(
            block_cache=self._block_cache,
            state_view_factory=self._state_view_factory,
            data_dir=self._data_dir,
            config_dir=self._config_dir,
            validator_id=self._validator_id)

        return fork_resolver.compare_forks(cur_fork_head, new_fork_head)
Beispiel #10
0
    def _resolve_fork(self, block):
        # ask head for branch bid
        bid = block.previous_block_id
        bbid = bytes.fromhex(bid)
        signer_id = block.signer_id.hex()
        block_id = block.block_id.hex()
        if signer_id == self._validator_id:
            if bid in self._branches:
                # head could be already changed - we can get new head for this branch
                chain_head = self._get_chain_head(bbid)
                branch = self._branches[bid]
                if branch.resolve_fork(chain_head, block):
                    self._committing = True
                else:
                    self.reset_state()
            else:
                LOGGER.info('HEAD FOR BLOCK=%s(%s) was changed',
                            block.block_num, _short_id(block_id),
                            signer_id[:8])
        else:
            # external block

            LOGGER.info('EXTERNAL BLOCK=%s(%s) num=%s prev=%s',
                        _short_id(block_id), signer_id[:8], block.block_num,
                        bid[:8])
            if block_id in self._peers_branches:
                # head could be already changed - we can get new head for this branch
                chain_head = self._get_chain_head(
                    None if bid == NULL_BLOCK_IDENTIFIER else
                    bbid)  # ask head for branch
                branch = self._peers_branches[block_id]
                LOGGER.info('RESOLVE FORK for BLOCK=%s(%s) branch=%s',
                            _short_id(block_id), signer_id[:8], branch.ind)
                if branch.resolve_fork(chain_head, block):
                    self._committing = True
                else:
                    self.reset_state()
Beispiel #11
0
    def _my_finalize_block(self):
        """
        in case DAG we should return parent for block which is ready  
        because we ask one of the initialized blocks
        """
        summary, parent_id = self._summarize_block()

        if summary is None:
            #LOGGER.debug('Block not ready to be summarized')
            return None
        bid = parent_id.hex()
        LOGGER.debug('Can FINALIZE NOW parent=%s branches=%s', _short_id(bid),
                     [key[:8] for key in self._branches.keys()])
        if bid in self._branches:
            LOGGER.debug('FINALIZE BRANCH=%s', bid[:8])
            branch = self._branches[bid]
            branch.finalize_block(parent_id, summary)
        """
Beispiel #12
0
    def __init__(self, service, component_endpoint, config_dir, data_dir,
                 key_dir):
        self._config_dir = config_dir
        self._data_dir = data_dir
        LOGGER.debug('BgtOracle: Stream key_dir=%s', key_dir)
        self._signer = _load_identity_signer(key_dir, 'validator')
        self._validator_id = self._signer.get_public_key().as_hex()
        self._service = service
        LOGGER.debug('BgtOracle: Stream component_endpoint=%s',
                     component_endpoint)
        stream = Stream(component_endpoint)

        self._block_cache = _BlockCacheProxy(service, stream)
        self._state_view_factory = _StateViewFactoryProxy(service)

        self._batch_publisher = _BatchPublisherProxy(stream, self._signer)
        self._publisher = None
        self._can_fail_block = False  #True
        LOGGER.debug('BgtOracle:validator=%s init DONE',
                     _short_id(self._validator_id))
Beispiel #13
0
    def finalize_block(self, parent_id, summary):
        consensus = b'Devmode'  #self._oracle.finalize_block(summary)
        if consensus is None:
            return None

        try:
            # say to validator that we are ready to finalize this block
            block_id = self._service.finalize_block(parent_id, consensus)
            LOGGER.info('Finalized summary=%s block_id=%s BRANCH=%s', summary,
                        _short_id(block_id.hex()), self._head_id[:8])
            self._building = True  # ONLY for testing new version - normal True
            self._published = True  # ONLY for testing new version- normal True
            # broadcast
            #LOGGER.debug('broadcast ...')
            #self._service.broadcast('message_type',b'payload')
            return block_id
        except exceptions.BlockNotReady:
            LOGGER.debug('Block not ready to be finalized')
            return None
        except exceptions.InvalidState:
            LOGGER.warning('block cannot be finalized')
            return None
Beispiel #14
0
 def commit_block(self, block_id):
     LOGGER.warning("commit_block: block_id=%s\n",
                    _short_id(block_id.hex()))
     self._service.commit_block(block_id)
Beispiel #15
0
    def _handle_committed_block(self, block_id):
        block_id = block_id.hex()

        def _update_head_branch(prev_num, block_num):
            LOGGER.info('   _update_head_branch check=%s', prev_num)
            for key, branch in self._branches.items():
                if branch.block_num == prev_num:
                    LOGGER.info('   update chain head for BRANCH=%s -> %s',
                                branch.ind, block_id[:8])
                    branch = self._branches.pop(key)
                    branch._parent_id = block_id
                    branch._block_num = block_num
                    self._branches[block_id] = branch
                    return True
            return False

        LOGGER.info(
            '=> BLOCK_COMMIT Chain head updated to %s, abandoning block in progress heads=%s',
            _short_id(block_id), [key[:8] for key in self._new_heads.keys()])
        # for DAG new head for branch will be this block_id
        # and we should use it for asking chain head for this branch
        if block_id in self._new_heads:
            hid = self._new_heads.pop(block_id)  # hid is parent of this block
            LOGGER.info(
                '   update chain head for BRANCH=%s->%s branches=%s+%s',
                hid[:8], block_id[:8],
                [key[:8] for key in self._branches.keys()],
                [key[:8] for key in self._peers_branches.keys()])
            if hid in self._branches:
                branch = self._branches.pop(hid)
                branch.cancel_block(block_id)  # change parent_id too
                branch.reset_state()
                self._branches[block_id] = branch
                self._TOTAL_BLOCK += 1
                if block_id in self._peers_branches:
                    del self._peers_branches[block_id]
                LOGGER.info(
                    '   set new head=%s for BRANCH=%s TOTAL=%s peers branches=%s',
                    block_id[:8], hid[:8], self._TOTAL_BLOCK,
                    [key[:8] for key in self._peers_branches.keys()])
            else:
                # external block
                LOGGER.info('head updated for=%s  peers branches=%s',
                            _short_id(block_id),
                            [key[:8] for key in self._peers_branches.keys()])
                if block_id in self._peers_branches:
                    branch = self._peers_branches[block_id]
                    branch.cancel_block(block_id)  # change parent_id too
                    branch.reset_state()
                    del self._peers_branches[block_id]
                    # update _branches
                    block = self._get_block(bytes.fromhex(block_id))
                    prev = block
                    while not _update_head_branch(prev.block_num,
                                                  block.block_num):
                        prev = self._get_block(
                            bytes.fromhex(prev.previous_block_id))

        LOGGER.info('=> BLOCK_COMMIT prepre=%s pre=%s branches=%s+%s',
                    len(self._pre_prepare_msgs), len(self._prepare_msgs),
                    [key[:8] for key in self._branches.keys()],
                    [key[:8] for key in self._peers_branches.keys()])
        self._process_pending_forks()
        self.reset_state()
Beispiel #16
0
    def _initialize_block(self, branch=None, new_branch=None, is_new=False):
        LOGGER.debug('BgtEngine: _initialize_block branch[%s] is_new=%s',
                     branch.hex()[:8] if branch is not None else None, is_new)
        """
        getting addition chain head for DAG in case call _get_chain_head(parent_head) where parent_head is point for making chain branch
        """
        try:
            # for switch current branch to another node add argument new_branch
            chain_head = self._get_chain_head(
                branch, new_branch, is_new
            )  # get MAIN chain_head. chain_head.block_id is ID of parent's block
            if not self._chain_head:
                self._chain_head = chain_head
        except exceptions.TooManyBranch:
            LOGGER.debug(
                'BgtEngine: CANT CREATE NEW BRANCH (limit is reached)')
            self._make_branch = False
            return False
        except exceptions.NoChainHead:
            # head was updated or not commited yet
            LOGGER.debug('BgtEngine: CANT GET CHAIN HEAD for=%s',
                         branch.hex()[:8] if branch is not None else None)
            return False
        LOGGER.debug('BgtEngine: _initialize_block ID=%s chain_head=(%s)',
                     _short_id(chain_head.block_id.hex()), chain_head)
        #initialize = True #self._oracle.initialize_block(chain_head)

        #if initialize:
        try:
            self._service.initialize_block(previous_id=chain_head.block_id)
            # for remove this branch to another point
            bid = branch.hex(
            ) if branch is not None else chain_head.block_id.hex()
            parent = chain_head.previous_block_id
            block_num = chain_head.block_num
            if bid in self._branches:
                #branch = self._branches[bid]

                branch = self._branches[bid]
                branch._published = True
                branch._parent_id = parent
                branch._block_num = block_num
                if new_branch is not None:
                    del self._branches[bid]
                    self._branches[new_branch.hex()] = branch
                LOGGER.debug('BgtEngine: _initialize_block USE Branch[%s]=%s',
                             branch.ind, bid[:8])
            else:
                LOGGER.debug('BgtEngine: _initialize_block NEW Branch[%s]=%s',
                             self._num_branches, bid[:8])
                self._branches[bid] = self.create_branch(
                    bid, parent, block_num)
                self._branches[bid]._try_branch = self._make_branch

        except exceptions.UnknownBlock:
            LOGGER.debug('BgtEngine: _initialize_block ERROR UnknownBlock')
            #return False
        except exceptions.InvalidState:
            LOGGER.debug('BgtEngine: _initialize_block ERROR InvalidState')
            self._skip = True
            return False
        except Exception as ex:
            LOGGER.debug(
                'BgtEngine: _initialize_block HEAD=%s.%s ERROR %s!!!\n',
                chain_head.block_num,
                chain_head.block_id.hex()[:8], ex)
            return False

        return True
Beispiel #17
0
 def check_block(self, block_id):
     # send in case of consensus was reached
     LOGGER.warning("check_block: block_id=%s\n", _short_id(block_id.hex()))
     self._service.check_blocks([block_id])
Beispiel #18
0
    def _handle_new_block(self, block):
        block = BgtBlock(block)
        block_id = block.block_id.hex()
        signer_id = block.signer_id.hex()
        summary = block.summary.hex()
        block_num = block.block_num
        num_peers = len(self._peers)
        LOGGER.info('=> NEW_BLOCK:Received block=%s.%s signer=%s summ=%s',
                    block_num, _short_id(block_id), signer_id[:8], summary[:8])

        def check_consensus():
            if num_peers > 0 and summary in self._prepare_msgs:
                blocks = self._prepare_msgs[summary]
                self.check_consensus(blocks, block_num, summary, num_peers)

        if signer_id == self._validator_id:
            # find branch for this block
            if block.previous_block_id in self._branches:
                branch = self._branches[block.previous_block_id]
                self._peers_branches[
                    block_id] = branch  # add ref on branch for own block
                result = branch.new_block(block, self._peers)
                if result == Consensus.done:
                    # refer for block on branch
                    self._new_heads[block_id] = block.previous_block_id
                    LOGGER.info('   NEW_HEAD=%s for BRANCh=%s', block_id[:8],
                                block.previous_block_id[:8])
                elif result == Consensus.fail:
                    LOGGER.info('Failed consensus check: %s', block_id[:8])
                else:
                    LOGGER.info('consensus started for block=%s->%s',
                                block_id[:8], block.previous_block_id[:8])
                    self._new_heads[block_id] = block.previous_block_id
                    if block_id in self._pre_prepare_msgs:
                        """
                        free PRE_PREPARE mess here. Now we have new_block and will ignore PRE_PREPARE
                        """
                        msg = self._pre_prepare_msgs[
                            block_id]  # self._pre_prepare_msgs.pop(block_id)
                        branch._send_prepare(msg)

                    check_consensus()
                    # Don't reset now - wait message INVALID_BLOCK
                    #self.reset_state()
        else:
            # external block from another node

            if block_id not in self._peers_branches:
                LOGGER.info('EXTERNAL NEW BLOCK=%s.%s peer=%s', block_num,
                            _short_id(block_id), _short_id(signer_id))
                branch = self.create_branch('', '', block_num)
                self._peers_branches[block_id] = branch
                LOGGER.info('START CONSENSUS for BLOCK=%s(%s) branch=%s',
                            _short_id(block_id), signer_id[:8], branch.ind)
                branch.new_block(block, self._peers)
                self._new_heads[block_id] = block.previous_block_id
                LOGGER.info('   NEW_HEAD=%s for BRANCh=%s', block_id[:8],
                            block.previous_block_id[:8])
                if block_id in self._pre_prepare_msgs:
                    """
                    free PRE_PREPARE mess here. Now we have new_block and will ignore PRE_PREPARE
                    """
                    msg = self._pre_prepare_msgs[
                        block_id]  # self._pre_prepare_msgs.pop(block_id)
                    branch._send_prepare(msg)
                # check maybe all messages arrived
                check_consensus()
            else:
                LOGGER.info(
                    'EXTERNAL BLOCK=%s.%s num=%s peer=%s IGNORE(already has)',
                    block_num, _short_id(block_id), _short_id(signer_id))