View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012114 | Taler | wallet-core | public | 2026-11-11 03:25 | 2026-11-11 04:16 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012114: initiatePeerPushDebit with a zero amount throws (7001, stack trace) instead of answering 7048 like checkPeerPushDebitV2 -- the positive-amount guard has one caller | ||||
| Description | `initiatePeerPushDebit` with an amount of zero does not return an error -- it throws. The request reaches coin selection, which raises a plain `Error`, and the caller gets WALLET_UNEXPECTED_EXCEPTION (7001) with a JavaScript stack trace: initiatePeerPushDebit{"restrictScope":{...KUDOS...}, "partialContractTerms":{"summary":"harness", "purse_expiration":{"t_s":1794370160},"amount":"KUDOS:0"}} finished wallet core request initiatePeerPushDebit with error: { "code": 7001, "hint": "unexpected exception (message: coin selection for peer-to-peer payment with amount of zero not supported)", "stack": " at selectPeerCoinsInTx (<compiled_js>:55227:20)\n ..." } The sibling request already handles this case properly. Ten seconds earlier in the same session, same amount: checkPeerPushDebitV2{"amount":"KUDOS:0",...} -> { "code": 7048, "hint": "peer push payment amount must be positive", "parameter": "amount" } Cause ----- packages/taler-wallet-core/src/pay-peer-push-debit.ts has the guard as a function of its own, export function requirePositivePeerPushDebitAmount(amount: AmountJson): void and on master (checked 2026-11-11) it has exactly one caller, line 628, in `checkPeerPushDebitV2`. `initiatePeerPushDebit` (line 1471) parses the amount and goes straight on, so zero arrives at coinSelection.ts:2535, if (Amounts.isZero(instructedAmount)) { // Other parts of the code assume that we have at least // one coin to spend. throw new Error("coin selection for peer-to-peer payment with amount of zero not supported"); which is an assertion, not an API answer. A client that skips the check, or ignores its result, turns a bad parameter into an "internal error". Fix (patch attached, initiate-peer-push-zero-amount.diff): call `requirePositivePeerPushDebitAmount(instructedAmount)` at the top of `initiatePeerPushDebit`, so both entry points give the same 7048. Four lines. The diff is against master and has not been compiled or run by me; it calls a function already defined and exported in the same file. How it was reached (iOS wallet) ------------------------------- Found by a UI fuzzing walk (c2h-21-e00044, 2026-11-11, no fault injected), in five taps from a wallet holding KUDOS 25: Actions -> Send -> Next (amount left at 0.00) -> type a subject -> "Send KUDOS 0.00 now" -> "Internal core error" + the JSON above, stack trace included Two things on the iOS side let it through, which may deserve their own decision rather than a fix here: * `AmountInputV.swift:71` -- `let isZero = amountToTransfer.isZero && !developerMode` -- so with Developer Mode on, Next is enabled at 0.00. That looks deliberate. With Developer Mode off the path is closed. * the subject screen then offers "Send KUDOS 0.00 now" although `checkPeerPushDebitV2` has just failed with 7048; the failed check does not disable the button. So the user-visible defect needs Developer Mode; the wallet-core defect does not need the iOS app at all. Severity: minor. No funds move and nothing is persisted (the request fails in 18 ms, before a transaction exists). The cost is an unexpected-exception report, with a stack trace, for what is an ordinary invalid parameter. | ||||
| Tags | No tags attached. | ||||
| Attached Files | 12114-initiate-peer-push-zero-amount.diff (699 bytes)
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts 2026-11-11 04:14:57.378485375 +0100
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts 2026-11-11 04:14:57.382485348 +0100
@@ -1475,6 +1475,10 @@
const instructedAmount = Amounts.parseOrThrow(
req.partialContractTerms.amount,
);
+ // Same guard as checkPeerPushDebitV2: without it a zero amount reaches
+ // selectPeerCoinsInTx, which throws a plain Error and surfaces as
+ // WALLET_UNEXPECTED_EXCEPTION with a stack trace.
+ requirePositivePeerPushDebitAmount(instructedAmount);
const currency = Amounts.currencyOf(instructedAmount);
const requestedExpiration = req.partialContractTerms.purse_expiration;
| ||||
| Reproduced as a deterministic UI test (harness scenario send-zero-devmode, 2026-11-11, no fault): fund with demo cash, Actions -> Send -> Next -> subject -> "Send KUDOS 0.00 now". Both log oracles held -- initiatePeerPushDebit was sent with KUDOS:0 and answered with the unexpected exception. Screenshot of the result attached (12114-send-zero-internal-core-error.png): the stack trace is on the user's screen. | |