github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/tests/test_completer/test.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  # pylint: disable=protected-access
    17  
    18  import unittest
    19  import random
    20  import hashlib
    21  
    22  import cbor
    23  
    24  from sawtooth_signing import create_context
    25  from sawtooth_signing import CryptoFactory
    26  from sawtooth_validator.journal.completer import Completer
    27  from sawtooth_validator.database.dict_database import DictDatabase
    28  from sawtooth_validator.journal.block_store import BlockStore
    29  from sawtooth_validator.journal.block_wrapper import NULL_BLOCK_IDENTIFIER
    30  from sawtooth_validator.protobuf.transaction_pb2 import TransactionHeader, \
    31      Transaction
    32  from sawtooth_validator.protobuf.batch_pb2 import BatchHeader, Batch
    33  from sawtooth_validator.protobuf.block_pb2 import BlockHeader, Block
    34  from test_completer.mock import MockGossip
    35  
    36  
    37  class TestCompleter(unittest.TestCase):
    38      def setUp(self):
    39          self.block_store = BlockStore(DictDatabase(
    40              indexes=BlockStore.create_index_configuration()))
    41          self.gossip = MockGossip()
    42          self.completer = Completer(self.block_store, self.gossip)
    43          self.completer._on_block_received = self._on_block_received
    44          self.completer._on_batch_received = self._on_batch_received
    45          self.completer._has_block = self._has_block
    46          self._has_block_value = True
    47  
    48          context = create_context('secp256k1')
    49          private_key = context.new_random_private_key()
    50          crypto_factory = CryptoFactory(context)
    51          self.signer = crypto_factory.new_signer(private_key)
    52  
    53          self.blocks = []
    54          self.batches = []
    55  
    56      def _on_block_received(self, block):
    57          return self.blocks.append(block.header_signature)
    58  
    59      def _on_batch_received(self, batch):
    60          return self.batches.append(batch.header_signature)
    61  
    62      def _has_block(self, batch):
    63          return self._has_block_value
    64  
    65      def _create_transactions(self, count, missing_dep=False):
    66          txn_list = []
    67  
    68          for _ in range(count):
    69              payload = {
    70                  'Verb': 'set',
    71                  'Name': 'name' + str(random.randint(0, 100)),
    72                  'Value': random.randint(0, 100)
    73              }
    74              intkey_prefix = \
    75                  hashlib.sha512('intkey'.encode('utf-8')).hexdigest()[0:6]
    76  
    77              addr = intkey_prefix + \
    78                  hashlib.sha512(payload["Name"].encode('utf-8')).hexdigest()
    79  
    80              payload_encode = hashlib.sha512(cbor.dumps(payload)).hexdigest()
    81  
    82              header = TransactionHeader(
    83                  signer_public_key=self.signer.get_public_key().as_hex(),
    84                  family_name='intkey',
    85                  family_version='1.0',
    86                  inputs=[addr],
    87                  outputs=[addr],
    88                  dependencies=[],
    89                  batcher_public_key=self.signer.get_public_key().as_hex(),
    90                  payload_sha512=payload_encode)
    91  
    92              if missing_dep:
    93                  header.dependencies.extend(["Missing"])
    94  
    95              header_bytes = header.SerializeToString()
    96  
    97              signature = self.signer.sign(header_bytes)
    98  
    99              transaction = Transaction(
   100                  header=header_bytes,
   101                  payload=cbor.dumps(payload),
   102                  header_signature=signature)
   103  
   104              txn_list.append(transaction)
   105  
   106          return txn_list
   107  
   108      def _create_batches(self, batch_count, txn_count,
   109                          missing_dep=False):
   110  
   111          batch_list = []
   112  
   113          for _ in range(batch_count):
   114              txn_list = self._create_transactions(txn_count,
   115                                                   missing_dep=missing_dep)
   116              txn_sig_list = [txn.header_signature for txn in txn_list]
   117  
   118              batch_header = BatchHeader(
   119                  signer_public_key=self.signer.get_public_key().as_hex())
   120              batch_header.transaction_ids.extend(txn_sig_list)
   121  
   122              header_bytes = batch_header.SerializeToString()
   123  
   124              signature = self.signer.sign(header_bytes)
   125  
   126              batch = Batch(
   127                  header=header_bytes,
   128                  transactions=txn_list,
   129                  header_signature=signature)
   130  
   131              batch_list.append(batch)
   132  
   133          return batch_list
   134  
   135      def _create_blocks(self,
   136                         block_count,
   137                         batch_count,
   138                         missing_predecessor=False,
   139                         missing_batch=False,
   140                         find_batch=True):
   141          block_list = []
   142  
   143          for i in range(0, block_count):
   144              batch_list = self._create_batches(batch_count, 2)
   145              batch_ids = [batch.header_signature for batch in batch_list]
   146  
   147              if missing_predecessor:
   148                  predecessor = "Missing"
   149              else:
   150                  predecessor = (block_list[i - 1].header_signature if i > 0 else
   151                                 NULL_BLOCK_IDENTIFIER)
   152  
   153              block_header = BlockHeader(
   154                  signer_public_key=self.signer.get_public_key().as_hex(),
   155                  batch_ids=batch_ids,
   156                  block_num=i,
   157                  previous_block_id=predecessor)
   158  
   159              header_bytes = block_header.SerializeToString()
   160  
   161              signature = self.signer.sign(header_bytes)
   162  
   163              if missing_batch:
   164                  if find_batch:
   165                      self.completer.add_batch(batch_list[-1])
   166                  batch_list = batch_list[:-1]
   167  
   168              block = Block(
   169                  header=header_bytes,
   170                  batches=batch_list,
   171                  header_signature=signature)
   172  
   173              block_list.append(block)
   174  
   175          return block_list
   176  
   177      def test_good_block(self):
   178          """
   179          Add completed block to completer. Block should be passed to
   180          on_block_recieved.
   181          """
   182          block = self._create_blocks(1, 1)[0]
   183          self.completer.add_block(block)
   184          self.assertIn(block.header_signature, self.blocks)
   185  
   186      def test_duplicate_block(self):
   187          """
   188          Submit same block twice.
   189          """
   190          block = self._create_blocks(1, 1)[0]
   191          self.completer.add_block(block)
   192          self.completer.add_block(block)
   193          self.assertIn(block.header_signature, self.blocks)
   194          self.assertEqual(len(self.blocks), 1)
   195  
   196      def test_block_missing_predecessor(self):
   197          """
   198          The block is completed but the predecessor is missing.
   199          """
   200          block = self._create_blocks(1, 1, missing_predecessor=True)[0]
   201          self._has_block_value = False
   202          self.completer.add_block(block)
   203          self.assertEqual(len(self.blocks), 0)
   204          self.assertIn("Missing", self.gossip.requested_blocks)
   205          header = BlockHeader(previous_block_id=NULL_BLOCK_IDENTIFIER)
   206          missing_block = Block(header_signature="Missing",
   207                                header=header.SerializeToString())
   208          self._has_block_value = True
   209          self.completer.add_block(missing_block)
   210          self.assertIn(block.header_signature, self.blocks)
   211          self.assertEqual(
   212              block,
   213              self.completer.get_block(block.header_signature).get_block())
   214  
   215      def test_block_with_extra_batch(self):
   216          """
   217          The block has a batch that is not in the batch_id list.
   218          """
   219          block = self._create_blocks(1, 1)[0]
   220          batches = self._create_batches(1, 1, True)
   221          block.batches.extend(batches)
   222          self.completer.add_block(block)
   223          self.assertEqual(len(self.blocks), 0)
   224  
   225      def test_block_missing_batch(self):
   226          """
   227          The block is a missing batch and the batch is in the cache. The Block
   228          will be build and passed to on_block_recieved. This puts the block
   229          in the self.blocks list.
   230          """
   231          block = self._create_blocks(1, 2, missing_batch=True)[0]
   232          self.completer.add_block(block)
   233          self.assertIn(block.header_signature, self.blocks)
   234          self.assertEqual(
   235              block,
   236              self.completer.get_block(block.header_signature).get_block())
   237  
   238      def test_block_missing_batch_not_in_cache(self):
   239          """
   240          The block is a missing batch and the batch is not in the cache.
   241            The batch will be requested and the block will not be passed to
   242            on_block_recieved.
   243          """
   244          block = self._create_blocks(
   245              1, 3, missing_batch=True, find_batch=False)[0]
   246          self.completer.add_block(block)
   247          header = BlockHeader()
   248          header.ParseFromString(block.header)
   249          self.assertIn(header.batch_ids[-1], self.gossip.requested_batches)
   250  
   251      def test_block_batches_wrong_order(self):
   252          """
   253          The block has all of its batches but they are in the wrong order. The
   254          batches will be reordered and the block will be passed to
   255          on_block_recieved.
   256          """
   257          block = self._create_blocks(1, 6)[0]
   258          batches = list(block.batches)
   259          random.shuffle(batches)
   260          del block.batches[:]
   261          block.batches.extend(batches)
   262          self.completer.add_block(block)
   263          self.assertIn(block.header_signature, self.blocks)
   264  
   265      def test_block_batches_wrong_batch(self):
   266          """
   267          The block has all the correct number of batches but one is not in the
   268          batch_id list. This block should be dropped.
   269          """
   270          block = self._create_blocks(1, 6)[0]
   271          batch = Batch(header_signature="Extra")
   272          batches = list(block.batches)
   273          batches[-1] = batch
   274          block.batches.extend(batches)
   275          self.completer.add_block(block)
   276          self.assertEqual(len(self.blocks), 0)
   277  
   278      def test_good_batch(self):
   279          """
   280          Add complete batch to completer. The batch should be passed to
   281          on_batch_received.
   282          """
   283          batch = self._create_batches(1, 1)[0]
   284          self.completer.add_batch(batch)
   285          self.assertIn(batch.header_signature, self.batches)
   286          self.assertEqual(batch,
   287                           self.completer.get_batch(batch.header_signature))
   288  
   289      def test_batch_with_missing_dep(self):
   290          """
   291          Add batch to completer that has a missing dependency. The missing
   292          transaction's batch should be requested add the missing batch is then
   293          added to the completer. The incomplete batch should be rechecked
   294          and passed to on_batch_received.
   295          """
   296          batch = self._create_batches(1, 1, missing_dep=True)[0]
   297          self.completer.add_batch(batch)
   298          self.assertIn("Missing",
   299                        self.gossip.requested_batches_by_txn_id)
   300  
   301          missing = Transaction(header_signature="Missing")
   302          missing_batch = Batch(header_signature="Missing_batch",
   303                                transactions=[missing])
   304          self.completer.add_batch(missing_batch)
   305          self.assertIn(missing_batch.header_signature, self.batches)
   306          self.assertIn(batch.header_signature, self.batches)
   307          self.assertEqual(missing_batch,
   308                           self.completer.get_batch_by_transaction("Missing"))