Skip to content

feat(transaction): regenerate transaction ID on expiry - #2657

Open
iron-prog wants to merge 19 commits into
hiero-ledger:mainfrom
iron-prog:feat/2643-regenerate-transaction-id
Open

iron-prog wants to merge 19 commits into
hiero-ledger:mainfrom
iron-prog:feat/2643-regenerate-transaction-id

Conversation

@iron-prog

Copy link
Copy Markdown
Contributor

Description:

Add support for regenerating transaction IDs when a transaction expires, with configurable client and transaction-level settings.

  • Add regenerate_transaction_id to Transaction
  • Add default_regenerate_transaction_id to Client
  • Regenerate transaction IDs and re-sign transaction bodies on TRANSACTION_EXPIRED
  • Preserve transaction ID locking and chunked transaction state during regeneration
  • Add unit tests for client defaults, transaction overrides, expiration retry, and re-signing

Related issue(s):

Fixes #2643

Notes for reviewer:

Transaction ID regeneration is enabled by default at the client level and can be overridden per transaction.

When TRANSACTION_EXPIRED is received and regeneration is enabled, the transaction ID is regenerated using the executing client's operator account, transaction bodies are rebuilt, and the transaction is re-signed before retrying.

Chunked transactions regenerate the complete transaction ID sequence while preserving the current chunk and initial transaction ID.

Tests:

  • pytest -q tests/unit
  • 3076 passed

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

Signed-off-by: iron-prog <dt915725@gmail.com>
Signed-off-by: iron-prog <dt915725@gmail.com>
Signed-off-by: iron-prog <dt915725@gmail.com>
@iron-prog
iron-prog force-pushed the feat/2643-regenerate-transaction-id branch from 29acd3a to 8c1d846 Compare September 13, 2026 17:09
@coderabbitai

coderabbitai Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Client and transaction settings now control transaction ID regeneration. Expired transactions can regenerate IDs, rebuild bodies, re-sign, and retry. Execution prevents concurrent calls. Chunked message tests verify ID and body updates.

Changes

Transaction ID regeneration

Layer / File(s) Summary
Configuration contracts
src/hiero_sdk_python/client/client.py, src/hiero_sdk_python/transaction/transaction.py, tests/unit/client_test.py, tests/unit/transaction_test.py
Client provides a default regeneration setting. Transaction supports an optional override, boolean validation, fluent setters, and freeze-time resolution.
Expired retry execution
src/hiero_sdk_python/transaction/transaction.py, tests/unit/executable_test.py
Enabled expired transactions receive new IDs, rebuild bodies, and re-sign with the execution client. Disabled transactions do not retry. Tests cover client selection, foreign signatures, missing operator accounts, shortened key prefixes, and concurrent execution.
Chunk body rebuilds
src/hiero_sdk_python/transaction/transaction.py, tests/unit/topic_message_submit_transaction_test.py
Chunk retries rebuild the expired chunk while preserving its content. First-chunk expiration updates the initial transaction ID referenced by all pending chunks.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Transaction
  participant Network
  Client->>Transaction: execute
  Transaction->>Network: submit transaction
  Network-->>Transaction: TRANSACTION_EXPIRED
  Transaction->>Transaction: generate ID and rebuild bodies
  Transaction->>Client: use execution operator key
  Transaction->>Network: submit re-signed transaction
  Network-->>Transaction: successful response
Loading
Merge Risk: 🟡 Moderate · up to 5c7fa

An expired transaction carrying an empty signature prefix can be retried without a required signer, causing the regenerated transaction to fail. Reject unverifiable prefixes before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements the settings, freeze-time precedence, expiration retry, ID locking, chunk-aware rebuilding, re-signing, and tests required by #2643. The executing client must supply the regenerated … Use the operator account ID from the current execute(client) context for regeneration, or update the regeneration account when execution starts. Add a test that executes a transaction with a client whose operator account differs from the …
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: regenerating transaction IDs when transactions expire.
Description check ✅ Passed The description directly explains configurable transaction ID regeneration, client and transaction settings, retry behavior, chunk handling, re-signing, and test coverage.
Out of Scope Changes check ✅ Passed The changes stay within #2643. Client and transaction settings, expiration handling, transaction-ID replacement, locking, chunk-state rebuilding, operator re-signing, concurrency protection, multi-sig…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 48 functions across 6 files.
Full details: Linked Issues check

Explanation

The PR implements the settings, freeze-time precedence, expiration retry, ID locking, chunk-aware rebuilding, re-signing, and tests required by #2643. The executing client must supply the regenerated ID account. In execute(), self.operator_account_id is assigned from the client only when it is None. _handle_transaction_id_regeneration() then calls TransactionId.generate(self.operator_account_id). A transaction frozen or previously used with one account can therefore regenerate an ID for a different account when executed by another client. The cross-client test checks re-signing, but it does not check the regenerated ID account.

