Example #1
0
    def signup_attempt_timed_out(self,
                                 signup_nonce,
                                 poet_settings_view,
                                 block_cache):
        """Checks whether too many blocks have elapsed since
        since the registration attempt.

        Args:
            signup_nonce (string): nonce (~ block id) used in signup
            poet_settings_view (PoetSettingsView): The current Poet config view
            block_cache (BlockCache): The block store cache

        Returns:
            bool: True if too many blocks have elapsed; False if you just need
            to chill out a little while longer.
        """
        # It's tempting to set this timeout as the lesser of retry_delay
        # and signup_commit_maximum_delay, but then we can't individually
        # control this timeout behavior. Consider behavior as a new node joins
        # an old network.
        depth = poet_settings_view.registration_retry_delay

        i = 0
        for block in block_cache.block_store.get_block_iter(reverse=True):
            if i > depth:
                return True
            block_id = block.identifier
            if signup_nonce == SignupInfo.block_id_to_nonce(block_id):
                return False
            i += 1
        return False
    def signup_attempt_timed_out(self,
                                 signup_nonce,
                                 poet_settings_view,
                                 block_cache):
        """Checks whether too many blocks have elapsed since
        since the registration attempt.

        Args:
            signup_nonce (string): nonce (~ block id) used in signup
            poet_settings_view (PoetSettingsView): The current Poet config view
            block_cache (BlockCache): The block store cache

        Returns:
            bool: True if too many blocks have elapsed; False if you just need
            to chill out a little while longer.
        """
        # It's tempting to set this timeout as the lesser of retry_delay
        # and signup_commit_maximum_delay, but then we can't individually
        # control this timeout behavior. Consider behavior as a new node joins
        # an old network.
        depth = poet_settings_view.registration_retry_delay

        i = 0
        for block in block_cache.block_store.get_block_iter(reverse=True):
            if i > depth:
                return True
            block_id = block.identifier
            if signup_nonce == SignupInfo.block_id_to_nonce(block_id):
                return False
            i += 1
        return False
Example #3
0
    def validator_signup_was_committed_too_late(self, validator_info,
                                                poet_settings_view,
                                                block_cache):
        """Determines if a validator's registry transaction committing it
        current PoET keys, etc., was committed too late - i.e., the number of
        blocks between when the transaction was submitted and when it was
        committed is greater than signup commit maximum delay.

        Args:
            validator_info (ValidatorInfo): The current validator information
            poet_settings_view (PoetSettingsView): The current Poet config view
            block_cache (BlockCache): The block store cache

        Returns:
            bool: True if the validator's registry transaction was committed
                beyond the signup commit maximum delay, False otherwise
        """
        # Figure out the block in which the current validator information
        # was committed.
        block = \
            block_cache.block_store.get_block_by_transaction_id(
                validator_info.transaction_id)
        commit_block_id = block.identifier

        # Starting with that block's immediate predecessor, walk back until
        # either we match the block ID with the nonce field in the signup
        # info, we have checked the maximum number of blocks, or we somehow
        # reached the beginning of the blockchain.  The first case is
        # success (i.e., the validator signup info passed the freshness
        # test) while the other two cases are failure.
        for _ in range(poet_settings_view.signup_commit_maximum_delay + 1):
            if SignupInfo.block_id_to_nonce(block.previous_block_id) == \
                    validator_info.signup_info.nonce:
                LOGGER.debug(
                    'Validator %s (ID=%s...%s): Signup committed block %s, '
                    'chain head was block %s', validator_info.name,
                    validator_info.id[:8], validator_info.id[-8:],
                    commit_block_id[:8], block.previous_block_id[:8])
                return False

            if utils.block_id_is_genesis(block.previous_block_id):
                LOGGER.error(
                    'Validator %s (ID=%s...%s): Signup committed block %s, '
                    'hit start of blockchain looking for block %s',
                    validator_info.name, validator_info.id[:8],
                    validator_info.id[-8:], commit_block_id[:8],
                    validator_info.signup_info.nonce[:8])
                return True

            block = block_cache[block.previous_block_id]

        LOGGER.error(
            'Validator %s (ID=%s...%s): Signup committed block %s, failed to '
            'find block with ID ending in %s in %d previous block(s)',
            validator_info.name, validator_info.id[:8], validator_info.id[-8:],
            commit_block_id[:8], validator_info.signup_info.nonce,
            poet_settings_view.signup_commit_maximum_delay + 1)
        return True
