def __getitem__(self, block_id):
        """Return the consensus state corresponding to the block ID

        Args:
            block_id (str): The ID of the block for which consensus state
                is being requested

        Returns:
            ConsensusState object

        Raises:
            KeyError if the block ID is not in the store
        """
        serialized_consensus_state = self._store_db[block_id]
        if serialized_consensus_state is None:
            raise KeyError('Block ID {} not found'.format(block_id))

        try:
            consensus_state = ConsensusState()
            consensus_state.parse_from_bytes(
                buffer=serialized_consensus_state)
            return consensus_state
        except ValueError as error:
            raise \
                KeyError(
                    'Cannot return block with ID {}: {}'.format(
                        block_id,
                        error))
    def setUp(self):
        # This is a little ham-handed, but we need to ensure that the
        # PoET enclave is set back to initial state at the start of every
        # test.
        self.poet_enclave_module = reload(poet_enclave)
        self.poet_enclave_module.initialize(self._temp_dir, self._temp_dir)

        self.mock_poet_settings_view = mock.Mock()
        self.mock_poet_settings_view.target_wait_time = 5.0
        self.mock_poet_settings_view.initial_wait_time = 0.0
        self.mock_poet_settings_view.population_estimate_sample_size = 50

        self.consensus_state = ConsensusState()
    def __str__(self):
        out = []
        for block_id in self._store_db.keys():
            try:
                serialized_consensus_state = self._store_db[block_id]
                consensus_state = ConsensusState()
                consensus_state.parse_from_bytes(
                    buffer=serialized_consensus_state)
                out.append('{}...{}: {{{}}}'.format(block_id[:8],
                                                    block_id[-8:],
                                                    consensus_state))
            except ValueError:
                pass

        return ', '.join(out)
Esempio n. 4
0
    def setUp(self):
        # This is a little ham-handed, but we need to ensure that the
        # PoET enclave is set back to initial state at the start of every
        # test.
        self.poet_enclave_module = reload(poet_enclave)
        self.poet_enclave_module.initialize(
            os.path.dirname(os.path.abspath(__file__)))

        self.mock_poet_config_view = mock.Mock()
        self.mock_poet_config_view.target_wait_time = 5.0
        self.mock_poet_config_view.initial_wait_time = 0.0
        self.mock_poet_config_view.minimum_wait_time = 1.0
        self.mock_poet_config_view.population_estimate_sample_size = 50

        self.consensus_state = ConsensusState()
Esempio n. 5
0
    def setUp(self):
        # This is a little ham-handed, but we need to ensure that the
        # PoET enclave is set back to initial state at the start of every
        # test.
        self.poet_enclave_module = reload(poet_enclave)

        args = {"NodeName": "DasValidator"}
        self.poet_enclave_module.initialize(**args)

        self.mock_poet_config_view = mock.Mock()
        self.mock_poet_config_view.target_wait_time = 5.0
        self.mock_poet_config_view.initial_wait_time = 0.0
        self.mock_poet_config_view.minimum_wait_time = 1.0
        self.mock_poet_config_view.population_estimate_sample_size = 50

        self.consensus_state = ConsensusState()
