|
1 | 1 | """ |
2 | | -Example demonstrating transaction byte serialization and deserialization. |
| 2 | +Refactored example demonstrating transaction byte serialization and deserialization. |
3 | 3 |
|
4 | 4 | This example shows how to: |
5 | | -- Freeze a transaction |
6 | | -- Serialize to bytes (for storage, transmission, or external signing) |
| 5 | +- Create and freeze a transaction |
| 6 | +- Serialize to bytes (for storage, transmission, or signing) |
7 | 7 | - Deserialize from bytes |
8 | | -- Sign after deserialization |
9 | | -
|
10 | | -Run with: |
11 | | - uv run examples/transaction/transaction_to_bytes.py |
12 | | - python examples/transaction/transaction_to_bytes.py |
| 8 | +- Sign a deserialized transaction |
13 | 9 | """ |
14 | 10 |
|
15 | 11 | import os |
|
26 | 22 | ) |
27 | 23 |
|
28 | 24 | load_dotenv() |
29 | | -network_name = os.getenv('NETWORK', 'testnet').lower() |
| 25 | +NETWORK = os.getenv("NETWORK", "testnet").lower() |
| 26 | +OPERATOR_ID = os.getenv("OPERATOR_ID", "") |
| 27 | +OPERATOR_KEY = os.getenv("OPERATOR_KEY", "") |
30 | 28 |
|
31 | | -def setup_client(): |
32 | | - """Initialize and set up the client with operator account""" |
33 | | - network = Network(network_name) |
34 | | - print(f"Connecting to Hedera {network_name} network!") |
35 | | - client = Client(network) |
36 | 29 |
|
| 30 | +def setup_client() -> Client: |
| 31 | + """Initialize the client using operator credentials from .env.""" |
37 | 32 | try: |
38 | | - operator_id = AccountId.from_string(os.getenv('OPERATOR_ID', '')) |
39 | | - operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY', '')) |
| 33 | + network = Network(NETWORK) |
| 34 | + client = Client(network) |
| 35 | + |
| 36 | + operator_id = AccountId.from_string(OPERATOR_ID) |
| 37 | + operator_key = PrivateKey.from_string(OPERATOR_KEY) |
| 38 | + |
40 | 39 | client.set_operator(operator_id, operator_key) |
41 | | - print(f"Client set up with operator id {client.operator_account_id}") |
42 | | - return client, operator_id, operator_key |
43 | | - |
44 | | - except (TypeError, ValueError): |
45 | | - print("❌ Error: Creating client, Please check your .env file") |
46 | | - sys.exit(1) |
47 | 40 |
|
| 41 | + print(f"Connected to Hedera {NETWORK} as {operator_id}") |
| 42 | + return client |
48 | 43 |
|
49 | | -def transaction_bytes_example(): |
50 | | - """ |
51 | | - Demonstrates transaction serialization and deserialization workflow. |
52 | | - """ |
53 | | - client, operator_id, operator_key = setup_client() |
| 44 | + except Exception: |
| 45 | + print("❌ Error: Could not initialize client. Check your .env file.") |
| 46 | + sys.exit(1) |
54 | 47 |
|
55 | | - receiver_id = AccountId.from_string("0.0.3") # Node account |
56 | 48 |
|
57 | | - # Step 1: Create and freeze transaction |
58 | | - print("\nSTEP 1: Creating and freezing transaction...") |
59 | | - transaction = ( |
| 49 | +def create_and_freeze_transaction(client: Client, sender: AccountId, receiver: AccountId): |
| 50 | + """Create and freeze a simple HBAR transfer transaction.""" |
| 51 | + tx = ( |
60 | 52 | TransferTransaction() |
61 | | - .add_hbar_transfer(operator_id, -100_000_000) # -1 HBAR |
62 | | - .add_hbar_transfer(receiver_id, 100_000_000) # +1 HBAR |
| 53 | + .add_hbar_transfer(sender, -100_000_000) # -1 HBAR |
| 54 | + .add_hbar_transfer(receiver, 100_000_000) # +1 HBAR |
63 | 55 | .set_transaction_memo("Transaction bytes example") |
64 | 56 | ) |
65 | | - transaction.freeze_with(client) |
66 | | - print(f"✅ Transaction frozen with ID: {transaction.transaction_id}") |
67 | | - |
68 | | - # Step 2: Serialize to bytes |
69 | | - print("\nSTEP 2: Serializing transaction to bytes...") |
70 | | - transaction_bytes = transaction.to_bytes() |
71 | | - print(f"✅ Transaction serialized: {len(transaction_bytes)} bytes") |
72 | | - print(f" First 40 bytes (hex): {transaction_bytes[:40].hex()}") |
73 | | - |
74 | | - # Step 3: Deserialize from bytes |
75 | | - print("\nSTEP 3: Deserializing transaction from bytes...") |
76 | | - restored_transaction = Transaction.from_bytes(transaction_bytes) |
77 | | - print(f"✅ Transaction restored from bytes") |
78 | | - print(f" Transaction ID: {restored_transaction.transaction_id}") |
79 | | - print(f" Node ID: {restored_transaction.node_account_id}") |
80 | | - print(f" Memo: {restored_transaction.memo}") |
81 | | - |
82 | | - # Step 4: Sign the restored transaction |
83 | | - print("\nSTEP 4: Signing the restored transaction...") |
84 | | - restored_transaction.sign(operator_key) |
85 | | - print(f"✅ Transaction signed") |
86 | | - |
87 | | - # Step 5: Verify round-trip produces identical bytes |
88 | | - print("\nSTEP 5: Verifying serialization...") |
89 | | - original_signed = transaction.sign(operator_key).to_bytes() |
90 | | - final_bytes = restored_transaction.to_bytes() |
91 | | - print(f"✅ Round-trip successful") |
92 | | - |
93 | | - print("\n✅ Example completed successfully!") |
94 | | - print("\nUse cases for transaction bytes:") |
95 | | - print(" • Store transactions in a database") |
96 | | - print(" • Send transactions to external signing services (HSM, hardware wallet)") |
97 | | - print(" • Transmit transactions over a network") |
98 | | - print(" • Create offline signing workflows") |
| 57 | + |
| 58 | + tx.freeze_with(client) |
| 59 | + return tx |
| 60 | + |
| 61 | + |
| 62 | +def serialize_transaction(transaction: Transaction) -> bytes: |
| 63 | + """Serialize transaction to bytes.""" |
| 64 | + return transaction.to_bytes() |
| 65 | + |
| 66 | + |
| 67 | +def deserialize_transaction(bytes_data: bytes) -> Transaction: |
| 68 | + """Restore a transaction from its byte representation.""" |
| 69 | + return Transaction.from_bytes(bytes_data) |
| 70 | + |
| 71 | + |
| 72 | +def main(): |
| 73 | + client = setup_client() |
| 74 | + operator_id = client.operator_account_id |
| 75 | + operator_key = client.operator_private_key |
| 76 | + |
| 77 | + receiver_id = AccountId.from_string("0.0.3") |
| 78 | + |
| 79 | + print("\nSTEP 1 — Creating and freezing transaction...") |
| 80 | + tx = create_and_freeze_transaction(client, operator_id, receiver_id) |
| 81 | + print(f"Transaction ID: {tx.transaction_id}") |
| 82 | + |
| 83 | + print("\nSTEP 2 — Serializing transaction...") |
| 84 | + tx_bytes = serialize_transaction(tx) |
| 85 | + print(f"Serialized size: {len(tx_bytes)} bytes") |
| 86 | + print(f"Preview (hex): {tx_bytes[:40].hex()}") |
| 87 | + |
| 88 | + print("\nSTEP 3 — Deserializing transaction...") |
| 89 | + restored_tx = deserialize_transaction(tx_bytes) |
| 90 | + print(f"Restored ID: {restored_tx.transaction_id}") |
| 91 | + print(f"Memo: {restored_tx.memo}") |
| 92 | + |
| 93 | + print("\nSTEP 4 — Signing restored transaction...") |
| 94 | + restored_tx.sign(operator_key) |
| 95 | + print("Signed successfully.") |
| 96 | + |
| 97 | + print("\nSTEP 5 — Verifying round-trip...") |
| 98 | + original_signed_bytes = tx.sign(operator_key).to_bytes() |
| 99 | + restored_signed_bytes = restored_tx.to_bytes() |
| 100 | + |
| 101 | + if original_signed_bytes == restored_signed_bytes: |
| 102 | + print("✅ Round-trip serialization successful.") |
| 103 | + else: |
| 104 | + print("❌ Round-trip mismatch!") |
| 105 | + |
| 106 | + print("\nExample completed.") |
99 | 107 |
|
100 | 108 |
|
101 | 109 | if __name__ == "__main__": |
102 | | - transaction_bytes_example() |
| 110 | + main() |
0 commit comments