def add_vote(self, vote: BlockVote): with self.__blocks_lock: if vote.block_hash != Hash32.empty() and vote.block_hash not in self.blocks: # util.logger.debug(f"-------------block_hash({block_hash}) self.blocks({self.blocks})") self.blocks[vote.block_hash] = CandidateBlock.from_hash(vote.block_hash, vote.block_height) if vote.block_hash != Hash32.empty(): self.blocks[vote.block_hash].add_vote(vote) else: for block in self.blocks.values(): if block.height == vote.block_height: block.add_vote(vote)
def add_vote(self, vote: 'BlockVote'): with self.__blocks_lock: if vote.block_hash != Hash32.empty( ) and vote.block_hash not in self.blocks: self.blocks[vote.block_hash] = CandidateBlock.from_hash( vote.block_hash, vote.block_height) if vote.block_hash != Hash32.empty(): self.blocks[vote.block_hash].add_vote(vote) else: for block in self.blocks.values(): if block.height == vote.block_height: block.add_vote(vote)
def to_hash32(self, value: Union[Hash32, bytes, bytearray, int, bool, dict]): if value is None: return Hash32.empty() elif isinstance(value, Hash32): return value elif isinstance(value, (bytes, bytearray)) and len(value) == 32: return Hash32(value) if isinstance(value, bool): value = b'\x01' if value else b'\x00' elif isinstance(value, int): if value < 0: raise RuntimeError(f"value : {value} is negative.") value = value.to_bytes((value.bit_length() + 7) // 8, "big") elif isinstance(value, dict): if self.type == BlockProverType.Receipt: value = dict(value) value.pop("failure", None) value.pop("blockHash", None) hash_generator = self.get_hash_generator() value = hash_generator.generate_salted_origin(value) value = value.encode() return Hash32(hashlib.sha3_256(value).digest())
def is_unrecorded(self) -> bool: """Return is unrecorded block :return: bool """ return (self.next_leader == ExternalAddress.empty() and self.reps_hash == self.next_reps_hash == Hash32.empty())
def vote_unconfirmed_block(self, block: Block, round_: int, is_validated): util.logger.debug( f"vote_unconfirmed_block() ({block.header.height}/{block.header.hash}/{is_validated})" ) vote = Vote.get_block_vote_class(block.header.version).new( signer=ChannelProperty().peer_auth, block_height=block.header.height, round_=round_, block_hash=block.header.hash if is_validated else Hash32.empty(), timestamp=util.get_time_stamp()) self.candidate_blocks.add_vote(vote) vote_serialized = vote.serialize() vote_dumped = json.dumps(vote_serialized) block_vote = loopchain_pb2.BlockVote(vote=vote_dumped, channel=ChannelProperty().name) target_reps_hash = block.header.reps_hash if not target_reps_hash: target_reps_hash = self.__channel_service.peer_manager.crep_root_hash self.__channel_service.broadcast_scheduler.schedule_broadcast( "VoteUnconfirmedBlock", block_vote, reps_hash=target_reps_hash) return vote
def _build_transactions_hash(self): if not self.transactions: return Hash32.empty() block_prover = BlockProver(self.transactions.keys(), BlockProverType.Transaction) return block_prover.get_proof_root()
def test_block_invalid_vote(self): ratio = 0.67 block_hash = Hash32(os.urandom(Hash32.size)) block_votes = BlockVotes(self.reps, ratio, 0, 0, block_hash) invalid_block_vote = BlockVote.new(self.signers[0], 0, 0, 1, block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote.new(self.signers[0], 0, 1, 0, block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote.new(self.signers[0], 0, 0, 0, Hash32(os.urandom(32))) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) invalid_block_vote = BlockVote(rep=self.reps[0], timestamp=0, signature=Signature(os.urandom(65)), block_height=0, round_=0, block_hash=block_hash) self.assertRaises(RuntimeError, block_votes.add_vote, invalid_block_vote) block_vote = BlockVote.new(self.signers[0], 0, 0, 0, block_hash) block_votes.add_vote(block_vote) duplicate_block_vote = BlockVote.new(self.signers[0], 0, 0, 0, Hash32.empty()) self.assertRaises(votes.VoteDuplicateError, block_votes.add_vote, duplicate_block_vote)
def get_result(self): true_vote_count = sum(1 for vote in self.votes if vote and vote.block_hash == self.block_hash) if true_vote_count >= self.quorum: return True false_vote_count = sum(1 for vote in self.votes if vote and vote.block_hash == Hash32.empty()) if false_vote_count >= len(self.reps) - self.quorum + 1: return False return None
def verify_vote(self, vote: BlockVote): if vote.block_height != self.block_height: raise RuntimeError(f"Vote block_height not match. {vote.block_height} != {self.block_height}\n" f"{vote}") if vote.round_ != self.round: raise RuntimeError(f"Vote round not match. {vote.round_} != {self.round}\n" f"{vote}") if vote.block_hash != self.block_hash and vote.block_hash != Hash32.empty(): raise RuntimeError(f"Vote block_hash not match. {vote.block_hash} != {self.block_hash}\n" f"{vote}") super().verify_vote(vote)
def test_block_votes_completed(self): ratio = 0.67 block_hash = Hash32(os.urandom(Hash32.size)) block_votes = BlockVotes(self.reps, ratio, 0, 0, block_hash) signers = list(enumerate(self.signers)) for i, signer in signers[:25]: block_vote = BlockVote.new(signer, 0, 0, 0, block_hash) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), False) self.assertEqual(block_votes.get_result(), None) for i, signer in signers[25:50]: block_vote = BlockVote.new(signer, 0, 0, 0, block_hash) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), False) self.assertEqual(block_votes.get_result(), None) for i, signer in signers[50:75]: block_vote = BlockVote.new(signer, 0, 0, 0, Hash32.empty()) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), False) self.assertEqual(block_votes.get_result(), None) for i, signer in signers[75:90]: block_vote = BlockVote.new(signer, 0, 0, 0, Hash32.empty()) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), True) self.assertEqual(block_votes.get_result(), False)
def test_block_votes_false(self): ratio = 0.67 block_hash = Hash32(os.urandom(Hash32.size)) block_votes = BlockVotes(self.reps, ratio, 0, 0, block_hash) for i, signer in enumerate(self.signers): if i % 4 == 0: block_vote = BlockVote.new(signer, 0, 0, 0, block_hash) else: block_vote = BlockVote.new(signer, 0, 0, 0, Hash32.empty()) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.quorum, len(self.reps) * ratio) self.assertEqual(block_votes.get_result(), False)
def test_block_votes_fail(self): ratio = 0.67 block_hash = Hash32(os.urandom(Hash32.size)) block_votes = BlockVotes(self.reps, ratio, 0, 0, block_hash) for i, signer in enumerate(self.signers): if i == 33: break block_vote = BlockVote.new(signer, 0, 0, 0, Hash32.empty()) block_votes.add_vote(block_vote) self.assertEqual(block_votes.quorum, len(self.reps) * ratio) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), False) self.assertEqual(block_votes.get_result(), None) block_vote = BlockVote.new(self.signers[99], 0, 0, 0, Hash32.empty()) block_votes.add_vote(block_vote) logging.info(block_votes) self.assertEqual(block_votes.is_completed(), True) self.assertEqual(block_votes.get_result(), False)
def _build_receipts_hash(self): if not self.receipts: return Hash32.empty() block_prover = BlockProver(self.receipts, BlockProverType.Receipt) return block_prover.get_proof_root()
def empty(cls, rep: ExternalAddress, block_height: int): return cls(rep, 0, Signature.empty(), block_height, Hash32.empty())
def result(self) -> bool: return self.block_hash != Hash32.empty()
def prep_changed(self) -> bool: """Return reason for prep changed :return: False means there is no change. """ return self.next_reps_hash != Hash32.empty()
def complained(self): return self.leader_votes_hash != Hash32.empty()
def get_proof_root(self) -> Hash32: if not self._merkle_tree.is_ready: self.make_tree() root = self._merkle_tree.get_merkle_root() return Hash32(root) if root is not None else Hash32.empty()
def test_prep_is_not_changed_if_next_reps_hash_is_empty(self, header_factory): header = header_factory(next_leader=ExternalAddress(os.urandom(ExternalAddress.size)), reps_hash=Hash32(os.urandom(Hash32.size)), next_reps_hash=Hash32.empty()) assert not header.prep_changed