Skip to content

Commit f44b00c

Browse files
upgrade(swap): Concurrent syncing, bdk upgrade, refactors (#180)
* upgrade sqlx to 0.8, add bdk_wallet and bdk_electrum The new dependencies are part of the bdk upgrade and include the improved wallet code. They, too, depend on sqlite3. However, they use a newer version than we currently use via sqlx. This necessitated the sqlx upgrade. This entailed trivial changes (use Pool directly instead of pool.acquire()). We might have to fix the CI as well, I kept getting compile errors from the macro until I ran swap/sqlx_dev_setup.sh. * move old wallet code to extra module * fix fee estimation for old client * bump bitcoin crate, add new wallet constructor * remove unused old Client, move code around for better readibility * make Wallet generic over Persister (database) and move more code around for readibility * add script history, start reimplementing client methods * update some imports * cargo fmt * Add comments, fix fee estimation, address generation and status_of_script * redo state update and wallet sync * fix bitcoin address validation and more imports, use Amount everywhere * fix tx cancel, lock, punish, redeem, refund * fix bitcoin::Address de-/serialisation * fix more address validation * fix more address parsing and validation, also some more imports * cargo fmt * fix wallet initialization, start wallet migration * fail test instead of ignoring it * perform full scan on creation, load from db if it exists * add more wallet info, fix wallet initialization * fix: default to null in config * migrate from old wallet if needed * change something * fix some tests * temporarily patch bdk_wallet and bdk_electrum * fix more tests * fix missing rustls * asb: only start tor client if register_hidden_service=true in the config * fix: use p2wsh_signature_hash instead of p2wpkh_signature_hash * fix some bitcoin address parsing and fee rate parsing * dprint fmt * add bitcoin-harness to this project and update to the new bitcoin version * fix max_givible again * create electrum client separately from wallet, clean up some code * add comment * ignore .env.development * log config file path on ./asb config * feat(monero-sys): Initial commit. Regtest integration test. Wrapper around basic Wallet functions, depends on monero#9464 * Revert "feat(monero-sys): Initial commit. Regtest integration test. Wrapper around basic Wallet functions, depends on monero#9464" This reverts commit 14a5b4c. * upgrade to rust toolchain 1.81 * Use new bdk update for code from master * fix * remove * fix: add empty .gitmodules file to fix Docker build * fix: clean up submodule references * fix: properly declare monero submodule with ignore flag * fix(wallet, bdk): only reveal new address if absolutely necessary * fix: private keys not loaded into bdk wallet * refactor: sync wallet progress log * dprint fmt * refactor: move bitcoin-harness to outside repo * refactor: remove redundant log message * Display sync progress * Remove redundant arg to swap/tests/harness/mod.rs function * fix: call rustls::crypto::ring::default_provider() * dprint fmt * refactor: remove debug code * refactor: move old bdk wallet export to own function, clear log messages * remove old migr for testnets (checksum mismatch), remove balance and stringified last revealed addresses from migration export * use revalidate_network function, remove redundant drop * Display progress of background tasks, TauriBackgroundProgressHandle struct * fix: almost satisfy clippy * fix: gen-bindings error * feat: add BackgroundRefund background type * feat: use builder pattern for constructing Bitcoin wallet * dprint ftm * sync electrum in seperate thread * do not allow user to start sync while sync is in progress * remove redundant log message * display random buffer in AlertWithLinearProgress progress * fix: use TauriContextStatusEvent.Available), dont show syncing wallet spinner if not syncing * differentiate between TestWalletBuilder and WalletBuilder * satisfy clippy * remove custom BackgroundRefund event, move into background process architecture * refactor * dprint fmt * progress: get unit tests compiling * fix: bitcoin unit tests specify const values like sync_interval * fix: get unit tests passing * make clippy happy * feat: display full sync progress, fix unit test import issues * dprint fmt * make clippy happy, use u32 for target_block and not usize * always spawn tor for asb * refactor: remove gen_background_progress_id and just use Uuid::new_v4() * refactor(hooks.ts): clarify comment on useConservativeBitcoinSyncProgress * fix typo * refactor: do not let WalletBuilder take entire env struct * dprint fmt * refactor: remove default feature from workspace patch of bdk * first try for concurrent syncing * refactor: concurrent syncing * fix(wallet.rs): Safely convert FeeRate from btc / kb to sats / kwu * feat(wallet.rs): persist published Bitcoin transactions without requiring re-scan This allows us to compute an updated Bitcoin balance without requiring a re-scan * refactor(wallet.rs): use just 5 concurrent sync requests * refactor: display snackbar error when Wallet refresh fails * fix: add missing space * dprint fmt * refactor: fancy traits for the CumulativeProgress struct, allow limiting amount of callback calls * make clippy happy * dprint fmt * refactor: clearly differntiate between SyncMutex and TokioMutex, use traits for converting to Arc<Mutex<_>>, move sync_ext into own moid * fix: skip syncing if no spks in wallet * fix: update bdk.sh to test migration from old wallet (pre 1.0.0 bdk) to new bdk * fix: increase bitcoin_lock_confirmed_timeout in RegTest env to 5 minutes * refactor: avoid usize where possible, create persistence only after full scan, transmit assumed_total for full scan to tauri, add some icons to progress displays * make clippy happy * fix(ci): change rust toolchain 1.81 * fix(cross compilation arm): use ring instead of aws-lc-rs * fmt * ignore failing rendezvous tests * fix printing_status_change_doesnt_spam_on_same_status * fix: given_bitcoin_address_network_mismatch_then_error test * ignore list_sellers_should_report_all_registered_asbs_with_a_quote test * feat: add tor icon * refactor(wallet.rs): reorder struct by abstraction level * refactor(bitcoin wallet): chunk size for syncing * fix(integration tests): decrease sync interval to 3s * fix(integration tests): parse_rpc_err method to take new bdk error, not old one * add changelog entry --------- Co-authored-by: Binarybaron <[email protected]> Co-authored-by: Mohan <[email protected]>
1 parent 3774da7 commit f44b00c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2877
-1186
lines changed

.github/workflows/build-release-binaries.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575

7676
- uses: dtolnay/rust-toolchain@master
7777
with:
78-
toolchain: "1.80"
78+
toolchain: "1.81"
7979

8080
- name: Configure apt for retries
8181
if: matrix.os == 'ubuntu-latest'

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828

2929
- uses: dtolnay/rust-toolchain@master
3030
with:
31-
toolchain: "1.80"
31+
toolchain: "1.81"
3232
components: clippy,rustfmt
3333

3434
- uses: Swatinem/[email protected]
@@ -182,7 +182,7 @@ jobs:
182182

183183
- uses: dtolnay/rust-toolchain@master
184184
with:
185-
toolchain: "1.80"
185+
toolchain: "1.81"
186186
targets: armv7-unknown-linux-gnueabihf
187187

188188
- name: Configure apt for retries

.github/workflows/draft-new-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
id: make-commit
5858
env:
5959
DPRINT_VERSION: "0.39.1"
60-
RUST_TOOLCHAIN: "1.80"
60+
RUST_TOOLCHAIN: "1.81"
6161
run: |
6262
rustup component add rustfmt --toolchain "$RUST_TOOLCHAIN-x86_64-unknown-linux-gnu"
6363
curl -fsSL https://dprint.dev/install.sh | sh -s $DPRINT_VERSION

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
target
2-
.vscode
2+
.vscode
3+
.claude/settings.local.json
4+
.DS_Store

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- CLI + ASB + GUI: We upgraded dependencies related to the Bitcoin wallet. When you boot up the new version for the first time, a migration process will be run to convert the old wallet format to the new one. This might take a few minutes. We also fixed a bug where we would generate too many unused addresses in the Bitcoin wallet which would cause the wallet to take longer to start up as time goes on.
11+
- GUI: We display detailed progress about running background tasks (Tor bootstrapping, Bitcoin wallet sync progress, etc.)
12+
1013
## [1.0.0-rc.21] - 2025-05-15
1114

1215
## [1.0.0-rc.20] - 2025-05-14

src-gui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"check-bindings": "typeshare --lang=typescript --output-file __temp_bindings.ts ../swap/src && dprint fmt __temp_bindings.ts && diff -wbB __temp_bindings.ts ./src/models/tauriModel.ts && rm __temp_bindings.ts",
8-
"gen-bindings": "typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src && dprint fmt ./src/models/tauriModel.ts",
8+
"gen-bindings": "RUST_LOG=debug RUST_BACKTRACE=1 typeshare --lang=typescript --output-file ./src/models/tauriModel.ts ../swap/src && dprint fmt ./src/models/tauriModel.ts",
99
"test": "vitest",
1010
"test:ui": "vitest --ui",
1111
"dev": "vite",

src-gui/src/models/tauriModelExt.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
ApprovalRequest,
44
ExpiredTimelocks,
55
GetSwapInfoResponse,
6+
PendingCompleted,
7+
TauriBackgroundProgress,
68
TauriSwapProgressEvent,
79
} from "./tauriModel";
810

