Skip to content

MF-L06: fix(contracts): clear stale challengeExpireAt on cooperative escrow finalization#754

Merged
nksazonov merged 1 commit into
fix/audit-findings-finalfrom
fix/mf-l06
May 13, 2026
Merged

MF-L06: fix(contracts): clear stale challengeExpireAt on cooperative escrow finalization#754
nksazonov merged 1 commit into
fix/audit-findings-finalfrom
fix/mf-l06

Conversation

@nksazonov
Copy link
Copy Markdown
Contributor

@nksazonov nksazonov commented May 13, 2026

Problem

_applyEscrowDepositEffects() and _applyEscrowWithdrawalEffects() only updated meta.challengeExpireAt when effects.newChallengeExpiry > 0. Since cooperative finalization leaves newChallengeExpiry at its zero default, this condition was never true — meaning a prior non-zero challengeExpireAt set during a challenge was silently left in storage after finalization.

The result: a cooperatively finalized escrow (transitioning from DISPUTED to FINALIZED) would still report a non-zero challengeExpiry through getEscrowDepositData() / getEscrowWithdrawalData(), misleading indexers, SDKs, and UIs.

The unilateral timeout paths cleared challengeExpireAt explicitly, so only the cooperative path from DISPUTED was affected.

Fix

Replace the > 0 guard with a != comparison in both apply helpers, mirroring the pattern already used in the channel apply path:

// before
if (effects.newChallengeExpiry > 0) {
    meta.challengeExpireAt = effects.newChallengeExpiry;
}

// after
if (meta.challengeExpireAt != effects.newChallengeExpiry) {
    meta.challengeExpireAt = effects.newChallengeExpiry;
}

Tests

Regression tests added to:

  • test/ChannelHub_units/ChannelHub_finalizeEscrowDeposit.t.soltest_cooperativeFinalize_fromDISPUTED_clearsChallengeExpiry
  • test/ChannelHub_units/ChannelHub_finalizeEscrowWithdrawal.t.soltest_cooperativeFinalize_fromDISPUTED_clearsChallengeExpiry

Each test: initiates an escrow → challenges it (asserting non-zero challengeExpireAt) → cooperatively finalizes before challenge expires → asserts challengeExpireAt == 0 and status FINALIZED.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed escrow challenge expiry handling to ensure the stored expiry value is properly updated in all scenarios, including when resetting to zero.
  • Tests

    • Added regression tests verifying cooperative escrow finalization correctly clears challenge expiry values after disputes.

Review Change Stack

…inalization

Regression tests added for both escrow deposit and escrow withdrawal.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a52d0ef-1272-423f-80ca-167decf23e7f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mf-l06

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowDeposit.t.sol (1)

9-9: 💤 Low value

Optional: Consider removing unused import.

The EscrowDepositEngine import doesn't appear to be referenced in the test code. While this doesn't affect functionality, removing unused imports can improve code clarity.

