Repository navigation
Conversation
…-broadcast-outcome
There was a problem hiding this comment.
detekt found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Regtest APKDownload bitkit-dev-debug universal APK (expires in 30 days). |
This comment has been minimized.
This comment has been minimized.
|
I pushed 6966111 with the scoped review fixes: original-send follow-up, exact-observation acknowledgement, running-process Accepted persistence repair, verified Shop proof delivery after guard replacement, and original transfer accounting. The visibility component test now states its actual coverage. The source batch passed 71 focused tests. Process loss before Accepted is durable still leaves the attempt guarded. This PR remains draft while corrected node artifacts, consumer validation and current native journeys are pending. |
|
I pushed ae0be37 with the hardware Shop proof correction. A Core txid is retained only as the original lookup candidate; proof and Success require fresh observation of that exact outgoing transaction in the original wallet. Identity/request/wallet context is captured, preparation failures block dispatch, and pending proof work cannot start another payment. The frozen batch passed 58 focused tests, including the guard, wrong-identity, inbound/mismatched lookup and original-context regressions. The hardware journey spec is parsed but unrun; physical hardware and native fault fixtures remain explicit QA gaps. This remains draft while both apps validate the published rc68 dependency and current native fixed/Max journeys. Prior rc67 media is labelled historical. |
|
I pushed 9de645a with the final hardware Shop guards and the production rc68 dependency pin. A candidate-save failure or a Core exception with no returned txid keeps the started request protected after dismissal/reopening. Shop Sent activity now requires fresh exact outgoing observation and durable local follow-up in the original wallet. The affected run passed 95 tests and app/test APK builds. The actual installed APK embeds the published rc68 native bytes; funded fixed 1,000-sat and Max 98,749-sat native sends passed, and both exact UI transaction IDs were independently observed on the backend. Current Preview replaces the historical accepted-send media. Controlled native refusal/response-loss and hardware Shop execution remain unrun; their QA entries remain explicit. No recovery scope was added. |
jvsena42
left a comment
There was a problem hiding this comment.
Two HIGH, one MEDIUM and two LOW inline. I reviewed this as a funds change. The refusal lockout and the legacy-proof gap are shared with synonymdev/bitkit-ios#844.
Checked and clean:
- No failure is reported for a tx that broadcast:
NodeException/NodeNotSetuprelease the guard only before dispatch in rc.68, and everything else keeps the guard. - Success is shown only on
Accepted. The replays at :4018/:4172 are for the same request with its original txid. HW Shop needs a fresh exact observation. admitblocks the samerequestId/orderId, and Lightning proof association is refused while an on-chain attempt exists.- Pending is persisted before dispatch.
- Old backups decode with the new field defaulting to false, and newer backups decode on older builds via
ignoreUnknownKeys. - The Keychain change is storage-only, with no seed material.
- The rc.66 → rc.68 bump carries the broadcast-result API.
Pre-existing: RBF/CPFP remain fire-and-forget.
Also LOW: the new toasts at TransferViewModel.kt:394/436 are hardcoded English.
|
Two independent reviews. needs changing before merge
worth doing, does not block
nits
|
jvsena42
left a comment
There was a problem hiding this comment.
Approving at 45358b6 with two LOWs open, to fix here or in a follow-up.
Re-reviewed at 45358b6. The two MEDIUMs from my last review are fixed, and I have no HIGH or MEDIUM left. Two LOWs: one as a reply on the fee-rate thread (the same no-op Retry when a tag is added), one inline. Both are reproduced by unit tests.
Fixed and checked:
- Refusal string: the parser now unwraps
Broadcast failed: Electrum server error: {json}and a nestedsendrawtransaction RPC error, reads only themessagemember and matches an allow-list. Unknown, malformed, non-string and look-alike messages stay guarded, so an uncertain broadcast remains pending. - Deadline re-lock: the refusal is kept as a navigation-only hint, persisted, restored after a restart, cleared at the next dispatch marker and excluded from backup. It never touches the durable guard, and no path signs again on Retry.
- A send released before the dispatch marker is reported as not dispatched; one whose marker was written stays pending. Shop attempts are never released there.
- Send-all success carries the recorded recipient amount.
- The 999 sat/vB point raised in the other review is addressed in the latest commit: the ceiling now applies to recovery only.
Read only for the commit after 30504cb; the reproductions ran at 30504cb. CI was still running at this head.
|
Pushed a11b479 to address the two remaining hardware retry findings. Changing display tags now retries the retained signed payment; a failure before the dispatch boundary keeps the prior refusal navigation state. A new dispatch still restores navigation protection, and the original payment guard remains intact. Focused hardware ViewModel regressions and detekt passed. Device refusal acceptance remains unrun; hosted CI is pending for this head. |
jvsena42
left a comment
There was a problem hiding this comment.
Approving at a11b479: both LOWs from my last review are fixed and no thread of mine is open.
- Tags: a retained attempted Shop payment now matches when only the displayed fee rate or the tags differ, so Retry rebroadcasts the original signed bytes after a tag is added. A changed payment identity or recipient is still rejected, and the new test pins that.
- Pre-dispatch failure: the failure path only sets the refusal hint and never clears it, and the lock is derived from the retained payment's own state. A Retry that fails at the dispatch marker or in device setup leaves the refused sheet dismissable; a real re-dispatch still clears the hint at the boundary.
Read only for this commit; it matches the fixes I had run with HwSendViewModelTest passing. CI was still running at this head.
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Follow-up review of changes since 30504cb, from the completed baseline review, at a11b479. This pass covered the two later commits: retaining confirmed initial presets above 999 sat/vB, and preserving hardware Shop retry navigation when tags change or a retry fails before dispatch. Unchanged baseline coverage is inherited.
No new actionable code findings.
The earlier retain rejection is fixed on this revision. Initial receipt retention accepts a positive rate through the UInt32 maximum. Recovery preparation and retention stay capped at 999 sat/vB. That matches prepareOnchainSend and the iOS split checked at ace6252 on bitkit-ios#844.
A Shop payment that has already attempted broadcast rebroadcasts the original signed transaction when only the displayed fee or tags change. Payer, request, recipient, amount, and deadline still have to match. A retry that fails before dispatch leaves the refused sheet dismissable. Entering dispatch clears the refusal hint on the stored proof. On iOS at ace6252, a changed displayed fee remains a different in-progress operation; the baseline review already recorded that difference.
Validation: the new tests in LightningServiceTest, OnchainSendCoordinatorTest, and HwSendViewModelTest were inspected and not executed in this review. On this revision, build, detekt, and lint succeeded. The author reports focused local regressions passed.
Device testing: not performed in this review. Funded checks of presets above 999 sat/vB, and hardware refusal dismissal, remain unrun.
Ready for device testing.
jvsena42
left a comment
There was a problem hiding this comment.
Device gate run at a11b479, and it changes my review: one MEDIUM, as a reply on the refusal thread. A real server refusal is not recognised, because this Electrum server wraps it differently from what the parser expects, so the sheet stays locked. My earlier approval at this head was given on the code read and the unit tests.
Device gate: a11b479 — Pixel 9 emulator with the Trezor emulator over the Bridge, a second emulator as the linked issuer.
shop-onchain-proof.xml, normal path: passed.
- A 100,000 sat request opened on confirmation with My Trezor selected. The sign screen and the Trezor showed the issuer's address, 0.001 and a 141 sat fee.
- One approval on the device ended on
SendSuccess. The backend has one transaction,04fac3d5…a44a, spending a hardware-account input and paying exactly 100,000 sats to the issuer's address. The issuer shows it received, the proof left the pending queue, and the request left the payer's pending list. - A second request for 50,000 sats, paid after two attempts that ended on the device (a Bridge timeout and a cancel), also produced one transaction and no duplicate.
Refusal: failed, see the thread reply.
- Retry after the refusal did not prompt the Trezor again, so the original signed bytes were reused.
|
Pushed 6558827 to recognize the reported Electrum JSON-string and coded RPC -25/-26 refusals. Complete replacement-fee refusals now allow leaving the hardware sheet while preserving the original signed payment and durable guard; unknown or malformed responses remain guarded. Focused parser and retained-payment navigation regressions passed. The reported restart case where the retained request appears Accepted without a Retry action still needs its original-payment reopen route fixed, so I am keeping the device finding and changes request open. Current-head CI/review and funded Shop acceptance remain pending. |
|
Pushed 37e4405 to restore the original refused hardware payment after restart. The exact request remains accessible and reopening selects its original wallet, recipient, amount, fee and signed transaction without preparing another payment or resolving a replacement merchant endpoint. Explicit authorization and the durable wallet/payment guards stay enforced. Focused reload/reopen, receipt-retention and authenticated retry regressions passed; introduced-line static findings are cleared. This completes the reported parser/restart-route code fixes. Actual hardware restart, funded Shop and fresh-device wallet/VSS acceptance remain unrun; current-head CI and human review are pending. |
The reported refusal parser and restart reopen route are addressed in65588274c and37e4405b8 with focused regressions; the device finding thread is resolved. Dismissing the superseded verdict; current-head human review and actual device acceptance remain pending.
jvsena42
left a comment
There was a problem hiding this comment.
Re-reviewed at 37e4405, with a device run on a Pixel 9 emulator and the Trezor emulator. One MEDIUM and two LOWs, all inline. The MEDIUM is reproduced by a unit test and on the device; the LOWs on the device.
Device gate: 37e4405 — failed on the reopen step.
- Refusal, passed. A 30,000 sat request was signed, the hardware-account coin was spent by another wallet first, and the server answered
sendrawtransaction RPC error -26: insufficient fee, rejecting replacement …. The button turned to Retry, Back returned to confirmation and swipe-down closed the sheet. The parser fix works with the real server string. - Restart, passed. After a mined block and a force-stop the request is listed again with Pay and Dismiss, and Pay opens the confirmation with the original wallet and fee.
- Retry after reopen, failed. Swipe To Pay shows the mismatch toast and never reaches the sign screen. See the inline comment.
- Normal path, passed at
a11b479, not re-run at this head.
Checked by reading and clean:
- Parser: still an allow-list. Both real server strings match; other codes, longer messages, malformed payloads and connectivity errors stay guarded.
- Reopen uses the address, amount, fee and wallet from the retained proof, bound to the same identity and request. No path signs again, and a Lightning or software payment cannot reach it.
- Back and swipe-down on the reopened sheet leave the proof and the request untouched.
|
Pushed b33dc94 to address the retained hardware request findings. Reopening now transfers the original request context before showing confirmation, so Swipe To Pay reaches the hardware retry route. The original signed fee and wallet cannot be changed, and Dismiss is disabled while that payment remains in flight. Focused JVM regressions and changed-line static checks cover the corrected route and controls. The original signed bytes, payer, request, amount and authenticated retry guard are preserved. Device retry after reopening, funded Shop checkout and fresh-device wallet/VSS acceptance remain unrun; current-head CI and human review remain pending. |
Addressed the three retained-payment findings in b33dc94: original request context survives reopening through confirmation, signed fee/wallet controls are fixed, and in-flight Dismiss is disabled. Focused regressions verified; dismissing this superseded changes request for review of the updated head.
jvsena42
left a comment
There was a problem hiding this comment.
Approving at b33dc94: the three findings from my last review are fixed, confirmed on the device, and I have no new finding in the commit.
Device gate: b33dc94 — passed, Pixel 9 emulator with the Trezor emulator. The refused 30,000 sat payment retained from the run at 37e4405 was still on the device.
- Payment Requests lists it with Pay enabled and Dismiss greyed out.
- Pay reopens the confirmation with My Trezor and
Custom (₿ 141). Tapping the fee row and the funding-source button changes nothing. - Swipe To Pay now reaches the sign screen with no mismatch toast.
- Retry did not prompt the Trezor, so the original bytes were reused. The server answered
sendrawtransaction RPC error -25: bad-txns-inputs-missingorspent, and Back and system Back left the sheet. Nothing reached the issuer's address.
Read of the commit, clean: the preparation is handed over before the sheet is shown, so the request context survives; the reopened flow cannot reach a new preparation or a second signature; the retained-confirmation flag is reset on every normal Send entry; Dismiss is disabled only for requests with a payment in flight.
a11b479) and a fresh refusal (passed at 37e4405).
For a follow-up, not a finding here: iOS now reopens a refused hardware payment for a subscription period (synonymdev/bitkit-ios#844), while on Android such a payment stays guarded and unreachable after a restart.
|
Published feb7b16: merged master b2d116d and resolved the contact-sync test conflict, preserving the incoming sign-in/membership checks and the existing retained-payment regressions. The merge inherits the reviewed Paykit Local verification covers the affected contact/link/reservation, payment proof/restore, authenticated hardware retry, migration and wallet-start behavior against the published SDK artifacts. The merged source introduced no conflict-resolution static finding. The PR description now distinguishes this integration from historical validation. Current-head CI and human review remain required. Funded Shop checkout, fresh-device wallet/VSS restore and physical hardware acceptance remain unrun; the restore acceptance conversation stays open. |
|
Published aa33095: adds the local-only Paykit fallback journey and setup recipe. Remote staging stays the default; fallback is conditional on a recorded pairing failure. The docs distinguish rc11 server/rc72 client matching from actual pairing, payment observation and Shop order completion. XML and configuration references checked; the journey remains unrun. No application code changed. |
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Full reassessment of aa330959d against merge base b2d116d90 because the comparison base changed and no usable baseline was supplied. Covered send/funding outcomes, durable guards and retries, Shop/hardware proof recovery, backup/restore, affected callers, tests and journeys. Compared affected contracts with iOS bb02e835; this was not a full iOS review.
2 actionable findings — resolve or provide an evidence-backed rebuttal.
Validation: isolated Kotlin 2.3.20 checks confirmed both fixture defects using the callback contract and unchanged attempt-store source; signed-transaction golden fixtures also passed. App JVM tests did not execute because GitHub Packages returned HTTP 401. The current-head build does not compile the instrumentation APK, so it does not clear these findings. After correction, compile that APK and run the observation fixture.
The author reports current-head fresh-device pending-proof restore; that evidence covers original proof recovery, not active-guard crash windows. Device testing: not performed in this review. Funded fault/retry, active-guard VSS restore and physical hardware acceptance were not executed here; visual parity was not established.
Findings
- [MEDIUM] Supply the third retry callback parameter in the instrumentation test — inline at
app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendPendingObservationDeviceTest.kt:127. - [MEDIUM] Seed an unresolved attempt before waiting for Pending recovery — inline at
app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendPendingObservationDeviceTest.kt:100.
|
I pushed 6b000fe to address the two pending-observation test findings: the retry callback now matches its three-argument interface, and the test seeds an unresolved receipt through its injected storage seam, then restores the exact original state. Production code is unchanged. Local verification: Android test APK compilation, focused attempt-store JVM tests and detekt. Device instrumentation was not run. |
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Follow-up review of changes since aa330959d, from the completed baseline review, at 6b000fe. This pass covers the two recovery-test updates and the restore/observation paths they exercise. Inherited baseline coverage remains for durable admission, send and funding UI, Shop proofs, backup and restore, the unchanged tests, journeys and Paykit recipe, iOS parity, and the pinned native and e2e contracts.
No new actionable code findings.
The retry-callback and unresolved-seed comments are addressed in this commit. retryOriginal now takes the attempt, fee rate, and prepared-fee approval callback, matching SendPendingScreen. The fixture writes the backup round-trip with Unknown evidence through the test keychain, asserts that the loaded attempt is unresolved, has no positive evidence, and has incomplete local follow-up before the screen mounts, then restores the original keychain string. The added unit assertions keep the completed accepted receipt when restoreActive is given Unknown evidence for the same attempt, which matches the same-operation restore branch. Production send, Shop, and backup code is unchanged in this delta.
Validation: the current-head CI build passed, including its testDevDebugUnitTest step. This review did not execute Gradle. PR CI does not assemble or run the Android instrumentation APK; UI Tests run on master or manual dispatch. The author reports that assembleDevDebugAndroidTest succeeds in the resolved threads above. Device testing: not performed in this review.
Recommended before device testing
Run SendPendingObservationDeviceTest.restoredOriginalObservationNavigatesWithoutRetryOrNativeSend on the owned Android fixture with expectedBackendTxid. Expected result: Payment Pending is shown for the seeded unresolved receipt, exact observation completes with the original amount, and the stored attempt is restored to the pre-test receipt.
Closes #1211
Twin: synonymdev/bitkit-ios#844
Refs:
Description
Hardware Shop payments persist the signed receipt and original private boundary before endpoint consumption. Interrupted software preparations proven never dispatched release the saved boundary before removing their proof.
0.1.0-rc72with LDK0.7.0-rc.71, retaining original payment guards, captured proof app IDs and cross-platform backup state.Required for Bitkit 2.6.0 Shop support. The identified recovery fixes are published and this PR is open for human review. LDK rc71 is merged and published; the original signed transaction ID, actual inputs and recipient amount are durable before submission. Human approval, current CI and the remaining wallet/merchant acceptance checks are still required before merging.
0.7.0-rc.71prepared-send bindings with the actual signed mining fee. Current Android compilation resolves the hosted rc71 Maven artifact with local repositories excluded; the built APK native libraries match the published artifact. Earlier rc70 remote resolution and native-byte checks are historical coverage.Why
Required for Shop support in Bitkit 2.6.0: buyers must be able to tell whether an on-chain payment was accepted and recover an uncertain checkout without paying twice.
These are release acceptance requirements. The app PRs include the selected Paykit updates for 2.6.0. Final validation must pay a Shop order with these app builds and verify that the merchant receives the payment proof and the order becomes paid on the existing Shop server.
Out of Scope
Design
N/A — no design available for the new unresolved-send state.
Preview
Prepared-send Pending preview before the backup follow-up: actual published rc69 native preparation in a funded regtest wallet, with Unknown injected before broadcasting. This shows the retained 1,000-sat original receipt and explicit retry action; it does not reproduce backend refusal or response loss.
Historical rc68 candidate before the current feedback batch: fixed 1,000-sat and Max 98,749-sat regtest sends using the actual published and resolved LDK package. Both exact transaction IDs matched native Accepted logs and independent backend observation.
Earlier Pending preview at 0ef4613: synthetic component UI only, with dummy transaction IDs/refusal text and mocked fiat value. It shows refusal copy, a selectable candidate ID and local Details without success; it does not reproduce backend refusal or persistent reopening.
Hardware submission boundary
QA Notes
Merchant acceptance (10 October)
amount_matched: trueand one confirmation. Locks completed verification, issued an access credential, and returned the exact protected content through an authenticated read. This Android result is separate from the iOS result below and does not establish Shop order completion.f33ff0363066237f61ce8e6e1db6bd845f226cafc9d36774058284712cf9a013has one original funding input, a 1,000-sat seller output and a 141-sat fee. Confirmed transaction bytes and native signed-input evidence were retained; this does not claim a pre-broadcast snapshot or an interrupted-send retry test.creatorparameter. Shop then called the fork-only/v0/accounts/pubky<seller>route and received HTTP 404; listing creation and Accept Bitcoin remained gated.paykit_invoice_creation_failed, including on one authorized new-bundle retry. The buyer's App Registry and identity-signed Noise authorization were present and verified. The exact remote deployment/configuration cause remains unknown; the successful isolated purchase does not identify it or prove the same Shop order becomes paid.ec0d05e9-9238-4c7d-a156-69a9be3c7908became confirmed withamount_matched: true; requesta7e5d0aa-7099-4af5-939a-5838f78e1ef3reachedproof_submitted. Transaction:283f408b3435eaf04258b5f929eed73a846f78ad21ff1990c2dcd203397b9a97.0KGP41FQ0W0F27EWPA2YBGJD6R, issued an access credential and served the exact protected content through an independently repeated authenticated read (73 bytes; SHA-25666126e151321afc148b133e059dd634f6f7beb1bf1b8b64c36097d021da21724). This proves iOS Locks merchant acceptance; it does not prove a Shop order-paid transition or wallet restore.SharedStateBusysession error cleared after stopping the payer app for 82 seconds and relaunching normally; no remote lock was edited. The simulator and app/log helpers were stopped after evidence capture; the backend was subsequently stopped after both platforms completed acceptance.c51f229cac163ea802a5495dd3c2bbdcbdee113382b7948dc254f65897722d1c), recovered the original payer and requeste470dd63-92e8-4b13-8584-fcab299e5f27, and queued its original proof at 03:28:08 UTC. The server independently changed that request fromacceptedtoproof_submitted; transaction34d03ad637596414c1f8bdb0f3c9a54c6d4ff820986a31f2c80ae8934852377ewas paid once. No application data or hold marker was copied. This covers ordinary pending-proof restore, not an active-guard crash window or a Shop order-paid transition. The Android PR includes the temporary hook and repeatable journey.380b66e9b7d9a85c02432ad6a71b7cf175800c5c89aa839c94cae1c0990e29f9). The retained proof kept request7c73247a-47aa-47c2-8486-3c5fce98ddd6, the original payer, 1,000-sat amount and transactiona6e55ae0b09afa69f216d3753d3fd040af5302d5c56ccd23ab7d4ee7a28bfcab. Normal reconciliation submitted the original proof; an independent server read verifiedproof_submittedandamount_matched: true. No new transaction was created or broadcast by the restored wallet. The disposable hook only deferred source proof delivery and logged ordinary backup/restore data; its marker was absent on the fresh simulator. Both owned simulators and helpers were stopped. Temporary XML, exact diff and reproduction steps are in the iOS PR.89ca8c49-65d5-423f-b2f8-3fd877601dd1becamepaid; payment4320c5ed-b2a1-4c05-a22b-fa3cf86042bebecameconfirmedthrough the service’s independent Locks worker. Paykit invoice26c3a793-e0f3-49d9-a49b-3a40c869293breportedamount_matched: trueand request8b2aaf89-6c93-4b08-a1e1-37b78440af45reachedproof_submitted. Locks completed the original bundleJPEHRR8WAH01B7Y6VKBZ3PTW3W. Transactionad00c6f43d748ff0cff0161ec2d1eb1cb00b387aa735def1a502e3e0e972ed5eused one original input, paid 1,000 sats with a 141-sat fee, and was not replaced.commitUpsertListing; it was removed before checkout. No readiness or payment-success override remained. The studio cannot author Locks listings. The service currently represents this Locks-backed listing as shipping and asks for an address; ordinary digital-delivery and physical-goods payment routes are not covered by this result../shop-order lock, provisioning publication and buyer payment were not driven through this packaged profile; the completed Android and iOS order-paid acceptance used the separately assembled runtime on the same component revisions. Tunnel checks came from the public internet, not a device. Commit 5972825 also isolates the Compose project and restores owned fetched sources to their exact pins.0bd26696-7c4a-4275-bf6a-56208d6b018dbecamepaidafter one 1,000-sat payment. Paymentc3d2633a-9cd8-4e4c-8d87-58e770d3ceff, bundleCF0YST4VWN7RS9BX098GGEBKNW, invoicecff3c171-e138-4e01-80cb-7cde2c54e4bband request7732ba03-e404-4d40-9b6d-5e23c9b6cecaretain the same buyer, seller and amount. Independent persisted-database reads confirmed the same order paid, Paykit confirmed withamount_matched: true, and the original requestproof_submitted. Transaction14a1d4700f2d9dd4eb2b24f48061bd6dcf498afe369f520e4adee07bc967fd52paid a 143-sat fee; its exact 222 signed bytes have SHA-2569ee854da632e366be2cd5ea7fdfa7ca6a99f9eedb02e4b92246c059e803c6e88. Locks completed and authenticated access returned the expected 66-byte content. No payment decision was modified.listing.registeralone omitted that snapshot; a normal seller-signed revision-2 publication throughcommitUpsertListingemittedlisting.syncand populated it. The first iOS setup order was refused before any bundle/invoice/payment and cancelled through the normal command; the successful order above used the corrected snapshot. Disposable provisioning code was removed before checkout. Both platforms’ test runtimes are stopped, with wallet state and database volumes preserved.Local-only fallback documentation (aa33095)
Current master integration (feb7b16)
0.1.0-rc72update while keeping LDK0.7.0-rc.71. The contact-sync test conflict preserves incoming sign-in/membership checks and retained-payment regressions.Current verification and acceptance
Earlier master integration
Earlier master integration
0.1.0-rc69from the merged Paykit PR; LDK remains0.7.0-rc.70.Private hardware cleanup (b71aff7)
Hardware dispatch-state recovery (91351de)
Hardware authorization recovery (3925ef2)
PaykitPaymentProofRepoTest,HwSendViewModelTestandAppViewModelSendFlowTest; the authorization-boundary backup regression reproduced the missing receipt before correction. Local verification: affected checks and Android build completed; physical hardware and fresh-wallet Shop VSS acceptance remain unrun.Current review corrections at 59df4f8 / 5843da2: direct-send fallback validates original attempt and wallet; hardware Shop Pending retains its result until proof completion. Missing proof app IDs remain readable and retained, and cannot be submitted with invented provenance. Focused repository, hardware-send and send-flow checks and the app build completed. Physical hardware proof-save failure, remote restore and final Shop order/proof checkout remain unvalidated.
Journeys
wallet-restore-unfinished-payment.xml— retained original proof restored from normal VSS backup without another payment (passed on disposable debug build).Temporary restore journey and debug hook
Not pushed. Base
aa330959dd34ab51fb46025a021b4155980676b5. One request-scoped gate defers only SDK proof enqueue on the source device. Production local guard completion remains unchanged; this does not claim active-guard crash-window coverage.In a detached worktree at the base above, save the diff below and run
git apply --check restore.patch, thengit apply restore.patch. Build./gradlew assembleDevDebugand install that APK. After testing, rungit apply -R restore.patchand verify a clean worktree; do not commit the hook.Manually arm
files/restore-proof-holdthrough debugrun-aswith exactly the new original request UUID. After one actual payment, require ordinary WALLET and METADATA VSS upload/readback hashes to match and the retained proof to be present. Force-stop the source app and shut down only its emulator; preserve its state. Create a fresh emulator, install the same APK without the marker, and restore the original mnemonic through the normal UI. Never copy application data. Compare the downloaded snapshot hash, original proof fields and actual merchant acceptance. No second payment or replacement request is allowed.An independent server read confirmed the same original server request changed from accepted to proof_submitted. Profile QR matches the original payer. Locks completion preceded restore and is not used as restored-proof evidence. Both owned Android runtimes stopped; no second payment.
new
local-paykit-fallback.xml— local-only rc11 pairing/payment checks after a remote staging pairing failure; setup inlocal-paykit-fallback.md. Unrun; standalone payment does not establish Shop order completion.new
onchain-original-payment-retry.xml— Pending → approve fee and normal payment authentication → retry the same recipient amount and input set; persist both candidates, keep uncertainty guarded and finish only the accepted/observed winner. Spec parsed; current funded retry journey remains unrun.new
shop-onchain-proof.xml— linked issuer and funded Bridge hardware wallet: exact original transaction and delivered proof, with no new signing/payment while observation is pending. Spec parsed; native hardware journey unrun.new
onchain-accepted-result.xml— accepted fixed-amount and send-all finish local activity and expose distinct exact transaction IDs in Details.Manual Tests
Automated Checks
Review correction at 6b000fe: the Android instrumentation callback supplies all three parameters; the observation fixture seeds unresolved state through the test-only storage seam and restores the exact prior state. Android test APK compilation, focused attempt-store JVM checks and detekt passed. Device execution of this corrected instrumentation test remains unrun. Production code is unchanged, so the recorded aa33095 app acceptance remains applicable.
updated
BroadcastExceptionExtTest.ktandHwSendViewModelTest.kt— known backend refusal permits dismissal without clearing the receipt; unknown/connectivity errors stay guarded and retry retains original signed bytes.updated
OnchainSendAttemptStoreTest.ktandOnchainSendCoordinatorTest.kt— prepared send-all funding rejects underfunding and amounts beyond approved terms before candidate retention; rejected receipts never reach broadcast.updated
ActiveOnchainAttemptBackupTest.kt— send-all funding restore rejects recipient amounts above the retained transaction total in all outcome states while preserving valid surplus.updated
ActiveOnchainAttemptBackupTest.kt— send-all funding backups retain the actual recipient amount and original order terms through restore; underfunding and fixed-amount mismatch remain rejected.updated
TransferViewModelTest.kt— fresh and resumed accepted funding pass complete original order terms to durable follow-up without another payment.updated
ActiveOnchainAttemptBackupTest.kt— restored original candidate fees must agree with the retained original fee rate; omission preserves the original fallback.updated
ActiveOnchainAttemptBackupTest.kt— restored funding amounts must equal their original order fee before installing the payment guard.updated
ActiveOnchainAttemptBackupTest.kt— every retained replacement has a valid fee rate before restore installs a payment guard; original transaction fee fallback is preserved.updated
ActiveOnchainAttemptBackupTest.kt— malformed restored contacts are rejected before installing the payment guard; absent and valid contacts remain accepted.updated
TransferRepoTest.kt— accepted original funding completes after connectivity recovery without another send.updated
OnchainSendAttemptStoreTest.kt— the captured contact is saved before dispatch and survives reopening the active payment guard.updated
AppViewModelSendFlowTest.kt— recovery follows the original Shop request and consumes an existing acceptance without returning to Pending.updated
AppViewModelSendFlowTest.kt— recovering an older blocked payment never assigns the new recipient’s contact to it.updated
BackupRepoTest.kt— an older unresolved backup resumes retained local acceptance after original context is restored, without requiring another transaction event.updated
ActiveOnchainAttemptBackupTest.ktandBackupRepoTest.kt— missing original follow-up context rejects restore before installing a blocking payment guard.updated
ActiveOnchainAttemptBackupTest.kt,BackupRepoTest.ktandOnchainSendAttemptStoreTest.kt— accepted replacement backups require the winning candidate’s fee rate; missing/partial maps fail restore, valid replacement rates and original-candidate fallback remain supported.updated
HwSendViewModelTest.kt— exact-wallet completion after a hardware Shop broadcast timeout clears the retained send guard and preserves the original amount/request/payer; unrelated observations cannot clear it. Build, unit and lint checks completed; the funded hardware journey remains unrun.updated
LightningRepoTest.kt— a retained accepted result after a broadcast failure stays Pending while durable repair fails, including after reopening; no second native send is prepared.updated
ActivityServiceTest.kt— accepted-payment restoration preserves a deliberately removed contact and allows local follow-up to finish. The previous behavior reattached the original contact in the regression. Local verification: Debug build, unit tests and lint. Device and merchant acceptance remain outstanding.updated
ActiveOnchainAttemptBackupTest.ktandLightningRepoTest.kt— unsigned backup attempts are rejected; retry preserves Pending when the accepted winner is not durable, without authorizing or dispatching another payment. Both defects reproduced before correction. Local verification: Debug build, unit tests and lint. Fresh-wallet Shop restoration and funded merchant acceptance remain outstanding.ran
PaykitPaymentProofRepoTest.ktandHwSendViewModelTest.ktregressions: definite software preparation failure retains the proof until its original private boundary is released; a restored signed hardware receipt blocks ordinary and other-request signing from the same wallet. Required local build, unit tests and lint passed at bcc22b1.ran
LightningRepoTest.ktandActivityRepoTest.ktregressions: failed accepted-outcome persistence and failed repair stay Pending across restart; contact-assignment retries finish marker cleanup and replacement propagation. Required local build, unit tests and lint passed at 4306d22.updated
HwSendViewModelTest.ktandPaykitPaymentProofRepoTest.kt— signed hardware Shop receipts survive encoded backup/repository restoration; explicit authorized retry broadcasts the original bytes without another signature. Wrong payer/wallet/address/amount cannot load the receipt. Restoration itself does not broadcast or prove acceptance.Hardware Shop connectivity failure retains its signed payment across cancellation and explicit retry, with one signature and the same original payer/request. The regression failed before correction; affected hardware-send/proof tests, app build and changed-line static analysis completed. Physical hardware remains unrun.
Non-connectivity hardware Shop broadcast errors also retain the original signed payment through cancellation and retry. The invalid-transaction regression failed before correction; affected hardware-send/proof checks, app build and changed-line static analysis completed. Physical hardware remains unrun.
added
BackupRepoTest.kt— unsigned ordinary sends and transfers defer the entire wallet snapshot before any remote write.added
HwSendViewModelTest.kt— save denial and storage exceptions preserve the signed payment across cancellation and retry it without signing again.added
OnchainSendCoordinatorTest.kt— accepted retry storage failure stays Pending with the original candidate family after restart.added
TransferViewModelTest.kt— changed funding address, client balance or service fee cannot complete the retained transfer or trigger another send.updated
OnchainSendAttemptStoreTest.kt— repeated restore retains an imported operation’s progressed fee and completion; changed payer or recipient is rejected, and a first import without follow-up context remains guarded.updated
ActivityRepoTest.kt— a failed Core contact write leaves the durable manual-detachment marker intact. Both new regressions failed before correction.updated
OnchainBackupRestoreDeviceTest.kt— verifies the remote wallet payload contains the accepted attempt, deletes its local attempt record and checks it is absent, then restores the original wallet, transaction, amount, exact inputs, candidate IDs and completed follow-up from VSS. Corrected device replay passed. The earlier completed-attempt replay was a false positive because backups excluded its completed receipt. This remains an ordinary-payment test on the same wallet, not fresh-wallet Shop request/proof recovery.ran repaired shared-state Paykit device fixtures at 5f800c2: app/test APK compilation and drawer/Pending component checks passed on Android 16, with exact installed app APK bytes verified. Removed duplicate profile arguments and obsolete settings dependencies. These are component checks, not funded PIN/recovery or merchant checkout.
updated
PaymentDeadlineSubmissionTest.kt,AppViewModelSendFlowTest.kt— queue-time expiry, expiry after immutable preparation, and exact original hardware cancellation with no release after dispatch.ran local JVM suite and detekt against Paykit rc65 and hosted LDK rc70; current funded device, hardware and Shop checkout journeys remain unrun. Detekt retains its existing
ignoreFailures=trueconfiguration.ran native rc70 consumer compilation and broadcast-outcome/recovery checks against the hosted Maven artifact with local Maven repositories excluded.
updated
OnchainSendAttemptStoreTest.kt,PaykitPaymentProofRepoTest.kt— durable admission precedes request consumption; failed writes preserve request details; restart cleanup preserves live, legacy and signed attempts, and proof-removal failure retains the guard.added
ActiveOnchainAttemptBackupTest.kt, updatedPaykitPaymentStateBackupTest.kt,BackupRepoTest.kt— shared golden wire, wallet/network/proof rejection and guard-before-proof restore.added
OnchainSendCoordinatorTest.kt— exact original amount/input retries, original winner races, payer change and UInt32 fee bounds.added
OnchainSendAttemptStoreTest.kt— serialized admission, durable guards, outcome persistence and exact transaction observation.added
SendPendingScreenTest.kt— refusal/candidate visibility and enabled local Details callback while remaining Pending; component checks do not reload durable state.updated
LightningRepoTest.kt,LightningServiceTest.kt— explicit accepted/rejected/unknown mapping and pre-dispatch boundaries.updated
AppViewModelSendFlowTest.kt,PaykitPaymentProofRepoTest.kt,TransferViewModelTest.kt— proven pre-admission errors, accepted follow-up failures, request protection and original hardware proof identity.updated
AppViewModelSendFlowTest.kt— permission denial, captured request/identity across asynchronous hardware authorization, and started-proof preservation after retry denial/cancellation.updated
TransferViewModelTest.kt— fresh Accepted and resumed Accepted/Observed funding retain one order/send and paid-success navigation after local activity failure; failed funding persistence retains the original order without success.updated
TransferRepoTest.kt,SendPendingViewModelTest.kt— startup/event funding resumption, partial storage idempotency and original-wallet Details without acceptance inference.updated
HwWalletRepoTest.kt,HwSendViewModelTest.kt,ActivityRepoTest.kt— exact outgoing original-account observation, Sent activity durability and one native broadcast while proof completion is pending.removed
PaykitOnchainPaymentProofLookupTest.kt— address/amount matching no longer establishes that an interrupted attempt was accepted.ran remote dependency validation with Maven local excluded —
0.7.0-rc.68resolved AAR SHA-2560cee2079291260aea12bf60ac9c8af8f459d9f24c3327d4cb021e5686035aa2f, identical to the published canonical artifact.Recovery completion and identity (7984a64)
Restart-safe Shop preparation (5a07a0f)
Historical validation before the shared-state Paykit integration
Local verification at 56398e3: master b29956a was integrated without manual edits; compilation and 484 focused tests passed (0 failures/errors/skips). Published rc68 AAR identity was verified with Maven local excluded. Detekt completed with
ignoreFailures=trueand 577 reported findings; it is not warning-free. All finding locations map to existing parent lines, which does not prove identical prior structural findings. Full-suite, device, native-fault and physical-hardware checks were not repeated; heavy hosted integration CI remains deferred to release.Earlier verification at 62c32cc: compilation and 470 focused tests passed (0 failures/errors) against unchanged published rc68, with Maven local excluded. The identity-switch-during-authorization regression failed before the captured identity/context checks and passes now; captured callbacks cannot authorize a replacement request. No device, native fault, physical hardware or contact-deletion journey was run on this merged source.
Earlier verification at 6c75463: compilation and 169 focused tests passed (0 failures/errors). All three new duplicate-funding regressions failed on the preceding production source by funding a second order; the persistence-failure regression remained fail closed. Maven local was excluded and the resolved AAR hash matches unchanged published rc68. No device or storage-fault journey was run for this fix.
Earlier verification at 0ef4613: compile, 3,079 unit tests (0 failures/errors/skips), app/test APK builds and two emulator component tests passed. Startup funding and pre-admission regressions failed before their fixes. Detekt exited successfully with
ignoreFailures: 570 findings remained, none on added or changed feedback lines. The installed APK’s native library matched rc68. Full-suite, Detekt and device checks were not repeated for 6c75463 or 62c32cc.Prior rc68 validation: 95 affected tests and funded fixed-amount/Max native journeys passed with independently verified exact backend transactions. Those native journeys were not repeated for this batch. Controlled native refusal, response-loss, storage-fault and hardware Shop journeys remain unrun; the new Preview is synthetic component coverage only.
Current integration verification at d252bcb: 925 focused tests passed; strengthened Shop rerun (203 cases), 96 Jade/Paykit integration tests and 13 emulator component tests passed. The regression for acknowledgement without durable original Shop activity failed on old behavior and passes after repair. Exact request/txid mismatches and identity switching cannot deliver or acknowledge another proof, and recovery never broadcasts. Test API integration repairs and final test-only formatting compiled successfully. Detekt exits successfully with ignoreFailures=true; existing findings remain, so it is not warning-free. Component refusal screens use synthetic fixtures, not a real native refusal. Final matched release-pair Shop, response-loss and physical hardware validation remain required.
Feedback verification at 5f7839a: both new pre-dispatch regression cases failed before the fix; 334 affected send-flow tests and compilation passed afterward. Detekt completed with existing findings and none on added lines. No devices or native fault fixtures were used in this batch; final recovery and release-pair validation remain outstanding.
Current recovery and backup verification: 307 affected checks passed against the published remote rc69 AAR before backup additions. Then 57 focused backup/metadata checks passed, including shared wire round-trip, wallet binding, original proof authorization/completion and restore ordering. The final UInt32 fee-boundary regression failed before its correction and passed afterward, along with current app/test APK builds. These overlapping counts are not additive. Built app native bytes match the hosted rc69 arm64 library; the current backup batch has not been installed or driven on a device. Detekt exited with
ignoreFailures=true: one changed-line complexity finding remains, zero changed-line formatting findings. Hosted Maven publication completed successfully. Earlier funded preparation and Pending/fee/PIN observations seeded uncertainty before submission; they do not prove backend refusal, response loss or a successful retry. Current funded fixed/Max retry, independent Android native VSS vector, physical hardware and final Shop release-pair QA remain unrun.Pending UI verification at 15f45fa: 16 focused JVM checks and 3 emulator component checks passed, along with app/test APK builds. The preceding view model failed the exact observed-successor regression; the preceding error layout failed the non-overlap assertion. Detekt exited successfully with ignoreFailures=true and no changed-line findings; existing findings remain. Component checks use synthetic state. Funded auto-navigation on this UI batch has not been repeated.
Native rc69 validation on preceding production head152b43d: five native fixture checks passed with installed hosted package bytes. A fixed1,000-sat retry with withheld backend acknowledgements remained Pending until independent exact successor observation and durable local completion. Native transport repeated the same transaction; no distinct second payment was observed. Max retained99,890sats and exactinputs: a higher fee failed before broadcast, then original-fee retry succeeded. Original uncertainty was seeded before the first broadcast, so this does not prove process-death recovery after initial dispatch. Native VSS derivation vectors passed. Physical hardware, native refusal and final production Shop merchant pairing remain unrun.
Published 5f8e105:
Validation: four regressions failed against the previous production behavior. 209 focused tests passed; final overlapping 31-case and 62-case checks passed, alongside app/test APK builds against published rc69 and detekt. Lint reported existing findings with zero introduced-line findings. Physical hardware, live backup restore and final selected-version Shop checkout remain unrun. Draft status remains unchanged.
Published 6c1a9cb: added the Pending observation device integration fixture. Two checks passed using the current production APK and published rc69. The shared backup reader preserved the original wallet, inputs and candidate family; injected exact-positive observation completed the production Pending callback with the original txid and amount. The fixture restored its original saved operation afterward. This validates the store/ViewModel/screen callback, not a native event, full-app success route, remote VSS restore or merchant checkout.
Lint/detekt and fixture APK compilation completed. Full accepted funding/VSS device restore, authenticated hardware Shop rejection, physical hardware and final merchant pairing remain unrun. Five new review findings are being addressed; draft status was retained at that validation point.
Published 66d32dd: preparation cancellation releases only an empty pre-dispatch guard; completing an older payment does not present the blocked new send as successful; uncertain transfer funding opens the original recovery surface; each candidate retains its authorized fee rate; proof reconciliation avoids the reversed attempt/proof lock order. The original order, payer, amount and exact inputs remain protected.
Validation: 96 focused tests passed, with meaningful prior-behavior failures for cancellation, misleading navigation, transfer routing, fee metadata, shared restore and lock ordering. App/test APK builds passed against published rc69. Detekt completed with 515 existing findings and zero introduced-line findings (
ignoreFailures=true). The shared optional candidate fee-rate map passed the same golden vector as iOS. Current transfer recovery device routing/PIN, funded restore, physical hardware and final selected-version merchant checkout remain outstanding.Published c7745fc: Accepted successors use the actual signed transaction input/prevout fee instead of the original fee. Missing or invalid evidence keeps follow-up guarded; verified fee/rate repair also updates an existing activity placeholder.
Validation: 174 affected JVM tests passed (0 failures/errors/skips), including exact previous-output calculation, missing/substituted inputs, invalid values and arithmetic overflow. The earlier stale-fee regression failed before the writer fix. Current transfer/PIN/device validation, four new review findings and final selected-version Shop checkout remain outstanding.
Published d3f3bec: both retry fallback paths verify original attempt and wallet identity. The prior behavior failed the later-winner regression; all 17 coordinator tests passed afterward. Three remaining review findings and current device/release-pair validation remain outstanding.
Published b9cf568: restored shared/iOS contact attribution no longer prevents completion of an accepted payment. The original contact fills only an empty activity contact; later edits remain unchanged. A failed contact write retains the payment guard.
Validation: the shared golden backup failed acknowledgement before the fix. All 175 affected JVM tests passed afterward (13 attempt-store, 159 send-repository, 3 activity tests; no failures/errors/skips), including contact write failure and later-edit preservation. Two remaining findings concern pre-prepare proof crash ordering and transfer Pending completion. Both apps still need matching rc70 consumer and current funded/Shop validation.
Published df05937: transfer Pending now observes durable completion of its exact original order, wallet, input set and candidate family, then opens the funded order. Accepted funding stays Pending until local follow-up is saved, and this navigation does not dispatch another payment.
Validation: the stale transfer Pending regression failed before the fix. Compilation and all 3,465 JVM tests passed before formatting; the final 355 affected tests and detekt passed after formatting. Detekt has 577 existing findings with zero findings on introduced lines (
ignoreFailures=true). No device or merchant checkout was run for this batch.The signed receipt and private boundary are persisted before hardware endpoint consumption. Fresh-wallet Shop request/proof recovery, current expiry device coverage, physical hardware and final merchant order/proof checkout remain outstanding; this PR remained draft at that validation point.
Original retry deadline verification (6686f9a)
Original input validation (fea3f42)
Funded Android payment-PIN verification (fea3f42, native rc70, Paykit rc65)
Real acknowledgement-loss recovery (2cb41e0, native rc70, Paykit rc65)
First-submission expiry (875a3cb)
Current-head device component verification at875a3cb7: rebuilt and installed APK identity verified;
SendPendingScreenTest.ktandDrawerMenuWidgetsTest.ktran on Android emulator. This covers uncertainty presentation, guarded retry controls and exact-candidate Details availability; it does not certify funded expiry/replacement, remote restore, hardware or merchant proof/order completion. Those acceptance checks remain open.Original-winner recovery accounting (a6ddeb0)
Restart-safe hardware expiry cleanup (d463af9)
Current validation gaps: the rc65 SDK fresh-grant remote request fixture and same-wallet ordinary-payment remote VSS restore passed. Fresh-wallet Shop request/proof recovery, funded channel recovery, current expiry device coverage, physical hardware and final merchant order/proof checkout remain unvalidated.
Payment storage recovery (8f4b85e)
Retained hardware completion (2592aa2)
Prior private payment boundary (e38369f)
Unsent private preparation cleanup (5214de9)
PaykitPaymentProofRepoTest,AppViewModelSendFlowTest, private payment and backup regressions, dev app build and changed-line formatting checks.Retained private payment version (08d35b7)
PaykitPaymentProofRepoTest.kt— restored signed receipts consume their saved private version before retry; failed private storage blocks dispatch, existing consumption remains valid, and another order cannot reuse the same contact while its signed payment is retained.Unsigned restart recovery
OnchainSendAttemptStoreTest.kt: ordinary send and transfer restart can back up and admit a later payment; current-process preparation remains blocked.Transfer backup totals
ActiveOnchainAttemptBackupTest.kt: totals aboveLong.MAX_VALUEare rejected; the maximum representable value remains valid.Prepared funding total correction
Retained winning fee correction
Ordinary activity recovery
Previous transfer recovery
Recovery feedback updates
Initial preset receipt retention
LightningServiceTest.kt— initial software send, send-all and Fast transfer pass through durable receipt retention before broadcast at 1000 sat/vB; the maximum UInt32 preset and saved fee provenance survive reopening and backup serialization.OnchainSendCoordinatorTest.kt— zero and overflowing initial presets are rejected; a recovery rate above 999 remains rejected before native preparation and at receipt retention.Retained hardware retry follow-up
HwSendViewModelTestand detekt passed; reproductions failed before the correction.Reported Electrum refusal formats (6558827)
Retained hardware request reopen (37e4405)
Retained hardware confirmation correction (b33dc94)