def validate(self): """블럭 검증 :return: 검증결과 """ mk_hash = self.__calculate_merkle_tree_root_hash() if self.height == 0 and len(self.confirmed_transaction_list) == 0: # Genesis Block 은 검증하지 않습니다. return True if len(self.confirmed_transaction_list) > 0: # 머클트리 검증은 Tx가 있을때에만 합니다. if mk_hash != self.merkle_tree_root_hash: raise BlockInValidError('Merkle Tree Root hash is not same') if self.block_hash != self.__generate_hash(): raise BlockInValidError('block Hash is not same generate hash') if self.time_stamp == 0: raise BlockError('block time stamp is 0') if len(self.prev_block_hash) == 0: raise BlockError('Prev Block Hash not Exist') # Transaction Validate for tx in self.confirmed_transaction_list: if tx.get_tx_hash() != Transaction.generate_transaction_hash(tx): logging.debug("TX HASH : %s vs %s", tx.get_tx_hash(), Transaction.generate_transaction_hash(tx)) raise TransactionInValidError('Transaction hash is not same') return True
def test_signature_validate(self): """ GIVEN success tx, invalid public key tx, invalid signature tx, WHEN validate 3 tx THEN only success tx validate return true """ # GIVEN # init peer_auth for signautre peer_auth = PeerAuthorization(public_file=conf.PUBLIC_PATH, pri_file=conf.PRIVATE_PATH, cert_pass=conf.DEFAULT_PW) # create txs success_tx = test_util.create_basic_tx("aaa", peer_auth) # public key and signature property must don't have setter invalid_public_tx = test_util.create_basic_tx("aaa", peer_auth) invalid_public_tx._Transaction__public_key = b'invalid_public' invalid_sign_tx = test_util.create_basic_tx("aaa", peer_auth) invalid_sign_tx._Transaction__signature = b'invalid_sign' # WHEN THEN self.assertTrue(Transaction.validate(success_tx)) logging.debug("start validate invalid public key") self.assertFalse( Transaction.validate(invalid_public_tx, is_exception_log=False)) logging.debug("start validate invalid signature") self.assertFalse( Transaction.validate(invalid_sign_tx, is_exception_log=False))
def test_put_data(self): """트랜잭션 생성확인 해쉬값의 존재여부 :return: """ tx = Transaction() txhash = tx.put_data("{args:[]}") self.assertNotEqual(txhash, "")
def generate_block(self): """ 임시 블럭 생성하는 메소드 :return: 임시 블럭 """ block = Block() for x in range(10): tx = Transaction() tx.put_data("{args:[]}") block.put_transaction(tx) block.generate_block(self.chain.last_block) return block
def restore(self, tx_json: str, channel): tx = Transaction() tx.put_meta(Transaction.SEND_TX_TYPE_KEY, conf.SendTxType.icx) tx.put_meta(Transaction.CHANNEL_KEY, channel) tx.put_meta(Transaction.METHOD_KEY, self.SEND_TX) self.__init_icx_tx(tx_json, tx) return tx
def create_tx(self, data): tx = Transaction() score_id = "" score_version = "" try: score_info = self._channel_service.score_info score_id = score_info[message_code.MetaParams.ScoreInfo.score_id] score_version = score_info[message_code.MetaParams.ScoreInfo.score_version] except KeyError as e: logging.debug(f"CreateTX : load score info fail\n" f"cause : {e}") send_tx_type = self._channel_service.get_channel_option()["send_tx_type"] tx.init_meta(ChannelProperty().peer_id, score_id, score_version, ChannelProperty().name, send_tx_type) tx.put_data(data) tx.sign_hash(self._channel_service.peer_auth) self._channel_service.broadcast_scheduler.schedule_job(BroadcastCommand.CREATE_TX, tx) try: data_log = json.loads(data) except Exception as e: data_log = {'tx_hash': tx.tx_hash} util.apm_event(ChannelProperty().peer_id, { 'event_type': 'CreateTx', 'peer_id': ChannelProperty().peer_id, 'peer_name': conf.PEER_NAME, 'channel_name': ChannelProperty().name, 'tx_hash': tx.tx_hash, 'data': data_log}) return tx.tx_hash
def generate_test_block(self): """ 임시 블럭 생성하는 메소드 :return: 임시 블럭 """ block = Block() for x in range(0, 10): tx = Transaction() hashed_value = tx.put_data("{args:[]}") self.assertNotEqual(hashed_value, "", "트랜잭션 생성 실패") self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패") return block
def create_tx(peer_id: str, data: str, peer_auth: PeerAuthorization) -> Transaction: """ create basic tx data is "{args:[]}" :param peer_id: peer_id :param data: tx_data :param peer_auth: :return: transaction """ tx = Transaction() tx.put_meta('peer_id', peer_id) tx.put_meta(Transaction.CHANNEL_KEY, conf.LOOPCHAIN_DEFAULT_CHANNEL) tx.put_data(data) tx.sign_hash(peer_auth) return tx
def test_diff_hash(self): """트랜잭션을 생성하여, 같은 값을 입력 하여도 트랜잭션 HASH는 달라야 함 1000건 생성하여 트랜잭션 비교 :return: """ sttime = time.time() tx_list = [] test_size = 1000 for x in range(test_size): tx1 = Transaction() hashed_value = tx1.put_data("{args:[]}") tx_list.append(hashed_value) self.assertTrue(len(set(tx_list)) == len(tx_list), "중복된 트랜잭션이 있습니다.") logging.debug("test_diff_hash %i times : %f", test_size, time.time() - sttime)
def __add_single_tx_to_block_return_tx_with_test(self): last_block = self.chain.last_block block = Block() tx = Transaction() hashed_value = tx.put_data("1234") self.assertNotEqual(hashed_value, "", "트랜잭션 생성 실패") self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패") logging.debug("tx_hash: " + tx.get_tx_hash()) block.generate_block(last_block) block.block_status = BlockStatus.confirmed # add_block include __add_tx_to_block_db what we want to test self.assertTrue(self.chain.add_block(block), "Fail Add Block to BlockChain in test_add_tx_to_block_db") return tx
def put_transaction(self, tx): """Block Generator 에서만 사용한다. tx는 단수 혹은 여러개 일 수 있다 :param tx: transaction (transaction을 담고 있는 list도 처리 가능) :return: True: 성공적으로 담겼을 때. """ if type(tx) is list: result = True for t in tx: result &= self.put_transaction(t) return result elif not isinstance(tx, Transaction): logging.error("트랜잭션 타입이 아님 %s", type(tx)) return False # TX 검증은 현재 받아들인 TX의 Hash값(Peer 생성)과 # block generator (leader) 에서 만든 Hash 값이 일치하는지 확인 후 block 에 추가합니다. # TX 는 최초 생성한 Peer 에서 block 에 담겨오는지 여부를 확인 할 때까지 보관해야 합니다. (TODO) if tx.status == TransactionStatus.unconfirmed: # transaction 검증 # logging.debug("Transaction Hash %s", tx.get_tx_hash()) if Transaction.validate(tx): tx.status = TransactionStatus.confirmed else: return False # Block 에 검증된 Transaction 추가 : 목록에 존재하는지 확인 필요 if tx not in self.confirmed_transaction_list: self.confirmed_transaction_list.append(tx) return True
def test_get_meta_and_put_meta(self): # GIVEN tx = Transaction() tx.put_meta("peer_id", "12345") # WHEN meta_data = tx.get_meta() tx.put_meta("peer_id", "ABCDE") # THEN logging.debug("tx peer_id(before): " + meta_data["peer_id"]) logging.debug("tx peer_id(after): " + tx.get_meta()["peer_id"]) self.assertNotEqual(meta_data["peer_id"], tx.get_meta()["peer_id"])
def generate_block(self): """ 블럭 생성기 :return: 임의생성블럭 """ genesis = Block() genesis.generate_block() # Block 생성 block = Block() # Transaction(s) 추가 for x in range(0, 10): tx = Transaction() tx.put_data("{args:[]}") block.put_transaction(tx) # Hash 생성 이 작업까지 끝내고 나서 Block을 peer에 보낸다 block.generate_block(genesis) return block
def test_dump_tx_size(self): # GIVEN tx = Transaction() tx.put_data("TEST") tx.transaction_type = TransactionStatus.confirmed tx_only_data = TransactionDataOnly() tx_only_data.data = "TEST" # WHEN dump_a = pickle.dumps(tx_only_data) dump_b = pickle.dumps(tx) # THEN logging.debug("size of tx_only_data: " + str(sys.getsizeof(dump_a))) logging.debug("size of tx: " + str(sys.getsizeof(dump_b))) self.assertLessEqual(sys.getsizeof(dump_a), sys.getsizeof(dump_b) * 1.5)
def create_basic_tx(peer_id: str, peer_auth: PeerAuthorization) -> Transaction: """ create basic tx data is "{args:[]}" :param peer_id: peer_id :param peer_auth: :return: transaction """ tx = Transaction() tx.put_meta('peer_id', peer_id) tx.put_data("{args:[]}") tx.sign_hash(peer_auth) return tx
def init_genesis_tx(self, genesis_data: dict): # utils.logger.spam(f"genesis_Data :: init_genesis_tx >>>{genesis_data}") if genesis_data is None: return False, None keys = genesis_data.keys() if "accounts" not in keys: return False, None if not self.__validate_params(genesis_data["accounts"]): logging.debug(f"Invalid genesis data::accounts") return False, None tx = Transaction() expected_tx_hash = self.hash_generator.generate_hash(genesis_data) utils.logger.spam(f"expected_tx_hash::{expected_tx_hash}") if not tx.put_genesis_data(genesis_data, expected_tx_hash): return False, None return True, tx
def test_transaction_performace(self): """트랜잭션의 생성 퍼포먼스 1초에 몇개까지 만들 수 있는지 확인 1초에 5000개 이상 :return: """ _sttime = time.time() tx_list = [] dummy_data = "TEST Transaction Data" put_data = 0 while time.time() - _sttime < 1.0: tx1 = Transaction() hashed_value = tx1.put_data("{args:[]}" + (dummy_data + str(put_data))) tx_list.append(hashed_value) put_data += 1 self.assertTrue(len(set(tx_list)) == len(tx_list), "중복된 트랜잭션이 있습니다.") logging.debug("TX generate %i in a second", len(tx_list)) self.assertTrue(len(tx_list) > 5000, len(tx_list))
def test_cert_signature(self): """GIVEN conf.TX_CERT_AUTH = True, PeerAuthorization create using cert WHEN create new tx and create signature THEN tx.public_key must be x.509 der cert """ channel_name = "cert_channel" conf.CHANNEL_OPTION = { channel_name: { "load_cert": True, "consensus_cert_use": True, "tx_cert_use": True, "key_load_type": conf.KeyLoadType.FILE_LOAD, "public_path": os.path.join(conf.LOOPCHAIN_ROOT_PATH, 'resources/default_certs/cert.pem'), "private_path": os.path.join(conf.LOOPCHAIN_ROOT_PATH, 'resources/default_certs/key.pem'), "private_password": None } } peer_auth = PeerAuthorization(channel_name) # WHEN tx = Transaction() tx.put_data('{"a":"b"}') tx.sign_hash(peer_auth) # THEN logging.debug(f"tx public key : {tx.public_key}") self.assertEqual(tx.public_key, peer_auth.tx_cert) # tx.publickey using for load certificate and not raise any error x509.load_der_x509_certificate(tx.public_key, default_backend())
def test_tx_json_serialize(self): # GIVEN tx = Transaction() tx.put_data("TEST") tx.transaction_type = TransactionStatus.confirmed logging.debug(f"transaction for test({tx})") # WHEN wrap_up = {"__Transaction__": tx} serialized = json.dumps(wrap_up, sort_keys=True, cls=CustomEncoder) logging.debug(f"serialized tx: {serialized}") tx_json = json.loads(serialized, object_hook=decode_object) logging.debug(f"deserialized tx: {tx_json}") wrap_up_again = {"__Transaction__": tx_json} serialized_again = json.dumps(wrap_up_again, sort_keys=True, cls=CustomEncoder) logging.debug(f"re-serialized tx: {serialized_again}") # THEN self.assertEqual(serialized, serialized_again)
def validate(block, tx_queue=None) -> bool: """validate block and all transactions in block :param: block :param: tx_queue :return validate success return true """ mk_hash = Block.__calculate_merkle_tree_root_hash(block) if block.height == 0 and len(block.confirmed_transaction_list) == 0: # Genesis Block 은 검증하지 않습니다. return True if len(block.confirmed_transaction_list) > 0: # 머클트리 검증은 Tx가 있을때에만 합니다. if mk_hash != block.merkle_tree_root_hash: raise BlockInValidError('Merkle Tree Root hash is not same') if block.block_hash != Block.__generate_hash(block): raise BlockInValidError('block Hash is not same generate hash') leader = ObjectManager().peer_service.channel_manager.get_peer_manager( block.__channel_name).get_leader_object() if not leader.cert_verifier.verify_hash(block.block_hash, block.signature): raise BlockInValidError('block signature invalid') if block.time_stamp == 0: raise BlockError('block time stamp is 0') if len(block.prev_block_hash) == 0: raise BlockError('Prev Block Hash not Exist') # Transaction Validate confirmed_tx_list = [] for tx in block.confirmed_transaction_list: if Transaction.validate(tx): confirmed_tx_list.append(tx.tx_hash) else: raise BlockInValidError( f"block ({block.block_hash}) validate fails \n" f"tx {tx.tx_hash} is invalid") if tx_queue is not None: block.__tx_validate_with_queue(tx_queue, confirmed_tx_list) return True
def test_put_transaction(self): """ Block 에 여러 개 transaction 들을 넣는 것을 test. """ block = Block() tx_list = [] tx_size = 10 for x in range(0, tx_size): tx = Transaction() tx2 = Transaction() hashed_value = tx.put_data("{args:[]}") tx2.put_data("{args:[]}") tx_list.append(tx2) self.assertNotEqual(hashed_value, "", "트랜잭션 생성 실패") self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패") self.assertTrue(block.put_transaction(tx_list), "Block에 여러 트랜잭션 추가 실패") self.assertEqual(len(block.confirmed_transaction_list), tx_size * 2, "트랜잭션 사이즈 확인 실패")
def validate(self, tx: Transaction): try: if Transaction.generate_transaction_hash(tx) != tx.tx_hash: self.__logging_tx_validate("hash validate fail", tx) return False # tx = self.__tx_validator() # Get Cert Verifier for signature verify public_verifier = PublicVerifierContainer.get_public_verifier(tx.meta[Transaction.CHANNEL_KEY], tx.public_key) # Signature Validate if public_verifier.verify_hash(tx.tx_hash, tx.signature): return True else: self.__logging_tx_validate("signature validate fail", tx) return False except Exception as e: self.__logging_tx_validate("signature validate fail", tx) return False
def test_block_rebuild(self): """ GIVEN 1Block with 3tx, and conf remove failed tx when in block WHEN Block call verify_through_score_invoke THEN all order 3tx must removed in block """ block = Block(conf.LOOPCHAIN_DEFAULT_CHANNEL) fail_tx_hash = None for i in range(3): tx = Transaction() tx.put_meta(Transaction.CHANNEL_KEY, conf.LOOPCHAIN_DEFAULT_CHANNEL) tx.put_data("aaaaa") tx.sign_hash(self.peer_auth) block.put_transaction(tx) if i == 2: fail_tx_hash = tx.tx_hash verify, need_rebuild, invoke_results = block.verify_through_score_invoke( True) self.assertTrue(need_rebuild) logging.debug(f"fail tx hash : {fail_tx_hash}") self.assertEqual(block.confirmed_tx_len, 2) for i, tx in enumerate(block.confirmed_transaction_list): self.assertNotEqual(i, 2, "index 2 must be deleted") self.assertNotEqual(tx.tx_hash, fail_tx_hash)
def restore(self, tx_json): tx = Transaction() if self.__init_genesis_tx(tx_json, tx): return tx else: return None
def test_generate_and_validate_hash(self): """트랜잭션 생성시 만들어진 hash 와 검증시 비교하는 hash 가 동일한지 확인하는 테스트 :return: """ # GIVEN tx = Transaction() tx.init_meta("AAAAA", "BBBBB", "CCCCC", conf.LOOPCHAIN_DEFAULT_CHANNEL, conf.SendTxType.pickle) tx.put_meta("1234", "5678") tx.put_meta("1", "5") tx.put_meta("2", "5") tx.put_meta("3", "5") tx.put_meta("4", "5") txhash1 = tx.put_data("TEST DATA DATA") txtime = tx.get_timestamp() tx2 = Transaction() tx2.init_meta("AAAAA", "BBBBB", "CCCCC", conf.LOOPCHAIN_DEFAULT_CHANNEL, conf.SendTxType.pickle) tx2.put_meta("1234", "5678") tx2.put_meta("1", "5") tx2.put_meta("2", "5") tx2.put_meta("3", "5") tx2.put_meta("4", "5") txhash2 = tx2.put_data("TEST DATA DATA", txtime) # WHEN txhash1_1 = Transaction.generate_transaction_hash(tx) # THEN logging.debug("txhash1: " + str(txhash1)) logging.debug("txhash1_1: " + str(txhash1_1)) logging.debug("txhash2: " + str(txhash2)) self.assertEqual(txhash1, txhash2) self.assertEqual(txhash1, txhash1_1) self.assertEqual(txhash2, txhash1_1)
def __init_genesis_tx(self, genesis_dumped_data: str, tx: Transaction): genesis_data = json.loads(genesis_dumped_data) tx_hash = self.hash_generator.generate_hash(genesis_data) return tx.put_genesis_data(genesis_data, tx_hash)
def decode_object(obj): if '__Transaction__' in obj: tx = Transaction() tx.__dict__.update(obj['__Transaction__']) return tx return obj
def __init_icx_tx(self, icx_dumped_data: str, tx: Transaction): icx_origin_data = json.loads(icx_dumped_data) tx.set_icx_origin_data(icx_origin_data, icx_dumped_data)
def restore(self, tx_json: str, channel): return Transaction.json_loads(tx_json)