def consensus_state_for_block_id(block_id,
                                     block_cache,
                                     state_view_factory,
                                     consensus_state_store,
                                     node=None,
                                     force=True):
        """Returns the consensus state for the block referenced by block ID,
            creating it from the consensus state history if necessary.

        Args:
            block_id (str): The ID of the block for which consensus state will
                be returned.
            block_cache (BlockCache): The block store cache
            state_view_factory (StateViewFactory): A factory that can be used
                to create state view object corresponding to blocks
            consensus_state_store (ConsensusStateStore): The consensus state
                store that is used to store interim consensus state created
                up to resulting consensus state
            

        Returns:
            ConsensusState object representing the consensus state for the
                block referenced by block_id
        """
        #LOGGER.debug('ConsensusState: consensus_state_for_block_id for block_id=%s',block_id)
        consensus_state = None
        #previous_wait_certificate = None
        #blocks = collections.OrderedDict()

        # Starting at the chain head, walk the block store backwards until we
        # either get to the root or we get a block for which we have already
        # created consensus state
        current_id = block_id
        #block = ConsensusState._block_for_id(block_id=current_id,block_cache=block_cache)
        #LOGGER.debug("ConsensusState: BLOCK for block_id=%s block=%s",_short_id(current_id),block)
        #LOGGER.debug("ConsensusState: ASK STATE for block_id=%s",current_id)
        consensus_state = consensus_state_store.get(block_id=current_id)
        if consensus_state is not None:

            LOGGER.debug(
                "ConsensusState: FOUND CONSENSUS_STATE for block_id=%s",
                _short_id(current_id))
            pass
        elif force:

            LOGGER.debug(
                "ConsensusState: CREATE CONSENSUS_STATE node=%s for block_id=%s",
                node, _short_id(current_id))
            consensus_state = ConsensusState(node)
        else:
            return None

        return consensus_state
        """
Esempio n. 2
0
    def _my_finalize_block(self):
        summary = self._summarize_block()

        if summary is None:
            #LOGGER.debug('Block not ready to be summarized')
            return None
        LOGGER.debug('Can FINALIZE NOW')
        consensus = b'devmode' #self._oracle.finalize_block(summary)

        if consensus is None:
            return None

        try:
            block_id = self._service.finalize_block(consensus)
            LOGGER.info('Finalized %s with ',_short_id(block_id.hex())) #json.loads(consensus.decode())
            self._building = True
            # broadcast 
            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
        except Exception as err:
            LOGGER.warning("error=%s",err)
            return None
Esempio n. 3
0
 def push(self, block):
     try:
         index = self._queue.index(block.previous_id)
     except ValueError:
         self._queue.insert(0, block.block_id)
         self._blocks[block.block_id] = block
         LOGGER.debug('PendingForks PUSH block_id=%s',_short_id(block.block_id.hex()))
         return
     try:
         del self._blocks[block.previous_id]
         LOGGER.debug('PendingForks DEL block_id=%s',_short_id(block.previous_id.hex()))
     except KeyError:
         pass
     LOGGER.debug('PendingForks PUSH  block_id=%s INSTEAD prev',_short_id(block.block_id.hex()))
     self._queue[index] = block.block_id
     self._blocks[block.block_id] = block
Esempio n. 4
0
 def _handle_peer_connected(self, block):
     """
     Messages about new peers
     """
     #block = PbftBlock(block)
     if not self._is_peer_connected:
         LOGGER.debug('=> PEER_CONNECTED: peer=%s', _short_id(block.peer_id.hex()))
         self._is_peer_connected = True
Esempio n. 5
0
 def _handle_valid_block(self, block_id):
     LOGGER.info('=> VALID_BLOCK:Received block_id=%s\n', _short_id(block_id.hex()))
     block = self._get_block(block_id)
     self._resolve_fork(block)    
     #self._pending_forks_to_resolve.push(block)
     #self._process_pending_forks()
     # after pending_forks block could be ingored
     self._oracle.message_consensus_handler(Message.CONSENSUS_NOTIFY_BLOCK_VALID,block)
Esempio n. 6
0
    def _resolve_fork(self, block):
        chain_head = self._get_chain_head()

        LOGGER.info('Choosing between chain heads -- current: %s -- new: %s',_short_id(chain_head.block_id.hex()),_short_id(block.block_id.hex()))

        if self._switch_forks(chain_head, block):
            LOGGER.info('stop process _process_pending_forks for block=%s', _short_id(block.block_id.hex()))
            #self._commit_block(block.block_id)
            # stop process _process_pending_forks()
            self._committing = True   
        else:
            LOGGER.info('Ignoring block=%s', _short_id(block.block_id.hex()))
            # mark block as ignored
            self._oracle.ignore_block(block)

            #self._fail_block(block.block_id)
            #self._cancel_block() # try cancel 
            self._ignore_block(block.block_id)
Esempio n. 7
0
    def _check_consensus(self, block= None):
        #if not self._is_peer_connected :
        return True
        if self._oracle.check_consensus(self._block):
            LOGGER.info('PbftEngine:Passed consensus check: %s', _short_id(self._block.block_id.hex()))
            self._check = False # stop checking
            #self._check_block(block.block_id)
        else:
            #LOGGER.info('PbftEngine: Failed consensus check: %s', self._block.block_id.hex())
            #self._fail_block(self._block.block_id)
            pass

        #self._start_consensus(block)
        return True
Esempio n. 8
0
    def _finalize_block(self):
        summary = self._summarize_block()

        if summary is None:
            #LOGGER.debug('Block not ready to be summarized')
            return None
        # only after that point we can receive new block msg
        LOGGER.debug('Block ready to be finalized summary=%s\n',_short_id(summary.hex()))
        consensus = self._oracle.finalize_block(summary)
        
        if consensus is None:
            return None
        self._block_id = None # should do initialize
        try:
            """
            Stop adding batches to the current block and finalize it. Include the given consensus data in the block.
            If this call is successful, a BlockNew update will be received with the new block
            """
            block_id = self._service.finalize_block(consensus)
            """
            now we have block_id for block with summary
            """
            #LOGGER.info('Finalized %s with %s',block_id.hex(),json.loads(consensus.decode()))
            LOGGER.info('Finalized block_id=%s DONE\n',_short_id(block_id.hex()))
            return block_id
        except exceptions.BlockNotReady:
            LOGGER.debug('PbftEngine:: Block not ready to be finalized')
            return None
        except exceptions.InvalidState:
            LOGGER.warning('PbftEngine::block cannot be finalized in InvalidState')
            self._building = False
            LOGGER.debug('PbftEngine: _cancel_block')
            self._cancel_block()
            return None
        except Exception as err:
            LOGGER.warning("PbftEngine::error=%s",err)
            return None
Esempio n. 9
0
    def _initialize_block(self):
        LOGGER.debug('PbftEngine: START _initialize_block')
        chain_head = self._get_chain_head()
        initialize = self._oracle.initialize_block(chain_head)
        LOGGER.debug('PbftEngine: _initialize_block initialize=%s ID=%s num=%s',initialize,_short_id(chain_head.block_id.hex()),chain_head.block_num)
        if initialize :
            try:

                
                if  self._block_id != chain_head.block_id:
                    self._block_id = chain_head.block_id # save current block id
                    self._block_num = chain_head.block_num
                    # open new block Candidate start from  chain_head.block_id 
                    self._service.initialize_block(previous_id=chain_head.block_id)
                    LOGGER.debug('PbftEngine: PROXY _initialize_block DONE\n')
                else:
                    self._block_id = chain_head.block_id
                    LOGGER.debug('PbftEngine: PROXY _initialize_block ALREADY WAS DONE\n')
            except exceptions.UnknownBlock:
                LOGGER.debug('BgtEngine: PROXY _initialize_block ERROR UnknownBlock')
                #return False
            except exceptions.InvalidState :
                LOGGER.debug('BgtEngine: PROXY _initialize_block ERROR InvalidState')
            except Exception as ex:
                LOGGER.debug('BgtEngine: PROXY _initialize_block ERROR %s',ex)
                return False
        return True #initialize
Esempio n. 10
0
 def _handle_committed_block(self, block_id):
     LOGGER.info('=> COMMITTED_BLOCK:Chain head updated to %s, abandoning block in progress',_short_id(block_id.hex()))
     block = self._get_block(block_id)
     self._oracle.message_consensus_handler(Message.CONSENSUS_NOTIFY_BLOCK_COMMIT,block)
     # make sure that bloock candidate is empty
     self._cancel_block()
     self.reset_loop_state()
Esempio n. 11
0
 def _handle_invalid_block(self,block_id):
     LOGGER.info('=> INVALID_BLOCK:Received id=%s\n', _short_id(block_id.hex()))
     block = self._get_block(block_id)
     self._oracle.message_consensus_handler(Message.CONSENSUS_NOTIFY_BLOCK_INVALID,block)
Esempio n. 12
0
    def _try_to_publish(self):
        if self._check:
            # check consensus after NEW BLOCK
            self._check_consensus()

        if self._published:
            return

        if not self._building:
            LOGGER.debug('_initialize_block OPEN NEW BLOCK CANDIDATE\n')
            if self._initialize_block():
                self._building = True
                LOGGER.debug('_initialize_block DONE')

        if self._building:
            #LOGGER.debug('PbftEngine: _check_publish_block ..')
            if self._check_publish_block():
                #LOGGER.debug('PbftEngine: _finalize_block ..')
                #self._check = False
                block_id = self._finalize_block()
                if block_id:
                    LOGGER.info("After finalize we have new Published block id=%s\n", _short_id(block_id.hex()))
                    self._published = True
                    #self._building = False
                    #self._consensus = True # ready for consensus
                """
