Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
057046f
feat(swift-sdk): add BLAST address sync for iOS (#3079)
QuantumExplorer Mar 17, 2026
a04b890
chore: cargo fmt
QuantumExplorer Mar 17, 2026
eb41d36
merge: resolve conflict with v3.1-dev in CoreWalletManager
QuantumExplorer Mar 17, 2026
e33556c
fix: address CodeRabbit review feedback
QuantumExplorer Mar 17, 2026
04aedca
fix(swift-sdk): validate Core addresses via Rust FFI before parsing
QuantumExplorer Mar 17, 2026
fb93ef8
fix(sdk): fix clippy warning for compacted loop condition
QuantumExplorer Mar 17, 2026
a32e3df
fix: outKeyLen capacity bug and restore compacted catch-up
QuantumExplorer Mar 18, 2026
a97e642
merge: resolve v3.1-dev conflicts and fix optional API changes
QuantumExplorer Mar 18, 2026
bc1720d
fix(rs-sdk-ffi): use Network::Mainnet instead of Network::Dash
QuantumExplorer Mar 18, 2026
6e8cf0c
fix(rs-sdk-ffi): fix clippy needless_range_loop warnings
QuantumExplorer Mar 18, 2026
fc1e8fb
feat(sdk): rewrite incremental catch-up and document sync flow
QuantumExplorer Mar 18, 2026
28c256b
test(drive-abci): add BLAST sync full flow strategy test
QuantumExplorer Mar 18, 2026
f78fef7
fix(test): fix BLAST sync strategy test signer issue
QuantumExplorer Mar 18, 2026
3c4698e
feat(sdk): skip compacted queries via key_exists_as_boundary
QuantumExplorer Mar 18, 2026
5c6052b
fix(swift-sdk): wrap ShieldedPoolClient handle in Sendable wrapper
QuantumExplorer Mar 18, 2026
b282f5a
docs(sdk): add full BLAST sync flowchart to book
QuantumExplorer Mar 18, 2026
73f6bf5
docs(sdk): replace ASCII flowchart with Mermaid diagram
QuantumExplorer Mar 18, 2026
675e53b
ci: add book preview deployment for PRs
QuantumExplorer Mar 18, 2026
c4b10b9
ci: auto-cleanup book preview on PR close/merge
QuantumExplorer Mar 18, 2026
3d5d73c
fix(ci): use artifacts instead of Pages for book PR previews
QuantumExplorer Mar 18, 2026
7c1847f
docs(sdk): improve BLAST sync book with mermaid, privacy, performance
QuantumExplorer Mar 18, 2026
429a8c9
fix(ci): remove nonisolated deinit and add SDK test coverage
QuantumExplorer Mar 18, 2026
ea9f3e3
feat(sdk): implement RangeAfter-based compaction detection
QuantumExplorer Mar 18, 2026
a69150a
fix(swift-sdk): replace Timer with async Task loop for periodic sync
QuantumExplorer Mar 18, 2026
ad080ae
fix(swift-sdk): add Last Recent Block to sync UI for debugging
QuantumExplorer Mar 18, 2026
ccaf832
fix(sdk): revert last_known_recent_block fallback to metadata tip
QuantumExplorer Mar 18, 2026
eda02b1
feat(sdk): add entry count metrics for recent/compacted queries
QuantumExplorer Mar 18, 2026
3510e5c
feat(sdk): skip compacted when recent tree is empty, fix sync loop
QuantumExplorer Mar 18, 2026
934972a
fix(sdk): simplify compacted skip logic
QuantumExplorer Mar 18, 2026
cef977f
docs(sdk): update BLAST sync flowchart and docs for RangeAfter
QuantumExplorer Mar 18, 2026
49fbbb1
fix(swift-sdk): always show Last Recent Block field in sync UI
QuantumExplorer Mar 18, 2026
717e58e
feat(sdk): return GroveDB proof bytes from address sync
QuantumExplorer Mar 18, 2026
5ec3a81
fix(swift-sdk): use onTapGesture for proof detail instead of Navigati…
QuantumExplorer Mar 18, 2026
584a5c2
feat(sdk): add formatted GroveDB proof viewer
QuantumExplorer Mar 18, 2026
1aaaf36
merge: resolve v3.1-dev conflict in CoreContentView
QuantumExplorer Mar 18, 2026
95c0810
feat(sdk): use boundaries() for smarter compaction detection
QuantumExplorer Mar 18, 2026
8dc1714
feat(sdk): simplify compaction detection using boundaries()
QuantumExplorer Mar 18, 2026
778aeb4
feat(sdk): persist platform balances across app launches and reduce s…
QuantumExplorer Mar 20, 2026
7b93f7e
fix(sdk): fix broken compaction tests and add compaction detection co…
QuantumExplorer Mar 20, 2026
f9f9c57
fix(ffi): address review feedback — catch_unwind, expect, checked_mul…
QuantumExplorer Mar 20, 2026
a1fec8b
fix(ffi): use unwrap_or_default for catch_unwind bool result (clippy)
QuantumExplorer Mar 20, 2026
50a201b
test(sdk): improve BLAST address sync test coverage across drive and sdk
QuantumExplorer Mar 20, 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
112 changes: 101 additions & 11 deletions book/src/sdk/blast-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,28 +223,118 @@ tree.
## Phase 2: Incremental Catch-Up

After the tree scan produces a snapshot at some checkpoint height, the wallet needs to
catch up to the chain tip. This is done with two sub-phases:
catch up to the chain tip. The incremental phase also runs on its own for frequent
re-syncs (when the elapsed time since the last sync is within `full_rescan_after_time_s`,
default 7 days).

**Compacted changes** -- Historical balance/nullifier changes aggregated across block
ranges. These cover the gap between the checkpoint height and recent history. Each
response covers a range of blocks and contains the net changes.
Platform stores per-block address balance changes in a "recent" tree. Every **64 blocks**
(or 2048 address entries), these per-block entries are compacted into aggregated range
entries covering the merged block span. The compacted entries have an expiration TTL
and are eventually cleaned up. This means:

**Recent changes** -- Per-block changes for the most recent blocks. These provide
granular updates from where compacted changes left off to the chain tip.
- The **recent tree** never has more than 64 block entries at any time.
- The **compacted tree** contains historical aggregated ranges for blocks that were
compacted away from the recent tree.

```
checkpoint_height chain_tip
│ │
▼ ▼
──────┬────────────────────────────┬───────────────┤
│ Compacted changes │ Recent changes│
│ (block ranges) │ (per-block) │
│ Compacted ranges │ Recent blocks │
│ (aggregated, ≤25/page) │ (per-block, │
│ Only exist after │ always ≤64) │
│ compaction events │ │
└────────────────────────────┴───────────────┘
```

On subsequent syncs, if the elapsed time since the last sync is within
`full_rescan_after_time_s` (default: 7 days), the tree scan is skipped entirely and
only the incremental catch-up runs. This makes frequent re-syncs very fast.
### Step 1: Query Recent First

The wallet always queries recent changes first:

```rust
GetRecentAddressBalanceChanges { start_height, prove: true }
```

Since the recent tree can never exceed 64 entries and the server limit is 100 per
request, **one query always covers the entire uncompacted range**. No pagination is
needed for recent changes.

The response contains `Vec<BlockAddressBalanceChanges>`, where each entry has a
`block_height` and a map of `PlatformAddress → CreditOperation` (either `SetCredits`
or `AddToCredits`).

**Important:** The results are held but not applied yet. They may need to be applied
after compacted data if compaction occurred.

### Step 2: Detect Compaction via Proof

The proof returned by the recent query contains Merkle boundary nodes. The wallet
uses `key_exists_as_boundary` to check whether the `start_height` key still exists
in the recent tree:

```rust
let cursor_exists = Drive::verify_key_exists_as_boundary(
proof,
&recent_tree_path,
start_height_key,
platform_version,
)?;
```

This works because the recent query uses an exclusive range (`RangeAfter`) where
`start_height` appears as a boundary node in the proof, not as a result entry.

| `cursor_exists` | Meaning | Action |
|-----------------|---------|--------|
| `true` | `start_height` still in recent tree — no compaction happened | Apply held recent results directly (Step 4) |
| `false` | `start_height` was compacted away | Query compacted first (Step 3), then apply recent |

**Special case:** On the first incremental catch-up after a tree scan (`start_height`
comes from the checkpoint, not from a previous recent sync), always query compacted
because there is no prior recent key to validate.

### Step 3: Query Compacted (only if needed)

Only runs when compaction is detected or on the first catch-up after a tree scan:

```rust
GetRecentCompactedAddressBalanceChanges { start_block_height, prove: true }
```

Returns `Vec<CompactedBlockAddressBalanceChanges>`, where each entry has:
- `start_block_height` and `end_block_height` (the compacted range)
- `changes: BTreeMap<PlatformAddress, BlockAwareCreditOperation>`
- `SetCredits(u64)` — absolute balance
- `AddToCreditsOperations(Vec<(height, credits)>)` — deltas by height

The server returns up to 25 ranges per request. If the response contains exactly 25
entries, the client paginates by setting `start_block_height = last_end_height + 1`
and querying again.

Compacted changes are applied to the result immediately, updating balances and
advancing the pagination cursor.

### Step 4: Apply Held Recent Results

After compacted data (if any) is applied, the held recent results from Step 1 are
Comment thread
QuantumExplorer marked this conversation as resolved.
processed:

- For each `BlockAddressBalanceChanges` entry, for each address change, if the address
matches one of the wallet's target addresses, update the balance and call
`provider.on_address_found()`.
- Advance `current_height` to `block_height + 1` for each processed entry.

### Finalization

```rust
result.new_sync_height = max(current_height, observed_tip_height);
result.new_sync_timestamp = latest_metadata.time_ms / 1000;
```

The caller persists `new_sync_height` and `new_sync_timestamp` for the next sync call.
On the next incremental sync, `new_sync_height` becomes `start_height` for the recent
query, and `new_sync_timestamp` determines whether a full tree rescan is needed.

## The TrunkBranchSyncOps Trait

Expand Down
Binary file added build_rs_cov.profraw
Binary file not shown.
Loading
Loading