class TestBlockManager(unittest.TestCase): def setUp(self): self.block_manager = BlockManager() def test_block_manager(self): block_a = _build_block(1, "A", NULL_BLOCK_IDENTIFIER) block_b = _build_block(2, "B", "A") block_c = _build_block(3, "C", "B") self.block_manager.put([block_a, block_b, block_c]) block_e = _build_block(5, "E", "D") with self.assertRaises(MissingPredecessor): self.block_manager.put([block_e]) block_d = _build_block(4, "D", "C") self.block_manager.put([block_d, block_e]) block_c2 = _build_block(3, "C2", "B") block_d2 = _build_block(4, "D2", "C2") block_e2 = _build_block(5, "E2", "D2") with self.assertRaises(MissingPredecessorInBranch): self.block_manager.put([block_c2, block_e2, block_d2]) block_id = "D" for block in self.block_manager.branch("D"): self.assertEqual(block.header_signature, block_id) header = block_pb2.BlockHeader() header.ParseFromString(block.header) block_id = header.previous_block_id self.block_manager.put([block_c2, block_d2, block_e2]) block_id = "D" for block in self.block_manager.branch_diff("D", "E2"): self.assertEqual(block.header_signature, block_id) header = block_pb2.BlockHeader() header.ParseFromString(block.header) block_id = header.previous_block_id for block in self.block_manager.get("C"): self.assertEqual(block.header_signature, "C")
class BlockTreeManager: def __str__(self): return str(self.block_cache) def __repr__(self): return repr(self.block_cache) def __init__(self, with_genesis=True): self.block_sender = MockBlockSender() self.batch_sender = MockBatchSender() self.dir = tempfile.mkdtemp() self.block_db = NativeLmdbDatabase( os.path.join(self.dir, 'block.lmdb'), BlockStore.create_index_configuration()) self.block_store = BlockStore(self.block_db) self.block_cache = BlockCache(self.block_store) self.state_db = NativeLmdbDatabase( os.path.join(self.dir, "merkle.lmdb"), MerkleDatabase.create_index_configuration()) self.state_view_factory = NativeStateViewFactory(self.state_db) self.block_manager = BlockManager() self.block_manager.add_commit_store(self.block_store) context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) identity_private_key = context.new_random_private_key() self.identity_signer = crypto_factory.new_signer(identity_private_key) chain_head = None if with_genesis: self.genesis_block = self.generate_genesis_block() chain_head = self.genesis_block self.block_manager.put([chain_head.block]) self.block_manager.persist(chain_head.block.header_signature, "commit_store") self.block_publisher = BlockPublisher( block_store=self.block_store, block_manager=self.block_manager, transaction_executor=MockTransactionExecutor(), state_view_factory=self.state_view_factory, block_sender=self.block_sender, batch_sender=self.block_sender, identity_signer=self.identity_signer, data_dir=None, config_dir=None, permission_verifier=MockPermissionVerifier(), batch_observers=[]) @property def chain_head(self): return self.block_store.chain_head def generate_block(self, previous_block=None, add_to_store=False, add_to_cache=False, batch_count=1, batches=None, status=BlockStatus.Unknown, invalid_consensus=False, invalid_batch=False, invalid_signature=False, weight=0): previous = self._get_block(previous_block) if previous is None: previous = self.chain_head header = BlockHeader( previous_block_id=previous.identifier, signer_public_key=self.identity_signer.get_public_key().as_hex(), block_num=previous.block_num + 1) block_builder = BlockBuilder(header) if batches: block_builder.add_batches(batches) if batch_count != 0: block_builder.add_batches( [self._generate_batch() for _ in range(batch_count)]) if invalid_batch: block_builder.add_batches( [self._generate_batch_from_payload('BAD')]) block_builder.set_state_hash('0' * 70) consensus = mock_consensus.BlockPublisher() consensus.finalize_block(block_builder.block_header, weight=weight) if invalid_consensus: block_builder.block_header.consensus = b'BAD' header_bytes = block_builder.block_header.SerializeToString() if invalid_signature: block_builder.set_signature('BAD') else: signature = self.identity_signer.sign(header_bytes) block_builder.set_signature(signature) block_wrapper = BlockWrapper(block_builder.build_block()) if batches: block_wrapper.block.batches.extend(batches) if batch_count: block_wrapper.block.batches.extend( [self.generate_batch() for _ in range(batch_count)]) if invalid_signature: block_wrapper.block.header_signature = "BAD" if invalid_consensus: block_wrapper.header.consensus = b'BAD' block_wrapper.status = status self.block_manager.put([block_wrapper.block]) if add_to_cache: self.block_cache[block_wrapper.identifier] = block_wrapper if add_to_store: self.block_store.put_blocks([block_wrapper.block]) LOGGER.debug("Generated %s", dumps_block(block_wrapper)) return block_wrapper def generate_chain(self, root_block, blocks, params=None, exclude_head=True): """ Generate a new chain based on the root block and place it in the block cache. """ if params is None: params = {} if root_block is None: previous = self.generate_genesis_block() self.block_store.put_blocks([previous.block]) else: previous = self._get_block(root_block) try: block_defs = [self._block_def(**params) for _ in range(blocks)] if exclude_head: block_defs[-1] = self._block_def() except TypeError: block_defs = blocks out = [] for block_def in block_defs: new_block = self.generate_block(previous_block=previous, **block_def) out.append(new_block) previous = new_block return out def create_block(self, payload='payload', batch_count=1, previous_block_id=NULL_BLOCK_IDENTIFIER, block_num=0): header = BlockHeader( previous_block_id=previous_block_id, signer_public_key=self.identity_signer.get_public_key().as_hex(), block_num=block_num) block_builder = BlockBuilder(header) block_builder.add_batches([ self._generate_batch_from_payload(payload) for _ in range(batch_count) ]) block_builder.set_state_hash('0' * 70) header_bytes = block_builder.block_header.SerializeToString() signature = self.identity_signer.sign(header_bytes) block_builder.set_signature(signature) block_wrapper = BlockWrapper(block_builder.build_block()) LOGGER.debug("Generated %s", dumps_block(block_wrapper)) return block_wrapper def generate_genesis_block(self): return self.create_block(payload='Genesis', previous_block_id=NULL_BLOCK_IDENTIFIER, block_num=0) def _block_def(self, add_to_store=False, add_to_cache=False, batch_count=1, status=BlockStatus.Unknown, invalid_consensus=False, invalid_batch=False, invalid_signature=False, weight=0): return { "add_to_cache": add_to_cache, "add_to_store": add_to_store, "batch_count": batch_count, "status": status, "invalid_consensus": invalid_consensus, "invalid_batch": invalid_batch, "invalid_signature": invalid_signature, "weight": weight } def _get_block_id(self, block): if block is None: return None elif isinstance(block, (Block, BlockWrapper)): return block.header_signature else: return str(block) def _get_block(self, block): if block is None: return None elif isinstance(block, Block): return BlockWrapper(block) elif isinstance(block, BlockWrapper): return block elif isinstance(block, str): return self.block_cache[block] else: # WTF try something crazy return self.block_cache[str(block)] def generate_batch(self, txn_count=2, missing_deps=False, txns=None): batch = self._generate_batch(txn_count, missing_deps, txns) LOGGER.debug("Generated Batch:\n%s", dumps_batch(batch)) return batch def _generate_batch(self, txn_count=2, missing_deps=False, txns=None): if txns is None: txns = [] if txn_count != 0: txns += [ self.generate_transaction('txn_' + str(i)) for i in range(txn_count) ] if missing_deps: target_txn = txns[-1] txn_missing_deps = self.generate_transaction( payload='this one has a missing dependency', deps=[target_txn.header_signature]) # replace the targeted txn with the missing deps txn txns[-1] = txn_missing_deps batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature for txn in txns]).SerializeToString() batch = Batch(header=batch_header, header_signature=self.signer.sign(batch_header), transactions=txns) return batch def generate_transaction(self, payload='txn', deps=None): payload_encoded = payload.encode('utf-8') hasher = hashlib.sha512() hasher.update(payload_encoded) txn_header = TransactionHeader( dependencies=([] if deps is None else deps), batcher_public_key=self.signer.get_public_key().as_hex(), family_name='test', family_version='1', nonce=_generate_id(16), payload_sha512=hasher.hexdigest().encode(), signer_public_key=self.signer.get_public_key().as_hex( )).SerializeToString() txn = Transaction(header=txn_header, header_signature=self.signer.sign(txn_header), payload=payload_encoded) return txn def _generate_batch_from_payload(self, payload): txn = self.generate_transaction(payload) batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature]).SerializeToString() batch = Batch(header=batch_header, header_signature=self.signer.sign(batch_header), transactions=[txn]) return batch
class BlockTreeManager: def __str__(self): return str(self.block_cache) def __repr__(self): return repr(self.block_cache) def __init__(self, with_genesis=True): self.block_sender = MockBlockSender() self.batch_sender = MockBatchSender() self.dir = tempfile.mkdtemp() self.block_db = NativeLmdbDatabase( os.path.join(self.dir, 'block.lmdb'), BlockStore.create_index_configuration()) self.block_store = BlockStore(self.block_db) self.block_cache = BlockCache(self.block_store) self.state_db = NativeLmdbDatabase( os.path.join(self.dir, "merkle.lmdb"), MerkleDatabase.create_index_configuration()) self.state_view_factory = NativeStateViewFactory(self.state_db) self.block_manager = BlockManager() self.block_manager.add_commit_store(self.block_store) context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) identity_private_key = context.new_random_private_key() self.identity_signer = crypto_factory.new_signer(identity_private_key) chain_head = None if with_genesis: self.genesis_block = self.generate_genesis_block() chain_head = self.genesis_block self.block_manager.put([chain_head.block]) self.block_manager.persist( chain_head.block.header_signature, "commit_store") self.block_publisher = BlockPublisher( block_manager=self.block_manager, transaction_executor=MockTransactionExecutor(), transaction_committed=self.block_store.has_transaction, batch_committed=self.block_store.has_batch, state_view_factory=self.state_view_factory, block_sender=self.block_sender, batch_sender=self.block_sender, chain_head=chain_head.block, identity_signer=self.identity_signer, data_dir=None, config_dir=None, permission_verifier=MockPermissionVerifier(), batch_observers=[]) @property def chain_head(self): return self.block_store.chain_head def generate_block(self, previous_block=None, add_to_store=False, add_to_cache=False, batch_count=1, batches=None, status=BlockStatus.Unknown, invalid_consensus=False, invalid_batch=False, invalid_signature=False, weight=0): previous = self._get_block(previous_block) if previous is None: previous = self.chain_head header = BlockHeader( previous_block_id=previous.identifier, signer_public_key=self.identity_signer.get_public_key().as_hex(), block_num=previous.block_num + 1) block_builder = BlockBuilder(header) if batches: block_builder.add_batches(batches) if batch_count != 0: block_builder.add_batches( [self._generate_batch() for _ in range(batch_count)]) if invalid_batch: block_builder.add_batches( [self._generate_batch_from_payload('BAD')]) block_builder.set_state_hash('0' * 70) consensus = mock_consensus.BlockPublisher() consensus.finalize_block(block_builder.block_header, weight=weight) if invalid_consensus: block_builder.block_header.consensus = b'BAD' header_bytes = block_builder.block_header.SerializeToString() if invalid_signature: block_builder.set_signature('BAD') else: signature = self.identity_signer.sign(header_bytes) block_builder.set_signature(signature) block_wrapper = BlockWrapper(block_builder.build_block()) if batches: block_wrapper.block.batches.extend(batches) if batch_count: block_wrapper.block.batches.extend( [self.generate_batch() for _ in range(batch_count)]) if invalid_signature: block_wrapper.block.header_signature = "BAD" if invalid_consensus: block_wrapper.header.consensus = b'BAD' block_wrapper.status = status self.block_manager.put([block_wrapper.block]) if add_to_cache: self.block_cache[block_wrapper.identifier] = block_wrapper if add_to_store: self.block_store.put_blocks([block_wrapper.block]) LOGGER.debug("Generated %s", dumps_block(block_wrapper)) return block_wrapper def generate_chain(self, root_block, blocks, params=None, exclude_head=True): """ Generate a new chain based on the root block and place it in the block cache. """ if params is None: params = {} if root_block is None: previous = self.generate_genesis_block() self.block_store.put_blocks([previous.block]) else: previous = self._get_block(root_block) try: block_defs = [self._block_def(**params) for _ in range(blocks)] if exclude_head: block_defs[-1] = self._block_def() except TypeError: block_defs = blocks out = [] for block_def in block_defs: new_block = self.generate_block( previous_block=previous, **block_def) out.append(new_block) previous = new_block return out def create_block(self, payload='payload', batch_count=1, previous_block_id=NULL_BLOCK_IDENTIFIER, block_num=0): header = BlockHeader( previous_block_id=previous_block_id, signer_public_key=self.identity_signer.get_public_key().as_hex(), block_num=block_num) block_builder = BlockBuilder(header) block_builder.add_batches( [self._generate_batch_from_payload(payload) for _ in range(batch_count)]) block_builder.set_state_hash('0' * 70) header_bytes = block_builder.block_header.SerializeToString() signature = self.identity_signer.sign(header_bytes) block_builder.set_signature(signature) block_wrapper = BlockWrapper(block_builder.build_block()) LOGGER.debug("Generated %s", dumps_block(block_wrapper)) return block_wrapper def generate_genesis_block(self): return self.create_block( payload='Genesis', previous_block_id=NULL_BLOCK_IDENTIFIER, block_num=0) def _block_def(self, add_to_store=False, add_to_cache=False, batch_count=1, status=BlockStatus.Unknown, invalid_consensus=False, invalid_batch=False, invalid_signature=False, weight=0): return { "add_to_cache": add_to_cache, "add_to_store": add_to_store, "batch_count": batch_count, "status": status, "invalid_consensus": invalid_consensus, "invalid_batch": invalid_batch, "invalid_signature": invalid_signature, "weight": weight } def _get_block_id(self, block): if block is None: return None elif isinstance(block, (Block, BlockWrapper)): return block.header_signature else: return str(block) def _get_block(self, block): if block is None: return None elif isinstance(block, Block): return BlockWrapper(block) elif isinstance(block, BlockWrapper): return block elif isinstance(block, str): return self.block_cache[block] else: # WTF try something crazy return self.block_cache[str(block)] def generate_batch(self, txn_count=2, missing_deps=False, txns=None): batch = self._generate_batch(txn_count, missing_deps, txns) LOGGER.debug("Generated Batch:\n%s", dumps_batch(batch)) return batch def _generate_batch(self, txn_count=2, missing_deps=False, txns=None): if txns is None: txns = [] if txn_count != 0: txns += [ self.generate_transaction('txn_' + str(i)) for i in range(txn_count) ] if missing_deps: target_txn = txns[-1] txn_missing_deps = self.generate_transaction( payload='this one has a missing dependency', deps=[target_txn.header_signature]) # replace the targeted txn with the missing deps txn txns[-1] = txn_missing_deps batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature for txn in txns] ).SerializeToString() batch = Batch( header=batch_header, header_signature=self.signer.sign(batch_header), transactions=txns) return batch def generate_transaction(self, payload='txn', deps=None): payload_encoded = payload.encode('utf-8') hasher = hashlib.sha512() hasher.update(payload_encoded) txn_header = TransactionHeader( dependencies=([] if deps is None else deps), batcher_public_key=self.signer.get_public_key().as_hex(), family_name='test', family_version='1', nonce=_generate_id(16), payload_sha512=hasher.hexdigest().encode(), signer_public_key=self.signer.get_public_key().as_hex() ).SerializeToString() txn = Transaction( header=txn_header, header_signature=self.signer.sign(txn_header), payload=payload_encoded) return txn def _generate_batch_from_payload(self, payload): txn = self.generate_transaction(payload) batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature] ).SerializeToString() batch = Batch( header=batch_header, header_signature=self.signer.sign(batch_header), transactions=[txn]) return batch