def requires_genesis(self): """ Determines if the system should be put in genesis mode """ genesis_file = os.path.join(self._data_dir, 'genesis.batch') has_genesis_batches = Path(genesis_file).is_file() LOGGER.debug('genesis_batch_file: %s', genesis_file if has_genesis_batches else 'None') has_chain_head = "chain_head_id" in self._block_store LOGGER.debug( 'chain_head: %s', self._block_store['chain_head_id'] if has_chain_head else 'None') block_chain_id = self._get_block_chain_id() is_genesis_node = block_chain_id is None LOGGER.debug('network_name: %s', block_chain_id) if has_genesis_batches and has_chain_head: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and an existing chain') if has_genesis_batches and not is_genesis_node: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and join an existing network' ) ret = has_genesis_batches and not has_chain_head and is_genesis_node LOGGER.debug('Requires genesis: %s', ret) return ret
def _get_block_publisher(self, state_hash): """Returns the block publisher based on the consensus module set by the "sawtooth_config" transaction family. Args: state_hash (str): The current state root hash for reading settings. Raises: InvalidGenesisStateError: if any errors occur getting the BlockPublisher. """ state_view = self._state_view_factory.create_view(state_hash) try: class BatchPublisher(object): def send(self, transactions): # Consensus implementations are expected to have handling # in place for genesis operation. This should includes # adding any authorization and registrations required # for the genesis node to the Genesis Batch list and # detecting validation of the Genesis Block and handle it # correctly. Batch publication is not allowed during # genesis operation since there is no network to validate # the batch yet. raise InvalidGenesisConsensusError( 'Consensus cannot send transactions during genesis.') consensus = ConsensusFactory.get_configured_consensus_module( state_view) return consensus.BlockPublisher(BlockCache(self._block_store), state_view=state_view, batch_publisher=BatchPublisher(), data_dir=self._data_dir) except UnknownConsensusModuleError as e: raise InvalidGenesisStateError(e)
def requires_genesis(self): """ Determines if the system should be put in genesis mode Returns: bool: return whether or not a genesis block is required to be generated. Raises: InvalidGenesisStateError: raises this error if there is invalid combination of the following: genesis.batch, existing chain head, and block chain id. """ genesis_file = os.path.join(self._data_dir, 'genesis.batch') has_genesis_batches = Path(genesis_file).is_file() LOGGER.debug('genesis_batch_file: %s', genesis_file if has_genesis_batches else 'not found') chain_head = self._block_store.chain_head has_chain_head = chain_head is not None if has_chain_head: LOGGER.debug('chain_head: %s', chain_head) block_chain_id = self._chain_id_manager.get_block_chain_id() is_genesis_node = block_chain_id is None LOGGER.debug( 'block_chain_id: %s', block_chain_id if not is_genesis_node else 'not yet specified') if has_genesis_batches and has_chain_head: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and an existing chain') if has_genesis_batches and not is_genesis_node: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and join an existing network' ) if not has_genesis_batches and not has_chain_head: LOGGER.info( 'No chain head and not the genesis node: starting in PEERING MODE!\n' ) return has_genesis_batches and not has_chain_head and is_genesis_node
def _save_block_chain_id(self, block_chain_id): LOGGER.debug('writing block chain id') block_chain_id_file = os.path.join(self._data_dir, 'block-chain-id') try: with open(block_chain_id_file, 'w') as f: f.write(block_chain_id) except IOError: raise InvalidGenesisStateError( 'The block-chain-id file exists, but is unwriteable')
def _get_block_chain_id(self): block_chain_id_file = os.path.join(self._data_dir, 'block-chain-id') if not Path(block_chain_id_file).is_file(): return None try: with open(block_chain_id_file, 'r') as f: block_chain_id = f.read() return block_chain_id if block_chain_id else None except IOError: raise InvalidGenesisStateError( 'The block-chain-id file exists, but is unreadable')
def requires_genesis(self): """ Determines if the system should be put in genesis mode Returns: bool: return whether or not a genesis block is required to be generated. Raises: InvalidGenesisStateError: raises this error if there is invalid combination of the following: genesis.batch, existing chain head, and block chain id. """ genesis_file = os.path.join(self._data_dir, 'genesis.batch') has_genesis_batches = Path(genesis_file).is_file() LOGGER.debug('genesis_batch_file: %s', genesis_file if has_genesis_batches else 'None') has_chain_head = "chain_head_id" in self._block_store LOGGER.debug( 'chain_head: %s', self._block_store['chain_head_id'] if has_chain_head else 'None') block_chain_id = self._get_block_chain_id() is_genesis_node = block_chain_id is None LOGGER.debug('block_chain_id: %s', block_chain_id) if has_genesis_batches and has_chain_head: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and an existing chain') if has_genesis_batches and not is_genesis_node: raise InvalidGenesisStateError( 'Cannot have a genesis_batch_file and join an existing network' ) ret = has_genesis_batches and not has_chain_head and is_genesis_node LOGGER.debug('Requires genesis: %s', ret) return ret
def _get_block_publisher(self, state_hash): """Returns the block publisher based on the consensus module set by the "sawtooth_config" transaction family. Args: state_hash (str): The current state root hash for reading settings. Raises: InvalidGenesisStateError: if any errors occur getting the BlockPublisher. """ state_view = self._state_view_factory.create_view(state_hash) try: consensus = ConsensusFactory.get_configured_consensus_module( state_view) return consensus.BlockPublisher(BlockCache(self._block_store), state_view=state_view) except UnknownConsensusModuleError as e: raise InvalidGenesisStateError(e)
def start(self, on_done): """ Starts the genesis block creation process. Will call the given `on_done` callback on successful completion. Args: on_done (function): a function called on completion Raises: InvalidGenesisStateError: raises this error if a genesis block is unable to be produced, or the resulting block-chain-id saved. """ genesis_file = os.path.join(self._data_dir, 'genesis.batch') try: with open(genesis_file, 'rb') as batch_file: genesis_data = genesis_pb2.GenesisData() genesis_data.ParseFromString(batch_file.read()) LOGGER.info('Producing genesis block from %s', genesis_file) except IOError: raise InvalidGenesisStateError( "Genesis File {} specified, but unreadable".format( genesis_file)) initial_state_root = self._context_manager.get_first_root() genesis_batches = [batch for batch in genesis_data.batches] if genesis_batches: scheduler = SerialScheduler( self._context_manager.get_squash_handler(), initial_state_root, always_persist=True) LOGGER.debug('Adding %s batches', len(genesis_data.batches)) for batch in genesis_data.batches: scheduler.add_batch(batch) self._transaction_executor.execute(scheduler) scheduler.finalize() scheduler.complete(block=True) state_hash = initial_state_root for batch in genesis_batches: result = scheduler.get_batch_execution_result( batch.header_signature) if result is None or not result.is_valid: raise InvalidGenesisStateError( 'Unable to create genesis block, due to batch {}'.format( batch.header_signature)) if result.state_hash is not None: state_hash = result.state_hash LOGGER.debug('Produced state hash %s for genesis block.', state_hash) block_builder = self._generate_genesis_block() block_builder.add_batches(genesis_batches) block_builder.set_state_hash(state_hash) block_publisher = self._get_block_publisher(initial_state_root) if not block_publisher.initialize_block(block_builder.block_header): LOGGER.error('Consensus refused to initialize consensus block.') raise InvalidGenesisConsensusError( 'Consensus refused to initialize genesis block.') if not block_publisher.finalize_block(block_builder.block_header): LOGGER.error('Consensus refused to finalize genesis block.') raise InvalidGenesisConsensusError( 'Consensus refused to finalize genesis block.') self._sign_block(block_builder) block = block_builder.build_block() blkw = BlockWrapper(block=block, status=BlockStatus.Valid) LOGGER.info('Genesis block created: %s', blkw) self._completer.add_block(block) self._block_store.update_chain([blkw]) self._chain_id_manager.save_block_chain_id(block.header_signature) LOGGER.debug('Deleting genesis data.') os.remove(genesis_file) if on_done is not None: on_done()
def start(self, on_done): """ Starts the genesis block creation process. Will call the given `on_done` callback on successful completion. Params: on_done - a function called on completion """ genesis_file = os.path.join(self._data_dir, 'genesis.batch') try: with open(genesis_file, 'rb') as batch_file: genesis_data = genesis_pb2.GenesisData() genesis_data.ParseFromString(batch_file.read()) LOGGER.info('Producing genesis block from %s', genesis_file) except IOError: raise InvalidGenesisStateError( "Genesis File {} specified, but unreadable".format( genesis_file)) initial_state_root = self._context_manager.get_first_root() block = GenesisController._generate_genesis_block() genesis_batches = [batch for batch in genesis_data.batches] if len(genesis_batches) > 0: scheduler = SerialScheduler( self._context_manager.get_squash_handler(), initial_state_root) LOGGER.debug('Adding %s batches', len(genesis_data.batches)) for batch in genesis_data.batches: scheduler.add_batch(batch) self._transaction_executor.execute(scheduler, require_txn_processors=True) scheduler.finalize() scheduler.complete(block=True) state_hash = initial_state_root for batch in genesis_batches: result = scheduler.get_batch_execution_result( batch.header_signature) if result is None or not result.is_valid: raise InvalidGenesisStateError( 'Unable to create genesis block, due to batch {}' .format(batch.header_signature)) state_hash = result.state_hash LOGGER.debug('Produced state hash %s for genesis block.', state_hash) block.add_batches(genesis_batches) block.set_state_hash(state_hash) GenesisController._sign_block(block) LOGGER.info('genesis block created: %s', block.header_signature) self._completer.add_block(block.get_block()) self._block_store['chain_head_id'] = block.header_signature block_state = BlockState(block_wrapper=block, weight=0, status=BlockStatus.Valid) self._block_store[block.header_signature] = block_state self._save_block_chain_id(block.header_signature) LOGGER.debug('deleting genesis data') os.remove(genesis_file) if on_done is not None: on_done()