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