fix(tempo): allow zero-amount session close for untouched channels (#291)#294
fix(tempo): allow zero-amount session close for untouched channels (#291)#294EfeDurmaz16 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39717df280
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let is_untouched_zero_close = cumulative_amount == 0 && spent == 0 && on_chain_settled == 0; | ||
| if !is_untouched_zero_close && cumulative_amount <= on_chain_settled { |
There was a problem hiding this comment.
Reject zero-close for unfunded on-chain channels
When the close request resolves to an escrow/channel whose getChannel returns the zero/default state, this carve-out now accepts 0/0/0 before any funded-channel check. handle_open treats on_chain.deposit == 0 as channel not funded on-chain, and before this change the same zero state was rejected by cumulative_amount <= on_chain_settled; with the default close_signer: None, handle_close can now mark the local channel finalized and return success even though no on-chain channel was closed. Please keep rejecting on_chain_deposit == 0 (or require a positive deposit in the carve-out).
Useful? React with 👍 / 👎.
39717df to
47076ac
Compare
…empoxyz#291) `validate_close_amount` rejected a cooperative close at amount 0 on a funded but unspent channel, stranding the deposit with no recovery besides the forced-close grace period (tempoxyz#291). Align the check with the current mppx session validator (handleCloseCredential in tempo/session/server/CredentialVerification): require cumulative >= spent and cumulative >= on-chain settled, bound the captured amount max(spent, settled) by the on-chain deposit, and reject a zero on-chain deposit outright (an unfunded or fully-settled channel is not a valid close target, matching how handle_open rejects deposit == 0). A funded untouched channel (deposit > 0, settled == 0, spent == 0) now closes at 0 and refunds the deposit. A close below a nonzero settled amount stays rejected, preserving the strict anti-replay property from CVE-2026-34209 / GHSA-mv9j-8jvg-j8mr, now expressed as cumulative >= settled. A zero-deposit close is rejected, so a None close_signer can no longer finalize a channel that was never funded on-chain.
47076ac to
46ab8a8
Compare
|
@brendanjryan could I get a review here when you have a chance? |
What this fixes
Closes #291.
validate_close_amountrejected a cooperative close at amount0on a funded but unspent channel, so an opened-but-unspent session left the deposit stranded with no recovery besides the forced-close grace period.The change
validate_close_amountis aligned with the current mppx session validator (handleCloseCredentialintempo/session/server/CredentialVerification.ts), replacing the older single-carve-out shape. The gates, in order:deposit > 0,settled == 0,spent == 0,cumulative == 0) now closes at0and refunds the deposit. This is the Session close fails on an unspent channel because validate_close_amount rejects a zero-amount close #291 case, and it falls out of the gates naturally rather than needing a special-case boolean.cumulative == settledis now valid (it captures the settled amount); a close below a nonzero settled stays rejected.max(spent, settled), matching mppx, not the raw voucher cumulative.Why it stays safe
A close below a nonzero on-chain settled amount remains rejected, preserving the strict anti-replay property behind CVE-2026-34209 / GHSA-mv9j-8jvg-j8mr (capture-replay, CWE-294), now expressed as
cumulative >= settled. The newon_chain_deposit == 0rejection means a zero-deposit channel is never a valid close target: with aNoneclose_signer,handle_closecan no longer mark a never-funded channel finalized and return success. This is the same guardhandle_openapplies when it treatsdeposit == 0as "not funded on-chain".Tests
validate_close_amounthas 15 unit tests pinning the contract, including:cumulative == settledallowed,cumulative < settledrejected (the CVE regression, now>=);cargo fmt --check,cargo clippy -- -D warnings, the tempo/server/client test suite (996 passing), and the CI ast-grep gate all pass locally.Reviewer note (Claude Fable 5)
Claude Fable 5 reviewed this PR. The change is one private function, so the thing to read is
validate_close_amountand the close-test block insession_method.rs. Worth your attention:is_untouched_zero_closespecial case modelled on the legacySession.ts. That has been replaced: the validator now followstempo/session/server/CredentialVerification.tsline for line (the>= spent,deposit == 0guard,>= settled, andcapture = max(spent, settled)bound). The zero-untouched-funded case is a consequence of those gates, not a hand-coded exception.0/0/0before any funded-channel check, letting aNoneclose_signer finalize an unfunded channel. Theon_chain_deposit == 0rejection now propagates through?before the store marks the channel closing/finalized, so that path returns an error. Covered bytest_close_rejects_zero_deposit_untouchedandtest_close_rejects_zero_deposit_with_value.cumulative == settled; a close below a nonzero settled is still rejected, which is the property CVE-2026-34209 turns on. No API, feature, or dependency surface moved.