Skip to content

Commit da2a702

Browse files
gakonstampcode-com
andcommitted
fix: cooperative close uses actual spent amount instead of voucher ceiling
On cooperative close, the server required the close voucher's cumulativeAmount to be >= highest_voucher_amount (the pre-authorized reservation). This meant clients were charged the full reservation even when actual usage was much lower (e.g., $4 for a SELECT 1 query). This ports the fix from mppx (wevm/mppx#196) and wallet (tempoxyz/wallet#349): 1. The minimum close amount is now max(channel.spent, on_chain.settled) instead of highest_voucher_amount, allowing clients to close at actual usage cost. 2. The store update only overwrites highest_voucher_amount when the close amount is actually higher, preserving the original voucher for settlement if the close is at a lower (actual spent) amount. Amp-Thread-ID: https://ampcode.com/threads/T-019d5503-2e85-70ff-81ed-b5093c920fd5 Co-authored-by: Amp <amp@ampcode.com>
1 parent dfea6bb commit da2a702

File tree

1 file changed

+26
-10
lines changed

1 file changed

+26
-10
lines changed

src/protocol/methods/tempo/session_method.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -821,12 +821,6 @@ where
821821
.parse()
822822
.map_err(|_| VerificationError::invalid_payload("invalid cumulativeAmount"))?;
823823

824-
if cumulative_amount < channel.highest_voucher_amount {
825-
return Err(VerificationError::new(
826-
"close voucher amount must be >= highest accepted voucher",
827-
));
828-
}
829-
830824
let channel_id_b256 = Self::parse_channel_id(channel_id_str)?;
831825
let escrow = self.resolve_escrow(details)?;
832826
let chain_id = self.resolve_chain_id(details);
@@ -839,9 +833,22 @@ where
839833
"channel is finalized on-chain",
840834
));
841835
}
842-
if cumulative_amount < on_chain.settled {
836+
837+
// Allow closing at the actual spent amount rather than the highest
838+
// pre-authorized voucher. This mirrors the fix in mppx (wevm/mppx#196)
839+
// and wallet (tempoxyz/wallet#349) so that clients are only charged for
840+
// actual usage, not the full reservation.
841+
let min_close_amount = if channel.spent > on_chain.settled {
842+
channel.spent
843+
} else {
844+
on_chain.settled
845+
};
846+
if cumulative_amount < min_close_amount {
843847
return Err(VerificationError::new(
844-
"close voucher cumulativeAmount is below on-chain settled amount",
848+
&format!(
849+
"close voucher amount must be >= {} (max of spent and on-chain settled)",
850+
min_close_amount,
851+
),
845852
));
846853
}
847854
if cumulative_amount > on_chain.deposit {
@@ -948,10 +955,19 @@ where
948955
Some(s) => s,
949956
None => return Ok(None),
950957
};
958+
let update_voucher = cumulative_amount > state.highest_voucher_amount;
951959
Ok(Some(ChannelState {
952960
deposit: on_chain.deposit,
953-
highest_voucher_amount: cumulative_amount,
954-
highest_voucher_signature: Some(sig_bytes),
961+
highest_voucher_amount: if update_voucher {
962+
cumulative_amount
963+
} else {
964+
state.highest_voucher_amount
965+
},
966+
highest_voucher_signature: if update_voucher {
967+
Some(sig_bytes)
968+
} else {
969+
state.highest_voucher_signature
970+
},
955971
finalized: true,
956972
..state
957973
}))

0 commit comments

Comments
 (0)