def validate_persist_block(self, block: BamiBlock, peer: Peer = None) -> bool: """ Validate a block and if it's valid, persist it. Raises: InvalidBlockException - if block is not valid """ block = (BamiBlock.unpack(block, self.serializer) if type(block) is bytes else block) block_blob = block if type(block) is bytes else block.pack() if not block.block_invariants_valid(): # React on invalid block raise InvalidBlockException("Block invalid", str(block), peer) else: if not self.persistence.has_block(block.hash): self.process_block_unordered(block, peer) chain_id = block.com_id prefix = block.com_prefix if (self.persistence.get_chain(prefix + chain_id) and self.persistence.get_chain(prefix + chain_id).versions.get( block.com_seq_num) and block.short_hash in self.persistence.get_chain( prefix + chain_id).versions[block.com_seq_num]): raise Exception( "Inconsisistency between block store and chain store", self.persistence.get_chain(prefix + chain_id).versions, block.com_dot, ) self.persistence.add_block(block_blob, block)
def __init__( self, transaction: bytes = None, previous: Links = None, key: LibNaCLSK = None, links: Links = None, com_prefix: bytes = b"", com_id: Any = None, block_type: bytes = b"test", ): crypto = default_eccrypto if not links: links = GENESIS_LINK com_seq_num = 1 else: com_seq_num = max(links)[0] + 1 if not previous: previous = GENESIS_LINK pers_seq_num = max(previous)[0] + 1 if not com_id: com_id = crypto.generate_key(u"curve25519").pub().key_to_bin() if key: self.key = key else: self.key = crypto.generate_key(u"curve25519") if not transaction: transaction = encode_raw({"id": 42}) BamiBlock.__init__( self, ( block_type, transaction, self.key.pub().key_to_bin(), pers_seq_num, encode_links(previous), encode_links(links), com_prefix, com_id, com_seq_num, EMPTY_SIG, 0, 0, ), ) self.sign(self.key)
def test_create_next_pers(self, monkeypatch): """ Test creating a block that points towards a previous block """ db = MockDBManager() prev = FakeBlock() monkeypatch.setattr( MockDBManager, "get_chain", lambda _, chain_id: MockChain(), ) monkeypatch.setattr( MockChain, "consistent_terminal", Links((prev.pers_dot, )), ) block = BamiBlock.create(b"test", encode_raw({b"id": 42}), db, prev.public_key) assert block.previous == Links((prev.pers_dot, )) assert block.sequence_number == prev.sequence_number + 1 assert block.public_key == prev.public_key assert block.signature == EMPTY_SIG assert block.type == b"test" assert block.transaction == encode_raw({b"id": 42}) assert block.com_id == EMPTY_PK assert block.com_seq_num == UNKNOWN_SEQ
def test_create_link_to_com_chain(self, monkeypatch): """ Test creating a linked half that points back towards a previous block """ key = default_eccrypto.generate_key(u"curve25519") com_key = default_eccrypto.generate_key(u"curve25519").pub().key_to_bin() db = MockDBManager() com_link = Links(((1, ShortKey("30303030")),)) link = FakeBlock(com_id=com_key, links=com_link) monkeypatch.setattr( MockDBManager, "get_chain", lambda _, chain_id: MockChain() if chain_id == com_key else None, ) monkeypatch.setattr( MockChain, "consistent_terminal", Links((link.com_dot,)), ) block = BamiBlock.create( b"test", encode_raw({"id": 42}), db, key.pub().key_to_bin(), com_id=com_key ) # include the personal community # Attach to the assert block.links == Links((link.com_dot,)) assert block.previous == Links((GENESIS_DOT,)) assert block.sequence_number == GENESIS_SEQ assert block.com_seq_num == link.com_seq_num + 1 assert block.public_key == key.pub().key_to_bin() assert block.signature == EMPTY_SIG assert block.type == b"test" assert block.transaction == encode_raw({"id": 42}) assert block.com_id == com_key
def test_block_payload(self): blk = FakeBlock() blk_bytes = blk.pack() unpacked = blk.serializer.ez_unpack_serializables([BlockPayload], blk_bytes) blk2 = BamiBlock.from_payload(unpacked[0]) assert blk2 == blk
def insert_to_chain_or_blk_store( chain_obj: Union[BaseChain, BaseDB], blk: BamiBlock, personal_chain: bool = True, ): if isinstance(chain_obj, BaseChain): yield from insert_to_chain(chain_obj, blk, personal_chain) else: yield chain_obj.add_block(blk.pack(), blk)
def __init__(self, chain_factory: BaseChainFactory, block_store: BaseBlockStore): super().__init__() self._chain_factory = chain_factory self._block_store = block_store self.chains = dict() self.last_reconcile_seq_num = defaultdict(lambda: defaultdict(int)) self.last_frontier = defaultdict(lambda: defaultdict(lambda: Frontier( terminal=GENESIS_LINK, holes=(), inconsistencies=()))) # Sync chains with block store for block_blob in self._block_store.iterate_blocks(): self.add_block(block_blob[1], BamiBlock.unpack(block_blob[1]))
def get_block_and_blob_by_dot( self, chain_id: bytes, dot: Dot ) -> Tuple[bytes, BamiBlock]: """Get blob and serialized block and by the chain_id and dot. Can raise DatabaseDesynchronizedException if no block found.""" blk_blob = self.persistence.get_block_blob_by_dot(chain_id, dot) if not blk_blob: raise DatabaseDesynchronizedException( "Block is not found in db: {chain_id}, {dot}".format( chain_id=chain_id, dot=dot ) ) block = BamiBlock.unpack(blk_blob, self.serializer) return blk_blob, block
def test_create_genesis(self): """ Test creating a genesis block """ key = default_eccrypto.generate_key(u"curve25519") db = MockDBManager() block = BamiBlock.create(b"test", encode_raw({b"id": 42}), db, key.pub().key_to_bin()) assert block.previous == Links((GENESIS_DOT, )) assert block.sequence_number == GENESIS_SEQ assert block.public_key == key.pub().key_to_bin() assert block.signature == EMPTY_SIG assert block.type == b"test" assert block.transaction == encode_raw({b"id": 42}) assert block.com_id == EMPTY_PK assert block.com_seq_num == UNKNOWN_SEQ
def create_signed_block( self, block_type: bytes = b"unknown", transaction: Optional[bytes] = None, prefix: bytes = b"", com_id: bytes = None, links: Links = None, personal_links: Links = None, use_consistent_links: bool = True, ) -> BamiBlock: """ This function will create, sign, persist block with given parameters. Args: block_type: bytes of the block transaction: bytes blob of the transaction, or None to indicate an empty transaction payload prefix: prefix for the community id. For example b'w' - for witnessing transactions com_id: sub-community id if applicable links: explicitly link to certain links in the sub-community. Warning - may lead to forks! personal_links: explicitly link to certain blocks in the own chain. Warning - may lead to forks! use_consistent_links (): Returns: signed block """ if not transaction: transaction = encode_raw(b"") block = BamiBlock.create( block_type, transaction, self.persistence, self.my_pub_key_bin, com_id=com_id, com_links=links, pers_links=personal_links, com_prefix=prefix, use_consistent_links=use_consistent_links, ) block.sign(self.my_peer_key) self.validate_persist_block(block, self.my_peer) return block
def received_block_broadcast(self, peer: Peer, payload: BlockBroadcastPayload): block = BamiBlock.from_payload(payload, self.serializer) self.validate_persist_block(block, peer) self.process_broadcast_block(block, payload.ttl)
def received_raw_block_broadcast( self, peer: Peer, payload: RawBlockBroadcastPayload) -> None: block = BamiBlock.unpack(payload.block_bytes, self.serializer) self.validate_persist_block(block, peer) self.process_broadcast_block(block, payload.ttl)
def received_block(self, peer: Peer, payload: BlockPayload): block = BamiBlock.from_payload(payload, self.serializer) self.logger.debug("Received block from push gossip %s from peer %s", block.com_dot, peer) self.validate_persist_block(block, peer)
def received_raw_block(self, peer: Peer, payload: RawBlockPayload) -> None: block = BamiBlock.unpack(payload.block_bytes, self.serializer) self.logger.debug("Received block from pull gossip %s from peer %s", block.com_dot, peer) self.validate_persist_block(block, peer)
def test_pack_unpack(self): blk = FakeBlock() blk_bytes = blk.pack() blk2 = BamiBlock.unpack(blk_bytes, blk.serializer) assert blk == blk2