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
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)))