Skip to content

Commit ffeef74

Browse files
committed
fix: address CodeRabbit review — chunk info restoration, key types, metadata sentinel, schedule admin key
Signed-off-by: Mounil Kanakhara <mounilkankhara@gmail.com>
1 parent a6c2ecb commit ffeef74

54 files changed

Lines changed: 1357 additions & 50 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/hiero_sdk_python/consensus/topic_message_submit_transaction.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,15 @@ def _from_protobuf(cls, transaction_body, body_bytes: bytes, sig_map):
240240
body = transaction_body.consensusSubmitMessage
241241
if body.HasField("topicID"):
242242
transaction.topic_id = TopicId._from_proto(body.topicID)
243-
transaction.message = body.message.decode("utf-8") if body.message else None
244-
transaction._total_chunks = transaction.get_required_chunks()
243+
transaction.message = body.message.decode("utf-8", errors="replace") if body.message else None
244+
if body.HasField("chunkInfo"):
245+
chunk = body.chunkInfo
246+
transaction._total_chunks = chunk.total
247+
transaction._current_chunk_index = chunk.number - 1
248+
if chunk.HasField("initialTransactionID"):
249+
transaction._initial_transaction_id = TransactionId._from_proto(chunk.initialTransactionID)
250+
else:
251+
transaction._total_chunks = transaction.get_required_chunks()
245252
return transaction
246253

247254
def sign(self, private_key: PrivateKey) -> TopicMessageSubmitTransaction:

src/hiero_sdk_python/file/file_create_transaction.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ def _from_proto(self, proto: file_create_pb2.FileCreateTransactionBody) -> FileC
201201
"""
202202
self.keys = [PublicKey._from_proto(key) for key in proto.keys.keys] if proto.keys.keys else []
203203
self.contents = proto.contents
204-
self.expiration_time = Timestamp._from_protobuf(proto.expirationTime) if proto.expirationTime else None
204+
self.expiration_time = (
205+
Timestamp._from_protobuf(proto.expirationTime) if proto.HasField("expirationTime") else None
206+
)
205207
self.file_memo = proto.memo
206208
return self

src/hiero_sdk_python/file/file_update_transaction.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from hiero_sdk_python.channels import _Channel
99
from hiero_sdk_python.crypto.key import Key
10-
from hiero_sdk_python.crypto.public_key import PublicKey
1110
from hiero_sdk_python.executable import _Method
1211
from hiero_sdk_python.file.file_id import FileId
1312
from hiero_sdk_python.hapi.services.basic_types_pb2 import KeyList as KeyListProto
@@ -40,7 +39,7 @@ class FileUpdateTransaction(Transaction):
4039
def __init__(
4140
self,
4241
file_id: FileId | None = None,
43-
keys: list[PublicKey] | None = None,
42+
keys: list[Key] | None = None,
4443
contents: str | bytes | None = None,
4544
expiration_time: Timestamp | None = None,
4645
file_memo: str | None = None,
@@ -50,7 +49,7 @@ def __init__(
5049
5150
Args:
5251
file_id (FileId, optional): The ID of the file to update.
53-
keys (Optional[list[PublicKey]], optional): The new keys that are allowed to
52+
keys (Optional[list[Key]], optional): The new keys that are allowed to
5453
update/delete the file.
5554
contents (str | bytes, optional): The new contents of the file.
5655
Strings will be automatically encoded as UTF-8 bytes.
@@ -59,7 +58,7 @@ def __init__(
5958
"""
6059
super().__init__()
6160
self.file_id: FileId | None = file_id
62-
self.keys: list[PublicKey] | None = keys
61+
self.keys: list[Key] | None = keys
6362
self.contents: bytes | None = self._encode_contents(contents)
6463
self.expiration_time: Timestamp | None = expiration_time
6564
self.file_memo: str | None = file_memo
@@ -95,19 +94,19 @@ def set_file_id(self, file_id: FileId | None) -> FileUpdateTransaction:
9594
self.file_id = file_id
9695
return self
9796