♻️ Optional cleanup
-import {EscrowDepositEngine} from "../../src/EscrowDepositEngine.sol";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowDeposit.t.sol` at
line 9, The import of EscrowDepositEngine is unused in the test
(EscrowDepositEngine) — remove the unused import statement from
ChannelHub_finalizeEscrowDeposit.t.sol to clean up the file; locate the line
importing EscrowDepositEngine and delete it so the test compiles and reads
without the unnecessary symbol.
contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowWithdrawal.t.sol (1)

9-9: 💤 Low value

Optional: Consider removing unused import.

The EscrowWithdrawalEngine import doesn't appear to be referenced in the test code. While this doesn't affect functionality, removing unused imports can improve code clarity.

♻️ Optional cleanup
-import {EscrowWithdrawalEngine} from "../../src/EscrowWithdrawalEngine.sol";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowWithdrawal.t.sol` at
line 9, Remove the unused import of EscrowWithdrawalEngine from the test file by
deleting the import line that references EscrowWithdrawalEngine; the symbol
EscrowWithdrawalEngine is not referenced anywhere in
ChannelHub_finalizeEscrowWithdrawal tests, so removing that unused import cleans
up the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowDeposit.t.sol`:
- Line 9: The import of EscrowDepositEngine is unused in the test
(EscrowDepositEngine) — remove the unused import statement from
ChannelHub_finalizeEscrowDeposit.t.sol to clean up the file; locate the line
importing EscrowDepositEngine and delete it so the test compiles and reads
without the unnecessary symbol.

In `@contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowWithdrawal.t.sol`:
- Line 9: Remove the unused import of EscrowWithdrawalEngine from the test file
by deleting the import line that references EscrowWithdrawalEngine; the symbol
EscrowWithdrawalEngine is not referenced anywhere in
ChannelHub_finalizeEscrowWithdrawal tests, so removing that unused import cleans
up the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c0514c21-8991-4afc-8065-94093bc7369c

📥 Commits

Reviewing files that changed from the base of the PR and between 0cbb9f8 and e245365.

📒 Files selected for processing (3)
  • contracts/src/ChannelHub.sol
  • contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowDeposit.t.sol
  • contracts/test/ChannelHub_units/ChannelHub_finalizeEscrowWithdrawal.t.sol

@nksazonov nksazonov merged commit 36c9832 into fix/audit-findings-final May 13, 2026
3 checks passed
@nksazonov nksazonov deleted the fix/mf-l06 branch May 13, 2026 14:48
nksazonov pushed a commit that referenced this pull request May 13, 2026
- MF-L01: fix(contracts/ChannelHub): cap ERC20 transfer returndata copy to 32 bytes (#726)                                                
- MF-H01: fix(nitronode): paginate get_last_key_states endpoints (#724)                                                                   
- MF-I01-I02: fix(contracts): address security audit findings I-01 and I-02 (#728)                                                        
- MF-C01: rpc: cap inbound WebSocket frame size and rate-limit per connection (#723)                                                      
- MF-L02: docs(protocol): qualify enforcement guarantee for intent-specific execution paths (#737)                                        
- MF-L02-I03-I04_I05: fix(contracts): add more Node trust assumptions and requirements (#738)                                             
- MF-M01: backfill state user_sig from on-chain events (#731)               
- MF-M02: fix(rpc): release Serve wait group on processSink overflow (#732)                                                               
- Fix SDK acknowledgement before home channel creation (#734)               
- MF-I06: fix(nitronode): gate escrow transitions on home channel onchain materialization (#730)                                          
- MF-M05: fix(nitronode): enforce TLS by default for Postgres (#733)                                                                      
- MF-M07: Unblock receiver states after finalized escrow operations (#735)
- MF-M04: feat: provide tooling for and enhance docs on ValidatorRegistered event (#744)                                                  
- MF-L04: fix(contracts): reject redundant native value (#741)              
- MF-H02: bind session key registration to a single owner per kind (#739)                                                                 
- MF-I07: fix(contracts): enforce max challenge duration (#752)             
- MF-M08: fix(rpc): replace Origin label with application_id on connection gauge (#745)                                                   
- MF-C02: fix(core): add ChannelStatusClosing to gate post-finalize state transitions (#746)                                              
- MF-L06: fix(contracts): clear stale challengeExpireAt on cooperative escrow finalization (#754)
- MF-I08: docs: document ChannelClosed event orientation ambiguity during abandoned migration (#755)                                      
- MF-M09: fix(nitronode): auto-challenge home channel on withheld escrow finalize (#753)
- MF-L09: fix(nitronode): validate parsed app session nonce (#751)                                                                        
- MF-L05: docs(contracts): document informational events not guaranteed to emit (#756)
- MF-L08: fix(nitronode/api): default get_last_key_states to active-only with include_inactive opt-in (#749)                              
- MF-L10: fix: emit escrowIds array in EscrowDepositsPurged event and handle it in Nitronode (#757)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants