diff --git a/src/archethic/transaction_builder.py b/src/archethic/transaction_builder.py index fe5cbee..64e9bf3 100644 --- a/src/archethic/transaction_builder.py +++ b/src/archethic/transaction_builder.py @@ -13,12 +13,11 @@ "token": 251, # Network based transaction types "code_proposal": 7, - "code_approval": 8 + "code_approval": 8, } class TransactionBuilder: - def __init__(self, tx_type: str) -> None: """ Create a new instance of the transaction builder by specifying firstly the type of transaction :param @@ -29,8 +28,10 @@ def __init__(self, tx_type: str) -> None: self.address: bytes = bytes() self.previous_public_key: bytes = bytes() self.previous_signature: bytes = bytes() - assert tx_type in TX_TYPES, "Invalid transaction type. \n Transaction type must be 'transfer', 'hosting', " \ - "'keychain_access', 'keychain', 'token', 'code_proposal', 'code_approval' " + assert tx_type in TX_TYPES, ( + "Invalid transaction type. \n Transaction type must be 'transfer', 'hosting', " + "'keychain_access', 'keychain', 'token', 'code_proposal', 'code_approval' " + ) self.version = VERSION self.tx_type = tx_type @@ -70,7 +71,9 @@ def set_content(self, content: Union[str, bytes]) -> None: raise TypeError("Content must be string or bytes") return - def add_ownership(self, secret_key: Union[str, bytes], authorized_keys: list) -> None: + def add_ownership( + self, secret_key: Union[str, bytes], authorized_keys: list + ) -> None: """ Add an ownership with a secret and its authorized public keys to the transaction :param secret_key: The secret key of the ownership (str or bytes) @@ -119,7 +122,9 @@ def add_ownership(self, secret_key: Union[str, bytes], authorized_keys: list) -> } ) - self.data["ownerships"].append({"secret": secret_key, "authorizedKeys": new_authorized_keys}) + self.data["ownerships"].append( + {"secret": secret_key, "authorizedKeys": new_authorized_keys} + ) return def add_uco_transfer(self, send_to: Union[str, bytes], amount: float): @@ -141,10 +146,18 @@ def add_uco_transfer(self, send_to: Union[str, bytes], amount: float): isinstance(amount, float or int), "Amount must be float or int" assert amount > 0, "Amount must be greater than 0" - self.data["ledger"]["uco"]["transfers"].append({"to": send_to, "amount": utils.to_big_int(amount)}) + self.data["ledger"]["uco"]["transfers"].append( + {"to": send_to, "amount": utils.to_big_int(amount)} + ) return - def add_token_transfer(self, send_to: Union[str, bytes], amount: float, token_adress: Union[str, bytes], token_id: int): + def add_token_transfer( + self, + send_to: Union[str, bytes], + amount: float, + token_adress: Union[str, bytes], + token_id: int, + ): """ Add a token transfer to the transaction :param send_to: The public key of the receiver @@ -179,7 +192,14 @@ def add_token_transfer(self, send_to: Union[str, bytes], amount: float, token_ad isinstance(token_id, int), "Token id must be int" assert token_id >= 0, "Token id must be greater or equal to 0" - self.data["ledger"]["token"]["transfers"].append({"to": send_to, "amount": utils.to_big_int(amount), "token": token_adress, "token_id": token_id}) + self.data["ledger"]["token"]["transfers"].append( + { + "to": send_to, + "amount": utils.to_big_int(amount), + "token": token_adress, + "token_id": token_id, + } + ) return def add_recipient(self, send_to: Union[str, bytes]): @@ -200,7 +220,9 @@ def add_recipient(self, send_to: Union[str, bytes]): self.data["recipients"].append(send_to) return - def set_previous_signature_and_previous_public_key(self, signature: Union[str, bytes], public_key: Union[str, bytes]) -> None: + def set_previous_signature_and_previous_public_key( + self, signature: Union[str, bytes], public_key: Union[str, bytes] + ) -> None: """ Set the transaction builder with Previous Publickey and Previous Signature :param signature: The previous signature of the transaction @@ -248,14 +270,22 @@ def set_address(self, address: Union[str, bytes]): self.address = address return - def build(self, seed: Union[str, bytes], index: int, curve: str = "ed25519", hash_algo: str = "sha256") -> None: + def build( + self, + seed: Union[str, bytes], + index: int, + curve: str = "ed25519", + hash_algo: str = "sha256", + ) -> None: private_key, public_key = crypto.derive_keypair(seed, index, curve) - address = crypto.derive_address(seed, index+1, curve, hash_algo) + address = crypto.derive_address(seed, index + 1, curve, hash_algo) self.set_address(address) self.previous_public_key = bytes.fromhex(public_key) payload_for_previous_signature = self.previous_signature_payload() - self.previous_signature = crypto.sign(payload_for_previous_signature, private_key) + self.previous_signature = crypto.sign( + payload_for_previous_signature, private_key + ) return def origin_sign(self, private_key: Union[str, bytes]) -> None: @@ -273,7 +303,9 @@ def origin_sign(self, private_key: Union[str, bytes]) -> None: else: raise TypeError("Private key must be hex string or bytes") - self.origin_signature = crypto.sign(self.origin_signature_payload(), private_key) + self.origin_signature = crypto.sign( + self.origin_signature_payload(), private_key + ) return def previous_signature_payload(self): @@ -290,7 +322,13 @@ def previous_signature_payload(self): for ownership in self.data["ownerships"]: authorizedKeys = ownership.get("authorizedKeys") secret = ownership.get("secret") - authorized_keys_buffer = [bytearray([len(authorizedKeys)])] + + buff_auth_key_length = bytearray([len(authorizedKeys)]) + authorized_keys_buffer = [ + bytearray([len(buff_auth_key_length)]), + buff_auth_key_length, + ] + for _authorizedKey in authorizedKeys: public_key = _authorizedKey.get("publicKey") encrypted_secret_key = _authorizedKey.get("encryptedSecretKey") @@ -298,30 +336,58 @@ def previous_signature_payload(self): authorized_keys_buffer.append(encrypted_secret_key) ownerships_buffer.append( - utils.int_to_32(len(bytes(secret))) + - secret + - b''.join(authorized_keys_buffer) + utils.int_to_32(len(bytes(secret))) + + secret + + b"".join(authorized_keys_buffer) ) - uco_transfers_buffers = [transfer['to'] + utils.int_to_64(transfer['amount']) for transfer in self.data["ledger"]["uco"]["transfers"]] - token_transfers_buffers = [transfer['token'] + transfer['to'] + utils.int_to_64(transfer['amount']) + bytearray([transfer['token_id']]) for transfer in self.data["ledger"]["token"]["transfers"]] + uco_transfers_buffers = [ + transfer["to"] + utils.int_to_64(transfer["amount"]) + for transfer in self.data["ledger"]["uco"]["transfers"] + ] + token_transfers_buffers = [ + transfer["token"] + + transfer["to"] + + utils.int_to_64(transfer["amount"]) + + bytearray([transfer["token_id"]]) + for transfer in self.data["ledger"]["token"]["transfers"] + ] + + buf_ownership_length = bytearray([len(self.data["ownerships"])]) + buf_uco_transfer_length = bytearray( + [len(self.data["ledger"]["uco"]["transfers"])] + ) + buf_token_transfer_length = bytearray( + [len(self.data["ledger"]["token"]["transfers"])] + ) + buf_recipient_length = bytearray([len(self.data["recipients"])]) return ( utils.int_to_32(VERSION) + self.address + bytearray([TX_TYPES[self.tx_type]]) + # code + buff_code_size + self.data["code"] + # content + buf_content_size + self.data["content"] - + bytearray([len(self.data["ownerships"])]) - + b''.join(ownerships_buffer) - + bytearray([len(self.data["ledger"]["uco"]["transfers"])]) - + b''.join(uco_transfers_buffers) - + bytearray([len(self.data["ledger"]["token"]["transfers"])]) - + b''.join(token_transfers_buffers) - + bytearray([len(self.data["recipients"])]) - + b''.join(self.data["recipients"]) + # ownerships + + bytearray([len(buf_ownership_length)]) + + buf_ownership_length + + b"".join(ownerships_buffer) + # uco transfers + + bytearray([len(buf_uco_transfer_length)]) + + buf_uco_transfer_length + + b"".join(uco_transfers_buffers) + # token transfers + + bytearray([len(buf_token_transfer_length)]) + + buf_token_transfer_length + + b"".join(token_transfers_buffers) + # recipients + + bytearray([len(buf_recipient_length)]) + + buf_recipient_length + + b"".join(self.data["recipients"]) ) def set_origin_sign(self, signature: Union[str, bytes]) -> None: @@ -353,32 +419,54 @@ def origin_signature_payload(self): def json(self): - data = { + data = { "version": VERSION, "address": self.address.hex(), "type": self.tx_type, "data": { "content": self.data["content"].hex(), "code": self.data["code"].decode("utf-8"), - "ownerships": [{"secret": _ownership.get('secret').hex(), "authorizedKeys": [{"publicKey": _authorizedKey.get('publicKey').hex(), "encryptedSecretKey": _authorizedKey.get('encryptedSecretKey').hex()} for _authorizedKey in _ownership.get('authorizedKeys')]} for _ownership in self.data["ownerships"]], + "ownerships": [ + { + "secret": _ownership.get("secret").hex(), + "authorizedKeys": [ + { + "publicKey": _authorizedKey.get("publicKey").hex(), + "encryptedSecretKey": _authorizedKey.get( + "encryptedSecretKey" + ).hex(), + } + for _authorizedKey in _ownership.get("authorizedKeys") + ], + } + for _ownership in self.data["ownerships"] + ], "ledger": { "uco": { - "transfers": [{"to": transfer["to"].hex(), "amount": transfer["amount"]} for transfer in self.data["ledger"]["uco"]["transfers"]] + "transfers": [ + {"to": transfer["to"].hex(), "amount": transfer["amount"]} + for transfer in self.data["ledger"]["uco"]["transfers"] + ] }, "token": { - "transfers": [{"to": transfer["to"].hex(), "token": transfer["token"].hex(), "amount": transfer["amount"], "token_id": transfer["token_id"]} for transfer in self.data["ledger"]["token"]["transfers"]] - } + "transfers": [ + { + "to": transfer["to"].hex(), + "token": transfer["token"].hex(), + "amount": transfer["amount"], + "token_id": transfer["token_id"], + } + for transfer in self.data["ledger"]["token"]["transfers"] + ] + }, }, - "recipients": [recipient.hex() for recipient in self.data["recipients"]], - + "recipients": [ + recipient.hex() for recipient in self.data["recipients"] + ], }, "previousPublicKey": self.previous_public_key.hex(), "previousSignature": self.previous_signature.hex(), - "originSignature": self.origin_signature.hex() + "originSignature": self.origin_signature.hex(), } return json.dumps(data) - - - - diff --git a/src/tests/test_transaction_builder.py b/src/tests/test_transaction_builder.py index bbc0410..a46ab7e 100644 --- a/src/tests/test_transaction_builder.py +++ b/src/tests/test_transaction_builder.py @@ -2,52 +2,69 @@ from archethic import crypto, utils import json + def test_type(): - transaction = TransactionBuilder('transfer') + transaction = TransactionBuilder("transfer") isinstance(transaction, TransactionBuilder) def test_set_code(): - transaction = TransactionBuilder('transfer') + transaction = TransactionBuilder("transfer") transaction.set_code("my smart contract code") - assert transaction.data['code'].decode() == "my smart contract code" + assert transaction.data["code"].decode() == "my smart contract code" def test_set_content(): - transaction = TransactionBuilder('transfer') + transaction = TransactionBuilder("transfer") transaction.set_content("my super content") - assert transaction.data['content'].decode() == "my super content" + assert transaction.data["content"].decode() == "my super content" def test_add_ownership(): - transaction = TransactionBuilder('transfer') - transaction.add_ownership("00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", [ + transaction = TransactionBuilder("transfer") + transaction.add_ownership( + "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + [ + { + "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + } + ], + ) + assert transaction.data["ownerships"][0]["secret"] == bytes.fromhex( + "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ) + assert transaction.data["ownerships"][0]["authorizedKeys"] == [ { - "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", - "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + "publicKey": bytes.fromhex( + "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646" + ), + "encryptedSecretKey": bytes.fromhex( + "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ), } - ]) - assert transaction.data['ownerships'][0]['secret'] == bytes.fromhex('00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88') - assert transaction.data['ownerships'][0]['authorizedKeys'] == [{ - "publicKey": bytes.fromhex("0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"), - "encryptedSecretKey": bytes.fromhex("00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88") - }] + ] def test_add_uco_transfer(): - transaction = TransactionBuilder('transfer') - transaction.add_uco_transfer('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646', 10.03) - assert len(transaction.data['ledger']['uco']['transfers']) == 1 - assert transaction.data['ledger']['uco']['transfers'][0]['to'] == bytes.fromhex('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646') - assert transaction.data['ledger']['uco']['transfers'][0]['amount'] == 1003000000 + transaction = TransactionBuilder("transfer") + transaction.add_uco_transfer( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 10.03 + ) + assert len(transaction.data["ledger"]["uco"]["transfers"]) == 1 + assert transaction.data["ledger"]["uco"]["transfers"][0]["to"] == bytes.fromhex( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646" + ) + assert transaction.data["ledger"]["uco"]["transfers"][0]["amount"] == 1003000000 # TODO test_add_token_transfer def test_add_token_transfer(): return True + def test_previous_signature_payload(): - code = ''' + code = """ condition inherit: [ uco_transferred: 0.020 ] @@ -56,98 +73,169 @@ def test_previous_signature_payload(): set_type transfer add_uco_ledger to: "000056E763190B28B4CF9AAF3324CF379F27DE9EF7850209FB59AA002D71BA09788A", amount: 0.020 end - ''' + """ content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet leo egestas, lobortis lectus a, dignissim orci." secret = "mysecret" - transaction = TransactionBuilder('transfer') - transaction.add_ownership(secret, [{ - "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", - "encryptedSecretKey": '00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88' - }]) - transaction.add_uco_transfer('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646', 0.2020) - transaction.add_token_transfer('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646', 100, '0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88', 1) + transaction = TransactionBuilder("transfer") + transaction.add_ownership( + secret, + [ + { + "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + } + ], + ) + transaction.add_uco_transfer( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.2020 + ) + transaction.add_token_transfer( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + 100, + "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + 1, + ) transaction.set_code(code) transaction.set_content(content) - transaction.add_recipient('0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88') - transaction.build('seed', 0, 'P256') + transaction.add_recipient( + "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ) + transaction.build("seed", 0, "P256") - sk, pk = crypto.derive_keypair('seed', 0, 'P256') + sk, pk = crypto.derive_keypair("seed", 0, "P256") previous_signature = crypto.sign(transaction.previous_signature_payload(), sk) payload = transaction.origin_signature_payload() expected_binary = ( - - utils.int_to_32(1) + - transaction.address + - bytearray([253]) + - - utils.int_to_32(len(code)) + - code.encode() + - - utils.int_to_32(len(content)) + - content.encode() + - - bytearray([1]) + - - utils.int_to_32(len(secret)) + - secret.encode() + - - bytearray([1]) + - - bytes.fromhex('0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646') + bytes.fromhex('00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88') + - - bytearray([1]) + - bytes.fromhex('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646') + - utils.int_to_64(utils.to_big_int(0.2020)) + - - bytearray([1]) + - bytes.fromhex('0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88') + - bytes.fromhex('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646') + - utils.int_to_64(utils.to_big_int(100)) + - bytearray([1]) + - - bytearray([1]) + - bytes.fromhex('0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88') + - bytes.fromhex(pk) + - bytearray([len(previous_signature)]) + - previous_signature + # version + utils.int_to_32(1) + + transaction.address + + bytearray([253]) + + + # code size + utils.int_to_32(len(code)) + + code.encode() + + + # content size + utils.int_to_32(len(content)) + + content.encode() + + + # Nb of byte to encode nb of ownerships + bytearray([1]) + + + # Nb of ownerships + bytearray([1]) + + + # Secret size + utils.int_to_32(len(secret)) + + secret.encode() + + + # Nb of byte to encode nb of authorized keys + bytearray([1]) + + + # Nb of authorized keys + bytearray([1]) + + + # Authorized keys encoding + bytes.fromhex( + "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646" + ) + + bytes.fromhex( + "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ) + + + # Nb of byte to encode nb of uco transfers + bytearray([1]) + + + # Nb of uco transfers + bytearray([1]) + + bytes.fromhex( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646" + ) + + utils.int_to_64(utils.to_big_int(0.2020)) + + + # Nb of byte to encode nb of Token transfers + bytearray([1]) + + + # Nb of Token transfers + bytearray([1]) + + bytes.fromhex( + "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ) + + bytes.fromhex( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646" + ) + + utils.int_to_64(utils.to_big_int(100)) + + bytearray([1]) # missing bytearray 0 ? + + + # Nb of byte to encode nb of recipients + bytearray([1]) + + + # Nb of recipients + bytearray([1]) + + bytes.fromhex( + "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88" + ) + + bytes.fromhex(pk) + + bytearray([len(previous_signature)]) + + previous_signature ) + assert len(payload) == len(expected_binary) assert payload == expected_binary + def test_origin_signature(): - sk, pk = crypto.derive_keypair('origin_seed', 0) - tx = TransactionBuilder('transfer') - tx.build('seed',0) + sk, pk = crypto.derive_keypair("origin_seed", 0) + tx = TransactionBuilder("transfer") + tx.build("seed", 0) tx.origin_sign(sk) assert crypto.verify(tx.origin_signature, tx.origin_signature_payload(), pk) is True def test_json(): - origin_keypair = crypto.derive_keypair('origin_seed', 0) - transaction_keypair = crypto.derive_keypair('seed', 0) - - tx = TransactionBuilder('transfer') - tx.add_uco_transfer('0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646', 0.2193) - tx.add_ownership(bytes(bytearray([0,1,2,3,4])), [{ - "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", - "encryptedSecretKey": '00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88' - }]) - tx.build('seed', 0) + origin_keypair = crypto.derive_keypair("origin_seed", 0) + transaction_keypair = crypto.derive_keypair("seed", 0) + + tx = TransactionBuilder("transfer") + tx.add_uco_transfer( + "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.2193 + ) + tx.add_ownership( + bytes(bytearray([0, 1, 2, 3, 4])), + [ + { + "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + } + ], + ) + tx.build("seed", 0) tx.origin_sign(origin_keypair[0]) parsed_tx = json.loads(tx.json()) - previous_signature = crypto.sign(tx.previous_signature_payload(), transaction_keypair[0]) + previous_signature = crypto.sign( + tx.previous_signature_payload(), transaction_keypair[0] + ) origin_signature = crypto.sign(tx.origin_signature_payload(), origin_keypair[0]) - - assert parsed_tx['address'] == crypto.derive_address('seed', 1) - assert parsed_tx['previousPublicKey'] == transaction_keypair[1] - assert parsed_tx['previousSignature'] == previous_signature.hex() - assert parsed_tx['data']['ownerships'][0]['secret'] == bytearray([0, 1, 2, 3, 4]).hex() - assert parsed_tx['data']['ledger']['uco']['transfers'][0] == {"to": "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", "amount": utils.to_big_int(0.2193)} - assert parsed_tx['data']['ownerships'][0]['authorizedKeys'] == [{ "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"}] - assert parsed_tx['originSignature'] == origin_signature.hex() \ No newline at end of file + assert parsed_tx["address"] == crypto.derive_address("seed", 1) + assert parsed_tx["previousPublicKey"] == transaction_keypair[1] + assert parsed_tx["previousSignature"] == previous_signature.hex() + assert ( + parsed_tx["data"]["ownerships"][0]["secret"] == bytearray([0, 1, 2, 3, 4]).hex() + ) + assert parsed_tx["data"]["ledger"]["uco"]["transfers"][0] == { + "to": "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + "amount": utils.to_big_int(0.2193), + } + assert parsed_tx["data"]["ownerships"][0]["authorizedKeys"] == [ + { + "publicKey": "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + "encryptedSecretKey": "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", + } + ] + assert parsed_tx["originSignature"] == origin_signature.hex()