Esempio n. 13
0
 def _fail_block(self, block_id):
     LOGGER.warning("PbftEngine: _fail_block: block_id=%s",_short_id(block_id.hex()))
     self._service.fail_block(block_id)
Esempio n. 14
0
 def _check_block(self, block_id):
     # just check there is this block or not 
     LOGGER.warning("PbftEngine: _check_block: block_id=%s",_short_id(block_id))
     self._service.check_blocks([block_id])
Esempio n. 15
0
    def finalize_block(self, block_header):
        """Finalize a block to be claimed. Provide any signatures and
        data updates that need to be applied to the block before it is
        signed and broadcast to the network.

        Args:
            block_header (BlockHeader): The block header for the candidate
                block that needs to be finalized.
        Returns:
            Boolean: True if the candidate block good and should be generated.
            False if the block should be abandoned.
        """
        summary = block_header.hex()
        LOGGER.debug('FINALIZE BLOCK CANDIDATE: block_id=%s summary=%s',_short_id(self._block_id),_short_id(summary))
        if isinstance(block_header, bytes):
            """
            At this point _block_id is previous and summary for current block
            save state with block_header key
            """
            """
            state = ConsensusState.consensus_state_for_block_id(
                    block_id=summary,
                    block_cache=self._block_cache,
                    state_view_factory=self._state_view_factory,
                    consensus_state_store=self._consensus_state_store,
                    force=True
                    )


            LOGGER.debug('FINALIZE BLOCK CANDIDATE: state=%s',state)
            # save in store
            state.set_consensus_state_for_block_id(summary,self._consensus_state_store)
            """
            """
            state_view = BlockWrapper.state_view_for_block(
                    block_wrapper=self._block_cache.block_store.chain_head,
                    state_view_factory=self._state_view_factory)
            """
            # We need to create a wait certificate for the block and
            # then serialize that into the block header consensus field.
            if _VREG_:
                active_key = self._pbft_key_state_store.active_key
                pbft_key_state = self._pbft_key_state_store[active_key]
                sealed_signup_data = pbft_key_state.sealed_signup_data
            consensus = b'pbft' 
            LOGGER.debug('PbftBlockPublisher::finalize_block isinstance DONE')
            return consensus

        # To compute the block hash, we are going to perform a hash using the
        # previous block ID and the batch IDs contained in the block
        hasher = hashlib.sha256(block_header.previous_block_id.encode())
        for batch_id in block_header.batch_ids:
            hasher.update(batch_id.encode())
        block_hash = hasher.hexdigest()

        # Using the current chain head, we need to create a state view so we
        # can create a PBFT enclave.
        state_view = BlockWrapper.state_view_for_block(
                block_wrapper=self._block_cache.block_store.chain_head,
                state_view_factory=self._state_view_factory)
        if _VREG_:
            # We need to create a wait certificate for the block and then serialize
            # that into the block header consensus field.
            active_key = self._pbft_key_state_store.active_key
            pbft_key_state = self._pbft_key_state_store[active_key]
            sealed_signup_data = pbft_key_state.sealed_signup_data

        #block_header.consensus = b'pbft' 
        LOGGER.debug('PbftBlockPublisher::finalize_block: DONE')

        return True
