fix: the placeholder sweep, the SDK's success-for-nothing rows - #1039
Merged
Merged
Conversation
… never a substitute The envelope builder re-derived the sender's public key under the state lock and, when that failed, took the caller's copy or the persisted app-state key with an empty default. The routes already resolve the key from the signing authority fail-closed and the params check requires 64 bytes, so the builder embeds exactly that key or no evidence at all. The receiver roots verification in its stored contact either way.
… not nothing in flight counterparty_has_unconverged_inbound skipped any frozen half that did not decode, so an unreadable inbound transfer read as absent and a send could cross it. A half that does not decode is now an error, which the barrier reports as a failed staging read. Tests: an_unconverged_inbound_half_names_its_sender, a_staged_half_that_does_not_decode_is_an_error_not_absence (the skip restored -> red).
…mpty release The reply-window sweep submitted whatever release bytes the row held, an empty slice when it held none, which the sender refuses. A promoted row without its post-admission release is logged as a defect and left unmarked; nothing is submitted for it.
…ot the peer's with default fields The route answered a BilateralPrepareResponse, the peer's message, with every field but the commitment defaulted. It now answers a BilateralTransferResponse: the prepare went out, transaction_hash names the proposal's commitment. The frontend reads that answer, treats any other payload as unexpected, and no longer decodes a reject the route never produced; the tests stub the real answer.
Nothing in the page fetches an external host: the map the tile hosts served is gone and the only fetch is a local sound. The proxy, the allowlist and the test that locked it are deleted; the WebView serves APK assets and handles everything else itself.
…CSP and service worker name no host the page uses getHeaders answered a window global once it had been filled and never asked the bridge again, so a changed identity was never seen. It now reads the bridge on every call; the tests that reset the global are updated and the cache test asserts the opposite property. The CSP's tile, maplibre, dsm-wallet.io and localhost entries and the service worker's tile cache had no consumer.
…t-level gate BootstrapMeasurementReport envelopes (started, progress, finalize, aborted, error) and the finalize path gated on a caller-supplied trust level described as a C-DBRW entropy health test had no sender; C-DBRW is gone and Genesis v2 (system.createGenesisV2) is the one bootstrap path. Such an envelope now reaches the Core bridge and is refused as unsupported. Two startup tests are renamed after the real op they exercise.
It fixes the identity pair to two 32-byte arrays and nothing else. The unused domain-tag parameter and the doc's claims of hashing, canonization and being the only object the Core trusts are gone.
…committed one acceptOfflineTransfer emitted wallet.bilateralCommitted with committed: true as soon as the accept answer arrived, before the peer's confirm. The event is now wallet.bilateralAccepted and names the transfer (commitment and counterparty) and nothing else; the transfer commits when the confirm arrives as a BLE event. The toast it drives already said accepted.
…nted for a code nothing emits The unwrapper rewrote codes 460, 404 and 408 into peer-connection, stale- state and peer-timeout stories; the Kotlin dispatcher emits codes 1 to 8 and none of those. The message the bridge sent is shown with its code.
…t filled with false Rust fills every nested status on every snapshot. The decoder answered false, true and 'none' for anything absent and the store spread defaults under whatever passed a five-field guard. The decoder now refuses a snapshot without its lock or hardware status, reads the rest as sent, and the store keeps the snapshot as decoded, adding only that it arrived.
…e; the response catch can release its entry Any window message carrying a port replaced the bridge port, whenever it arrived. The port is now taken once, from the empty-string message Kotlin posts with it; a later port-bearing message, or one without the handshake, is ignored and logged. In the port handler the response id was declared inside the try, so the catch's cleanup never saw it and a response that failed mid-decode held its pending entry until the timeout; the id is declared outside.
The commit before last carried only the renamed provider test: its add command failed on the old test path and staged nothing else. This is the rest of the rename: the event map, the emitter, the accept path, the provider's signal and the tests that name the event.
…ed protobuf classes BridgeEnvelopeCodec carried its own varint and length-delimited parser with hard-coded field numbers for every bridge message, beside the rule that Kotlin implements no wire decoder. It now uses the generated classes of dsm_app.proto for the request, the response, the error, the app-router, preference and bilateral payloads and the envelope's error, keeping its public shape. A request without a method is refused. Tests that asserted the hand-rolled parser's private rules (a second oneof member, a wrong wire type as a failure, an empty-versus-absent debug string) now assert protobuf's semantics.
…y Rust's source tag, never by message text The page recognised a safety refusal by a regex over any error message, and Kotlin scanned every answer for an Envelope error with source tag 11, which never parsed a framed answer and so never matched. Rust tags DsmError::DeterministicSafety with source tag 11 and carries the class in the error's context; the page now reads exactly that, at the two places every transport error surfaces (decodeFramedEnvelopeV3 and the ingress unwrapper). The regex, its callers and Kotlin's scan are gone.
…pendencies they carried bluetooth, storage, threadsafe, jni, web-stack and formal gated no code in the Core; three of them only pulled optional dependencies the Core never used (jni, tokio-stream, rocksdb, axum, tower). Deleted with the build-info function that only listed them and the build script's rustc and target stamps it alone read. The lockfile loses rocksdb and its native build chain and nothing else. sphincs-trace and bitcoin-testnet-bypass gate code and stay.
…ule are gone deterministic_id.rs produced hex UUID strings and had no caller. calculate_next_entropy and hash_blake3 were reached only by their own tests; init_crypto had no caller and was the only caller of the init_sphincs self-test. All deleted with those tests; init_kyber stays, key generation calls it.
cryptskii
added a commit
that referenced
this pull request
Sep 27, 2026
- Rust gates (G1, red since #1035): `sofi::smt::fold::batch_fold` had no production caller; the verifier folds through `verify_batch`. The wrapper goes; the tests call the one fold over the economic tree's hashes. - Rust tests (dsm_sdk): the offline-step fixture installed its host appliance into the process-wide factory and never removed it, so the offline-cash gate tests that run after it, which assert what a device with no appliance is refused, found one attached ("insufficient online balance"). `install` now returns a handle that uninstalls on drop. - Android unit tests: `BridgeLoggerTest` asserted payload bytes in the bridge log, which #1038 removed on purpose (a payload can be the mnemonic). The two tests now assert the size is logged and no byte of the payload is. - Android instrumented tests: `t64` asserted a 16-byte accept commitment was answered as success. Since #1039 the dispatcher answers the arm's error; the test asserts that error and its reason. Reproduced first: the offline-step tests then the offline-cash tests in one process fail exactly as CI does (2 failed, "insufficient online balance"); after the fix 27 passed. `dsm` lib sofi fold + validation: 59 passed. `ci/sofi_reachability.py`: pass. `BridgeLoggerTest`: 10 passed. Android test sources compile.
cryptskii
added a commit
that referenced
this pull request
Sep 27, 2026
…older schema fails it in its own words (#1041) * fix(startup): a failed startup is the session's error, and a store at an older schema fails it in its own words Both appliance phones sat on "STARTING RUNTIME / WARMING UP BRIDGE" with no end: their stores were left at schema 24 by an older build, this build expects 25 and does not migrate, and nothing told the page. `initialize_sdk_core` set SDK_READY false and returned the error to a caller that logged it, so the phase stayed `runtime_loading`; Kotlin then logged "SDK initialized" after `initSdk` answered false. - A failed startup is recorded as the session's fatal error, so the phase is `error` and the page shows Rust's reason. A later successful startup takes back the failure it recorded; an error anyone else reported stays. - Startup opens the client store. A store this build refuses fails startup in the store's words. Unopened, the refusal surfaced through whatever read the store first: on Android that was the sealed-seed read, which was downgraded to a warning and re-reported as "wallet seed not unlocked". A seed vault that cannot be read now fails init as itself. - Kotlin logs a failed `initSdk` as a failure. - `initSdkV3` had no caller and declared the SDK ready after setting only the storage directory. It goes with its declarations and `InitFailed`, which only it produced; Envelope field 31 is reserved. Test, on the running fleet: a device created as wallet creation creates it, its store left as schema 24 left it (stamped 24, without the table 25 added), the process restarted; startup fails with SCHEMA RESET REQUIRED, the session's phase is `error` with that message, and after the wipe the refusal names, startup succeeds and the error is gone. Mutations: the recording dropped, then the store open dropped; each turns the test red. * fix(ci): the four jobs red on main since the sweep merges - Rust gates (G1, red since #1035): `sofi::smt::fold::batch_fold` had no production caller; the verifier folds through `verify_batch`. The wrapper goes; the tests call the one fold over the economic tree's hashes. - Rust tests (dsm_sdk): the offline-step fixture installed its host appliance into the process-wide factory and never removed it, so the offline-cash gate tests that run after it, which assert what a device with no appliance is refused, found one attached ("insufficient online balance"). `install` now returns a handle that uninstalls on drop. - Android unit tests: `BridgeLoggerTest` asserted payload bytes in the bridge log, which #1038 removed on purpose (a payload can be the mnemonic). The two tests now assert the size is logged and no byte of the payload is. - Android instrumented tests: `t64` asserted a 16-byte accept commitment was answered as success. Since #1039 the dispatcher answers the arm's error; the test asserts that error and its reason. Reproduced first: the offline-step tests then the offline-cash tests in one process fail exactly as CI does (2 failed, "insufficient online balance"); after the fix 27 passed. `dsm` lib sofi fold + validation: 59 passed. `ci/sofi_reachability.py`: pass. `BridgeLoggerTest`: 10 passed. Android test sources compile.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The sixth chunk of the placeholder sweep (#1037 write-up, #1038 first five chunks): the SDK's success-for-nothing rows B-5 to B-9 of the ledger's §6.36. One verified fix per commit:
recipient_staging::counterparty_has_unconverged_inbound).wallet.sendOfflineanswersBilateralTransferResponse(its own message: the prepare went out under this commitment) instead of the peer'sBilateralPrepareResponsewith every field but the commitment defaulted; the frontend reads that answer, treats any other payload as unexpected, and no longer decodes a reject the route never produced (B-8).execute_simplified_bilateral_transfer) is corrected in the ledger: no such function exists on main.Seventh chunk (gates that gate nothing):
dsm-wallet.ioand localhost entries and the service worker's tile cache.getHeadersreads the bridge on every call; the window cache that never invalidated, and the tests' resets of it, are gone (C-F10).BootstrapMeasurementReportenvelopes and the finalize path gated on a caller-supplied trust level "from the C-DBRW entropy health test") had no sender; deleted with its helpers, Genesis v2 being the one bootstrap path (S-INGRESS).pbi.rssays what it does — the identity pair as two 32-byte arrays, checked for length and nothing else; the unused domain tag and the claims of hashing and sole trust are gone (S-PBI).Eighth chunk (the frontend's fakes):
wallet.bilateralAccepted, naming the transfer, instead ofwallet.bilateralCommittedwithcommitted: truebefore the peer's confirm (B-F4).try, so the catch's cleanup releases the pending entry it used to miss (the page half of D-CODEC).Ninth chunk:
BridgeEnvelopeCodecdecodes and encodes through the generated protobuf classes ofdsm_app.proto(request, response, error, app-router, preference and bilateral payloads, the envelope's error) instead of its own varint parser with hard-coded field numbers; Kotlin holds no wire decoder. A request without a method is refused. Tests that asserted the hand-rolled parser's private rules now assert protobuf's semantics (the Kotlin half of D-CODEC).Tenth chunk:
Error.source_tag(11) and reads the class from the error's context, at the two places every transport error surfaces (decodeFramedEnvelopeV3, the ingress unwrapper); the regex over message text and Kotlin's scan of the answer bytes (which never parsed a framed answer) are gone (D-SAFETY).bluetooth,storage,threadsafe,jni,web-stackandformalgated only a name; deleted with the optional dependencies they carried (jni,tokio-stream,rocksdb,axum,tower), the build-info function that only listed them and the build script's stamps it alone read (C-C19). The lockfile loses rocksdb and its native build chain and nothing else.deterministic_id.rs(hex ids),calculate_next_entropyandhash_blake3(each reached only by its own test),init_cryptoand theinit_sphincsself-test it alone called are deleted (D-C15, E-C15–18 for the named items).Verification
dsm_sdklibstorage::client_db::recipient_staging: 11 passed, the two new tests among them; mutation: the skip restored →a_staged_half_that_does_not_decode_is_an_error_not_absencered, restored.dsm_sdklibsdk::b0x_sdk::testson the Postgres test nodes, single-threaded: 33 passed.cargo ndk -t arm64-v8a check -p dsm_sdk --features jni,bluetooth: clean.type-check,lint: pass.ci/conformance_evidence.py: pass.make lint: passed. Device: the APK with this chunk is installed on the four phones; on the relaunched 5GN, DSM launches without a crash and the wallet's SEND tab renders. The rig's wallets currently hold no contacts and no ERA, so an offline send could not be driven; the answer decode is covered by the frontend tests against the exact message the route now produces.dsmlibpbi(11 passed);dsm_sdklibingresson Postgres, single-threaded (18 passed);cargo ndk … check: clean;./gradlew compileDebugKotlin compileDebugUnitTestKotlin: clean; frontend related jest (51 suites, 326 tests; the identity suite 14 passed after the cache test became the no-cache test),type-check,lint: pass; the five repository gates andci/conformance_evidence.py: pass;make lint: passed.type-check,lint: pass; the five repository gates andci/conformance_evidence.py: pass (no Rust or Kotlin change in this chunk)../gradlew compileDebugKotlin compileDebugUnitTestKotlin: clean; unit testsBridgeEnvelopeCodecTest(63),BridgeEnvelopeCodecBilateralPayloadTest(1),SinglePathWebViewBridgeErrorResponseTest(2),SinglePathWebViewBridgeFuzzTest(5): 71 passed; the five repository gates andci/conformance_evidence.py: pass.cargo check -p dsm --all-targets: clean;dsmlibstate_machine(12 passed);cargo ndk … check: clean;./gradlew compileDebugKotlin compileDebugUnitTestKotlin: clean; unit testsBridgeEnvelopeCodecTest(58),BridgeEnvelopeCodecBilateralPayloadTest(1),SinglePathWebViewBridgeErrorResponseTest(2),SinglePathWebViewBridgeFuzzTest(5): 66 passed; frontend related jest (84 suites, 497 tests),type-check,lint: pass; the five repository gates andci/conformance_evidence.py: pass;make lint: passed.