github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/sawtooth_validator/journal/genesis.py (about) 1 # Copyright 2017 Intel Corporation 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # ------------------------------------------------------------------------------ 15 16 import logging 17 import os 18 from pathlib import Path 19 20 from sawtooth_validator.exceptions import InvalidGenesisStateError 21 from sawtooth_validator.exceptions import InvalidGenesisConsensusError 22 from sawtooth_validator.exceptions import UnknownConsensusModuleError 23 24 from sawtooth_validator.execution.scheduler_serial import SerialScheduler 25 26 from sawtooth_validator.journal.block_builder import BlockBuilder 27 from sawtooth_validator.journal.block_cache import BlockCache 28 from sawtooth_validator.journal.block_wrapper import BlockWrapper 29 from sawtooth_validator.journal.block_wrapper import BlockStatus 30 from sawtooth_validator.journal.block_wrapper import NULL_BLOCK_IDENTIFIER 31 from sawtooth_validator.journal.consensus.consensus_factory import \ 32 ConsensusFactory 33 from sawtooth_validator.protobuf import genesis_pb2 34 from sawtooth_validator.protobuf import block_pb2 35 36 37 LOGGER = logging.getLogger(__name__) 38 39 40 class GenesisController(object): 41 def __init__(self, 42 context_manager, 43 transaction_executor, 44 completer, 45 block_store, 46 state_view_factory, 47 identity_signer, 48 data_dir, 49 config_dir, 50 chain_id_manager, 51 batch_sender): 52 """Creates a GenesisController. 53 54 Args: 55 context_manager (:obj:`ContextManager`): A `ContextManager` 56 instance. 57 transaction_executor (:obj:`TransactionExecutor`): A 58 TransactionExecutor instance. 59 completer (:obj:`Completer`): A Completer instance. 60 block_store (:obj:): The block store, with dict-like access. 61 state_view_factory (:obj:`StateViewFactory`): The state view 62 factory for creating state views during processing. 63 identity_signer (:obj:`Signer`): A cryptographic signer used for 64 signing blocks. 65 data_dir (str): The directory for data files. 66 config_dir (str): The directory for config files. 67 chain_id_manager (ChainIdManager): utility class to manage the 68 chain id file. 69 batch_sender: interface to broadcast batches to the network. 70 """ 71 self._context_manager = context_manager 72 self._transaction_executor = transaction_executor 73 self._completer = completer 74 self._block_store = block_store 75 self._state_view_factory = state_view_factory 76 self._identity_signer = identity_signer 77 self._data_dir = data_dir 78 self._config_dir = config_dir 79 self._chain_id_manager = chain_id_manager 80 self._batch_sender = batch_sender 81 82 def requires_genesis(self): 83 """ 84 Determines if the system should be put in genesis mode 85 86 Returns: 87 bool: return whether or not a genesis block is required to be 88 generated. 89 90 Raises: 91 InvalidGenesisStateError: raises this error if there is invalid 92 combination of the following: genesis.batch, existing chain 93 head, and block chain id. 94 """ 95 96 genesis_file = os.path.join(self._data_dir, 'genesis.batch') 97 has_genesis_batches = Path(genesis_file).is_file() 98 LOGGER.debug('genesis_batch_file: %s', 99 genesis_file if has_genesis_batches else 'not found') 100 101 chain_head = self._block_store.chain_head 102 has_chain_head = chain_head is not None 103 if has_chain_head: 104 LOGGER.debug('chain_head: %s', chain_head) 105 106 block_chain_id = self._chain_id_manager.get_block_chain_id() 107 is_genesis_node = block_chain_id is None 108 LOGGER.debug( 109 'block_chain_id: %s', 110 block_chain_id if not is_genesis_node else 'not yet specified') 111 112 if has_genesis_batches and has_chain_head: 113 raise InvalidGenesisStateError( 114 'Cannot have a genesis_batch_file and an existing chain') 115 116 if has_genesis_batches and not is_genesis_node: 117 raise InvalidGenesisStateError( 118 'Cannot have a genesis_batch_file and join an existing network' 119 ) 120 121 if not has_genesis_batches and not has_chain_head: 122 LOGGER.info('No chain head and not the genesis node: ' 123 'starting in peering mode') 124 125 return has_genesis_batches and not has_chain_head and is_genesis_node 126 127 def start(self, on_done): 128 """ 129 Starts the genesis block creation process. Will call the given 130 `on_done` callback on successful completion. 131 132 Args: 133 on_done (function): a function called on completion 134 135 Raises: 136 InvalidGenesisStateError: raises this error if a genesis block is 137 unable to be produced, or the resulting block-chain-id saved. 138 """ 139 genesis_file = os.path.join(self._data_dir, 'genesis.batch') 140 try: 141 with open(genesis_file, 'rb') as batch_file: 142 genesis_data = genesis_pb2.GenesisData() 143 genesis_data.ParseFromString(batch_file.read()) 144 LOGGER.info('Producing genesis block from %s', genesis_file) 145 except IOError: 146 raise InvalidGenesisStateError( 147 "Genesis File {} specified, but unreadable".format( 148 genesis_file)) 149 150 initial_state_root = self._context_manager.get_first_root() 151 152 genesis_batches = [batch for batch in genesis_data.batches] 153 if genesis_batches: 154 scheduler = SerialScheduler( 155 self._context_manager.get_squash_handler(), 156 initial_state_root, 157 always_persist=True) 158 159 LOGGER.debug('Adding %s batches', len(genesis_data.batches)) 160 for batch in genesis_data.batches: 161 scheduler.add_batch(batch) 162 163 self._transaction_executor.execute(scheduler) 164 165 scheduler.finalize() 166 scheduler.complete(block=True) 167 168 state_hash = initial_state_root 169 for batch in genesis_batches: 170 result = scheduler.get_batch_execution_result( 171 batch.header_signature) 172 if result is None or not result.is_valid: 173 raise InvalidGenesisStateError( 174 'Unable to create genesis block, due to batch {}' 175 .format(batch.header_signature)) 176 if result.state_hash is not None: 177 state_hash = result.state_hash 178 LOGGER.debug('Produced state hash %s for genesis block.', state_hash) 179 180 block_builder = self._generate_genesis_block() 181 block_builder.add_batches(genesis_batches) 182 block_builder.set_state_hash(state_hash) 183 184 block_publisher = self._get_block_publisher(initial_state_root) 185 if not block_publisher.initialize_block(block_builder.block_header): 186 LOGGER.error('Consensus refused to initialize consensus block.') 187 raise InvalidGenesisConsensusError( 188 'Consensus refused to initialize genesis block.') 189 190 if not block_publisher.finalize_block(block_builder.block_header): 191 LOGGER.error('Consensus refused to finalize genesis block.') 192 raise InvalidGenesisConsensusError( 193 'Consensus refused to finalize genesis block.') 194 195 self._sign_block(block_builder) 196 197 block = block_builder.build_block() 198 199 blkw = BlockWrapper(block=block, status=BlockStatus.Valid) 200 201 LOGGER.info('Genesis block created: %s', blkw) 202 203 self._completer.add_block(block) 204 self._block_store.update_chain([blkw]) 205 206 self._chain_id_manager.save_block_chain_id(block.header_signature) 207 208 LOGGER.debug('Deleting genesis data.') 209 os.remove(genesis_file) 210 211 if on_done is not None: 212 on_done() 213 214 def _get_block_publisher(self, state_hash): 215 """Returns the block publisher based on the consensus module set by the 216 "sawtooth_settings" transaction family. 217 218 Args: 219 state_hash (str): The current state root hash for reading settings. 220 221 Raises: 222 InvalidGenesisStateError: if any errors occur getting the 223 BlockPublisher. 224 """ 225 state_view = self._state_view_factory.create_view(state_hash) 226 try: 227 class BatchPublisher(object): 228 def send(self, transactions): 229 # Consensus implementations are expected to have handling 230 # in place for genesis operation. This should includes 231 # adding any authorization and registrations required 232 # for the genesis node to the Genesis Batch list and 233 # detecting validation of the Genesis Block and handle it 234 # correctly. Batch publication is not allowed during 235 # genesis operation since there is no network to validate 236 # the batch yet. 237 raise InvalidGenesisConsensusError( 238 'Consensus cannot send transactions during genesis.') 239 240 consensus = ConsensusFactory.get_configured_consensus_module( 241 NULL_BLOCK_IDENTIFIER, 242 state_view) 243 return consensus.BlockPublisher( 244 BlockCache(self._block_store), 245 state_view_factory=self._state_view_factory, 246 batch_publisher=BatchPublisher(), 247 data_dir=self._data_dir, 248 config_dir=self._config_dir, 249 validator_id=self._identity_signer.get_public_key().as_hex()) 250 except UnknownConsensusModuleError as e: 251 raise InvalidGenesisStateError(e) 252 253 def _generate_genesis_block(self): 254 """ 255 Returns a blocker wrapper with the basics of the block header in place 256 """ 257 genesis_header = block_pb2.BlockHeader( 258 block_num=0, 259 previous_block_id=NULL_BLOCK_IDENTIFIER, 260 signer_public_key=self._identity_signer.get_public_key().as_hex()) 261 262 return BlockBuilder(genesis_header) 263 264 def _sign_block(self, block): 265 """ The block should be complete and the final 266 signature from the publishing validator (this validator) needs to 267 be added. 268 """ 269 block_header = block.block_header 270 header_bytes = block_header.SerializeToString() 271 signature = self._identity_signer.sign(header_bytes) 272 block.set_signature(signature) 273 return block