Esempio n. 6
0
def get_consensus_state_for_block_id(block_id, block_cache, state_view_factory,
                                     consensus_state_store,
                                     poet_enclave_module):
    """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
        poet_enclave_module (module): The PoET enclave module

    Returns:
        ConsensusState object representing the consensus state for the block
            referenced by 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
    while True:
        block = _block_for_id(block_id=block_id, block_cache=block_cache)
        if block is None:
            break

        # Try to fetch the consensus state.  If that succeeds, we can
        # stop walking back as we can now build on that consensus
        # state.
        consensus_state = consensus_state_store.get(block_id=block_id)
        if consensus_state is not None:
            break

        wait_certificate = \
            deserialize_wait_certificate(
                block=block,
                poet_enclave_module=poet_enclave_module)

        # If this is a PoET block (i.e., it has a wait certificate), get the
        # validator info for the validator that signed this block and add the
        # block information we will need to set validator state in the block's
        # consensus state.
        if wait_certificate is not None:
            state_view = \
                state_view_factory.create_view(
                    state_root_hash=block.state_root_hash)
            validator_registry_view = \
                ValidatorRegistryView(state_view=state_view)
            validator_info = \
                validator_registry_view.get_validator_info(
                    validator_id=block.header.signer_pubkey)

            LOGGER.debug('We need to build consensus state for block: %s...%s',
                         block_id[:8], block_id[-8:])

            blocks[block_id] = \
                BlockInfo(
                    wait_certificate=wait_certificate,
                    validator_info=validator_info)

        # Otherwise, this is a non-PoET block.  If we don't have any blocks
        # yet or the last block we processed was a PoET block, put a
        # placeholder in the list so that when we get to it we know that we
        # need to reset the statistics.
        elif len(blocks) == 0 or previous_wait_certificate is not None:
            blocks[block_id] = \
                BlockInfo(
                    wait_certificate=None,
                    validator_info=None)

        previous_wait_certificate = wait_certificate

        # Move to the previous block
        block_id = block.previous_block_id

    # At this point, if we have not found any consensus state, we need to
    # create default state from which we can build upon
    if consensus_state is None:
        consensus_state = ConsensusState()

    # Now, walk through the blocks for which we were supposed to create
    # consensus state, from oldest to newest (i.e., in the reverse order in
    # which they were added), and store state for PoET blocks so that the next
    # time we don't have to walk so far back through the block chain.
    for block_id, block_info in reversed(blocks.items()):
        # If the block was not a PoET block (i.e., didn't have a wait
        # certificate), reset the consensus state statistics.  We are not
        # going to store this in the consensus state store, but we will use it
        # as the starting for the next PoET block.
        if block_info.wait_certificate is None:
            consensus_state = ConsensusState()

        # Otherwise, update the consensus state statistics and fetch the
        # validator state for the validator which claimed the block, create
        # updated validator state for the validator, set/update the validator
        # state in the consensus state object, and then associate the
        # consensus state with the corresponding block in the consensus state
        # store.
        else:
            validator_state = \
                get_current_validator_state(
                    validator_info=block_info.validator_info,
                    consensus_state=consensus_state,
                    block_cache=block_cache)
            consensus_state.set_validator_state(
                validator_id=block_info.validator_info.id,
                validator_state=create_next_validator_state(
                    validator_info=block_info.validator_info,
                    current_validator_state=validator_state,
                    block_cache=block_cache))

            LOGGER.debug('Store consensus state for block: %s...%s',
                         block_id[:8], block_id[-8:])

            consensus_state.total_block_claim_count += 1
            consensus_state_store[block_id] = consensus_state

    return consensus_state
Esempio n. 7
0
def get_consensus_state_for_block_id(block_id, block_cache, state_view_factory,
                                     consensus_state_store,
                                     poet_enclave_module):
    """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
        poet_enclave_module (module): The PoET enclave module

    Returns:
        ConsensusState object representing the consensus state for the block
            referenced by block_id
    """

    # 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
    block = \
        block_cache[block_id] if block_id != NULL_BLOCK_IDENTIFIER else None
    consensus_state = None
    block_ids = collections.deque()

    while block is not None:
        # Try to fetch the consensus state.  If that succeeds, we can
        # stop walking back as we can now build on that consensus
        # state.
        consensus_state = consensus_state_store.get(block.identifier)
        if consensus_state is not None:
            break

        # If this is a PoET block, then we want to create consensus state
        # for it when we are done
        if deserialize_wait_certificate(block, poet_enclave_module):
            LOGGER.debug(
                'We need to build consensus state for block ID %s...%s',
                block.identifier[:8], block.identifier[-8:])
            block_ids.appendleft(block.identifier)

        # Move to the previous block
        block = \
            block_cache[block.previous_block_id] \
            if block.previous_block_id != NULL_BLOCK_IDENTIFIER else None

    # If didn't find any consensus state, see if there is any "before" any
    # blocks were created (this might be because we are the first validator
    # and PoET signup information was created, including sealed signup data
    # that was saved in the consensus state store).
    if consensus_state is None:
        consensus_state = consensus_state_store.get(NULL_BLOCK_IDENTIFIER)

    # At this point, if we have not found any consensus state, we need to
    # create default state from which we can build upon
    if consensus_state is None:
        consensus_state = ConsensusState()

    # Now, walking forward through the blocks for which we were supposed to
    # create consensus state, we are going to create and store state for each
    # one so that the next time we don't have to walk so far back through the
    # block chain.
    for identifier in block_ids:
        block = block_cache[identifier]

        # Get the validator registry view for this block's state view and
        # then fetch the validator info for the validator that signed this
        # block.
        state_view = state_view_factory.create_view(block.state_root_hash)
        validator_registry_view = ValidatorRegistryView(state_view)

        validator_id = block.header.signer_pubkey
        validator_info = \
            validator_registry_view.get_validator_info(
                validator_id=validator_id)

        # Fetch the current validator state, set/update the validator state
        # in the consensus state object, and then create the consensus state
        # in the consensus state store and associate it with this block
        validator_state = \
            consensus_state.get_validator_state(validator_id=validator_id)
        consensus_state.set_validator_state(
            validator_id=validator_id,
            validator_state=create_validator_state(
                validator_info=validator_info,
                current_validator_state=validator_state))

        LOGGER.debug('Store consensus state for block ID %s...%s',
                     identifier[:8], identifier[-8:])

        consensus_state_store[identifier] = consensus_state

    return consensus_state
    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 PoET 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.
        """
        # Using the current chain head, we need to create a state view so we
        # can create a PoET enclave.
        state_view = \
            BlockWrapper.state_view_for_block(
                block_wrapper=self._block_cache.block_store.chain_head,
                state_view_factory=self._state_view_factory)

        poet_enclave_module = \
            factory.PoetEnclaveFactory.get_poet_enclave_module(state_view)

        # Check the consensus state to see if we have current sealed signup
        # information.
        consensus_state = \
            utils.get_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,
                poet_enclave_module=poet_enclave_module)
        if consensus_state is not None and \
                consensus_state.sealed_signup_data is not None:
            # Check to see if the sealed signup data we have cached is the
            # same as what is in the consensus store.  If not, then it is
            # stale and so we need to unseal it and update our cached copy.
            if consensus_state.sealed_signup_data != \
                    PoetBlockPublisher._sealed_signup_data:
                LOGGER.debug('Unseal signup data %s...%s',
                             consensus_state.sealed_signup_data[:8],
                             consensus_state.sealed_signup_data[-8:])
                PoetBlockPublisher._sealed_signup_data = \
                    consensus_state.sealed_signup_data
                PoetBlockPublisher._poet_public_key = \
                    SignupInfo.unseal_signup_data(
                        poet_enclave_module=poet_enclave_module,
                        validator_address=block_header.signer_pubkey,
                        sealed_signup_data=consensus_state.sealed_signup_data)

        # Otherwise, if we don't already have a public key, we need to create
        # signup information and create a transaction to add it to the
        # validator registry.
        elif PoetBlockPublisher._poet_public_key is None:
            LOGGER.debug(
                'No public key found, so going to register new signup '
                'information')
            self._register_signup_information(
                block_header=block_header,
                poet_enclave_module=poet_enclave_module)
            return False

        # Otherwise, at this point we need to check the validator registry to
        # see if our _current_ validator registry information was added to the
        # validator registry.
        else:
            validator_registry_view = ValidatorRegistryView(state_view)
            try:
                validator_id = block_header.signer_pubkey
                validator_info = \
                    validator_registry_view.get_validator_info(
                        validator_id=validator_id)

                LOGGER.debug(
                    'Our Validator Registry Entry: Name=%s, ID=%s...%s, PoET '
                    'public key=%s...%s', validator_info.name,
                    validator_info.id[:8], validator_info.id[-8:],
                    validator_info.signup_info.poet_public_key[:8],
                    validator_info.signup_info.poet_public_key[-8:])

                # We need to verify that our validator registry entry is
                # current - basically this means verifying that the PoET
                # public key that will be used to verify the validity of
                # our wait certificates is the PoET public key matching our
                # current private key.
                if validator_info.signup_info.poet_public_key != \
                        PoetBlockPublisher._poet_public_key:
                    LOGGER.debug(
                        'Our Validator Registry Entry PoET public key '
                        '(%s...%s) doesn'
                        't match the PoET public key '
                        'expected (%s...%s)',
                        validator_info.signup_info.poet_public_key[:8],
                        validator_info.signup_info.poet_public_key[-8:],
                        PoetBlockPublisher._poet_public_key[:8],
                        PoetBlockPublisher._poet_public_key[-8:])
                    return False

                # At this point, we know that we are in the validator registry
                # and the entry is current.  We can save the sealed signup
                # data to the consensus state for the previous block and can
                # clear out our cached copies of the sealed signup data and
                # the PoET public key.
                if consensus_state is None:
                    consensus_state = ConsensusState()
                consensus_state.sealed_signup_data = \
                    PoetBlockPublisher._sealed_signup_data

                self._consensus_state_store[block_header.previous_block_id] = \
                    consensus_state

                PoetBlockPublisher._sealed_signup_data = None
                PoetBlockPublisher._poet_public_key = None
            except KeyError:
                LOGGER.debug(
                    'We cannot initialize the block because our PoET signup '
                    'information is not in the validator registry')
                return False

            # Since we are registering, don't bother trying to initialize
            # the block
            return False

        # Create a list of certificates for the wait timer.  This seems to have
        # a little too much knowledge of the WaitTimer implementation, but
        # there is no use getting more than
        # WaitTimer.certificate_sample_length wait certificates.
        certificates = \
            utils.build_certificate_list(
                block_header=block_header,
                block_cache=self._block_cache,
                poet_enclave_module=poet_enclave_module,
                maximum_number=WaitTimer.certificate_sample_length)

        # 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
        self._wait_timer = \
            WaitTimer.create_wait_timer(
                poet_enclave_module=poet_enclave_module,
                validator_address=block_header.signer_pubkey,
                certificates=list(certificates))

        LOGGER.debug('Created wait timer: %s', self._wait_timer)

        return True