@@ -230,3 +232,17 @@ export function isPendingLockBitcoinApprovalEvent(
230232
// Check if the request is a LockBitcoin request
231233
return event.content.details.type === "LockBitcoin";
232234
}
235+
236+
export function isPendingBackgroundProcess(
237+
process: TauriBackgroundProgress,
238+
): process is TauriBackgroundProgress {
239+
return process.progress.type === "Pending";
240+
}
241+
242+
export type TauriBitcoinSyncProgress = Extract<TauriBackgroundProgress, { componentName: "SyncingBitcoinWallet" }>;
243+
244+
export function isBitcoinSyncProgress(
245+
progress: TauriBackgroundProgress,
246+
): progress is TauriBitcoinSyncProgress {
247+
return progress.componentName === "SyncingBitcoinWallet";
248+
}

src-gui/src/renderer/background.ts

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { listen } from "@tauri-apps/api/event";
2-
import { TauriSwapProgressEventWrapper, TauriContextStatusEvent, TauriLogEvent, BalanceResponse, TauriDatabaseStateEvent, TauriTimelockChangeEvent, TauriBackgroundRefundEvent, ApprovalRequest } from "models/tauriModel";
3-
import { contextStatusEventReceived, receivedCliLog, rpcSetBalance, timelockChangeEventReceived, rpcSetBackgroundRefundState, approvalEventReceived } from "store/features/rpcSlice";
2+
import { TauriContextStatusEvent, TauriEvent } from "models/tauriModel";
3+
import { contextStatusEventReceived, receivedCliLog, rpcSetBalance, timelockChangeEventReceived, approvalEventReceived, backgroundProgressEventReceived } from "store/features/rpcSlice";
44
import { swapProgressEventReceived } from "store/features/swapSlice";
55
import logger from "utils/logger";
66
import { fetchAllConversations, updateAlerts, updatePublicRegistry, updateRates } from "./api";
77
import { checkContextAvailability, getSwapInfo, initializeContext, updateAllNodeStatuses } from "./rpc";
88
import { store } from "./store/storeRenderer";
9+
import { exhaustiveGuard } from "utils/typescriptUtils";
10+
11+
const TAURI_UNIFIED_EVENT_CHANNEL_NAME = "tauri-unified-event";
912

