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 test_prep_changed_by_penalty_if_exists_next_reps_hash_and_next_leader(self, header_factory): header = header_factory(next_leader=ExternalAddress(os.urandom(ExternalAddress.size)), reps_hash=Hash32(os.urandom(Hash32.size)), next_reps_hash=Hash32(os.urandom(Hash32.size))) assert header.prep_changed assert header.prep_changed_reason == NextRepsChangeReason.TermEnd
def _build_merkle_tree_root_hash(self): merkle_tree_root_hash = None mt_list = [tx_hash.hex() for tx_hash in self.transactions.keys()] while True: tree_length = len(mt_list) tmp_mt_list = [] if tree_length <= 1: break elif tree_length % 2 == 1: mt_list.append(mt_list[tree_length - 1]) tree_length += 1 for row in range(int(tree_length / 2)): idx = row * 2 mt_nodes = [ mt_list[idx].encode(encoding='UTF-8'), mt_list[idx + 1].encode(encoding='UTF-8') ] mk_sum = b''.join(mt_nodes) mk_hash = hashlib.sha256(mk_sum).hexdigest() tmp_mt_list.append(mk_hash) mt_list = tmp_mt_list if len(mt_list) == 1: merkle_tree_root_hash = mt_list[0] if merkle_tree_root_hash: return Hash32.fromhex(merkle_tree_root_hash, True) return Hash32(bytes(Hash32.size))
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 test_prep_changed_by_term_end_if_next_leader_is_empty(self, header_factory): header = header_factory(next_leader=ExternalAddress.empty(), reps_hash=Hash32(os.urandom(Hash32.size)), next_reps_hash=Hash32(os.urandom(Hash32.size))) assert header.prep_changed assert header.prep_changed_reason is NextRepsChangeReason.TermEnd
def make_proof_serializable(proof: list): proof_serializable = [] for item in proof: try: left = Hash32(item["left"]) proof_serializable.append({"left": left.hex_0x()}) except KeyError: right = Hash32(item["right"]) proof_serializable.append({"right": right.hex_0x()}) return proof_serializable
def make_proof_deserializable(proof: list): proof_deserializable = [] for item in proof: try: left: str = item["left"] proof_deserializable.append({"left": Hash32.fromhex(left)}) except KeyError: right: str = item["right"] proof_deserializable.append({"right": Hash32.fromhex(right)}) return proof_deserializable
def _deserialize_header_data(self, json_data: dict): hash_ = Hash32.fromhex(json_data["hash"]) prev_hash = json_data.get('prevHash') prev_hash = Hash32.fromhex(prev_hash) if prev_hash else None peer_id = json_data.get('leader') peer_id = ExternalAddress.fromhex(peer_id) if peer_id else None signature = json_data.get('signature') signature = Signature.from_base64str(signature) if signature else None next_leader = json_data.get("nextLeader") next_leader = ExternalAddress.fromhex( next_leader) if next_leader else None transactions_hash = json_data["transactionsHash"] transactions_hash = Hash32.fromhex(transactions_hash) receipts_hash = json_data["receiptsHash"] receipts_hash = Hash32.fromhex(receipts_hash) state_hash = json_data["stateHash"] state_hash = Hash32.fromhex(state_hash) reps_hash = json_data["repsHash"] reps_hash = Hash32.fromhex(reps_hash) next_reps_hash = json_data["nextRepsHash"] next_reps_hash = Hash32.fromhex(next_reps_hash) leader_votes_hash = json_data["leaderVotesHash"] leader_votes_hash = Hash32.fromhex(leader_votes_hash) prev_votes_hash = json_data["prevVotesHash"] prev_votes_hash = Hash32.fromhex(prev_votes_hash) height = json_data["height"] height = int(height, 16) timestamp = json_data["timestamp"] timestamp = int(timestamp, 16) return { "hash": hash_, "prev_hash": prev_hash, "height": height, "timestamp": timestamp, "peer_id": peer_id, "signature": signature, "next_leader": next_leader, "transactions_hash": transactions_hash, "receipts_hash": receipts_hash, "state_hash": state_hash, "reps_hash": reps_hash, "next_reps_hash": next_reps_hash, "leader_votes_hash": leader_votes_hash, "prev_votes_hash": prev_votes_hash, "logs_bloom": BloomFilter.fromhex(json_data["logsBloom"]) }
def _header(hash_: Hash32 = Hash32.new(), prev_hash: Hash32 = Hash32.new(), height: int = 0, timestamp: int = 0, peer_id: ExternalAddress = ExternalAddress.new(), signature: Signature = Signature.new(), next_leader: Address = Address.new(), merkle_tree_root_hash: Hash32 = Hash32.new(), commit_state: dict = dict()) -> BlockHeader_v0_1a: return BlockHeader_v0_1a(hash_, prev_hash, height, timestamp, peer_id, signature, next_leader, merkle_tree_root_hash, commit_state)
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 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 reset_all_peers(self, reps_hash, reps, update_now=True): util.logger.debug(f"reset_all_peers." f"\nresult roothash({reps_hash})" f"\npeer_list roothash({self.reps_hash().hex()})" f"\nupdate now({update_now})") if not update_now: self._reps_reset_data = (reps_hash, reps) return blockchain = ObjectManager().channel_service.block_manager.blockchain if reps_hash == self.reps_hash().hex(): util.logger.debug(f"There is no change in load_peers_from_iiss.") return self._peer_list_data.peer_list.clear() self._prepared_reps_hash = None for order, rep_info in enumerate(reps, 1): peer = Peer(rep_info["id"], rep_info["p2pEndpoint"], order=order) self.add_peer(peer) new_reps = blockchain.find_preps_addresses_by_roothash( Hash32.fromhex(reps_hash, ignore_prefix=True)) new_node_type = NodeType.CommunityNode if ChannelProperty( ).peer_address in new_reps else NodeType.CitizenNode is_switched_role = new_node_type != ChannelProperty().node_type blockchain.reset_leader_made_block_count(is_switched_role)
async def __get_block(self, block_hash, block_height): if block_hash == "" and block_height == -1 and self._blockchain.last_block: block_hash = self._blockchain.last_block.header.hash.hex() block = None confirm_info = b'' fail_response_code = None if block_hash: block = self._blockchain.find_block_by_hash(block_hash) if block is None: fail_response_code = message_code.Response.fail_wrong_block_hash confirm_info = bytes() else: confirm_info = self._blockchain.find_confirm_info_by_hash( Hash32.fromhex(block_hash, True)) elif block_height != -1: block = self._blockchain.find_block_by_height(block_height) if block is None: fail_response_code = message_code.Response.fail_wrong_block_height confirm_info = bytes() else: confirm_info = self._blockchain.find_confirm_info_by_hash( block.header.hash) else: fail_response_code = message_code.Response.fail_wrong_block_hash return block, block_hash, bytes(confirm_info), fail_response_code
def get_hash(self, block_dumped: Union[str, dict]): if isinstance(block_dumped, str): block_dumped = json.loads(block_dumped) hash_ = block_dumped.get("block_hash") or block_dumped.get("hash") ignore_prefix = block_dumped['version'] == '0.1a' return Hash32.fromhex(hash_, ignore_prefix) if isinstance(hash_, str) else hash_
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
async def __get_block(self, block_hash, block_height, unconfirmed=False): if block_hash == "" and block_height == -1 and self._blockchain.last_block: block_hash = self._blockchain.last_block.header.hash.hex() block = None confirm_info = b'' fail_response_code = None try: if block_hash: block = self._blockchain.find_block_by_hash(block_hash) if block is None: fail_response_code = message_code.Response.fail_wrong_block_hash confirm_info = bytes() else: confirm_info = self._blockchain.find_confirm_info_by_hash(Hash32.fromhex(block_hash, True)) elif block_height != -1: block = self._blockchain.find_block_by_height(block_height) is_wrong_block_height: bool = ( block is None or unconfirmed is False and self._blockchain.is_last_unconfirmed_block(block.header.height) ) if is_wrong_block_height: fail_response_code = message_code.Response.fail_wrong_block_height confirm_info = bytes() else: confirm_info = self._blockchain.find_confirm_info_by_hash(block.header.hash) else: fail_response_code = message_code.Response.fail_wrong_block_hash except PrunedHashDataError as e: logging.warning(f"{e!r}") fail_response_code = e.message_code return block, block_hash, bytes(confirm_info), fail_response_code
def create_icx_origin_v3(self, is_raw_data=False): params = dict() params["version"] = "0x3" params["from"] = self.address params["to"] = self.to_address params["value"] = hex(int(self.value * ICX_FACTOR)) params["stepLimit"] = "0x3000000" params["timestamp"] = hex(utils.get_now_time_stamp()) params["nonce"] = "0x0" params["nid"] = self.nid if self.message is not None: params["dataType"] = "message" params["data"] = VarBytes(self.message.encode('utf-8')).hex_0x() hash_for_sign = self.__hash_generators["0x3"].generate_hash(params) params["signature"] = self.create_signature(hash_for_sign) if self.is_logging: logging.debug(f"icx_sendTransaction params for v3: {params}") self.__last_tx_hash = Hash32(hash_for_sign).hex_0x() icx_origin = dict() icx_origin["jsonrpc"] = "2.0" icx_origin["method"] = "icx_sendTransaction" icx_origin["id"] = random.randrange(0, 100000) icx_origin["params"] = params return icx_origin if is_raw_data else params
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 from_(self, tx_data: dict) -> 'Transaction': tx_data_copied = dict(tx_data) tx_data_copied.pop('method', None) hash = tx_data_copied.pop('tx_hash', None) signature = tx_data_copied.pop('signature', None) timestamp = tx_data_copied.pop('timestamp', None) from_address = tx_data_copied.pop('from', None) to_address = tx_data_copied.pop('to', None) value = tx_data_copied.pop('value', None) fee = tx_data_copied.pop('fee', None) nonce = tx_data_copied.pop('nonce', None) extra = tx_data_copied value = int_fromhex(value) fee = int_fromhex(fee) if nonce is not None: nonce = int_fromstr(nonce) return Transaction( raw_data=tx_data, hash=Hash32.fromhex(hash, ignore_prefix=True, allow_malformed=False), signature=Signature.from_base64str(signature), timestamp=int(timestamp) if timestamp is not None else None, from_address=ExternalAddress.fromhex(from_address, ignore_prefix=False, allow_malformed=True), to_address=ExternalAddress.fromhex(to_address, ignore_prefix=False, allow_malformed=True), value=value, fee=fee, nonce=nonce, extra=extra, )
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_vote(self): signer = self.signers[0] block_hash = Hash32(os.urandom(Hash32.size)) block_vote = BlockVote.new(signer, 0, 0, 0, block_hash) block_vote.verify() origin = f"icx_vote.blockHash.{block_vote.block_hash.hex_0x()}.blockHeight.{hex(block_vote.block_height)}." origin += f"rep.{block_vote.rep.hex_hx()}.round_.{block_vote.round_}.timestamp.{hex(block_vote.timestamp)}" origin_data = block_vote.to_origin_data(**block_vote.origin_args()) self.assertEqual( origin, vote.hash_generator.generate_salted_origin(origin_data)) self.assertEqual( Hash32(hashlib.sha3_256(origin.encode('utf-8')).digest()), block_vote.to_hash(**block_vote.origin_args()))
def __init__(self, channel_name, amqp_target, amqp_key, rollback=False): self.__block_manager: BlockManager = None self.__score_container: CommonSubprocess = None self.__score_info: dict = None self.__peer_auth: Signer = None self.__broadcast_scheduler: BroadcastScheduler = None self.__rs_client: RestClient = None self.__timer_service = TimerService() self.__node_subscriber: NodeSubscriber = None self._rollback: bool = rollback loggers.get_preset().channel_name = channel_name loggers.get_preset().update_logger() channel_queue_name = conf.CHANNEL_QUEUE_NAME_FORMAT.format(channel_name=channel_name, amqp_key=amqp_key) self.__inner_service = ChannelInnerService( amqp_target, channel_queue_name, conf.AMQP_USERNAME, conf.AMQP_PASSWORD, channel_service=self) logging.info(f"ChannelService : {channel_name}, Queue : {channel_queue_name}") ChannelProperty().name = channel_name ChannelProperty().amqp_target = amqp_target ChannelProperty().crep_root_hash = Hash32.fromhex(conf.CHANNEL_OPTION[channel_name].get('crep_root_hash')) StubCollection().amqp_key = amqp_key StubCollection().amqp_target = amqp_target command_arguments.add_raw_command(command_arguments.Type.Channel, channel_name) command_arguments.add_raw_command(command_arguments.Type.AMQPTarget, amqp_target) command_arguments.add_raw_command(command_arguments.Type.AMQPKey, amqp_key) ObjectManager().channel_service = self self.__state_machine = ChannelStateMachine(self)
def to_origin_data(cls, rep: ExternalAddress, timestamp: int, block_height: int, round: int, block_hash: Hash32): origin_data = super().to_origin_data(rep, timestamp) origin_data["blockHeight"] = hex(block_height) origin_data["round"] = hex(round) origin_data["blockHash"] = block_hash.hex_0x( ) if block_hash is not None else None return origin_data
def _build_prev_votes_hash(self): if not self.prev_votes: return Hash32.new() block_prover = BlockProver( (vote.hash() if vote else None for vote in self.prev_votes), BlockProverType.Vote) return block_prover.get_proof_root()
def test_get_hash(self, tx_factory: TxFactory): tx: Transaction = tx_factory(self.tx_version) ts = TransactionSerializer.new(version=tx.version, type_=self.type_, versioner=tx_versioner) full_data = ts.to_full_data(tx) tx_hash = ts.get_hash(full_data) assert tx.hash == Hash32.fromhex(tx_hash, ignore_prefix=True)
def _load_peers_from_db() -> list: blockchain = ObjectManager().channel_service.block_manager.blockchain last_block = blockchain.last_block rep_root_hash = (last_block.header.reps_hash if last_block else Hash32.fromhex(conf.CHANNEL_OPTION[ ChannelProperty().name].get('crep_root_hash'))) return blockchain.find_preps_by_roothash(rep_root_hash)
def test_get_hash(self, tx_factory: TxFactory): tx: Transaction = tx_factory(self.tx_version) ts = TransactionSerializer.new(version=tx.version, type_=tx.type(), versioner=tx_versioner) full_data = ts.to_full_data(tx) tx_hash = ts.get_hash(full_data) assert tx.hash == Hash32(tx_hash)
async def prove_receipt(self, tx_hash: str, proof: list) -> Union[str, dict]: try: proof = make_proof_deserializable(proof) except Exception as e: return make_error_response(JsonError.INTERNAL_ERROR, str(e)) try: return "0x1" if self._blockchain.prove_receipt(Hash32.fromhex(tx_hash), proof) else "0x0" except Exception as e: return make_error_response(JsonError.INVALID_PARAMS, str(e))
async def get_receipt_proof(self, tx_hash: str) -> Union[list, dict]: try: proof = self._blockchain.get_receipt_proof(Hash32.fromhex(tx_hash)) except Exception as e: return make_error_response(JsonError.INVALID_PARAMS, str(e)) try: return make_proof_serializable(proof) except Exception as e: return make_error_response(JsonError.INTERNAL_ERROR, str(e))