Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
318 commits
Select commit Hold shift + click to select a range
0066a5a
feat(platform-wallet-storage)!: public SecretStore API exposing Secre…
lklimek May 22, 2026
c636ac0
refactor(platform-wallet-storage): string-only keyring_core From; typ…
lklimek May 22, 2026
a5c5bf0
fix(platform-wallet-storage): box typed FileStoreError into keyring_c…
lklimek May 22, 2026
e1c7fa9
refactor(platform-wallet-storage): remove MemoryCredentialStore; reti…
lklimek May 22, 2026
671ce69
fix(platform-wallet-storage): enforce lowercase-hex service, widen ex…
lklimek May 22, 2026
dc492cc
docs(platform-wallet-storage): strip historical comments + license he…
lklimek May 22, 2026
c58a2b5
feat(platform-wallet-storage): log swallowed mlock + corruption/write…
lklimek May 22, 2026
316c4ee
fix(platform-wallet): serde derives on shielded changeset types
lklimek May 22, 2026
6aa2942
docs(platform-wallet-storage): drop deleted MemoryCredentialStore / _…
lklimek May 22, 2026
f78f2e6
fix(platform-wallet-storage): gate test-only contacts reader off-state
lklimek May 22, 2026
b5a8439
refactor(platform-wallet-storage)!: native FK schema, drop barrel, co…
lklimek May 22, 2026
eb2b6b0
fix(platform-wallet-storage): resolve real account_index for multi-ac…
lklimek May 22, 2026
ce8ca25
fix(platform-wallet-storage): fast-fail mixed-wallet and divergent id…
lklimek May 22, 2026
36577d2
refactor(platform-wallet-storage): drop delete-wallet CLI, propagate …
lklimek May 22, 2026
a996b93
test(platform-wallet-storage): cover #3625 hardening (native FK, mult…
lklimek May 22, 2026
df202ea
test(platform-wallet-storage): assert UTXO row survival in TC-048; sc…
lklimek May 22, 2026
4c9664d
chore(platform-wallet-storage): drop unused key-wallet-manager depend…
lklimek May 22, 2026
b4ffcbc
fix(platform-wallet-storage): canonicalize TC-031 expected dir for ma…
lklimek May 22, 2026
df1e59c
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 25, 2026
9c1bec9
fix(platform-wallet-storage): atomic restore + 0o600 backup file + WA…
lklimek May 25, 2026
36728e5
feat(platform-wallet-storage): cheap pre-staging schema sniff in rest…
lklimek May 25, 2026
8f1d0c4
fix(platform-wallet-storage): restore drained buffer on delete_wallet…
lklimek May 25, 2026
ad3810a
feat(platform-wallet-storage): forward-version gate on open() symmetr…
lklimek May 25, 2026
9b2664e
fix(platform-wallet-storage): serialize delete-wallet against concurr…
lklimek May 25, 2026
ad6c25e
fix(platform-wallet-storage): bounded blob decode + typed BlobTooLarg…
lklimek May 25, 2026
7dd8ef0
fix(platform-wallet-storage): strict consumed-count on identity_keys …
lklimek May 25, 2026
dcf1655
refactor(platform-wallet-storage): collapse list_active into load_sta…
lklimek May 25, 2026
49d429e
test(platform-wallet-storage): assert clap usage exit code Some(2) (C…
lklimek May 25, 2026
614c43d
chore(platform-wallet-storage): clippy cleanups for new test files
lklimek May 25, 2026
cfb93a2
refactor(platform-wallet): seedless watch-only load via Wallet::new_w…
lklimek May 25, 2026
21215d3
refactor(platform-wallet-ffi): drop resolver arg from load_from_persi…
lklimek May 25, 2026
92f849b
fix(swift-sdk): align PlatformWalletManager.loadFromPersistor with se…
lklimek May 25, 2026
3cd4264
style: cargo fmt across seedless-load touch points
lklimek May 25, 2026
f57b117
docs(platform-wallet): adjust rehydration_load test header to reflect…
lklimek May 25, 2026
81ed297
Merge branch 'feat/platform-wallet-storage-secrets' into feat/platfor…
lklimek May 25, 2026
b117ff2
fix(platform-wallet-storage): backup::run_to atomic via NamedTempFile…
lklimek May 25, 2026
9b39783
fix(platform-wallet-storage): restore_from holds exclusive lock + chm…
lklimek May 25, 2026
ae9e629
feat(platform-wallet-storage): commit_writes returns CommitReport on …
lklimek May 25, 2026
fb3362e
feat(platform-wallet-storage): Drop logs uncommitted dirty buffer in …
lklimek May 25, 2026
a23afd9
fix(platform-wallet-storage): expand is_transient to cover I/O-class …
lklimek May 25, 2026
df54420
feat(platform-wallet-storage): backup::prune accumulates per-file err…
lklimek May 25, 2026
df76e1a
feat(platform-wallet-storage): integrity_check before migrations on o…
lklimek May 25, 2026
c0b45c0
refactor(platform-wallet-storage): remove unreachable MigrationDirty …
lklimek May 25, 2026
1840b35
chore(platform-wallet-storage): document ensure_dir TOCTOU probe as b…
lklimek May 25, 2026
722de93
docs(platform-wallet-storage): atomicity contract rustdoc (N-3/N-4/N-…
lklimek May 25, 2026
fe01634
docs(platform-wallet-storage): Drop side-effect comment + restore loc…
lklimek May 25, 2026
34c8ecb
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek May 25, 2026
54621bc
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 26, 2026
db7b6b5
Merge branch 'feat/platform-wallet-sqlite-persistor' into feat/platfo…
lklimek May 26, 2026
543d0da
Merge branch 'feat/platform-wallet-storage-secrets' into feat/platfor…
lklimek May 26, 2026
cf5f87a
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 27, 2026
e7e1de8
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek May 27, 2026
4d21651
Merge remote-tracking branch 'origin/feat/platform-wallet-storage-sec…
lklimek May 27, 2026
e014555
feat(platform-wallet)!: rs-platform-wallet-storage crate (SQLite pers…
Claudius-Maginificent May 27, 2026
64a85a8
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 27, 2026
016987c
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 27, 2026
187a026
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek May 28, 2026
eadae31
chore(Cargo.lock): sync platform-wallet-storage to workspace version …
lklimek May 28, 2026
436c196
fix(platform-wallet-ffi): drop From<String> usage in shielded FFI loa…
lklimek May 28, 2026
03fd3dd
docs(platform-wallet-storage): add SQLite SCHEMA.md with Mermaid ER d…
lklimek May 28, 2026
97713ee
docs(platform-wallet-storage): split SCHEMA.md into domain-grouped di…
lklimek May 28, 2026
aa95add
feat(platform-wallet-storage): enforce enum-domain CHECK constraints …
lklimek May 28, 2026
8c4a88a
feat(platform-wallet-storage): add generic key/value store
lklimek May 28, 2026
9bc1fc2
feat(platform-wallet-storage): gate KV store behind `kv` feature
lklimek May 28, 2026
cd76fcd
ci: bump dorny/paths-filter from v3 (Node 20, deprecated) to v4
lklimek May 28, 2026
22e496d
feat(platform-wallet): keyring_core secret backends — encrypted-file …
Claudius-Maginificent May 29, 2026
aeb1f04
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek May 29, 2026
d04685e
chore(Cargo.lock): bump platform-wallet-storage to workspace 3.1.0-dev.8
lklimek May 29, 2026
9ba47e1
refactor(platform-wallet-storage): drop footprint mechanism + tighten…
lklimek May 29, 2026
8a6fe8a
feat(platform-wallet-storage): cap KV get value size at 16 MiB (CMT-006)
lklimek May 29, 2026
f9b3438
fix(platform-wallet-storage): drop dest lock conn before WAL/SHM unli…
lklimek May 29, 2026
c35be7a
fix(platform-wallet-storage): tighten CLI gates (CMT-010, CMT-014)
lklimek May 29, 2026
5c37004
chore(platform-wallet-storage): apply CMT-007/012/013/016/018/020-022…
lklimek May 29, 2026
9698927
feat(platform-wallet-storage): swap kv_store for six meta_* tables in…
lklimek Jun 1, 2026
1b1c6a4
feat(platform-wallet-storage): reshape KvStore to per-ObjectId scope
lklimek Jun 1, 2026
27188a6
test(platform-wallet-storage): cover per-object metadata (TC-MD-001..…
lklimek Jun 1, 2026
3b4a038
docs(platform-wallet-storage): document meta_* tables in SCHEMA.md
lklimek Jun 1, 2026
08b0ed9
test(platform-wallet-storage): tighten metadata QA assertions + doc f…
lklimek Jun 1, 2026
0556928
fix(platform-wallet-storage): allow metadata writes before parent exi…
lklimek Jun 1, 2026
05d54d2
test(platform-wallet-storage): cover recursive_triggers enforcement +…
lklimek Jun 1, 2026
35e4a2f
refactor(platform-wallet-storage): drop inert recursive_triggers; met…
lklimek Jun 1, 2026
1053caa
fix(platform-wallet-storage): single-read KV get to close size-cap TO…
lklimek Jun 1, 2026
f1f0717
docs(platform-wallet-storage): surface cross-process rollback caveat …
lklimek Jun 1, 2026
d638c4f
refactor(platform-wallet-storage): encode outpoints via bincode (CMT-…
lklimek Jun 1, 2026
3f69c31
test(platform-wallet-storage): tighten malformed-outpoint assertion +…
lklimek Jun 1, 2026
269e578
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek Jun 2, 2026
ffdc28b
fix(platform-wallet): gate shielded-only event_manager field to fix d…
lklimek Jun 2, 2026
9e2d2b0
feat(platform-wallet): add contacts and identity-key rehydration (ite…
Claudius-Maginificent Jun 2, 2026
7c2b2f9
Merge remote-tracking branch 'origin/feat/platform-wallet-sqlite-pers…
lklimek Jun 2, 2026
d99d7a5
chore(platform-wallet): untrack .review-3625 review scratch + gitignore
lklimek Jun 2, 2026
14d868e
refactor(platform-wallet): remove dead wrong-seed-gate scaffolding (#…
lklimek Jun 2, 2026
0a9c972
Merge remote-tracking branch 'origin/feat/platform-wallet-rehydration…
lklimek Jun 2, 2026
c3cf8b0
fix(platform-wallet-storage): reconcile identity_keys schema with #36…
lklimek Jun 2, 2026
ddfa66e
test(platform-wallet-storage): pin identity_keys dual-FK cascade + no…
lklimek Jun 2, 2026
e78eb55
fix(platform-wallet-storage): enforce identity-wallet precondition at…
lklimek Jun 2, 2026
4e38975
fix(platform-wallet-storage): drop keyutils backend + dependency (CMT…
lklimek Jun 2, 2026
3fc28b9
refactor(platform-wallet-storage): rename SecretStoreError + IO path …
lklimek Jun 2, 2026
932b923
refactor(platform-wallet-storage): unify contact tables into one life…
lklimek Jun 2, 2026
052db80
Merge commit '932b923b2b277dc80c7c0dd59a332e0ab7dc76b1' into feat/pla…
lklimek Jun 2, 2026
3166090
Merge branch 'v3.1-dev' into feat/platform-wallet-sqlite-persistor
lklimek Jun 3, 2026
2f35190
Merge #3625 (feat/platform-wallet-sqlite-persistor) into rehydration
lklimek Jun 3, 2026
0466241
chore: update Cargo.lock
lklimek Jun 3, 2026
3f2e7d2
Merge #3625 (feat/platform-wallet-sqlite-persistor @ 04662411cf) into…
lklimek Jun 3, 2026
89a433a
build(wallet-storage): exact-pin bincode and getrandom; strip transie…
lklimek Jun 3, 2026
d241f61
fix(platform-wallet-storage): make meta_* soft-cascade state-agnostic…
lklimek Jun 3, 2026
57f0f72
fix(platform-wallet-storage): promote contact to established when bot…
lklimek Jun 3, 2026
0f7b7ae
fix(platform-wallet-storage): reject unbucketable unspent UTXOs and m…
lklimek Jun 3, 2026
569a19a
refactor(platform-wallet-storage): drop per-table row counting from d…
lklimek Jun 3, 2026
0435f3e
refactor(platform-wallet): slim persistence trait and relocate report…
lklimek Jun 3, 2026
19e8980
docs(platform-wallet): record deferred persistence contract and FFI f…
lklimek Jun 3, 2026
4bc8d2a
fix(wallet-storage): drop SQLITE_OPEN_URI from the read-only open path
lklimek Jun 3, 2026
bab3c63
refactor(wallet-storage): fix secrets error taxonomy and unify wallet…
lklimek Jun 3, 2026
0bba1f7
docs(wallet-storage): strip transient review IDs from backup comments
lklimek Jun 3, 2026
1b89b4b
docs(wallet-storage): align README/SCHEMA/SECRETS with shipped behaviour
lklimek Jun 3, 2026
f4aa2df
feat(platform-wallet-storage): reconstruct platform per_account state…
lklimek Jun 3, 2026
17ea61f
feat(platform-wallet-storage): cap KV value size on put and document …
lklimek Jun 3, 2026
30955c6
refactor(platform-wallet-storage): narrow the public module surface t…
lklimek Jun 3, 2026
0abdb12
fix(platform-wallet-storage): pre-create the SQLite DB owner-only to …
lklimek Jun 3, 2026
c6dbc11
test(platform-wallet-storage): add cascade-completeness test and prun…
lklimek Jun 3, 2026
30147e0
docs(platform-wallet-storage): strip transient review IDs from core s…
lklimek Jun 3, 2026
97be1e9
docs(platform-wallet-storage): strip transient review IDs from edited…
lklimek Jun 3, 2026
522d4ce
chore(platform-wallet-storage): finish review-ID strip, fix secret-sc…
lklimek Jun 3, 2026
3eac940
merge: integrate deps/docs/secrets/conn tail into #3625 fix batch
lklimek Jun 3, 2026
a90c84d
docs(platform-wallet-storage): document zero-pad diagnostic risk + st…
lklimek Jun 3, 2026
1d7c94d
docs(platform-wallet-storage): fix secrets-guard test ref + honest me…
lklimek Jun 3, 2026
bfc34cc
test(platform-wallet-storage): assert reconstructed per-account state
lklimek Jun 3, 2026
c92e9f9
test(platform-wallet-storage): catch over-broad wallet-delete cascade
lklimek Jun 3, 2026
b262e1b
chore(platform-wallet-storage): strip transient review IDs from comments
lklimek Jun 3, 2026
6f1e3e4
docs(platform-wallet-storage): document orphan metadata as accepted l…
lklimek Jun 3, 2026
c9d1c81
docs(platform-wallet-storage): lead crate docs with why/value before …
lklimek Jun 3, 2026
7de6678
refactor(platform-wallet-storage): rename wallet_metadata table to wa…
lklimek Jun 8, 2026
f69746d
merge: propagate #3625 (platform-wallet-storage incl. wallets rename)…
lklimek Jun 8, 2026
8ba49c3
test(platform-wallet-storage): reconcile #3692 tests with #3625's str…
lklimek Jun 8, 2026
9e1248c
fix(platform-wallet-storage): un-gate schema readers used by producti…
lklimek Jun 8, 2026
5a9cc73
test(platform-wallet-storage): cover garbage/truncated/unknown-versio…
lklimek Jun 8, 2026
6dba193
chore(platform-wallet-storage): harden secrets dependency closure
lklimek Jun 8, 2026
62e9c96
refactor(platform-wallet-storage): granular AEAD errors + typed salt …
lklimek Jun 8, 2026
f2f54a9
fix(platform-wallet-storage): harden the encrypted-file vault seams
lklimek Jun 8, 2026
a7e5d85
fix(platform-wallet-storage): authenticate the KDF header via verify-…
lklimek Jun 8, 2026
77ec94b
test(platform-wallet-storage): sound zeroize coverage + SPI seam doc
lklimek Jun 8, 2026
bdf2c27
chore(platform-wallet-storage): record argon2 zeroize edge in Cargo.lock
lklimek Jun 8, 2026
6e6a78d
refactor(platform-wallet-storage): graceful put rollback + write-bit …
lklimek Jun 8, 2026
86c5e63
feat(platform-wallet-storage): seal the no-key-material-in-DB invariant
lklimek Jun 8, 2026
f07f3c5
docs(platform-wallet-storage): keyring metadata + SPI error-format ha…
lklimek Jun 8, 2026
f36b719
test(platform-wallet-storage): fuzz the vault parser + tamper coverage
lklimek Jun 8, 2026
ff296df
docs(platform-wallet-storage): annotate accepted risks + narrow unsaf…
lklimek Jun 8, 2026
409dd5a
test(platform-wallet-storage): secure integration tempdir for parent-…
lklimek Jun 8, 2026
6502171
test(platform-wallet-storage): honest header-tamper tests + verify-to…
lklimek Jun 8, 2026
975cd47
test(platform-wallet-storage): pin delete_wallet pre-flush BEGIN-EXCL…
lklimek Jun 8, 2026
0bacb45
test(platform-wallet-storage): cover IdentityChangeSet.removed tombst…
lklimek Jun 8, 2026
88cee36
test(platform-wallet-storage): cover money-column read overflow + com…
lklimek Jun 8, 2026
5949439
test(platform-wallet-storage): pin prune embedded-timestamp parse + s…
lklimek Jun 8, 2026
f543e31
test(platform-wallet-storage): cover FK parent-before-child ordering …
lklimek Jun 8, 2026
af32973
fix(platform-wallet-storage): restore buffer when pre-flush BEGIN EXC…
lklimek Jun 8, 2026
cc33189
feat(platform-wallet-storage): wallet-DB identity gates + journal-mod…
lklimek Jun 8, 2026
d4523c0
feat(platform-wallet-storage): refuse a second in-process open on the…
lklimek Jun 8, 2026
e42f64b
fix(platform-wallet-storage): wallet-scope identity tombstone; correc…
lklimek Jun 8, 2026
08239c1
refactor(platform-wallet-storage): centralize i64->u32 boundary casts…
lklimek Jun 8, 2026
8e50d48
fix(platform-wallet-storage): fail-hard watermark, deterministic utxo…
lklimek Jun 8, 2026
40f9997
docs(platform-wallet-storage): reconcile README/SCHEMA/SECRETS with s…
lklimek Jun 8, 2026
475e3ae
docs(platform-wallet-storage): scope vault cross-process lock to loca…
lklimek Jun 8, 2026
e79e6cf
fix(platform-wallet-storage): drop SQLITE_OPEN_URI from CLI schema peek
lklimek Jun 8, 2026
039596d
chore(platform-wallet-storage): acknowledge RUSTSEC-2025-0141 (bincod…
lklimek Jun 8, 2026
9d40bdb
style(platform-wallet-storage): collapse single-line match arm per ru…
lklimek Jun 8, 2026
b4a3aa9
test(platform-wallet-storage): rename cascade test to reflect actual …
lklimek Jun 8, 2026
66c2142
docs(platform-wallet-storage): compact verbose comments in sqlite core
lklimek Jun 9, 2026
d73071f
docs(platform-wallet-storage): compact verbose comments in secrets mo…
lklimek Jun 9, 2026
197949d
docs(platform-wallet-storage): compact verbose comments in schema mod…
lklimek Jun 9, 2026
0ceb56e
docs(platform-wallet-storage): compact verbose comments in tests
lklimek Jun 9, 2026
60f2bd7
fix(platform-wallet-storage): repair comment-edit fallout (clippy doc…
lklimek Jun 9, 2026
855471e
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek Jun 9, 2026
2d22cf3
merge: propagate v3.1-dev (shielded spend detection #3819, IdentityCr…
lklimek Jun 9, 2026
20b20e7
fix(platform-wallet-storage): rehydrate core_derived_addresses from p…
lklimek Jun 9, 2026
4f432c9
fix(platform-wallet-storage): repair partial-state derived-address re…
lklimek Jun 9, 2026
67d0eba
fix(platform-wallet-storage): harden core_derived_addresses with BIP3…
lklimek Jun 10, 2026
ff1208a
test(platform-wallet-storage): close core_derived_addresses coverage …
lklimek Jun 10, 2026
d8d2239
fix(platform-wallet-storage): fail loud when a pool-declared address …
lklimek Jun 11, 2026
cccd217
test(platform-wallet-storage): assert resolved account_index of the s…
lklimek Jun 11, 2026
63fea7a
fix(platform-wallet): emit in-band pool snapshot on derivation so acc…
lklimek Jun 11, 2026
f7b6136
test(platform-wallet-storage): prove in-band pool-snapshot resolution…
lklimek Jun 11, 2026
ebb4b30
refactor(platform-wallet-storage): replace pool mirror/reconcile with…
lklimek Jun 11, 2026
50a4a0a
Merge remote-tracking branch 'origin/v3.1-dev' into merge/wallet-rehy…
lklimek Jun 15, 2026
925dfcb
Merge branch 'merge/wallet-rehydration' into merge/wallet-core-derived
lklimek Jun 15, 2026
156bd98
fix(platform-wallet-storage): add account_index to core_derived_addre…
lklimek Jun 16, 2026
925b109
fix(platform-wallet-storage): repair label-split fallout in pool_type…
lklimek Jun 16, 2026
b450649
fix(platform-wallet): add background_generation guard to PlatformAddr…
lklimek Jun 18, 2026
1f3ea29
fix(platform-wallet): close shielded_sync generation-guard TOCTOU (lo…
lklimek Jun 18, 2026
fa4584d
docs(platform-wallet-storage): update stale doc comment on ACCOUNT_IN…
lklimek Jun 22, 2026
aed5652
docs(platform-wallet): correct CHECK-column count and port generation…
lklimek Jun 22, 2026
ca68690
test(platform-wallet): cover generation-guard restart and contacts.st…
lklimek Jun 22, 2026
8d8724e
refactor(platform-wallet)!: hardcode core UTXO account_index=0; retir…
lklimek Jun 22, 2026
e8308ed
fix(platform-wallet): persist non-default-account UTXOs under index 0…
lklimek Jun 22, 2026
ea0082e
docs(platform-wallet-storage): drop deleted-table refs from accounts.…
lklimek Jun 22, 2026
a4ed0b5
feat(platform-wallet-storage): SecretString serde/schemars/is_blank (…
lklimek Jun 22, 2026
1827a39
feat(platform-wallet-storage): error taxonomy for Tier-2 secret prote…
lklimek Jun 22, 2026
f68f00e
feat(platform-wallet-storage): Tier-2 secret envelope (wrap/unwrap)
lklimek Jun 22, 2026
491229b
feat(platform-wallet-storage)!: strict fail-closed Tier-2 read (L-1 k…
lklimek Jun 22, 2026
c19c23b
feat(platform-wallet-storage): SecretStore Tier-2 write API + reprotect
lklimek Jun 22, 2026
d4d311c
feat(platform-wallet-storage)!: Tier-1 blank-passphrase guard + open_…
lklimek Jun 22, 2026
351d1e6
refactor(platform-wallet-storage): use keyring_core::mock::Store; ann…
lklimek Jun 22, 2026
d3df41e
docs(platform-wallet-storage): QA fixes — rustdoc clarity + ephemeral…
lklimek Jun 22, 2026
b5ede7d
test(platform-wallet-storage): QA fixes — Os read bound, Os crash tes…
lklimek Jun 22, 2026
fb7953e
test(platform-wallet-storage): cover Os read-size guard; pin new() so…
lklimek Jun 22, 2026
f3354f6
feat(platform-wallet)!: shutdown() joins coordinator threads and retu…
lklimek Jun 22, 2026
261178e
fix(platform-wallet): RAII-guard is_syncing so a coordinator panic ca…
lklimek Jun 22, 2026
42d734d
refactor(rs-dash-async): add AtomicFlagGuard RAII helper
lklimek Jun 23, 2026
6e78b77
fix(platform-wallet): refine CoordinatorThreadStatus variants + tight…
lklimek Jun 23, 2026
5f80450
test(rs-dash-async): assert AtomicFlagGuard contract + add #[must_use]
lklimek Jun 23, 2026
6b2cd39
fix(platform-wallet): make coordinator passes cancellable + converge …
lklimek Jun 23, 2026
13a22dd
fix(platform-wallet): bound clear_shielded + tidy shutdown docs/logging
lklimek Jun 23, 2026
93b8954
fix(platform-wallet-ffi): timeout-bound the shielded sync stop bridge
lklimek Jun 23, 2026
747f5f0
Merge branch 'v3.1-dev' into feat/platform-wallet-shutdown-join
lklimek Jun 23, 2026
2bd9501
fix(platform-wallet)!: close residual coordinator-thread UAF on shutdown
lklimek Jun 23, 2026
7c975ed
fix(platform-wallet)!: surface non-clean shielded drain on clear/stop
lklimek Jun 23, 2026
5f63c95
fix(platform-wallet): reap prior coordinator thread outside backgroun…
lklimek Jun 23, 2026
2b068ba
fix(platform-wallet): close shielded epilogue TOCTOU + pin restart reap
lklimek Jun 24, 2026
5017ba1
fix(swift-sdk): retain wallet callback context on incomplete shutdown
lklimek Jun 24, 2026
b491773
test(platform-wallet): bound cleanup quiesce in restart-reap regressi…
lklimek Jun 24, 2026
76c8bee
fix(platform-wallet): track detached coordinator threads so shutdown(…
lklimek Jun 24, 2026
3cca1cf
perf(platform-wallet): drain coordinators concurrently in shutdown() …
lklimek Jun 24, 2026
8c52811
feat(dash-async): add shared ThreadRegistry worker-lifecycle engine
lklimek Jun 24, 2026
ac9a51a
feat(dash-async): key-scope parked orphans for any_alive_for()
lklimek Jun 24, 2026
d20aed0
refactor(platform-wallet): migrate sync coordinators onto shared Thre…
lklimek Jun 24, 2026
d190f29
test(dash-async): anchor DrainHook compile_fail doctest to E0277 + no…
lklimek Jun 24, 2026
3e81fc1
fix(dash-async,platform-wallet): harden ThreadRegistry lifecycle + do…
lklimek Jun 24, 2026
911f99f
refactor(platform-wallet): extract CoordinatorLifecycle to dedup the …
lklimek Jun 24, 2026
22647a7
fix(platform-wallet): raise quiescing gate in CoordinatorLifecycle::q…
lklimek Jun 25, 2026
7f3aeb5
fix(dash-async): park a restarted worker's prior under the slot lock …
lklimek Jun 25, 2026
41791c0
fix(platform-wallet-ffi): gate shielded_sync_stop success on orphan l…
lklimek Jun 25, 2026
4b099a9
fix(platform-wallet): bound clear_shielded's drain and hold its quies…
lklimek Jun 25, 2026
7be68c5
refactor(dash-async): full spawn-failure rollback + drop stale doc hi…
lklimek Jun 25, 2026
3821389
docs(swift-sdk): broaden deinit comment for shielded_sync_stop's orph…
lklimek Jun 25, 2026
748c4f8
fix(platform-wallet): make the quiescing<->is_syncing handshake self-…
lklimek Jun 25, 2026
0e73de7
fix(rs-dash-async): surface reaped-orphan terminal status in Shutdown…
lklimek Jun 25, 2026
e762bb5
docs(platform-wallet): correct coordinator stop() docs — pass cancell…
lklimek Jun 25, 2026
f94fed9
fix(platform-wallet-ffi): make shielded_sync_stop cancel-only — elimi…
lklimek Jun 25, 2026
97d3a21
fix(platform-wallet): drain buffered wallet events on cancel in event…
lklimek Jun 25, 2026
43cbfce
docs(pr3954): apply QA findings from Marvin/Adams — trim, drop tombst…
lklimek Jun 25, 2026
bc16d9f
Merge remote-tracking branch 'origin/v3.1-dev' into feat/platform-wal…
lklimek Jun 25, 2026
6ed5200
fix(pr3954): apply grumpy-review findings — SEC-001, RUST-001/004-007…
lklimek Jun 25, 2026
f11a9e0
refactor(platform-wallet): hoist coordinator poll-loop into Coordinat…
lklimek Jun 26, 2026
3acfbcb
refactor(platform-wallet): collapse CoordinatorThreadStatus/Coordinat…
lklimek Jun 26, 2026
b43a348
refactor(platform-wallet): collapse CoordinatorThreadStatus / Coordin…
lklimek Jun 26, 2026
801f35b
feat(rs-dash-async): per-key clearing latch closes the T11 RESIDUAL (…
lklimek Jun 26, 2026
284752a
docs(rs-dash-async,platform-wallet): comment-hygiene sweep on the Thr…
lklimek Jun 26, 2026
97e373b
fix(swift-sdk): mark deinit-accessed handler properties nonisolated(u…
lklimek Jun 26, 2026
432309b
fix(platform-wallet): spawn_periodic_loop must not lower the quiescin…
lklimek Jun 26, 2026
9b3d6f8
merge: PR #3692 + #3828 + #3953 stack into feat/platform-wallet-shutd…
lklimek Jun 26, 2026
bcf9388
merge: pull the PR #3692 + #3828 + #3953 stack onto feat/platform-wal…
lklimek Jun 26, 2026
6b655aa
fix(rs-dash-async,platform-wallet): refcount ClearingGuard + gate pub…
lklimek Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions packages/rs-dash-async/src/atomic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::sync::atomic::{AtomicBool, Ordering};

/// RAII guard that clears an [`AtomicBool`] flag to `false` on drop.
///
/// Callers set the flag to `true` before constructing the guard (typically
/// via a `compare_exchange`); the guard resets it on every exit path,
/// including panics, so a panicked holder can never leave the flag wedged.
///
/// **Panic-strategy caveat:** the clear-on-panic guarantee relies on
/// destructors running while the stack unwinds, so it holds under
/// `panic = "unwind"` (the default). Under `panic = "abort"` — e.g. the
/// iOS release profiles — a panic aborts the process immediately and no
/// `Drop` runs; there is simply no "after" left for the flag to gate.
#[must_use = "AtomicFlagGuard clears the flag on drop; binding to `_` or using as a statement drops it immediately"]
pub struct AtomicFlagGuard<'a>(&'a AtomicBool);
Comment thread
Claudius-Maginificent marked this conversation as resolved.

impl<'a> AtomicFlagGuard<'a> {
/// Wrap `flag`. Does **not** set it to `true` — the caller is
/// responsible for doing that before constructing the guard.
pub fn new(flag: &'a AtomicBool) -> Self {
Comment thread
Claudius-Maginificent marked this conversation as resolved.
Self(flag)
}
}

impl Drop for AtomicFlagGuard<'_> {
fn drop(&mut self) {
self.0.store(false, Ordering::Release);
Comment thread
Claudius-Maginificent marked this conversation as resolved.
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::panic::{catch_unwind, AssertUnwindSafe};

/// A guard constructed over a `true` flag holds it while in scope and
/// clears it to `false` on a normal scope exit.
#[test]
fn clears_flag_on_normal_drop() {
let flag = AtomicBool::new(true);
{
let _guard = AtomicFlagGuard::new(&flag);
assert!(flag.load(Ordering::Acquire), "flag stays set while held");
}
assert!(!flag.load(Ordering::Acquire), "flag cleared on drop");
}

/// The clear also runs while unwinding a panic — the load-bearing
/// property the sync coordinators lean on so a panicked pass can't
/// leave `is_syncing` latched and wedge `quiesce()`'s drain.
#[test]
fn clears_flag_while_unwinding_panic() {
let flag = AtomicBool::new(true);
let result = catch_unwind(AssertUnwindSafe(|| {
let _guard = AtomicFlagGuard::new(&flag);
panic!("boom while holding the guard");
}));
assert!(result.is_err(), "the panic propagated out of catch_unwind");
assert!(
!flag.load(Ordering::Acquire),
"Drop ran during unwinding and cleared the flag"
);
}
}
4 changes: 4 additions & 0 deletions packages/rs-dash-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
//!
//! Provides [`block_on`] -- a function that bridges async futures into sync code,
//! handling multiple tokio runtime flavors (no runtime, current-thread, multi-thread).
//!
//! Also provides [`AtomicFlagGuard`] — a RAII guard for panic-safe `AtomicBool` flag resets.

mod atomic;
mod block_on;

pub use atomic::AtomicFlagGuard;
pub use block_on::{block_on, AsyncError};
2 changes: 1 addition & 1 deletion packages/rs-platform-wallet-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ rs-sdk-ffi = { path = "../rs-sdk-ffi" }
once_cell = "1.19"
parking_lot = { version = "0.12", features = ["send_guard"] }
lazy_static = "1.4"
tokio = { version = "1", features = ["rt-multi-thread"] }
tokio = { version = "1", features = ["rt-multi-thread", "time"] }
tokio-metrics = { workspace = true, optional = true }

# Core dependencies (for Network type)
Expand Down
58 changes: 58 additions & 0 deletions packages/rs-platform-wallet-ffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,33 @@ pub enum PlatformWalletFFIResultCode {
/// and could double-send if the original spend landed.
ErrorShieldedSpendUnconfirmed = 18,

/// A background coordinator drain did not complete cleanly within the
/// join deadline — one or more `!Send` sync threads may still be alive
/// and still hold a reference to the host-owned callback context, so they
/// could fire one final callback through it. On this code the host **must
/// not** free the callback context immediately: either keep it alive for a
/// further grace period, or accept the (statistically tiny) race.
///
/// Returned by three callers, which differ in whether the operation may
/// be **retried**:
/// - `platform_wallet_manager_destroy`: the manager **IS** torn down
/// (removed from storage) regardless — do **not** retry `destroy`; the
/// handle is already gone. Only the callback-context lifetime caveat
/// above applies.
/// - `platform_wallet_manager_shielded_sync_stop`: the manager is **NOT**
/// torn down — only the shielded loop's drain was non-clean. The host
/// may retry the stop (or proceed to `destroy`); the handle stays valid.
/// - `platform_wallet_manager_shielded_clear`: the manager is **NOT** torn
/// down and the store was left **intact** (Clear aborted before touching
/// it). The host may retry the clear, and must **not** commit its own
/// persistence wipe — doing so would desync the host's rows from the
/// still-populated shared tree.
///
/// Distinct from a normal operation error (the underlying operation may
/// well have made progress); the terminal coordinator status is rendered
/// into the result message.
ErrorShutdownIncomplete = 19,

NotFound = 98, // Used exclusively for all the Option that are retuned as errors
ErrorUnknown = 99,
}
Expand Down Expand Up @@ -237,6 +264,14 @@ impl From<PlatformWalletError> for PlatformWalletFFIResult {
PlatformWalletError::ShieldedSpendUnconfirmed { .. } => {
PlatformWalletFFIResultCode::ErrorShieldedSpendUnconfirmed
}
// A Clear that refused because the in-flight shielded pass didn't
// drain cleanly: surface it as ErrorShutdownIncomplete (symmetric
// with `platform_wallet_manager_destroy`) so the host defers
// freeing its callback context AND does not commit its own
// persistence wipe — the store was intentionally left intact.
PlatformWalletError::ShieldedShutdownIncomplete { .. } => {
PlatformWalletFFIResultCode::ErrorShutdownIncomplete
}
_ => PlatformWalletFFIResultCode::ErrorUnknown,
};
PlatformWalletFFIResult::err(code, error.to_string())
Expand Down Expand Up @@ -595,6 +630,29 @@ mod tests {
assert_eq!(msg, rendered, "Display payload must survive verbatim");
}

/// A Clear that refused on a non-clean shielded drain must surface as
/// `ErrorShutdownIncomplete` (symmetric with `destroy`), not flatten to
/// `ErrorUnknown`, so the host knows to defer freeing its callback
/// context and to NOT commit its own persistence wipe. The typed Display
/// rendering (carrying the terminal coordinator status) survives verbatim.
#[test]
fn shielded_shutdown_incomplete_maps_to_dedicated_code() {
let err = PlatformWalletError::ShieldedShutdownIncomplete {
status: platform_wallet::CoordinatorThreadStatus::Timeout,
};
let rendered = err.to_string();
let result: PlatformWalletFFIResult = err.into();
assert_eq!(
result.code,
PlatformWalletFFIResultCode::ErrorShutdownIncomplete,
"ShieldedShutdownIncomplete should map to ErrorShutdownIncomplete (rendered: {rendered})"
);
let msg = unsafe { std::ffi::CStr::from_ptr(result.message) }
.to_string_lossy()
.into_owned();
assert_eq!(msg, rendered, "Display payload must survive verbatim");
}

/// Other wallet-error variants without a dedicated FFI arm still
/// fall through to `ErrorUnknown` while carrying the typed
/// Display rendering as the message. Pin this so the catch-all
Expand Down
24 changes: 23 additions & 1 deletion packages/rs-platform-wallet-ffi/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,29 @@ pub unsafe extern "C" fn platform_wallet_manager_destroy(
// left alive to fire a callback against freed memory.
// `shutdown()` is idempotent, so this is safe even if the host
// already stopped some sync managers before calling destroy.
runtime().block_on(manager.shutdown());
// It now joins the coordinator OS threads and returns their
// per-thread exit status; the C ABI exposes none of that, so we
// just log it (a panicked loop is worth surfacing) and drop it.
let status = runtime().block_on(manager.shutdown());
if !status.all_clean() {
tracing::warn!(
?status,
"platform wallet coordinator(s) did not exit cleanly; \
host must not free the callback context immediately"
);
// Return a distinct non-ok code so the host can delay freeing
// its callback context. A lingering coordinator thread (e.g. one
// that timed out) still holds an Arc to the event handler and may
// fire one final callback through the host-owned context pointer;
// returning ok() here would signal that the context is safe to
// free when it may not be yet.
return PlatformWalletFFIResult::err(
PlatformWalletFFIResultCode::ErrorShutdownIncomplete,
format!("coordinator(s) did not exit cleanly: {status:?}"),
);
} else {
tracing::debug!(?status, "platform wallet coordinators joined cleanly");
}
}
PlatformWalletFFIResult::ok()
Comment thread
Claudius-Maginificent marked this conversation as resolved.
}
Expand Down
68 changes: 59 additions & 9 deletions packages/rs-platform-wallet-ffi/src/shielded_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,20 @@ pub unsafe extern "C" fn platform_wallet_manager_shielded_sync_start(
/// Stop the shielded sync manager and wait for any in-flight pass to
/// drain before returning. No-op if not running.
///
/// Uses `quiesce` rather than cancel-only stop, so on return: the loop
/// is cancelled, no new pass will start, and any in-flight pass has
/// Uses `quiesce` rather than cancel-only stop, so on a clean return: the
/// loop is cancelled, no new pass will start, and any in-flight pass has
/// fully drained — its **persistence callbacks have completed** (no
/// note/sync-state row can be written after this returns) and its
/// completion-event *dispatch* on the Rust side has run.
///
/// Returns `ErrorShutdownIncomplete` instead of `Success` when that drain
/// did **not** complete cleanly (the in-flight pass timed out on the join
/// backstop, or the loop ended non-cleanly). The terminal coordinator
/// status is rendered into the result message. On this code the host must
/// **not** free the callback context immediately — a lingering pass may
/// still fire one final callback through it (symmetric with
/// `platform_wallet_manager_destroy`).
///
/// Caveat on host-observed events: a host that marshals the completion
/// callback onto its own executor (e.g. the Swift trampoline hops it to
/// the `@MainActor`) may still observe that final, already-dispatched
Expand All @@ -88,9 +96,40 @@ pub unsafe extern "C" fn platform_wallet_manager_shielded_sync_stop(
handle: Handle,
) -> PlatformWalletFFIResult {
let option = PLATFORM_WALLET_MANAGER_STORAGE.with_item(handle, |manager| {
runtime().block_on(manager.shielded_sync().quiesce());
runtime().block_on(async {
// Bound the quiesce with the same backstop `shutdown()` uses so
// a stalled in-flight pass can't hang the host's stop call
// forever. Cancellation makes the drain prompt; this only
// matters if a pass's drop wedges. A timeout (the future was
// dropped at the deadline) is reported as the non-clean
// `Timeout` status, matching `shutdown()`'s backstop
// substitution, so the host learns the drain may be incomplete.
match tokio::time::timeout(
Duration::from_secs(platform_wallet::SHUTDOWN_JOIN_TIMEOUT_SECS),
manager.shielded_sync().quiesce(),
)
.await
{
Ok(status) => status,
Err(_elapsed) => platform_wallet::CoordinatorThreadStatus::Timeout,
}
})
});
unwrap_option_or_return!(option);
let status = unwrap_option_or_return!(option);
// Symmetric with `platform_wallet_manager_destroy`: a non-clean drain
// means the shielded loop may still hold a reference to the host-owned
// event-handler / persister context and could fire one final callback,
// so signal the host to defer freeing that context rather than returning
// ok() and inviting a use-after-free.
if !status.is_clean() {
return PlatformWalletFFIResult::err(
PlatformWalletFFIResultCode::ErrorShutdownIncomplete,
format!(
"shielded sync stop did not drain cleanly ({status:?}); \
host must not free the callback context immediately"
),
);
}
PlatformWalletFFIResult::ok()
}
Comment thread
Claudius-Maginificent marked this conversation as resolved.

Expand Down Expand Up @@ -417,7 +456,9 @@ pub unsafe extern "C" fn platform_wallet_manager_configure_shielded(
/// via the changeset path.
///
/// Returns `ErrorWalletOperation` if the Rust-side store reset
/// fails. The host **must** check this before wiping its own
/// fails, or `ErrorShutdownIncomplete` if the in-flight sync pass
/// did not drain cleanly first (in which case the store is left
/// intact). The host **must** check this before wiping its own
/// persistence: a silent failure would leave the shared tree
/// populated while the host drops its rows, and the next cold
/// resync would gate-skip every re-downloaded position against the
Expand All @@ -443,10 +484,19 @@ pub unsafe extern "C" fn platform_wallet_manager_shielded_clear(
});
let result = unwrap_option_or_return!(option);
if let Err(e) = result {
return PlatformWalletFFIResult::err(
PlatformWalletFFIResultCode::ErrorWalletOperation,
format!("clear_shielded failed: {e}"),
);
// A non-clean / timed-out quiesce aborts the clear *before* the store
// is touched: surface it as ErrorShutdownIncomplete (symmetric with
// destroy / shielded_sync_stop) so the host defers freeing its
// callback context and does NOT commit its own persistence wipe — the
// store was intentionally left intact. Every other clear failure is a
// store-reset error → ErrorWalletOperation, as before.
let code = match &e {
platform_wallet::PlatformWalletError::ShieldedShutdownIncomplete { .. } => {
PlatformWalletFFIResultCode::ErrorShutdownIncomplete
}
_ => PlatformWalletFFIResultCode::ErrorWalletOperation,
};
return PlatformWalletFFIResult::err(code, format!("clear_shielded failed: {e}"));
}
PlatformWalletFFIResult::ok()
}
Expand Down
1 change: 1 addition & 0 deletions packages/rs-platform-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bimap = "0.6"
# Async runtime
tokio = { version = "1", features = ["sync", "rt", "time", "macros"] }
tokio-util = { version = "0.7.12" }
dash-async = { path = "../rs-dash-async" }

# Logging
tracing = "0.1"
Expand Down
21 changes: 21 additions & 0 deletions packages/rs-platform-wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,27 @@ pub enum PlatformWalletError {

#[error("Shielded sub-wallet not bound: call bind_shielded first")]
ShieldedNotBound,

/// A Clear/wipe could not safely complete because the shielded sync
/// coordinator's in-flight pass did not drain cleanly first — it either
/// timed out on the join backstop or its loop ended non-cleanly
/// (cancelled / panicked). The shared commitment-tree store is therefore
/// **left intact** (not wiped): a still-running pass could re-persist
/// notes into the store immediately after a `clear()`, desyncing the
/// host's wiped rows from a repopulated tree and gate-skipping every
/// re-downloaded position on the next cold resync. The host **must not**
/// commit its own persistence wipe; retry Clear once the pass settles.
/// Carries the terminal [`CoordinatorThreadStatus`] for diagnostics.
///
/// [`CoordinatorThreadStatus`]: crate::manager::CoordinatorThreadStatus
#[error(
"shielded clear aborted: sync coordinator did not drain cleanly \
({status:?}); commitment-tree store left intact so an in-flight pass \
cannot re-persist into a wiped store — retry once the pass settles"
)]
ShieldedShutdownIncomplete {
status: crate::manager::CoordinatorThreadStatus,
},
}

/// Check whether an SDK error indicates that an InstantSend lock proof was
Expand Down
5 changes: 4 additions & 1 deletion packages/rs-platform-wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ pub use manager::platform_address_sync::{
PlatformAddressSyncManager, PlatformAddressSyncSummary, WalletSyncOutcome,
DEFAULT_SYNC_INTERVAL_SECS,
};
pub use manager::PlatformWalletManager;
pub use manager::{
CoordinatorExitStatus, CoordinatorThreadStatus, PlatformWalletManager,
SHUTDOWN_JOIN_TIMEOUT_SECS,
};
pub use spv::SpvRuntime;
pub use wallet::asset_lock::manager::AssetLockManager;
pub use wallet::asset_lock::tracked::{AssetLockStatus, TrackedAssetLock};
Expand Down
Loading
Loading