Resolution

Use the operator account ID from the current execute(client) context for regeneration, or update the regeneration account when execution starts. Add a test that executes a transaction with a client whose operator account differs from the account used during freezing or an earlier execution, then verifies that the regenerated TransactionId.account_id matches the executing client.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ff5d30a6-73ae-444d-8adb-fec299bd19f7

📥 Commits

Reviewing files that changed from the base of the PR and between 881853c and 29acd3a.

📒 Files selected for processing (6)
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/transaction/transaction.py
  • tests/unit/client_test.py
  • tests/unit/executable_test.py
  • tests/unit/topic_message_submit_transaction_test.py
  • tests/unit/transaction_test.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
@github-actions github-actions Bot added open to community review PR is open for community review and feedback queue:junior-committer PR awaiting initial quality review labels Sep 13, 2026
Comment thread src/hiero_sdk_python/client/client.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated

@aceppaluni aceppaluni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iron-prog Nice work! Just a few questions from me.

For execute_all(), if an earlier chunk has already succeeded and a later chunk expires, why do we regenerate the IDs for all chunks? Could this invalidate or otherwise confuse the state representing chunks that have already been submitted successfully? Should we add a test covering a multi-chunk transaction where an earlier chunk succeeds, a later chunk expires, and we verify the already-submitted chunks aren't accidentally re-executed or otherwise affected?

Does regeneration intentionally discard all existing signatures? For transactions requiring signatures from multiple keys, clearing _signature_map and then signing only with the executing client's operator key appears to leave the regenerated transaction incompletely signed. Is that the intention? Or should we preserve/reapply signatures that are still valid, or explicitly reject regeneration for transactions with additional signatures?

What happens if the same transaction is executed concurrently with two clients and both receive TRANSACTION_EXPIRED?

Should there be tests for regeneration when operator is missing and for a multi-signature transaction?

@iron-prog

iron-prog commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor Author

@iron-prog Nice work! Just a few questions from me.

For execute_all(), if an earlier chunk has already succeeded and a later chunk expires, why do we regenerate the IDs for all chunks? Could this invalidate or otherwise confuse the state representing chunks that have already been submitted successfully? Should we add a test covering a multi-chunk transaction where an earlier chunk succeeds, a later chunk expires, and we verify the already-submitted chunks aren't accidentally re-executed or otherwise affected?

Does regeneration intentionally discard all existing signatures? For transactions requiring signatures from multiple keys, clearing _signature_map and then signing only with the executing client's operator key appears to leave the regenerated transaction incompletely signed. Is that the intention? Or should we preserve/reapply signatures that are still valid, or explicitly reject regeneration for transactions with additional signatures?

What happens if the same transaction is executed concurrently with two clients and both receive TRANSACTION_EXPIRED?

Should there be tests for regeneration when operator is missing and for a multi-signature transaction?

You're right. The current implementation regenerates the complete transaction-ID sequence, which can alter IDs associated with chunks that have already been submitted. I checked the corresponding Hiero JS SDK implementation, and it regenerates only the transaction ID at the current index. I'll align the python implementation with that behavior and add a test where an earlier chunk succeeds and a later chunk expires.

@iron-prog
iron-prog marked this pull request as draft September 15, 2026 07:15
@github-actions github-actions Bot removed queue:junior-committer PR awaiting initial quality review open to community review PR is open for community review and feedback labels Sep 15, 2026
Signed-off-by: iron-prog <dt915725@gmail.com>
@iron-prog
iron-prog marked this pull request as ready for review September 18, 2026 02:49
@github-actions github-actions Bot added open to community review PR is open for community review and feedback queue:junior-committer PR awaiting initial quality review labels Sep 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 3c0ddae3-e66a-4cda-949d-94446f50ff7f

📥 Commits

Reviewing files that changed from the base of the PR and between 8c1d846 and 33717e8.

📒 Files selected for processing (4)
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/transaction/transaction.py
  • tests/unit/executable_test.py
  • tests/unit/topic_message_submit_transaction_test.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Comment thread src/hiero_sdk_python/transaction/transaction.py Outdated
Signed-off-by: iron-prog <dt915725@gmail.com>
@iron-prog
iron-prog force-pushed the feat/2643-regenerate-transaction-id branch from 33717e8 to 7aa38b7 Compare September 18, 2026 18:31
@github-actions github-actions Bot added python Pull requests that update python code skill: advanced requires knowledge of multiple areas in the codebase without defined steps to implement or examples labels Sep 29, 2026
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.33333% with 2 lines in your changes missing coverage. Please review.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2657      +/-   ##
==========================================
+ Coverage   95.68%   95.73%   +0.04%     
==========================================
  Files         169      169              
  Lines       11080    11174      +94     