Esempio n. 9
0
def do_genesis(args):
    """Executes the `poet genesis` subcommand.

    This command generates a validator registry transaction and saves it to a
    file, whose location is determined by the args.  The signup data, generated
    by the selected enclave, is also stored in a well-known location.
    """
    if args.enclave_module == 'simulator':
        module_name = SIMUATOR_MODULE
    elif args.enclave_module == 'sgx':
        module_name = SGX_MODULE
    else:
        raise AssertionError('Unknown enclave module: {}'.format(
            args.enclave_module))

    try:
        poet_enclave_module = importlib.import_module(module_name)
    except ImportError as e:
        raise AssertionError(str(e))

    poet_enclave_module.initialize(**{})

    pubkey, signing_key = _read_signing_keys(args.key)

    public_key_hash = sha256(pubkey.encode()).hexdigest()
    signup_info = SignupInfo.create_signup_info(
        poet_enclave_module=poet_enclave_module,
        validator_address=pubkey,
        originator_public_key_hash=public_key_hash,
        most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

    print('Writing sealed signup data to consensus state store')

    consensus_state = ConsensusState()
    consensus_state.sealed_signup_data = signup_info.sealed_signup_data

    consensus_state_store = \
        ConsensusStateStore(
            data_dir=config.get_data_dir(),
            validator_id=pubkey)
    consensus_state_store[NULL_BLOCK_IDENTIFIER] = consensus_state

    # Create the validator registry payload
    payload = \
        vr_pb.ValidatorRegistryPayload(
            verb='register',
            name='validator-{}'.format(pubkey[-8:]),
            id=pubkey,
            signup_info=vr_pb.SignUpInfo(
                poet_public_key=signup_info.poet_public_key,
                proof_data=signup_info.proof_data,
                anti_sybil_id=signup_info.anti_sybil_id),
        )
    serialized = payload.SerializeToString()

    # Create the address that will be used to look up this validator
    # registry transaction.  Seems like a potential for refactoring..
    validator_entry_address = \
        VR_NAMESPACE + sha256(pubkey.encode()).hexdigest()

    # Create a transaction header and transaction for the validator
    # registry update amd then hand it off to the batch publisher to
    # send out.
    addresses = [validator_entry_address, VALIDATOR_MAP_ADDRESS]

    header = \
        txn_pb.TransactionHeader(
            signer_pubkey=pubkey,
            family_name='sawtooth_validator_registry',
            family_version='1.0',
            inputs=addresses,
            outputs=addresses,
            dependencies=[],
            payload_encoding="application/protobuf",
            payload_sha512=sha512(serialized).hexdigest(),
            batcher_pubkey=pubkey,
            nonce=time.time().hex().encode()).SerializeToString()
    signature = signing.sign(header, signing_key)

    transaction = \
        txn_pb.Transaction(
            header=header,
            payload=serialized,
            header_signature=signature)

    batch = _create_batch(pubkey, signing_key, [transaction])
    batch_list = batch_pb.BatchList(batches=[batch])
    try:
        print('Generating {}'.format(args.output))
        with open(args.output, 'wb') as batch_file:
            batch_file.write(batch_list.SerializeToString())
    except IOError as e:
        raise CliException(
            'Unable to write to batch file: {}'.format(str(e)))