Esempio n. 16
0
    def initialize_block(self, block_header):
        """Do initialization necessary for the consensus to claim a block,
        this may include initiating voting activities, starting proof of work
        hash generation, or create a PBFT wait timer.

        Args:
            block_header (BlockHeader): The BlockHeader to initialize.
        Returns:
            Boolean: True if the candidate block should be built. False if
            no candidate should be built.
        """
        LOGGER.debug('PbftBlockPublisher::initialize_block previous_block_id=%s (%s)',_short_id(block_header.previous_block_id),block_header)
        # If the previous block ID matches our cached one, that means that we
        # have already determined that even if we initialize the requested
        # block we would not be able to claim it.  So, instead of wasting time
        # doing all of the checking again, simply short-circuit the failure so
        # that the validator can go do something more useful.
        if block_header.previous_block_id == PbftBlockPublisher._previous_block_id:
            LOGGER.debug("PbftBlockPublisher::initialize_block block_header.previous_block_id == PbftBlockPublisher._previous_block_id TRUE")
            return False
        PbftBlockPublisher._previous_block_id = block_header.previous_block_id
        # Using the current chain head, we need to create a state view so we
        # can create a PBFT enclave.
        if False:
            state_view = BlockWrapper.state_view_for_block(
                    block_wrapper=self._block_cache.block_store.chain_head,
                    state_view_factory=self._state_view_factory)

            pbft_settings_view = PbftSettingsView(state_view)
            LOGGER.debug("PbftBlockPublisher::pbft_settings_view node=%s",pbft_settings_view.pbft_node)
        #self._node = pbft_settings_view.pbft_node
        
        consensus_state = ConsensusState.consensus_state_for_block_id(
                block_id=block_header.previous_block_id,
                block_cache=self._block_cache,
                state_view_factory=self._state_view_factory,
                consensus_state_store=self._consensus_state_store,
                node=self._node
                )
        # shift into PrePrepare state
        consensus_state.next_step()
        #consensus_state.mark_as_own()
        consensus_state.set_consensus_state_for_block_id(block_header.previous_block_id,self._consensus_state_store)
        self._block_id = block_header.previous_block_id
        #consensus_state.set_node(self._node)
        LOGGER.debug("PbftBlockPublisher::initialize_block GET CONSENSUS_STATE=%s for block_id=%s ",consensus_state,_short_id(block_header.previous_block_id))
        # start 
        # Get our validator registry entry to see what PBFT public key
        # other validators think we are using.

        if _VREG_:
            validator_registry_view = ValidatorRegistryView(state_view)
            validator_info = None

            try:
                validator_id = block_header.signer_public_key
                validator_info = validator_registry_view.get_validator_info(validator_id=validator_id)
            except KeyError:
                pass

            # If we don't have a validator registry entry, then check the active
            # key.  If we don't have one, then we need to sign up.  If we do have
            # one, then our validator registry entry has not percolated through the
            # system, so nothing to to but wait.
            active_pbft_public_key = self._pbft_key_state_store.active_key
            if validator_info is None:
                if active_pbft_public_key is None:
                    LOGGER.debug('PbftBlockPublisher::initialize_block No public key found, so going to register new signup information')
                    self._register_signup_information(block_header=block_header)

                else:  # Check if we need to give up on this registration attempt
                    try:
                        nonce = self._pbft_key_state_store[active_pbft_public_key].signup_nonce
                    except (ValueError, AttributeError):
                        self._pbft_key_state_store.active_key = None
                        LOGGER.warning('PbftBlockPublisher::initialize_block Pbft Key State Store had inaccessible or '
                                       'corrupt active key [%s] clearing '
                                       'key.', active_pbft_public_key)
                        return False
                    LOGGER.debug('PbftBlockPublisher::initialize_block Check if we need to give up on this registration attempt')
                    self._handle_registration_timeout(
                        block_header=block_header,
                        pbft_enclave_module=None,#pbft_enclave_module,
                            state_view=state_view,
                        signup_nonce=nonce,
                        pbft_public_key=active_pbft_public_key
                    )
                LOGGER.debug("PbftBlockPublisher::initialize_block validator_info NONE")
                return True #False

                # Retrieve the key state corresponding to the PBFT public key in our
                # validator registry entry.
                pbft_key_state = None
                try:
                    pbft_key_state = self._pbft_key_state_store[validator_info.signup_info.pbft_public_key]
                except (ValueError, KeyError):
                    pass

                # If there is no key state associated with the PBFT public key that
                # other validators think we should be using, then we need to create
                # new signup information as we have no way whatsoever to publish
                # blocks that other validators will accept.
                LOGGER.debug("PbftBlockPublisher::check pbft_key_state=%s",pbft_key_state)
                if pbft_key_state is None:
                    LOGGER.debug('PbftBlockPublisher::initialize_block PBFT public key %s...%s in validator registry not found in key state store.  Sign up again',
                        validator_info.signup_info.pbft_public_key[:8],
                        validator_info.signup_info.pbft_public_key[-8:])
                    self._register_signup_information(block_header=block_header)

                    # We need to put fake information in the key state store for the
                    # PBFT public key the other validators think we are using so that
                    # we don't try to keep signing up.  However, we are going to mark
                    # that key state store entry as being refreshed so that we will
                    # never actually try to use it.
                    dummy_data = b64encode(b'No sealed signup data').decode('utf-8')
                    self._pbft_key_state_store[validator_info.signup_info.pbft_public_key] = PbftKeyState(
                            sealed_signup_data=dummy_data,
                            has_been_refreshed=True,
                            signup_nonce='unknown')

                    return False

        # Check the key state.  If it is marked as being refreshed, then we are
        # waiting until our PBFT public key is updated in the validator
        # registry and therefore we cannot publish any blocks.
        if _VREG_ and pbft_key_state.has_been_refreshed:
            LOGGER.debug(
                'PBFT public key %s...%s has been refreshed.  Wait for new '
                'key to show up in validator registry.',
                validator_info.signup_info.pbft_public_key[:8],
                validator_info.signup_info.pbft_public_key[-8:])

            # Check if we need to give up on this registration attempt
            self._handle_registration_timeout(
                block_header=block_header,
                pbft_enclave_module=pbft_enclave_module,
                state_view=state_view,
                signup_nonce=pbft_key_state.signup_nonce,
                pbft_public_key=active_pbft_public_key
            )
            return False

        # If the PBFT public key in the validator registry is not the active
        # one, then we need to switch the active key in the key state store.
        if _VREG_:
            if validator_info.signup_info.pbft_public_key != active_pbft_public_key:
                active_pbft_public_key = validator_info.signup_info.pbft_public_key
                self._pbft_key_state_store.active_key = active_pbft_public_key

            # Ensure that the enclave is using the appropriate keys
            try:
                    signup_data = json2dict(base64.b64decode(pbft_key_state.sealed_signup_data.encode()).decode())
                    unsealed_pbft_public_key = signup_data.get('pbft_public_key')
            except SystemError:
                # Signup data is unuseable
                LOGGER.error(
                    'Could not unseal signup data associated with PPK: %s..%s',
                    active_pbft_public_key[:8],
                    active_pbft_public_key[-8:])
                self._pbft_key_state_store.active_key = None
                return False
            LOGGER.debug("PbftBlockPublisher::unsealed_pbft_public_key=%s ~ %s signup_data=%s",unsealed_pbft_public_key,active_pbft_public_key,signup_data)
            assert active_pbft_public_key == unsealed_pbft_public_key

            LOGGER.debug('Using PBFT public key: %s...%s',active_pbft_public_key[:8],active_pbft_public_key[-8:])
            LOGGER.debug('Unseal signup data: %s...%s',pbft_key_state.sealed_signup_data[:8],pbft_key_state.sealed_signup_data[-8:])
            """
            LOGGER.debug("PbftBlockPublisher::initialize_block  ADD CONSENSUS_STATE for block_id=%s",block_header.previous_block_id)
            consensus_state = ConsensusState.consensus_state_for_block_id(
                    block_id=block_header.previous_block_id,
                    block_cache=self._block_cache,
                    state_view_factory=self._state_view_factory,
                    consensus_state_store=self._consensus_state_store,
                    pbft_enclave_module=None,
                    )
            """
            #pbft_settings_view = PbftSettingsView(state_view)
            #LOGGER.debug("PbftBlockPublisher::pbft_settings_view node=%s",pbft_settings_view.pbft_node)

            # If our signup information does not pass the freshness test, then we
            # know that other validators will reject any blocks we try to claim so
            # we need to try to sign up again.

                # Using the consensus state for the block upon which we want to
            # build, check to see how many blocks we have claimed on this chain
            # with this PBFT key.  If we have hit the key block claim limit, then
            # we need to check if the key has been refreshed.
                # We need to create a wait timer for the block...this is what we
            # will check when we are asked if it is time to publish the block
            pbft_key_state = self._pbft_key_state_store[active_pbft_public_key]
            sealed_signup_data = pbft_key_state.sealed_signup_data

            # At this point, we know that if we are able to claim the block we are
            # initializing, we will not be prevented from doing so because of PBFT
            # policies.

            self._wait_timer = 20
        self._wait_timer = 20
        PbftBlockPublisher._previous_block_id = None
        block_header.consensus = b"pbft"
        LOGGER.debug('PbftBlockPublisher::initialize_block DONE _wait_timer=%s',self._wait_timer)
        self._block_header = block_header
        return True