==========================================
+ Hits        10602    10697      +95     
+ Misses        478      477       -1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown

Hi, this is WorkflowBot.
Your pull request cannot be merged as it is not passing all our workflow checks.
Please click on each check to review the logs and resolve issues so all checks pass.
To help you:

Signed-off-by: iron-prog <dt915725@gmail.com>
Signed-off-by: iron-prog <dt915725@gmail.com>
@iron-prog

iron-prog commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Hi @manishdait @danielmarv @aceppaluni,
I pushed review changes (single-ID regeneration like JS, user-set / to_bytes() / from_bytes() IDs not regenerated, deepcopy/pickle fix, new tests).

For the open design questions (payer account, concurrency lock, batch inner IDs): do we need a meeting, or can you decide here in the PR? I wrote my proposals for each in the PR notes and I can change the code quickly either way. If a meeting is better, tell me which one and I will join.

@danielmarh

This comment was marked as resolved.

…nd guards

Rework TRANSACTION_EXPIRED handling to the agreed design:

- Pin the transaction ID when it is set, read through the public
  transaction_id property, serialized with to_bytes() or restored with
  from_bytes(); a pinned ID is never replaced.
- Regenerate only when enabled, the ID is unpinned, the payer is the
  executing operator, every signature on the rebuilt bodies is the
  operator's, the transaction is not a BatchTransaction, and at most
  once per execute() call.
- Regenerate the current chunk and every later chunk with consecutive
  IDs; move initialTransactionID when the first chunk expires.
- Hold the operator key in an execute()-scoped context instead of a
  public attribute; drop the execution lock and pickle hooks.
- Log and read the transaction ID internally without pinning it.
- Resolve the client default in freeze_with() after the frozen check.

Signed-off-by: Ntege Daniel <danientege785@gmail.com>
@iron-prog
iron-prog marked this pull request as ready for review October 6, 2026 17:09
@iron-prog

iron-prog commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

@danielmarv, thanks for the rework in e5dc445. I checked it locally and everything looks good. The execute-scoped operator key, single regeneration limit, and rebuilding later chunks look good.

A few small points:

  1. from_bytes() mappings: I couldn’t find focused coverage for the newly added _get_transaction_class() mappings (token_pause, util_prng, token_fee_schedule_update, tokenClaimAirdrop, etc.). Could we add a small parametrized test to verify that each protobuf transaction type resolves to the expected Python transaction class?
  2. Pinned IDs: Could we add a short note about the pinning behavior to docs/sdk_users/transaction_bytes.md? In particular, that transaction_id, to_bytes(), and from_bytes() prevent regeneration.
  3. One regeneration per execute(): This looks good. I’ll mention in the PR description that a second expiry raises PrecheckError.
  4. Integration test: I only ran the unit suite locally. Can you confirm whether the Solo integration job runs transaction_id_regeneration_e2e_test.py?

I’ll update the PR description and release-notes line accordingly. Thanks again!

@github-actions github-actions Bot added open to community review PR is open for community review and feedback queue:junior-committer PR awaiting initial quality review labels Oct 6, 2026
@danielmarv
danielmarv self-requested a review October 6, 2026 18:06
danielmarv and others added 3 commits October 6, 2026 21:23
- Document in transaction_bytes.md that setting, reading, serializing or
  deserializing a transaction ID pins it, so it is not regenerated on
  TRANSACTION_EXPIRED.
- Drop two executable_test cases duplicated in
  transaction_id_regeneration_test.py and move the remaining
  regeneration test there.

Signed-off-by: Ntege Daniel <danientege785@gmail.com>
Signed-off-by: Ntege Daniel <danientege785@gmail.com>
@aceppaluni
aceppaluni marked this pull request as draft October 18, 2026 15:54
@aceppaluni

Copy link
Copy Markdown
Contributor

@iron-prog Thank you for you working on this! Will be converting to draft while changes are address. Please feel free to convert back to "ready for review" when you are ready. Thank you!!

@github-actions github-actions Bot removed queue:junior-committer PR awaiting initial quality review open to community review PR is open for community review and feedback labels Oct 18, 2026
@danielmarv
danielmarv marked this pull request as ready for review October 18, 2026 16:22
@danielmarv

Copy link
Copy Markdown
Member

Hi @aceppaluni
this is ready for review

@github-actions github-actions Bot added open to community review PR is open for community review and feedback queue:junior-committer PR awaiting initial quality review labels Oct 18, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Issue has been approved by team member notes: extra care requires extra attention to detail or requirements open to community review PR is open for community review and feedback python Pull requests that update python code queue:junior-committer PR awaiting initial quality review skill: advanced requires knowledge of multiple areas in the codebase without defined steps to implement or examples status: discussion a new proposed feature that should be discussed before proceeding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add regenerate_transaction_id to the Transaction class

7 participants