Skip to content

Commit 19e7f37

Browse files
authored
fix: ChunkTransaction create all transaction_bytes when freeze (#2506)
Signed-off-by: Manish Dait <daitmanish88@gmail.com>
1 parent 1dd5533 commit 19e7f37

48 files changed

Lines changed: 955 additions & 469 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: 28 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
from hiero_sdk_python.channels import _Channel
66
from hiero_sdk_python.consensus.topic_id import TopicId
7-
from hiero_sdk_python.crypto.private_key import PrivateKey
87
from hiero_sdk_python.executable import _Method
98
from hiero_sdk_python.hapi.services import consensus_submit_message_pb2, transaction_pb2
109
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import (
1110
SchedulableTransactionBody,
1211
)
12+
from hiero_sdk_python.schedule.schedule_create_transaction import ScheduleCreateTransaction
1313
from hiero_sdk_python.transaction.chunked_transaction import ChunkedTransaction
1414
from hiero_sdk_python.transaction.custom_fee_limit import CustomFeeLimit
1515

@@ -107,33 +107,6 @@ def set_message(self, message: bytes | str) -> TopicMessageSubmitTransaction:
107107
self._total_chunks = self.get_required_chunks()
108108
return self
109109

110-
def set_chunk_size(self, chunk_size: int) -> TopicMessageSubmitTransaction:
111-
"""
112-
Set maximum chunk size in bytes.
113-
114-
Args:
115-
chunk_size (int): The size of each chunk in bytes.
116-
117-
Returns:
118-
TopicMessageSubmitTransaction: This transaction instance (for chaining).
119-
"""
120-
super().set_chunk_size(chunk_size)
121-
self._total_chunks = self.get_required_chunks()
122-
return self
123-
124-
def set_max_chunks(self, max_chunks: int) -> TopicMessageSubmitTransaction:
125-
"""
126-
Set maximum allowed chunks.
127-
128-
Args:
129-
max_chunks (int): The maximum number of chunks allowed.
130-
131-
Returns:
132-
TopicMessageSubmitTransaction: This transaction instance (for chaining).
133-
"""
134-
super().set_max_chunks(max_chunks)
135-
return self
136-
137110
def set_custom_fee_limits(self, custom_fee_limits: list[CustomFeeLimit]) -> TopicMessageSubmitTransaction:
138111
"""
139112
Sets the maximum custom fees that the user is willing to pay for the message.
@@ -186,27 +159,26 @@ def _build_proto_body(self) -> consensus_submit_message_pb2.ConsensusSubmitMessa
186159
if not self.message:
187160
raise ValueError("Missing required fields: message.")
188161

189-
content = self._message_as_bytes()
162+
contents = self._message_as_bytes()
190163

191-
start_index = self._current_chunk_index * self.chunk_size
192-
end_index = min(start_index + self.chunk_size, len(content))
193-
chunk_content = content[start_index:end_index]
164+
if self._total_chunks > 1 and self._current_chunk_index is not None:
165+
chunk_info = consensus_submit_message_pb2.ConsensusMessageChunkInfo(
166+
initialTransactionID=self._initial_transaction_id._to_proto(),
167+
total=self._total_chunks,
168+
number=self._current_chunk_index + 1,
169+
)
194170

195-
body = consensus_submit_message_pb2.ConsensusSubmitMessageTransactionBody(
196-
topicID=self.topic_id._to_proto() if self.topic_id else None, message=chunk_content
197-
)
171+
chunk_content = self._current_chunk_slice(contents)
198172

199-
# Multi-chunk metadata
200-
if self._total_chunks > 1:
201-
body.chunkInfo.CopyFrom(
202-
consensus_submit_message_pb2.ConsensusMessageChunkInfo(
203-
initialTransactionID=self._initial_transaction_id._to_proto(),
204-
total=self._total_chunks,
205-
number=self._current_chunk_index + 1,
206-
)
173+
return consensus_submit_message_pb2.ConsensusSubmitMessageTransactionBody(
174+
topicID=self.topic_id._to_proto() if self.topic_id else None,
175+
message=chunk_content,
176+
chunkInfo=chunk_info,
207177
)
208178

209-
return body
179+
return consensus_submit_message_pb2.ConsensusSubmitMessageTransactionBody(
180+
topicID=self.topic_id._to_proto() if self.topic_id else None, message=contents
181+
)
210182

211183
def build_transaction_body(self) -> transaction_pb2.TransactionBody:
212184
"""
@@ -220,6 +192,18 @@ def build_transaction_body(self) -> transaction_pb2.TransactionBody:
220192
transaction_body.consensusSubmitMessage.CopyFrom(consensus_submit_message_body)
221193
return transaction_body
222194

195+
def schedule(self) -> ScheduleCreateTransaction:
196+
"""
197+
Converts this transaction into a scheduled transaction.
198+
"""
199+
if self.message is not None and len(self._message_as_bytes()) > self.chunk_size:
200+
raise RuntimeError(
201+
f"Cannot schedule TopicMessageSubmitTransaction because the message exceeds "
202+
f"the maximum chunk size of {self.chunk_size} bytes"
203+
)
204+
205+
return super().schedule()
206+
223207
def build_scheduled_body(self) -> SchedulableTransactionBody:
224208
"""
225209
Builds the scheduled transaction body for this topic message submit transaction.
@@ -243,16 +227,3 @@ def _get_method(self, channel: _Channel) -> _Method:
243227
_Method: The method object with bound transaction execution.
244228
"""
245229
return _Method(transaction_func=channel.topic.submitMessage, query_func=None)
246-
247-
def sign(self, private_key: PrivateKey) -> TopicMessageSubmitTransaction:
248-
"""
249-
Signs the transaction using the provided private key.
250-
251-
Args:
252-
private_key (PrivateKey): The private key to sign the transaction with.
253-
254-
Returns:
255-
TopicMessageSubmitTransaction: This transaction instance (for chaining).
256-
"""
257-
super().sign(private_key)
258-
return self

src/hiero_sdk_python/file/file_append_transaction.py

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from hiero_sdk_python.hapi.services import file_append_pb2
2121
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import SchedulableTransactionBody
2222
from hiero_sdk_python.hbar import Hbar
23+
from hiero_sdk_python.schedule.schedule_create_transaction import ScheduleCreateTransaction
2324
from hiero_sdk_python.transaction.chunked_transaction import ChunkedTransaction
2425

2526

@@ -129,32 +130,6 @@ def set_contents(self, contents: str | bytes | None) -> FileAppendTransaction:
129130
self._total_chunks = self._calculate_total_chunks()
130131
return self
131132

132-
def set_max_chunks(self, max_chunks: int) -> FileAppendTransaction:
133-
"""
134-
Sets the maximum number of chunks allowed for this transaction.
135-
136-
Args:
137-
max_chunks (int): The maximum number of chunks allowed.
138-
139-
Returns:
140-
FileAppendTransaction: This transaction instance.
141-
"""
142-
super().set_max_chunks(max_chunks)
143-
return self
144-
145-
def set_chunk_size(self, chunk_size: int) -> FileAppendTransaction:
146-
"""
147-
Sets the chunk size for this transaction.
148-
149-
Args:
150-
chunk_size (int): The size of each chunk in bytes.
151-
152-
Returns:
153-
FileAppendTransaction: This transaction instance.
154-
"""
155-
super().set_chunk_size(chunk_size)
156-
return self
157-
158133
def _build_proto_body(self) -> file_append_pb2.FileAppendTransactionBody:
159134
"""
160135
Returns the protobuf body for the file append transaction.
@@ -169,15 +144,16 @@ def _build_proto_body(self) -> file_append_pb2.FileAppendTransactionBody:
169144
if self.file_id is None:
170145
raise ValueError("Missing required FileID")
171146

172-
if self.contents is None:
173-
chunk_contents = b""
174-
else:
175-
start_index = self._current_chunk_index * self.chunk_size
176-
end_index = min(start_index + self.chunk_size, len(self.contents))
177-
chunk_contents = self.contents[start_index:end_index]
147+
contents = self.contents if self.contents is not None else b""
148+
149+
if self._current_chunk_index is not None:
150+
chunk_contents = self._current_chunk_slice(contents)
151+
return file_append_pb2.FileAppendTransactionBody(
152+
fileID=self.file_id._to_proto() if self.file_id else None, contents=chunk_contents
153+
)
178154

179155
return file_append_pb2.FileAppendTransactionBody(
180-
fileID=self.file_id._to_proto() if self.file_id else None, contents=chunk_contents
156+
fileID=self.file_id._to_proto() if self.file_id else None, contents=contents
181157
)
182158

183159
def build_transaction_body(self) -> Any:
@@ -192,6 +168,18 @@ def build_transaction_body(self) -> Any:
192168
transaction_body.fileAppend.CopyFrom(file_append_body)
193169
return transaction_body
194170

171+
def schedule(self) -> ScheduleCreateTransaction:
172+
"""
173+
Converts this transaction into a scheduled transaction.
174+
"""
175+
if self.contents is not None and len(self.contents) > self.chunk_size:
176+
raise RuntimeError(
177+
f"Cannot schedule FileAppendTransaction because the contents exceeds "
178+
f"the maximum chunk size of {self.chunk_size} bytes"
179+
)
180+
181+
return super().schedule()
182+
195183
def build_scheduled_body(self) -> SchedulableTransactionBody:
196184
"""
197185
Builds the scheduled transaction body for this file append transaction.

src/hiero_sdk_python/lockable_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(self):
1919
def _require_not_locked(self) -> None:
2020
"""Raise an exception if the list is locked."""
2121
if self._locked:
22-
raise RuntimeError("list is unmutable")
22+
raise RuntimeError("list is immutable")
2323

2424
def set_list(self, items: list[T]) -> _LockableList[T]:
2525
"""Replace the contents of the list and reset the current index."""

src/hiero_sdk_python/query/fee_estimate_query.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def execute(self, client) -> FeeEstimateResponse:
137137
self._ensure_frozen(self._transaction, client)
138138

139139
if self._is_chunked():
140-
return self._execute_chunked(client, url, mode)
140+
return self._execute_chunked(url, mode)
141141

142142
return self._execute_single(url, mode)
143143

@@ -154,7 +154,7 @@ def _build_url(self, client: Client, mode: FeeEstimateMode) -> str:
154154
def _ensure_frozen(self, tx: Transaction, client) -> None:
155155
"""Ensure the transaction is frozen before serialization."""
156156
if not tx._transaction_body_bytes:
157-
tx.freeze_with(client) if hasattr(tx, "freeze_with") else tx.freeze()
157+
tx.freeze_with(client)
158158

159159
def _post(self, url: str, payload: bytes) -> dict:
160160
"""POST with retry for transient failures."""
@@ -191,20 +191,13 @@ def _post(self, url: str, payload: bytes) -> dict:
191191
raise RuntimeError("Unreachable")
192192

193193
def _execute_single(self, url: str, mode: FeeEstimateMode) -> FeeEstimateResponse:
194-
data = self._post(url, self._transaction.to_bytes())
194+
data = self._post(url, self._transaction._to_proto().SerializeToString())
195195
return self._to_response(data, mode)
196196

197-
def _execute_chunked(self, client, url: str, mode: FeeEstimateMode) -> FeeEstimateResponse:
197+
def _execute_chunked(self, url: str, mode: FeeEstimateMode) -> FeeEstimateResponse:
198198
"""
199199
Aggregate fees across all chunks into a single response.
200200
"""
201-
202-
# Save original state to restore later
203-
original_id = self._transaction.transaction_id
204-
original_index = getattr(self._transaction, "_current_chunk_index", 0)
205-
original_bodies = dict(self._transaction._transaction_body_bytes)
206-
original_signatures = dict(self._transaction._signature_map)
207-
208201
total_node_base = 0
209202
total_service_base = 0
210203
total_network_subtotal = 0
@@ -216,15 +209,9 @@ def _execute_chunked(self, client, url: str, mode: FeeEstimateMode) -> FeeEstima
216209
final_hvm = 0
217210

218211
try:
219-
for i, chunk_tx_id in enumerate(self._transaction._transaction_ids):
220-
self._transaction._current_chunk_index = i
221-
self._transaction.transaction_id = chunk_tx_id
222-
223-
self._transaction._transaction_body_bytes.clear()
212+
for _ in range(self._transaction.get_required_chunks()):
213+
tx_bytes = self._transaction._to_proto().SerializeToString()
224214

225-
self._transaction.freeze_with(client)
226-
227-
tx_bytes = self._transaction.to_bytes()
228215
data = self._post(url, tx_bytes)
229216
response = self._to_response(data, mode)
230217

@@ -241,13 +228,10 @@ def _execute_chunked(self, client, url: str, mode: FeeEstimateMode) -> FeeEstima
241228

242229
final_hvm = response.high_volume_multiplier
243230

231+
self._transaction._transaction_ids.advance()
232+
244233
finally:
245-
self._transaction.transaction_id = original_id
246-
self._transaction._current_chunk_index = original_index
247-
self._transaction._transaction_body_bytes.clear()
248-
self._transaction._transaction_body_bytes.update(original_bodies)
249-
self._transaction._signature_map.clear()
250-
self._transaction._signature_map.update(original_signatures)
234+
self._transaction._transaction_ids.set_index(0)
251235

252236
return FeeEstimateResponse(
253237
mode=mode,

0 commit comments

Comments
 (0)