1013
// Update the public registry every 5 minutes
1114
const PROVIDER_UPDATE_INTERVAL = 5 * 60 * 1_000;
@@ -25,7 +28,7 @@ function setIntervalImmediate(callback: () => void, interval: number): void {
2528
}
2629

2730
export async function setupBackgroundTasks(): Promise<void> {
28-
// // Setup periodic fetch tasks
31+
// Setup periodic fetch tasks
2932
setIntervalImmediate(updatePublicRegistry, PROVIDER_UPDATE_INTERVAL);
3033
setIntervalImmediate(updateAllNodeStatuses, STATUS_UPDATE_INTERVAL);
3134
setIntervalImmediate(updateRates, UPDATE_RATE_INTERVAL);
@@ -34,11 +37,10 @@ export async function setupBackgroundTasks(): Promise<void> {
3437
// Fetch all alerts
3538
updateAlerts();
3639

37-
// // Setup Tauri event listeners
38-
40+
// Setup Tauri event listeners
3941
// Check if the context is already available. This is to prevent unnecessary re-initialization
4042
if (await checkContextAvailability()) {
41-
store.dispatch(contextStatusEventReceived({ type: "Available" }));
43+
store.dispatch(contextStatusEventReceived(TauriContextStatusEvent.Available));
4244
} else {
4345
// Warning: If we reload the page while the Context is being initialized, this function will throw an error
4446
initializeContext().catch((e) => {
@@ -52,47 +54,50 @@ export async function setupBackgroundTasks(): Promise<void> {
5254
});
5355
}
5456

55-
listen<TauriSwapProgressEventWrapper>("swap-progress-update", (event) => {
56-
logger.info("Received swap progress event", event.payload);
57-
store.dispatch(swapProgressEventReceived(event.payload));
58-
});
59-
60-
listen<TauriContextStatusEvent>("context-init-progress-update", (event) => {
61-
logger.info("Received context init progress event", event.payload);
62-
store.dispatch(contextStatusEventReceived(event.payload));
63-
});
64-
65-
listen<TauriLogEvent>("cli-log-emitted", (event) => {
66-
store.dispatch(receivedCliLog(event.payload));
67-
});
68-
69-
listen<BalanceResponse>("balance-change", (event) => {
70-
logger.info("Received balance change event", event.payload);
71-
store.dispatch(rpcSetBalance(event.payload.balance));
72-
});
73-
74-
listen<TauriDatabaseStateEvent>("swap-database-state-update", (event) => {
75-
logger.info("Received swap database state update event", event.payload);
76-
getSwapInfo(event.payload.swap_id);
77-
78-
// This is ugly but it's the best we can do for now
79-
// Sometimes we are too quick to fetch the swap info and the new state is not yet reflected
80-
// in the database. So we wait a bit before fetching the new state
81-
setTimeout(() => getSwapInfo(event.payload.swap_id), 3000);
82-
});
83-
84-
listen<TauriTimelockChangeEvent>('timelock-change', (event) => {
85-
logger.info('Received timelock change event', event.payload);
86-
store.dispatch(timelockChangeEventReceived(event.payload));
87-
})
88-
89-
listen<TauriBackgroundRefundEvent>('background-refund', (event) => {
90-
logger.info('Received background refund event', event.payload);
91-
store.dispatch(rpcSetBackgroundRefundState(event.payload));
92-
})
93-
94-
listen<ApprovalRequest>("approval_event", (event) => {
95-
logger.info("Received approval_event:", event.payload);
96-
store.dispatch(approvalEventReceived(event.payload));
57+
// Listen for the unified event
58+
listen<TauriEvent>(TAURI_UNIFIED_EVENT_CHANNEL_NAME, (event) => {
59+
const { channelName, event: eventData } = event.payload;
60+
61+
switch (channelName) {
62+
case "SwapProgress":
63+
store.dispatch(swapProgressEventReceived(eventData));
64+
break;
65+
66+
case "ContextInitProgress":
67+
store.dispatch(contextStatusEventReceived(eventData));
68+
break;
69+
70+
case "CliLog":
71+
store.dispatch(receivedCliLog(eventData));
72+
break;
73+
74+
case "BalanceChange":
75+
store.dispatch(rpcSetBalance((eventData).balance));
76+
break;
77+
78+
case "SwapDatabaseStateUpdate":
79+
getSwapInfo(eventData.swap_id);
80+
81+
// This is ugly but it's the best we can do for now
82+
// Sometimes we are too quick to fetch the swap info and the new state is not yet reflected
83+
// in the database. So we wait a bit before fetching the new state
84+
setTimeout(() => getSwapInfo(eventData.swap_id), 3000);
85+
break;
86+
87+
case "TimelockChange":
88+
store.dispatch(timelockChangeEventReceived(eventData));
89+
break;
90+
91+
case "Approval":
92+
store.dispatch(approvalEventReceived(eventData));
93+
break;
94+
95+
case "BackgroundProgress":
96+
store.dispatch(backgroundProgressEventReceived(eventData));
97+
break;
98+
99+
default:
100+
exhaustiveGuard(channelName);
101+
}
97102
});
98103
}

0 commit comments

Comments
 (0)