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