Example #4
0
    def _register_signup_information(self, block_header, poet_enclave_module):
        # Create signup information for this validator, putting the block ID
        # of the block previous to the block referenced by block_header in the
        # nonce.  Block ID is better than wait certificate ID for testing
        # freshness as we need to account for non-PoET blocks.
        public_key_hash = \
            hashlib.sha256(
                block_header.signer_pubkey.encode()).hexdigest()
        nonce = SignupInfo.block_id_to_nonce(block_header.previous_block_id)
        signup_info = \
            SignupInfo.create_signup_info(
                poet_enclave_module=poet_enclave_module,
                originator_public_key_hash=public_key_hash,
                nonce=nonce)

        # Create the validator registry payload
        payload = \
            vr_pb.ValidatorRegistryPayload(
                verb='register',
                name='validator-{}'.format(block_header.signer_pubkey[:8]),
                id=block_header.signer_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,
                    nonce=nonce),
            )
        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 = \
            PoetBlockPublisher._validator_registry_namespace + \
            hashlib.sha256(block_header.signer_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.
        output_addresses = \
            [validator_entry_address,
             PoetBlockPublisher._validator_map_address]
        input_addresses = \
            output_addresses + \
            [SettingsView.setting_address(
                'sawtooth.poet.report_public_key_pem'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_measurements'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_basenames')]

        header = \
            txn_pb.TransactionHeader(
                signer_pubkey=block_header.signer_pubkey,
                family_name='sawtooth_validator_registry',
                family_version='1.0',
                inputs=input_addresses,
                outputs=output_addresses,
                dependencies=[],
                payload_sha512=hashlib.sha512(serialized).hexdigest(),
                batcher_pubkey=block_header.signer_pubkey,
                nonce=time.time().hex().encode()).SerializeToString()
        signature = \
            signing.sign(header, self._batch_publisher.identity_signing_key)

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

        LOGGER.info(
            'Register Validator Name=%s, ID=%s...%s, PoET public key=%s...%s, '
            'Nonce=%s',
            payload.name,
            payload.id[:8],
            payload.id[-8:],
            payload.signup_info.poet_public_key[:8],
            payload.signup_info.poet_public_key[-8:],
            nonce)

        self._batch_publisher.send([transaction])

        # Store the key state so that we can look it up later if need be and
        # set the new key as our active key
        LOGGER.info(
            'Save key state PPK=%s...%s => SSD=%s...%s',
            signup_info.poet_public_key[:8],
            signup_info.poet_public_key[-8:],
            signup_info.sealed_signup_data[:8],
            signup_info.sealed_signup_data[-8:])
        self._poet_key_state_store[signup_info.poet_public_key] = \
            PoetKeyState(
                sealed_signup_data=signup_info.sealed_signup_data,
                has_been_refreshed=False)
        self._poet_key_state_store.active_key = signup_info.poet_public_key
Example #5
0
def do_create(args):
    """Executes the `poet registration` 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.
    """
    signer = _read_signer(args.key)
    public_key = signer.get_public_key().as_hex()

    public_key_hash = sha256(public_key.encode()).hexdigest()

    with PoetEnclaveModuleWrapper(
            enclave_module=args.enclave_module,
            config_dir=config.get_config_dir(),
            data_dir=config.get_data_dir()) as poet_enclave_module:
        signup_info = SignupInfo.create_signup_info(
            poet_enclave_module=poet_enclave_module,
            originator_public_key_hash=public_key_hash,
            nonce=SignupInfo.block_id_to_nonce(args.block))

    print('Writing key state for PoET public key: {}...{}'.format(
        signup_info.poet_public_key[:8], signup_info.poet_public_key[-8:]))

    # Store the newly-created PoET key state, associating it with its
    # corresponding public key
    poet_key_state_store = \
        PoetKeyStateStore(
            data_dir=config.get_data_dir(),
            validator_id=public_key)
    poet_key_state_store[signup_info.poet_public_key] = \
        PoetKeyState(
            sealed_signup_data=signup_info.sealed_signup_data,
            has_been_refreshed=False)

    # Create the validator registry payload
    payload = \
        vr_pb.ValidatorRegistryPayload(
            verb='register',
            name='validator-{}'.format(public_key[:8]),
            id=public_key,
            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,
                nonce=SignupInfo.block_id_to_nonce(args.block)))
    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(public_key.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.
    output_addresses = [validator_entry_address, VALIDATOR_MAP_ADDRESS]
    input_addresses = \
        output_addresses + \
        [SettingsView.setting_address('sawtooth.poet.report_public_key_pem'),
         SettingsView.setting_address('sawtooth.poet.'
                                      'valid_enclave_measurements'),
         SettingsView.setting_address('sawtooth.poet.valid_enclave_basenames')]

    header = \
        txn_pb.TransactionHeader(
            signer_public_key=public_key,
            family_name='sawtooth_validator_registry',
            family_version='1.0',
            inputs=input_addresses,
            outputs=output_addresses,
            dependencies=[],
            payload_sha512=sha512(serialized).hexdigest(),
            batcher_public_key=public_key,
            nonce=time.time().hex().encode()).SerializeToString()
    signature = signer.sign(header)

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

    batch = _create_batch(signer, [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)))
    def _register_signup_information(self, block_header, poet_enclave_module):
        # Create signup information for this validator, putting the block ID
        # of the block previous to the block referenced by block_header in the
        # nonce.  Block ID is better than wait certificate ID for testing
        # freshness as we need to account for non-PoET blocks.
        public_key_hash = \
            hashlib.sha256(
                block_header.signer_public_key.encode()).hexdigest()
        nonce = SignupInfo.block_id_to_nonce(block_header.previous_block_id)
        signup_info = \
            SignupInfo.create_signup_info(
                poet_enclave_module=poet_enclave_module,
                originator_public_key_hash=public_key_hash,
                nonce=nonce)

        # Create the validator registry payload
        payload = \
            vr_pb.ValidatorRegistryPayload(
                verb='register',
                name='validator-{}'.format(block_header.signer_public_key[:8]),
                id=block_header.signer_public_key,
                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,
                    nonce=nonce),
            )
        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 = \
            PoetBlockPublisher._validator_registry_namespace + \
            hashlib.sha256(block_header.signer_public_key.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.
        output_addresses = \
            [validator_entry_address,
             PoetBlockPublisher._validator_map_address]
        input_addresses = \
            output_addresses + \
            [SettingsView.setting_address(
                'sawtooth.poet.report_public_key_pem'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_measurements'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_basenames')]

        header = \
            txn_pb.TransactionHeader(
                signer_public_key=block_header.signer_public_key,
                family_name='sawtooth_validator_registry',
                family_version='1.0',
                inputs=input_addresses,
                outputs=output_addresses,
                dependencies=[],
                payload_sha512=hashlib.sha512(serialized).hexdigest(),
                batcher_public_key=block_header.signer_public_key,
                nonce=time.time().hex().encode()).SerializeToString()

        signature = self._batch_publisher.identity_signer.sign(header)

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

        LOGGER.info(
            'Register Validator Name=%s, ID=%s...%s, PoET public key=%s...%s, '
            'Nonce=%s',
            payload.name,
            payload.id[:8],
            payload.id[-8:],
            payload.signup_info.poet_public_key[:8],
            payload.signup_info.poet_public_key[-8:],
            nonce)

        self._batch_publisher.send([transaction])

        # Store the key state so that we can look it up later if need be and
        # set the new key as our active key
        LOGGER.info(
            'Save key state PPK=%s...%s => SSD=%s...%s',
            signup_info.poet_public_key[:8],
            signup_info.poet_public_key[-8:],
            signup_info.sealed_signup_data[:8],
            signup_info.sealed_signup_data[-8:])
        self._poet_key_state_store[signup_info.poet_public_key] = \
            PoetKeyState(
                sealed_signup_data=signup_info.sealed_signup_data,
                has_been_refreshed=False,
                signup_nonce=nonce)
        self._poet_key_state_store.active_key = signup_info.poet_public_key
Example #7
0
def do_create(args):
    """Executes the `poet registration` 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.
    """
    signer = _read_signer(args.key)
    public_key = signer.get_public_key().as_hex()

    public_key_hash = sha256(public_key.encode()).hexdigest()
    nonce = SignupInfo.block_id_to_nonce(args.block)
    with PoetEnclaveModuleWrapper(
            enclave_module=args.enclave_module,
            config_dir=config.get_config_dir(),
            data_dir=config.get_data_dir()) as poet_enclave_module:
        signup_info = SignupInfo.create_signup_info(
            poet_enclave_module=poet_enclave_module,
            originator_public_key_hash=public_key_hash,
            nonce=nonce)

    print(
        'Writing key state for PoET public key: {}...{}'.format(
            signup_info.poet_public_key[:8],
            signup_info.poet_public_key[-8:]))

    # Store the newly-created PoET key state, associating it with its
    # corresponding public key
    poet_key_state_store = \
        PoetKeyStateStore(
            data_dir=config.get_data_dir(),
            validator_id=public_key)
    poet_key_state_store[signup_info.poet_public_key] = \
        PoetKeyState(
            sealed_signup_data=signup_info.sealed_signup_data,
            has_been_refreshed=False,
            signup_nonce=nonce)

    # Create the validator registry payload
    payload = \
        vr_pb.ValidatorRegistryPayload(
            verb='register',
            name='validator-{}'.format(public_key[:8]),
            id=public_key,
            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,
                nonce=SignupInfo.block_id_to_nonce(args.block)))
    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(public_key.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.
    output_addresses = [validator_entry_address, VALIDATOR_MAP_ADDRESS]
    input_addresses = \
        output_addresses + \
        [SettingsView.setting_address('sawtooth.poet.report_public_key_pem'),
         SettingsView.setting_address('sawtooth.poet.'
                                      'valid_enclave_measurements'),
         SettingsView.setting_address('sawtooth.poet.valid_enclave_basenames')]

    header = \
        txn_pb.TransactionHeader(
            signer_public_key=public_key,
            family_name='sawtooth_validator_registry',
            family_version='1.0',
            inputs=input_addresses,
            outputs=output_addresses,
            dependencies=[],
            payload_sha512=sha512(serialized).hexdigest(),
            batcher_public_key=public_key,
            nonce=time.time().hex().encode()).SerializeToString()
    signature = signer.sign(header)

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

    batch = _create_batch(signer, [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)))
    def validator_signup_was_committed_too_late(self,
                                                validator_info,
                                                poet_settings_view,
                                                block_cache):
        """Determines if a validator's registry transaction committing it
        current PoET keys, etc., was committed too late - i.e., the number of
        blocks between when the transaction was submitted and when it was
        committed is greater than signup commit maximum delay.

        Args:
            validator_info (ValidatorInfo): The current validator information
            poet_settings_view (PoetSettingsView): The current Poet config view
            block_cache (BlockCache): The block store cache

        Returns:
            bool: True if the validator's registry transaction was committed
                beyond the signup commit maximum delay, False otherwise
        """
        # Figure out the block in which the current validator information
        # was committed.
        try:
            block = block_cache.block_store.get_block_by_transaction_id(
                validator_info.transaction_id)
        except ValueError:
            LOGGER.warning(
                'Validator %s (ID=%s...%s): Signup txn %s not found in block.',
                validator_info.name,
                validator_info.id[:8],
                validator_info.id[-8:],
                validator_info.transaction_id[:8])
            return False

        commit_block_id = block.identifier

        # Starting with that block's immediate predecessor, walk back until
        # either we match the block ID with the nonce field in the signup
        # info, we have checked the maximum number of blocks, or we somehow
        # reached the beginning of the blockchain.  The first case is
        # success (i.e., the validator signup info passed the freshness
        # test) while the other two cases are failure.
        for _ in range(poet_settings_view.signup_commit_maximum_delay + 1):
            if SignupInfo.block_id_to_nonce(block.previous_block_id) == \
                    validator_info.signup_info.nonce:
                LOGGER.debug(
                    'Validator %s (ID=%s...%s): Signup committed block %s, '
                    'chain head was block %s',
                    validator_info.name,
                    validator_info.id[:8],
                    validator_info.id[-8:],
                    commit_block_id[:8],
                    block.previous_block_id[:8])
                return False

            if utils.block_id_is_genesis(block.previous_block_id):
                LOGGER.info(
                    'Validator %s (ID=%s...%s): Signup committed block %s, '
                    'hit start of blockchain looking for block %s',
                    validator_info.name,
                    validator_info.id[:8],
                    validator_info.id[-8:],
                    commit_block_id[:8],
                    validator_info.signup_info.nonce[:8])
                return True

            block = block_cache[block.previous_block_id]

        LOGGER.info(
            'Validator %s (ID=%s...%s): Signup committed block %s, failed to '
            'find block with ID ending in %s in %d previous block(s)',
            validator_info.name,
            validator_info.id[:8],
            validator_info.id[-8:],
            commit_block_id[:8],
            validator_info.signup_info.nonce,
            poet_settings_view.signup_commit_maximum_delay + 1)
        return True