Repository navigation
Conversation
0dde3eb to
145330c
Compare
jvsena42
left a comment
There was a problem hiding this comment.
Requesting changes: one HIGH and one MEDIUM on fund limits, plus one LOW. All three are inline with failing tests.
Allowances pay a contact without a per-payment confirmation, so the bar here is that no request can take more than what the user saw and approved. I reviewed this as a funds change.
Checked and clean:
- Concurrent or retried requests: the SDK rechecks capacity atomically at reservation.
- Clock manipulation: the trusted-time watermark only moves forward.
- Ended allowances: an ended or revoked allowance can't pay; lifecycle is checked at accept, reserve and begin.
- Lightning invoice amount: must equal the request, or be amountless.
- Crash recovery:
SENDINGis durable before an automatic send, and unjournaled attempts stay UNKNOWN with their capacity still counted. - Wipe: state is keyed per identity and wiped.
- Migration: v2.5.1 has no allowance state.
- Gating: allowances ride the Paykit UI flag, on by default, so they're user-facing from the next release.
Already tracked, not repeated: #1583 (journal backup), #1584 (durable totals), #1585 (released on-chain attempts).
Not covered by any of the 7 new journeys: a contact-proposed allowance, and on-chain auto-pay fees.
Perf-relevant for the device gate later: every Paykit refresh now runs paykitAllowanceRepo.refresh() and beginPaymentSubmission() even with no allowances. Every manual request payment adds ensureReconciled, reserveManualPayment and a keychain journal write before sending.
Device gate: not run (HIGH/MEDIUM open).
ben-kaufman
left a comment
There was a problem hiding this comment.
Please see the inline comments.
|
Pushed deada17, then dadaeb9 (master merged): fixes from the reviews by @jvsena42 and @ben-kaufman on e1e5d4e.
Unit tests and detekt pass locally. |
|
Pushed 6e36ce4: automatic allowance payments now go over Lightning only, following @jvsena42's fee findings here and on synonymdev/bitkit-ios#799.
Unit tests and detekt pass locally. |
|
Pushed 9f7ca64: the payee's allowance row now shows the per-payment limit in the payer's short form ( |
jvsena42
left a comment
There was a problem hiding this comment.
New finding after the fixes in deada17, 6e36ce4 and 9f7ca64.
The HIGH (uncapped proposal) and the LOW (SUBMITTED→FAILED) are fixed: from() drops payer terms without both ceilings and fits fails closed, and manual on-chain attempts journal at SENDING. On-chain auto-pay is gone, so the on-chain half of the MEDIUM is fixed too.
One MEDIUM remains, inline: Lightning routing fees on automatic payments are uncapped and uncounted.
Residual LOW, display only, not blocking: isBounded checks the raw strings, so a max like 0.000000001 that parses to null still shows "—" and can be accepted. fits never pays on it.
Device gate: not run (MEDIUM open).
|
@jvsena42 your re-review was on 9f7ca64, before the routing-fee fix: f20cb57 caps the total routing fee of automatic sends at 1% of the request (thread, resolved), matching synonymdev/bitkit-ios#799. Pushed a8d73c1 for the residual LOW: terms this wallet would pay under now need a per-payment and monthly limit that actually parse to sats (and a monthly anchor), so a dust max that reads as "—" is dropped and can no longer be accepted; covered in |
jvsena42
left a comment
There was a problem hiding this comment.
Code scan at a8d73c1: no HIGH/MEDIUM open. The routing-fee MEDIUM is fixed (thread answered). The new parse gate in PaykitAllowance.kt:105-111 only drops records, so it fails safe.
Device gate (partial): not run yet. Pending: the 7 journeys/allowances/ journeys cross-device with synonymdev/bitkit-ios#799, plus the perf check I noted earlier (allowance refresh on every Paykit refresh, and the extra writes before each manual request payment, measured against master). Approval follows the device run.
# Conflicts: # app/src/main/java/to/bitkit/repositories/PaykitPaymentProofRepo.kt # app/src/main/java/to/bitkit/repositories/PrivatePaykitRepo.kt # app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
|
Pushed 5b0bfe8.
detekt, Android Lint, the Kotlin compiles with warnings as errors and the unit tests pass locally. |
# Conflicts: # app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
|
Pushed f561e63: allowance accounting is now kept per contact, so another contact's payments under the same allowance id never fill this contact's cap (a false Limit Reached) or show in its paid-so-far total. The executor picks the allowance whose contact matches the request, capacity attempts carry the contact from the occurrence and |
# Conflicts: # app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
jvsena42
left a comment
There was a problem hiding this comment.
Delta at e7b4477: no issues found. Reviewed as a funds change. The merges da0719c, a04b91a and e7b4477 resolve conflicts cleanly. finishManualAllowancePayment is kept after the on-chain proof call, and master's direct-confirm path still goes through beginManualAllowancePayment.
Two issues found on the iOS twin (synonymdev/bitkit-ios#799) were also present here, and both are fixed:
- Different contacts sharing an allowance id were merged into one entry, so Accept answered every member and the PIN gate followed the primary's role. Fixed in
e7c076a:PaykitAllowance.keyisnormalized(counterparty)/allowanceId;group(containing, counterparty)matches the contact;publishgroups by group id, else by key, sorespondand the PIN gate act on one contact's records;- presented ids use the key.
- Automatic-payment accounting was keyed by the bare id. Fixed in
f561e63: the executor lookup matches the contact,Attemptcarries it andusedSatsfilters on it, andautoPaidSatsis grouped by key.
The reproduction tests from those reviews are in PaykitAllowanceRepoTest / PaykitAllowanceExecutorTest unchanged. v2.5.1 has no allowance store, so the key change needs no migration. My earlier threads are all resolved, with fixes at head.
Device gate (partial): e7b4477 — not run yet. Pending: the 7 journeys/allowances/ journeys, cross-device with the iOS twin in both directions. Device results will follow in a separate review.
Closes #1352
Twin: synonymdev/bitkit-ios#799
Refs:
This PR adds allowances: a payer sets a per-payment and a monthly limit for a Paykit contact, and that contact's requests within the limits are paid without asking.
Description
It builds against Paykit
0.1.0, the version master pins. That release includes the allowance stack (pubky/paykit-rs#158 to #161) and the shared identity runtime, where one Encrypted Link serves a contact's identity and the allowance is bound to the two identities instead of to a receiver folder.Out of Scope
PaykitAllowanceExecutor.ktrecovery: a send stopped by a process kill stays at SENDING, so its request neither pays nor asks; follow-upPaykitAllowanceExecutor.ktrecovery: it settles every open attempt in the identity-wide ledger, which assumes Bitkit is the only app paying from it; follow-up before another app shares the ledgerLightningRepo.ktsync(): a cancellation is recorded as a sync error and starts the retry loop instead of being rethrown; follow-upPaykitAllowanceStore.ktbackup: the allowance journal and automatic-payment history are not in the wallet backup, so a restore loses them; tracked in bug: allowance history and open attempts are lost after a wallet restore #1583 (thread)PaykitAllowanceExecutor.ktjournal trim: totals and Auto-paid tags are rebuilt from the last 200 journal entries; tracked in bug: older auto-paid totals and tags disappear after 200 allowance payments #1584 (thread)PaykitAllowanceExecutor.kton-chain auto-pay: an on-chain request stays manual until fees count against the allowance limits; tracked in feat: pay allowance requests on-chain once fees count against the limits #1592 (thread)PaykitAllowanceExecutor.ktrecovery: a manual on-chain attempt whose send was released stays unknown in the ledger, since a release leaves no durable record to read; tracked in bug: a released on-chain allowance payment keeps holding allowance capacity #1585 (thread)Design
Figma "Bitkit - Experimental New": Allowances empty state, Set Allowance, Allowances list. The receiver's review sheet and the detail sheet have no frame and follow the drawn film. Deliberate differences: the first tab stays "Overview", the list row's right column reads the monthly limit, and the copy typos are fixed.
Preview
side-by-side.mp4
QA Notes
Journeys
set-and-accept.xml— the offer opens on the payee by itself and both rows turn Activeauto-pay-under-limit.xml— a $2 request pays itself with only a Payment Executed notificationabove-limit-asks.xml— a $20 request arrives as an ordinary Payment Requestmonthly-cap-reached.xml— the third $4 request on a $10 cap raises Limit Reached and asksend-stops-auto-pay.xml— after either side ends it, the next request asksrestart-never-pays-twice.xml— a kill right after the hand-off never leads to a second paymentpin-gates-allowance-changes.xml— with a PIN required for payments, saving and ending ask for it, a cancel changes nothing, and the payee's accept and automatic payments never askAutomated Checks
PaykitAllowanceTest.kt— limits, terms, capacity and month anchoring of the allowance model, and status and capacity on real time while the subscription clock offset is setPaykitAllowanceExecutorTest.kt— reserve, hand-off, outcome recording and restart recovery of automatic payments, the execution claim before the automatic acceptance, and admission on real time while the subscription clock offset is set; a request in another asset or with only an on-chain endpoint stays manual before any acceptance; an uncertain Lightning send stays with the node; an unsaved journal stage stops the attempt before any send and gives back the consumed payment list; an on-chain attempt recovers as paid only on the wallet's positive evidencePaykitAllowancePayerTest.kt— a manual on-chain allowance payment counts as paid only when the wallet's send attempt for that request was accepted or observed, or a verified proof of its transaction is heldPaykitAllowanceRepoTest.kt— admission decisions: automatic, manual, deferred and limit reached, one grant per contact identity, and coverage on real time while the subscription clock offset is setPrivatePaykitAllowancePaymentTest.kt— resolving the payee's request-bound private endpoint for an automatic paymentAllowancesViewModelTest.kt— list, set, review and detail state; the payer's save and end wait for the PIN check when payments require it, a cancelled check changes nothing, and the payee's answers and the payer's decline never askPaykitPaymentRequestRepoTest.kt— an automatic acceptance saves this install as the owner and drops it on a definite failureAppViewModelSendFlowTest.kt— the send flow with the allowance repo wired inPaykitPaymentProofRepoTest.kt— payment proofs carry the allowance id and the paying appPaykitPaymentStateBackupTest.kt— a pending proof keeps its allowance id through backup and restoreset-and-accept.xml,auto-pay-under-limit.xml,above-limit-asks.xmlandend-stops-auto-pay.xmlon two Android 15 emulators with fresh wallets against the Blocktank staging regtest LSP and the staging homegate, on0b1f2664a, before the sync with master — all four pass; a $2 request paid itself over Lightning, a $20 request asked, and after an end the next request asked. Staging was slow: the automatic payment arrived about 5 minutes after the request, and two payment request sends timed out once before succeeding on retry. The review sheet opens on its own only when no intro sheet is on top, and the payer row of an ended allowance reads "Ended" whereend-stops-auto-pay.xmlexpects "Ended · $0.00 paid automatically"; the journey text is not updatedmonthly-cap-reached.xmlandrestart-never-pays-twice.xml; the cap and the restart rules are covered byPaykitAllowanceExecutorTest.kt; not run yet:pin-gates-allowance-changes.xml, whose gate logic is covered byAllowancesViewModelTest.ktbut whose PIN pad over the sheet is not checked on a device