98-
def set_keys(self, keys: list[PublicKey] | None | PublicKey) -> FileUpdateTransaction:
97+
def set_keys(self, keys: list[Key] | None | Key) -> FileUpdateTransaction:
9998
"""
10099
Sets the new list of keys that can modify or delete the file.
101100
102101
Args:
103-
keys (list[PublicKey] | PublicKey | None): The new keys to set for the file.
104-
Can be a list of PublicKey objects, a single PublicKey, or None.
102+
keys (list[Key] | Key | None): The new keys to set for the file.
103+
Can be a list of Key objects, a single Key, or None.
105104
106105
Returns:
107106
FileUpdateTransaction: This transaction instance.
108107
"""
109108
self._require_not_frozen()
110-
if isinstance(keys, PublicKey):
109+
if isinstance(keys, Key):
111110
self.keys = [keys]
112111
else:
113112
self.keys = keys

src/hiero_sdk_python/nodes/node_create_transaction.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from hiero_sdk_python.address_book.endpoint import Endpoint
99
from hiero_sdk_python.channels import _Channel
1010
from hiero_sdk_python.crypto.key import Key
11-
from hiero_sdk_python.crypto.public_key import PublicKey
1211
from hiero_sdk_python.executable import _Method
1312
from hiero_sdk_python.hapi.services.node_create_pb2 import NodeCreateTransactionBody
1413
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import (
@@ -30,7 +29,7 @@ class NodeCreateParams:
3029
service_endpoints (list[Endpoint]): The service endpoints of the node.
3130
gossip_ca_certificate (bytes, optional): The gossip ca certificate of the node.
3231
grpc_certificate_hash (bytes, optional): The grpc certificate hash of the node.
33-
admin_key (PublicKey, optional): The admin key of the node.
32+
admin_key (Key, optional): The admin key of the node.
3433
decline_reward (bool, optional): The decline reward of the node.
3534
grpc_web_proxy_endpoint (Endpoint, optional): The grpc web proxy endpoint of the node.
3635
"""
@@ -41,7 +40,7 @@ class NodeCreateParams:
4140
service_endpoints: list[Endpoint] = field(default_factory=list)
4241
gossip_ca_certificate: bytes | None = None
4342
grpc_certificate_hash: bytes | None = None
44-
admin_key: PublicKey | None = None
43+
admin_key: Key | None = None
4544
decline_reward: bool | None = None
4645
grpc_web_proxy_endpoint: Endpoint | None = None
4746
associated_registered_nodes: list[int] = field(default_factory=list)
@@ -74,7 +73,7 @@ def __init__(self, node_create_params: NodeCreateParams | None = None):
7473
self.service_endpoints: list[Endpoint] = node_create_params.service_endpoints
7574
self.gossip_ca_certificate: bytes | None = node_create_params.gossip_ca_certificate
7675
self.grpc_certificate_hash: bytes | None = node_create_params.grpc_certificate_hash
77-
self.admin_key: PublicKey | None = node_create_params.admin_key
76+
self.admin_key: Key | None = node_create_params.admin_key
7877
self.decline_reward: bool | None = node_create_params.decline_reward
7978
self.grpc_web_proxy_endpoint: Endpoint | None = node_create_params.grpc_web_proxy_endpoint
8079
self.associated_registered_nodes: list[int] = node_create_params.associated_registered_nodes
@@ -169,12 +168,12 @@ def set_grpc_certificate_hash(self, grpc_certificate_hash: bytes | None) -> Node
169168
self.grpc_certificate_hash = grpc_certificate_hash
170169
return self
171170

172-
def set_admin_key(self, admin_key: PublicKey | None) -> NodeCreateTransaction:
171+
def set_admin_key(self, admin_key: Key | None) -> NodeCreateTransaction:
173172
"""
174173
Sets the admin key for this node create transaction.
175174
176175
Args:
177-
admin_key (PublicKey):
176+
admin_key (Key):
178177
The admin key of the node.
179178
180179
Returns:

src/hiero_sdk_python/nodes/node_update_transaction.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from hiero_sdk_python.address_book.endpoint import Endpoint
1212
from hiero_sdk_python.channels import _Channel
1313
from hiero_sdk_python.crypto.key import Key
14-
from hiero_sdk_python.crypto.public_key import PublicKey
1514
from hiero_sdk_python.executable import _Method
1615
from hiero_sdk_python.hapi.services.node_update_pb2 import (
1716
AssociatedRegisteredNodeList,
@@ -37,7 +36,7 @@ class NodeUpdateParams:
3736
service_endpoints (list[Endpoint]): The service endpoints of the node.
3837
gossip_ca_certificate (bytes, None): The gossip ca certificate of the node.
3938
grpc_certificate_hash (bytes, None): The grpc certificate hash of the node.
40-
admin_key (PublicKey, optional): The admin key of the node.
39+
admin_key (Key, optional): The admin key of the node.
4140
decline_reward (bool, optional): The decline reward of the node.
4241
grpc_web_proxy_endpoint (Endpoint, optional): The grpc web proxy endpoint of the node.
4342
"""
@@ -49,7 +48,7 @@ class NodeUpdateParams:
4948
service_endpoints: list[Endpoint] = field(default_factory=list)
5049
gossip_ca_certificate: bytes | None = None
5150
grpc_certificate_hash: bytes | None = None
52-
admin_key: PublicKey | None = None
51+
admin_key: Key | None = None
5352
decline_reward: bool | None = None
5453
grpc_web_proxy_endpoint: Endpoint | None = None
5554
associated_registered_nodes: list[int] | None = None
@@ -83,7 +82,7 @@ def __init__(self, node_update_params: NodeUpdateParams | None = None):
8382
self.service_endpoints: list[Endpoint] = node_update_params.service_endpoints
8483
self.gossip_ca_certificate: bytes | None = node_update_params.gossip_ca_certificate
8584
self.grpc_certificate_hash: bytes | None = node_update_params.grpc_certificate_hash
86-
self.admin_key: PublicKey | None = node_update_params.admin_key
85+
self.admin_key: Key | None = node_update_params.admin_key
8786
self.decline_reward: bool | None = node_update_params.decline_reward
8887
self.grpc_web_proxy_endpoint: Endpoint | None = node_update_params.grpc_web_proxy_endpoint
8988
self.associated_registered_nodes: list[int] | None = node_update_params.associated_registered_nodes
@@ -193,12 +192,12 @@ def set_grpc_certificate_hash(self, grpc_certificate_hash: bytes | None) -> Node
193192
self.grpc_certificate_hash = grpc_certificate_hash
194193
return self
195194

196-
def set_admin_key(self, admin_key: PublicKey | None) -> NodeUpdateTransaction:
195+
def set_admin_key(self, admin_key: Key | None) -> NodeUpdateTransaction:
197196
"""
198197
Sets the admin key for this node update transaction.
199198
200199
Args:
201-
admin_key (PublicKey):
200+
admin_key (Key):
202201
The admin key of the node.
203202
204203
Returns:

src/hiero_sdk_python/schedule/schedule_create_transaction.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from hiero_sdk_python.account.account_id import AccountId
88
from hiero_sdk_python.channels import _Channel
9+
from hiero_sdk_python.crypto.key import Key
910
from hiero_sdk_python.crypto.public_key import PublicKey
1011
from hiero_sdk_python.executable import _Method
1112
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import (
@@ -238,7 +239,7 @@ def _from_protobuf(cls, transaction_body, body_bytes: bytes, sig_map):
238239
if body.HasField("payerAccountID"):
239240
transaction.payer_account_id = AccountId._from_proto(body.payerAccountID)
240241
if body.HasField("adminKey"):
241-
transaction.admin_key = PublicKey._from_proto(body.adminKey)
242+
transaction.admin_key = Key.from_proto_key(body.adminKey)
242243
if body.HasField("scheduledTransactionBody"):
243244
transaction.schedulable_body = body.scheduledTransactionBody
244245
transaction.schedule_memo = body.memo

src/hiero_sdk_python/tokens/token_mint_transaction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,5 @@ def _from_protobuf(cls, transaction_body, body_bytes: bytes, sig_map):
145145
if body.HasField("token"):
146146
transaction.token_id = TokenId._from_proto(body.token)
147147
transaction.amount = body.amount if body.amount else None
148-
transaction.metadata = list(body.metadata)
148+
transaction.metadata = list(body.metadata) if body.metadata else None
149149
return transaction

tests/unit/account_allowance_approve_transaction_test.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from hiero_sdk_python.hbar import Hbar
1717
from hiero_sdk_python.tokens.nft_id import NftId
1818
from hiero_sdk_python.tokens.token_id import TokenId
19+
from hiero_sdk_python.transaction.transaction import Transaction
20+
from hiero_sdk_python.transaction.transaction_id import TransactionId
1921

2022

2123
pytestmark = pytest.mark.unit
@@ -404,3 +406,38 @@ def test_zero_amount_allowances(account_allowance_transaction, sample_accounts,
404406
assert len(account_allowance_transaction.token_allowances) == 1
405407
assert account_allowance_transaction.hbar_allowances[0].amount == 0
406408
assert account_allowance_transaction.token_allowances[0].amount == 0
409+
410+
411+
def test_from_bytes(mock_account_ids):
412+
"""Test round-trip via _from_protobuf for AccountAllowanceApproveTransaction."""
413+
operator_id, _, node_account_id, _, _ = mock_account_ids
414+
owner = AccountId(0, 0, 200)
415+
spender = AccountId(0, 0, 300)
416+
token_id = TokenId(0, 0, 500)
417+
nft_id = NftId(token_id, 1)
418+
419+
tx = AccountAllowanceApproveTransaction()
420+
tx.approve_hbar_allowance(owner, spender, Hbar(10))
421+
tx.approve_token_allowance(token_id, owner, spender, 100)
422+
tx.approve_token_nft_allowance(nft_id, owner, spender)
423+
tx.transaction_id = TransactionId.generate(operator_id)
424+
tx.node_account_id = node_account_id
425+
tx.freeze()
426+
427+
reconstructed = Transaction.from_bytes(tx.to_bytes())
428+
429+
assert isinstance(reconstructed, AccountAllowanceApproveTransaction)
430+
assert len(reconstructed.hbar_allowances) == 1
431+
assert reconstructed.hbar_allowances[0].owner_account_id == owner
432+
assert reconstructed.hbar_allowances[0].spender_account_id == spender
433+
assert reconstructed.hbar_allowances[0].amount == Hbar(10).to_tinybars()
434+
assert len(reconstructed.token_allowances) == 1
435+
assert reconstructed.token_allowances[0].token_id == token_id
436+
assert reconstructed.token_allowances[0].owner_account_id == owner
437+
assert reconstructed.token_allowances[0].spender_account_id == spender
438+
assert reconstructed.token_allowances[0].amount == 100
439+
assert len(reconstructed.nft_allowances) == 1
440+
assert reconstructed.nft_allowances[0].token_id == token_id
441+
assert reconstructed.nft_allowances[0].owner_account_id == owner
442+
assert reconstructed.nft_allowances[0].spender_account_id == spender
443+
assert nft_id.serial_number in reconstructed.nft_allowances[0].serial_numbers

tests/unit/account_allowance_delete_transaction_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from hiero_sdk_python.tokens.nft_id import NftId
1818
from hiero_sdk_python.tokens.token_id import TokenId
1919
from hiero_sdk_python.tokens.token_nft_allowance import TokenNftAllowance
20+
from hiero_sdk_python.transaction.transaction import Transaction
21+
from hiero_sdk_python.transaction.transaction_id import TransactionId
2022

2123

2224
pytestmark = pytest.mark.unit
@@ -259,3 +261,25 @@ def test_empty_nft_wipe_list(account_allowance_delete_transaction):
259261

260262
proto_body = account_allowance_delete_transaction._build_proto_body()
261263
assert len(proto_body.nftAllowances) == 0
264+
265+
266+
def test_from_bytes(mock_account_ids):
267+
"""Test round-trip via _from_protobuf for AccountAllowanceDeleteTransaction."""
268+
operator_id, _, node_account_id, _, _ = mock_account_ids
269+
owner = AccountId(0, 0, 200)
270+
token_id = TokenId(0, 0, 100)
271+
nft_id = NftId(token_id, 1)
272+
273+
tx = AccountAllowanceDeleteTransaction()
274+
tx.delete_all_token_nft_allowances(nft_id, owner)
275+
tx.transaction_id = TransactionId.generate(operator_id)
276+
tx.node_account_id = node_account_id
277+
tx.freeze()
278+
279+
reconstructed = Transaction.from_bytes(tx.to_bytes())
280+
281+
assert isinstance(reconstructed, AccountAllowanceDeleteTransaction)
282+
assert len(reconstructed.nft_wipe) == 1
283+
assert reconstructed.nft_wipe[0].token_id == token_id
284+
assert reconstructed.nft_wipe[0].owner_account_id == owner
285+
assert reconstructed.nft_wipe[0].serial_numbers == [1]

tests/unit/account_create_transaction_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
TransactionResponse as TransactionResponseProto,
2626
)
2727
from hiero_sdk_python.response_code import ResponseCode
28+
from hiero_sdk_python.transaction.transaction import Transaction
2829
from hiero_sdk_python.transaction.transaction_id import TransactionId
2930
from tests.unit.mock_server import mock_hedera_servers
3031

@@ -539,3 +540,40 @@ def test_set_stake_account_id_reset_stake_node_id():
539540
tx.set_staked_account_id(AccountId(0, 0, 1))
540541
assert tx.staked_account_id == AccountId(0, 0, 1)
541542
assert tx.staked_node_id is None
543+
544+
545+
def test_from_bytes(mock_account_ids):
546+
"""Test round-trip via _from_protobuf for AccountCreateTransaction."""
547+
operator_id, node_account_id = mock_account_ids
548+
549+
from hiero_sdk_python.hbar import Hbar
550+
551+
tx = AccountCreateTransaction()
552+
tx.set_initial_balance(Hbar(5).to_tinybars())
553+
tx.set_receiver_signature_required(True)
554+
tx.transaction_id = generate_transaction_id(operator_id)
555+
tx.node_account_id = node_account_id
556+
tx.freeze()
557+
558+
reconstructed = Transaction.from_bytes(tx.to_bytes())
559+
560+
assert isinstance(reconstructed, AccountCreateTransaction)
561+
assert reconstructed.initial_balance == Hbar(5).to_tinybars()
562+
assert reconstructed.receiver_signature_required is True
563+
564+
565+
def test_from_bytes_without_auto_renew_period(mock_account_ids):
566+
"""When auto_renew_period is None, round-trip must preserve None (not restore the default)."""
567+
operator_id, node_account_id = mock_account_ids
568+
569+
tx = AccountCreateTransaction(auto_renew_period=None)
570+
tx.set_initial_balance(1000)
571+
tx.transaction_id = generate_transaction_id(operator_id)
572+
tx.node_account_id = node_account_id
573+
tx.freeze()
574+
575+
reconstructed = Transaction.from_bytes(tx.to_bytes())
576+
577+
assert isinstance(reconstructed, AccountCreateTransaction)
578+
assert reconstructed.initial_balance == 1000
579+
assert reconstructed.auto_renew_period is None

0 commit comments

Comments
 (0)