Skip to content

feat: bdk rust bindings#566

Merged
michael1011 merged 21 commits into
masterfrom
feat/bdk-rust-bindings
Nov 4, 2025
Merged

feat: bdk rust bindings#566
michael1011 merged 21 commits into
masterfrom
feat/bdk-rust-bindings

Conversation

@jackstar12

@jackstar12 jackstar12 commented Oct 1, 2025

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Full Bitcoin wallet via BDK: create/connect, sync/full-scan, addresses, balances, transactions, send (incl. send-all), bump fees, apply raw tx, derive descriptors/xpubs; server now initializes BTC wallet and tests support BTC.
  • Chores

    • Build/binding flow for BDK/native bindings added; lint exclusion and .gitignore updated.
  • Tests

    • Expanded BTC tests (fee bumping, credential scenarios, pagination); Send test skipped in CI.
  • Behavior

    • Removed automatic BTC subaccount creation; tighter duplicate-import and xpub validation; FullScan API added.

@coderabbitai

coderabbitai Bot commented Oct 1, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new Rust "bdk" crate with UniFFI exports and generated C/Go bindings, integrates a BDK-backed Bitcoin wallet into the Go server and tests, expands Makefile build targets to build BDK and generate bindings, exposes FullScan across wallets and mocks, and excludes generated bindings from golangci-lint.

Changes

Cohort / File(s) Summary
Lint config
\.golangci.yml
Added exclusion path internal/onchain/bitcoin-wallet/bdk/.
Build & Makefile
Makefile
Added bindgen-go, bdk-bindings, build-bdk targets; added BDK_BINDINGS_PATH var; lwk-bindings now depends on bindgen-go; build depends on build-bdk.
Rust crate & bindgen entry
bdk/Cargo.toml, bdk/uniffi-bindgen.rs, bdk/src/lib.rs
New bdk crate manifest, added uniffi bindgen binary entry, and enabled UniFFI scaffolding in crate root.
BDK types & wallet logic (Rust)
bdk/src/types.rs, bdk/src/wallet.rs
Added UniFFI-facing types (Error, Network, Balance, WalletTransaction, WalletSendResult, WalletCredentials) and a BDK-backed wallet implementation exposing constructors and methods (derive, sync/full_scan, send, bump-fee, transactions, persistence, Electrum client).
Uniffi C header (autogenerated)
internal/onchain/bitcoin-wallet/bdk/bdk.h
Added UniFFI-generated C header with RustBuffer, per-type futures, callbacks, rust-future APIs, checksum/contract-version helpers, and FFI declarations for backend/wallet lifecycle and methods.
Go UniFFI bindings (generated)
internal/onchain/bitcoin-wallet/bdk/bdk.go, internal/onchain/bitcoin-wallet/bdk/dynamic.go, internal/onchain/bitcoin-wallet/bdk/static.go
Added generated Go FFI layer: buffer types, converters, FfiObject lifecycle, exported types/methods (Backend, Wallet, ChainClient, data models), and cgo static/dynamic variants.
Go bitcoin-wallet integration
internal/onchain/bitcoin-wallet/wallet.go, internal/rpcserver/server.go, internal/test/test.go
New onchain Bitcoin wallet backend wiring using bdk bindings; server startup initializes Bitcoin backend and datadir; tests derive descriptors via bdk.DeriveDefaultXpub and add BTC backend path.
Application wallet layer & tests
internal/onchain/wallet_test.go, internal/rpcserver/rpcserver_test.go, internal/onchain/wallet/wallet_test.go
Extended tests to include BTC (added TestWallet_BumpTransactionFee), adjusted import-credential expectations, modified swap test sequencing, added a GH Actions skip in one test, and refined transaction tests/pagination.
Router & misc
internal/rpcserver/router.go, .gitignore
Removed automatic BTC subaccount creation during wallet creation; replaced Sync→FullScan in login; added .gitignore entry internal/rpcserver/test/bitcoin-wallet/.
Onchain helpers & interfaces, mocks
internal/onchain/wallet.go, internal/onchain/onchain.go, internal/cln/cln.go, internal/lnd/lnd.go, internal/mocks/*
Added FullScan() to wallet interface and implementations/mocks, updated ValidateWalletCredentials xpub deprecation message, added ElectrumOptions.String(), and updated mocks to include FullScan.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Server
  participant GoBDK as "Go bdk binding"
  participant RustBDK as "Rust bdk crate"
  participant DB as "SQLite"
  participant Electrum as "Electrum"

  Server->>GoBDK: NewBackend(network, electrumOpts, dbPath)
  GoBDK->>RustBDK: FFI call -> construct Backend
  RustBDK->>DB: open/create DB
  RustBDK->>Electrum: configure Electrum client(s)
  RustBDK-->>GoBDK: Backend handle
  GoBDK-->>Server: Backend object
Loading
sequenceDiagram
  autonumber
  actor App
  participant WalletGo as "Go Wallet wrapper"
  participant GoBDK as "Go bdk binding"
  participant RustBDK as "Rust bdk crate"
  participant DB as "SQLite"
  participant Electrum as "Electrum"

  App->>WalletGo: SendToAddress(addr, amt, feeRate, sendAll)
  WalletGo->>GoBDK: SendToAddress FFI
  GoBDK->>RustBDK: invoke Rust wallet send
  RustBDK->>DB: persist PSBT / wallet state
  RustBDK->>Electrum: broadcast / sync
  RustBDK-->>GoBDK: WalletSendResult(tx_hex, fee, send_amount)
  GoBDK-->>WalletGo: result
  WalletGo-->>App: tx info
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Files/areas to inspect closely:

  • bdk/src/wallet.rs — key derivation, descriptor parsing, PSBT signing/finalization, fee-bump logic, Electrum interactions, SQLite persistence.
  • internal/onchain/bitcoin-wallet/bdk/* (generated C header and Go bindings) — ownership, lifetimes, async futures, cgo signatures and memory management.
  • internal/onchain/bitcoin-wallet/wallet.go and internal/rpcserver/server.go — binding usage, concurrency, DB path handling, error propagation.
  • Makefile changes — build ordering, bindgen target, BDK library packaging and CI implications.
  • Tests that changed/added (bump-fee flow, credential permutations, swap sequencing).

Possibly related PRs

Suggested reviewers

  • michael1011
  • kilrau

Poem

I hopped through Rust and UniFFI glue,
stitched headers, Go, and SQLite too,
Electrum hums while PSBTs take flight,
wallets scan beneath the quiet night,
🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: bdk rust bindings' accurately summarizes the primary change: adding Rust BDK bindings. It is concise, clear, and reflects the main objective evident in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/bdk-rust-bindings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from bf9f869 to 024c055 Compare October 1, 2025 10:59
@jackstar12 jackstar12 requested a review from michael1011 October 1, 2025 10:59
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch 2 times, most recently from 70ad6a8 to a599213 Compare October 1, 2025 11:42
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch 3 times, most recently from ab40d08 to 150c9c1 Compare October 1, 2025 11:54
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from 150c9c1 to bfecbf2 Compare October 1, 2025 11:57
@jackstar12

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 1, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (4)
bdk/src/wallet.rs (2)

224-230: Use real timestamp for unconfirmed tx application

Passing 0 loses useful recency info used by policies/UX. Use current UNIX time.

 pub fn apply_transaction(&self, tx_hex: String) -> Result<(), Error> {
     let mut wallet = self.get_wallet()?;
     let tx: Transaction = deserialize_hex(tx_hex.as_str()).context("deserialize tx")?;
-    wallet.apply_unconfirmed_txs(vec![(Arc::new(tx), 0)]);
+    let now = std::time::SystemTime::now()
+        .duration_since(std::time::UNIX_EPOCH)
+        .unwrap_or_default()
+        .as_secs();
+    wallet.apply_unconfirmed_txs(vec![(Arc::new(tx), now)]);
     self.persist(&mut wallet)?;
     Ok(())
 }

Please confirm apply_unconfirmed_txs expects seconds as u64 in your bdk_wallet version. Based on learnings


211-222: Use incremental sync in normal operation

full_scan() every sync is expensive; prefer sync() for steady-state and full_scan() only on restore/import.

If bdk_electrum::BdkElectrumClient::sync() is available, consider:

let request = { let wallet = self.get_wallet()?; wallet.start_sync() };
let update = self.backend.electrum.sync(request).context("sync")?;

Based on learnings

bdk/src/types.rs (2)

38-68: Consider deriving Clone for FFI records

Cloning these lightweight records often simplifies UniFFI consumers.

-#[derive(uniffi::Record)]
+#[derive(uniffi::Record, Clone)]
 pub struct Balance { ... }
 
-#[derive(uniffi::Record)]
+#[derive(uniffi::Record, Clone)]
 pub struct WalletTransaction { ... }
 
-#[derive(uniffi::Record)]
+#[derive(uniffi::Record, Clone)]
 pub struct WalletTransactionOutput { ... }
 
-#[derive(uniffi::Record)]
+#[derive(uniffi::Record, Clone)]
 pub struct WalletSendResult { ... }

3-19: Error wrapping is fine; optional: add From<PoisonError<_>>

You already map poisoned mutexes at call sites; adding a blanket From improves consistency.

impl<T> From<std::sync::PoisonError<T>> for Error {
    fn from(e: std::sync::PoisonError<T>) -> Self {
        Error::Generic(e.to_string())
    }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc74c8c and bfecbf2.

⛔ Files ignored due to path filters (1)
  • bdk/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .golangci.yml (1 hunks)
  • Makefile (2 hunks)
  • bdk/Cargo.toml (1 hunks)
  • bdk/src/lib.rs (1 hunks)
  • bdk/src/types.rs (1 hunks)
  • bdk/src/wallet.rs (1 hunks)
  • bdk/uniffi-bindgen.rs (1 hunks)
🔇 Additional comments (10)
.golangci.yml (1)

6-6: LGTM!

The exclusion path for BDK bindings mirrors the existing pattern for LWK and appropriately excludes generated bindings from linting.

Makefile (3)

57-58: LGTM!

The bindgen-go target correctly installs the uniffi-bindgen-go tool with the appropriate version tag matching the uniffi dependency version (0.28.3) used in the BDK crate.


64-66: LGTM!

The bdk-bindings target follows the same pattern as lwk-bindings and correctly generates Go bindings for the BDK library.


113-113: LGTM!

The BDK_BINDINGS_PATH variable is well-defined and aligns with the repository structure.

bdk/Cargo.toml (4)

1-5: LGTM!

Package metadata is well-defined with a standard edition (2021) and appropriate versioning for an initial release.


6-12: LGTM!

Dependency versions align with the latest stable releases documented in the retrieved learnings. The bdk_wallet features (keys-bip39, file_store, std, rusqlite) provide a comprehensive wallet implementation with key management, persistent storage, and database support. The uniffi dependency with "cli" feature is appropriate for the FFI bindings workflow.

Based on learnings.


14-15: LGTM!

The uniffi build-dependency with the "build" feature is correctly configured for the build-time scaffolding generation required by uniffi's binding workflow.


17-19: LGTM!

The library configuration correctly specifies multiple crate types (staticlib for C FFI, cdylib for dynamic linking, rlib for Rust) and sets the library name to "bdk", which cargo will compile to libbdk.{a,so,dylib}.

bdk/src/lib.rs (1)

1-4: LGTM!

The crate root is cleanly structured with public module declarations for the wallet API surface and uniffi scaffolding setup. This minimal integration follows uniffi best practices for FFI binding generation.

Based on learnings.

bdk/uniffi-bindgen.rs (1)

1-3: LGTM!

This minimal binary entry point correctly delegates to uniffi's binding generation workflow. The uniffi_bindgen_main() function handles all command-line parsing and error handling internally, making this simple wrapper appropriate for the build pipeline.

Based on learnings.

Comment thread bdk/src/types.rs
Comment thread bdk/src/wallet.rs
Comment thread bdk/src/wallet.rs Outdated
Comment thread bdk/src/wallet.rs
Comment thread Makefile
Comment thread Makefile Outdated
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from bfecbf2 to 36807d7 Compare October 1, 2025 12:25
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from 041d716 to 08242ec Compare October 2, 2025 10:16
@jackstar12 jackstar12 force-pushed the feat/bdk branch 2 times, most recently from 52f3bbe to 51a1abf Compare October 8, 2025 14:44
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from 08242ec to 667843d Compare October 8, 2025 14:46
@jackstar12 jackstar12 changed the base branch from feat/bdk to master October 8, 2025 14:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
internal/onchain/wallet_test.go (1)

290-301: Remove or document commented-out test case.

The commented-out Xpub test case should either be removed if it's no longer needed, or documented with a comment explaining why it's disabled and when it might be re-enabled.

internal/onchain/bitcoin-wallet/wallet.go (5)

47-55: Ensure DB path portability and directory creation

Use filepath.Join and create DataDir before opening the SQLite file to avoid cross‑platform and runtime issues.

Apply this diff:

-	backend, err := bdk.NewBackend(
-		convertNetwork(cfg.Network),
-		url,
-		cfg.DataDir+"/bdk.sqlite",
-	)
+	// Ensure data dir exists and build a portable path
+	if err := os.MkdirAll(cfg.DataDir, 0o700); err != nil {
+		return nil, fmt.Errorf("ensure data dir: %w", err)
+	}
+	dbPath := filepath.Join(cfg.DataDir, "bdk.sqlite")
+	backend, err := bdk.NewBackend(
+		convertNetwork(cfg.Network),
+		url,
+		dbPath,
+	)

Add imports:

 import (
 	"errors"
 	"fmt"
 	"time"
 
 	"github.com/BoltzExchange/boltz-client/v2/internal/onchain"
 	"github.com/BoltzExchange/boltz-client/v2/internal/onchain/bitcoin-wallet/bdk"
 	"github.com/BoltzExchange/boltz-client/v2/pkg/boltz"
+	"os"
+	"path/filepath"
 )

71-88: Make read‑only explicit when no mnemonic

Currently sets Readonly=false when mnemonic provided, but not explicitly true when absent. Clarify state to avoid ambiguity.

 	if credentials.Mnemonic != "" {
 		creds.Mnemonic = &credentials.Mnemonic
 		info.Readonly = false
+	} else {
+		info.Readonly = true
 	}

90-92: Naming mismatch: returns an xpub, not a descriptor

Method is named DeriveDefaultDescriptor but calls DeriveDefaultXpub and returns an xpub. Consider renaming to DeriveDefaultXpub (or return a descriptor).


114-124: Apply bumped transaction to keep wallet state consistent

After broadcasting the bumped tx, also apply it locally (mirrors SendToAddress behavior).

 	newTxId, err := w.txProvider.BroadcastTransaction(txHex)
 	if err != nil {
 		return "", err
 	}
-	return newTxId, nil
+	if err := w.Wallet.ApplyTransaction(txHex); err != nil {
+		return "", err
+	}
+	return newTxId, nil

167-169: Free FFI resources on Disconnect

Invoke Destroy on the underlying BDK wallet for deterministic release instead of relying solely on finalizers.

 func (w *Wallet) Disconnect() error {
-	return nil
+	w.Wallet.Destroy()
+	return nil
 }
internal/onchain/bitcoin-wallet/bdk/bdk.go (1)

582-593: Optional: prefer io.ReadFull for exact reads

reader.Read is not guaranteed to fill the buffer. io.ReadFull avoids partial reads.

-	read_length, err := reader.Read(buffer)
-	if err != nil && err != io.EOF {
-		panic(err)
-	}
-	if read_length != int(length) {
-		panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length))
-	}
+	if _, err := io.ReadFull(reader, buffer); err != nil {
+		panic(err)
+	}

Note: This file is generated; consider updating the generator template instead.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfecbf2 and 667843d.

⛔ Files ignored due to path filters (1)
  • bdk/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .golangci.yml (1 hunks)
  • Makefile (2 hunks)
  • bdk/Cargo.toml (1 hunks)
  • bdk/src/lib.rs (1 hunks)
  • bdk/src/types.rs (1 hunks)
  • bdk/src/wallet.rs (1 hunks)
  • bdk/uniffi-bindgen.rs (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.h (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/dynamic.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/static.go (1 hunks)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
  • internal/onchain/wallet_test.go (2 hunks)
  • internal/rpcserver/server.go (2 hunks)
  • internal/test/test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • bdk/src/lib.rs
  • bdk/src/wallet.rs
  • bdk/src/types.rs
  • bdk/Cargo.toml
  • Makefile
🧰 Additional context used
🧬 Code graph analysis (5)
internal/test/test.go (5)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/bitcoin-wallet/bdk/bdk.go (4)
  • DeriveDefaultXpub (1410-1422)
  • NetworkRegtest (1259-1259)
  • NewBackend (687-697)
  • Network (1254-1254)
internal/electrum/electrum.go (1)
  • NewClient (20-52)
internal/onchain/onchain.go (3)
  • RegtestElectrumConfig (71-74)
  • TxProvider (51-55)
  • FeeProvider (39-41)
internal/onchain/bitcoin-wallet/wallet.go (2)
  • NewBackend (35-56)
  • Config (26-33)
internal/onchain/wallet_test.go (3)
internal/test/test.go (7)
  • WalletBackend (186-208)
  • WalletCredentials (65-79)
  • FundWallet (81-123)
  • GetCli (218-224)
  • GetNewAddress (258-260)
  • SendToAddress (262-264)
  • MineBlock (244-248)
pkg/boltz/currency.go (2)
  • CurrencyBtc (11-11)
  • CurrencyLiquid (12-12)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
internal/rpcserver/server.go (3)
internal/onchain/bitcoin-wallet/wallet.go (2)
  • Config (26-33)
  • NewBackend (35-56)
internal/onchain/onchain.go (2)
  • TxProvider (51-55)
  • FeeProvider (39-41)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/bitcoin-wallet/wallet.go (4)
internal/onchain/bitcoin-wallet/bdk/bdk.go (11)
  • Backend (683-685)
  • Wallet (757-759)
  • Network (1254-1254)
  • NewBackend (687-697)
  • NetworkBitcoin (1257-1257)
  • NetworkTestnet (1258-1258)
  • NetworkRegtest (1259-1259)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
  • Balance (928-931)
  • WalletTransaction (1052-1059)
internal/onchain/onchain.go (3)
  • TxProvider (51-55)
  • FeeProvider (39-41)
  • ElectrumOptions (61-64)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
internal/onchain/bitcoin-wallet/wallet.go (3)
  • NewBackend (35-56)
  • Backend (13-18)
  • Wallet (20-24)
internal/test/test.go (1)
  • WalletCredentials (65-79)
🪛 GitHub Actions: CI
internal/onchain/wallet_test.go

[warning] 1-1: Multiple test runs surfaced consistent DB-open failures; likely environmental/file-system permissions or temp data cleanup issues affecting test wallets.

🔇 Additional comments (10)
.golangci.yml (1)

6-6: LGTM!

Excluding the BDK bindings directory from linting is appropriate, as it contains generated FFI code that doesn't need to conform to Go linting standards.

bdk/uniffi-bindgen.rs (1)

1-3: LGTM!

Standard UniFFI binding generator entry point. The implementation correctly delegates to the UniFFI library's main function.

internal/rpcserver/server.go (1)

251-264: LGTM!

The Bitcoin wallet backend initialization follows the same pattern as the Liquid wallet backend above it, with proper configuration assembly and error handling.

internal/onchain/bitcoin-wallet/bdk/static.go (1)

1-8: LGTM!

The static build configuration is correct, with appropriate linker flags (--no-as-needed and -ldl) for static linking with libbdk.a.

internal/test/test.go (2)

72-74: LGTM!

The BTC credentials derivation using bdk.DeriveDefaultXpub is appropriate and follows the pattern established for Liquid wallets.


190-200: LGTM!

The BTC backend test wiring correctly mirrors the Liquid backend pattern, with proper electrum client initialization and configuration.

internal/onchain/bitcoin-wallet/bdk/bdk.h (1)

1-797: Verify if auto-generated file should be committed.

This is an auto-generated C header file for UniFFI bindings. While committing generated FFI bindings can be intentional for reproducible builds, verify this aligns with your project's conventions.

If the file is regenerated during the build process, consider adding it to .gitignore and documenting the generation step in your build instructions.

internal/onchain/bitcoin-wallet/bdk/dynamic.go (1)

5-6: Verify runtime availability of libbdk.so.

The rpath is set to ${SRCDIR}, so libbdk.so must be present at runtime in that directory (or on LD_LIBRARY_PATH). I didn’t find it in the repo—ensure your build and deployment process installs it alongside the Go binary.

internal/onchain/wallet_test.go (1)

224-255: Use require.Eventually for mempool wait and clean up disabled tests.

  • Replace the fixed time.Sleep(2*time.Second) with a require.Eventually loop (as in TestWallet_GetBalance) that calls wallet.Sync() and checks for txId in wallet.GetTransactions().
  • Remove the commented-out Xpub test (Lines 290–301) or add a clear TODO explaining why it’s disabled.
  • Verify and resolve the CI “DB-open failures” warning to ensure tests don’t clash when running in parallel.
internal/onchain/bitcoin-wallet/wallet.go (1)

163-165: No updates needed for errors.ErrUnsupported: go.mod targets Go 1.24.6 and errors.ErrUnsupported has been available since Go 1.21.

Comment thread internal/onchain/bitcoin-wallet/bdk/bdk.go
Comment thread internal/onchain/bitcoin-wallet/wallet.go
Comment thread bdk/src/types.rs
Comment on lines +21 to +36
#[derive(uniffi::Enum)]
pub enum Network {
Bitcoin,
Testnet,
Regtest,
}

impl From<Network> for BdkNetwork {
fn from(network: Network) -> Self {
match network {
Network::Bitcoin => BdkNetwork::Bitcoin,
Network::Testnet => BdkNetwork::Testnet,
Network::Regtest => BdkNetwork::Regtest,
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, but we dont

@michael1011 michael1011 Oct 27, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend does support signet but we don't have a deployment there. And the client doesn't support signet in many places, so whatever. Doesn't matter

Comment thread Makefile
Comment thread Makefile Outdated
Comment thread internal/rpcserver/server.go Outdated
Comment thread internal/onchain/wallet_test.go Outdated
Comment on lines +290 to +301
/*
{
name: "Xpub/BTC",
credentials: onchain.WalletCredentials{
WalletInfo: onchain.WalletInfo{
Currency: boltz.CurrencyBtc,
},
Xpub: "vpub5XzEwP9YWe4cJD6pB3njrMgWahQbzHhfGAyuErnswtPuzm6QdLqHH79DSZ6YW3McdE1pwxvr7wHU2nMtVbPZ1jW4tqg8ggx4ZV19U7i69pd",
},
shouldError: false,
},
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that commented out?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that still commented out?

Comment thread bdk/src/wallet.rs Outdated
Comment thread bdk/src/wallet.rs
};

let descriptor_pubkey = DescriptorPublicKey::MultiXPub(multi);
Ok(Wpkh::new(descriptor_pubkey).context("wpkh")?.to_string())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to default to P2WPKH or rather P2TR?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is P2TR supported widely enough in wallets for default?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We agreed to default to P2WPKH

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Current code does so

Comment thread bdk/src/wallet.rs Outdated

pub fn bump_transaction_fee(&self, tx_id: String, sat_per_vbyte: f64) -> Result<String, Error> {
let tx_id = Txid::from_str(&tx_id).context("parse tx id")?;
let fee_rate = FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0) as u64);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread bdk/src/wallet.rs Outdated
sat_per_vbyte: f64,
send_all: bool,
) -> Result<WalletSendResult, Error> {
let fee_rate = FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0) as u64);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The from_sat_per_vb only accepts integers, but we accept a floats.

Comment thread bdk/src/wallet.rs
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch 2 times, most recently from aa22d55 to a953735 Compare October 18, 2025 09:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/test/test.go (2)

143-155: Data race: concurrent writes to result map

eg.Go goroutines write to map without synchronization. Use a mutex.

Apply:

-    result := make(map[boltz.Currency]*wallet.Wallet)
+    result := make(map[boltz.Currency]*wallet.Wallet)
+    var mu sync.Mutex
@@
-            result[currency] = wallet
+            mu.Lock()
+            result[currency] = wallet
+            mu.Unlock()

Also add:

@@
-import (
+import (
+    "sync"

249-255: Guard against negative block counts in MineUntil

If height <= current, blocks becomes negative and the CLI call is invalid.

Apply:

 func MineUntil(t *testing.T, cli Cli, height int64) {
     blockHeight, err := strconv.ParseInt(cli("getblockcount"), 10, 64)
     require.NoError(t, err)
     blocks := height - blockHeight
+    if blocks <= 0 {
+        return
+    }
     cli(fmt.Sprintf("-generate %d", blocks))
     syncCln()
 }
♻️ Duplicate comments (7)
internal/onchain/wallet_test.go (1)

290-301: Clean up commented-out test block

This has been flagged before. Either delete it or convert into a t.Run with t.Skip and a brief rationale.

bdk/src/wallet.rs (3)

213-224: Avoid holding wallet mutex across network I/O in sync()

Build the request under lock, drop it before full_scan, then re-lock to apply.

Apply:

 pub fn sync(&self) -> Result<(), Error> {
-    let mut wallet = self.get_wallet()?;
-    let request = wallet.start_full_scan();
-    let update = self
+    let request = {
+        let mut wallet = self.get_wallet()?;
+        wallet.start_full_scan()
+    };
+    let update = self
         .backend
         .electrum
         .full_scan(request, 20, 50, false)
         .context("full scan")?;
-    wallet.apply_update(update).context("apply update")?;
-    self.persist(&mut wallet)?;
+    let mut wallet = self.get_wallet()?;
+    wallet.apply_update(update).context("apply update")?;
+    self.persist(&mut wallet)?;
     Ok(())
 }

319-323: Pagination order bug (take before skip)

Call skip() before take() to page correctly.

Apply:

-    .transactions()
-    .take(limit as usize)
-    .skip(offset as usize)
+    .transactions()
+    .skip(offset as usize)
+    .take(limit as usize)

62-85: Wrong coin type in derivation path (mainnet-only path used for testnet/regtest)

Use BIP-84 coin type 1 for Testnet/Regtest.

Apply:

 fn derive_default_xpub(network: Network, mnemonic: String) -> Result<String, Error> {
     let secp = Secp256k1::new();
-    let derivation_path = DerivationPath::from_str("m/84h/0h/0h").context("derivation path")?;
+    let coin_type = match network {
+        Network::Bitcoin => 0u32,
+        Network::Testnet | Network::Regtest => 1u32,
+    };
+    let derivation_path = DerivationPath::from_str(
+        &format!("m/84h/{}h/0h", coin_type)
+    ).context("derivation path")?;
internal/onchain/bitcoin-wallet/wallet.go (2)

148-159: Verify: GetSendFee may have unintended side effects.

GetSendFee calls SendToAddress, which might build, sign, and potentially broadcast a transaction or reserve coins. Past review suggested using a dedicated estimate-only endpoint (e.g., dry-run via TxBuilder) to compute fees without side effects.

Confirm whether BDK's SendToAddress binding modifies wallet state (e.g., reserves UTXOs, persists pending tx) when called but not broadcast. If it does, consider implementing a dedicated fee estimation method using BDK's TxBuilder in dry-run mode.


56-67: Fix nil dereference in panic path.

If network is nil, the default branch will panic on nil dereference. Use the pointer directly in the error message.

Apply this diff:

 func convertNetwork(network *boltz.Network) bdk.Network {
 	switch network {
 	case boltz.MainNet:
 		return bdk.NetworkBitcoin
 	case boltz.TestNet:
 		return bdk.NetworkTestnet
 	case boltz.Regtest:
 		return bdk.NetworkRegtest
 	default:
-		panic(fmt.Sprintf("unexpected network %v", *network))
+		panic(fmt.Sprintf("unexpected network %v", network))
 	}
 }
internal/onchain/bitcoin-wallet/bdk/bdk.go (1)

1244-1252: Critical bug: FfiDestroyerError.Destroy type mismatch causes panic.

The switch matches ErrorGeneric by value (line 1246), but errors are stored as pointers (see line 1186: err: &ErrorGeneric{...}). This will always fall through to the panic in the default case.

Apply this diff:

 func (_ FfiDestroyerError) Destroy(value *Error) {
 	switch variantValue := value.err.(type) {
-	case ErrorGeneric:
-		variantValue.destroy()
+	case *ErrorGeneric:
+		variantValue.destroy()
 	default:
 		_ = variantValue
 		panic(fmt.Sprintf("invalid error value `%v` in FfiDestroyerError.Destroy", value))
 	}
 }
🧹 Nitpick comments (5)
internal/onchain/wallet_test.go (2)

22-24: Currency coverage mismatch

walletTest iterates only Liquid, but comments imply both currencies. Either add BTC here or update the comment to avoid confusion.

Apply (if you want both):

- for _, currency := range []boltz.Currency{boltz.CurrencyLiquid} {
+ for _, currency := range []boltz.Currency{boltz.CurrencyBtc, boltz.CurrencyLiquid} {

Or adjust the comment at Line 80 accordingly.

Also applies to: 76-81


126-129: Remove debug print from assertions

fmt.Println in assertions adds noise to test output.

Apply:

-                            fmt.Println(o.Address, searchAddress)
                             return o.Address == searchAddress
Makefile (1)

114-119: Good fix on lib name; consider binding generation flow

Nice correction to check/copy libbdk.a. Optionally, wire bdk-bindings into build if bindings need regeneration during CI.

Example:

-build: download-gdk build-bolt12 build-lwk build-bdk
+build: download-gdk build-bolt12 build-lwk build-bdk bdk-bindings
bdk/Cargo.toml (1)

6-15: Correct version target: upgrade bdk_wallet to 2.1.0 (not 2.2.x)

The latest published version of bdk_wallet is 2.1.0, and the features ["keys-bip39","file_store","rusqlite","std"] are present and valid. Consider updating from 2.0.0 to 2.1.0. The overall MSRV for bdk_wallet is 1.63.0 and bdk_electrum requires MSRV 1.75.0, both of which are consistent with your setup.

internal/onchain/bitcoin-wallet/bdk/bdk.go (1)

1156-1156: Fix typo in comment.

"Convience" should be "Convenience".

-// Convience method to turn *Error into error
+// Convenience method to turn *Error into error
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 667843d and aa22d55.

⛔ Files ignored due to path filters (1)
  • bdk/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .golangci.yml (1 hunks)
  • Makefile (2 hunks)
  • bdk/Cargo.toml (1 hunks)
  • bdk/src/lib.rs (1 hunks)
  • bdk/src/types.rs (1 hunks)
  • bdk/src/wallet.rs (1 hunks)
  • bdk/uniffi-bindgen.rs (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.h (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/dynamic.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/static.go (1 hunks)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
  • internal/onchain/wallet_test.go (2 hunks)
  • internal/rpcserver/server.go (2 hunks)
  • internal/test/test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • bdk/src/lib.rs
  • internal/onchain/bitcoin-wallet/bdk/dynamic.go
  • .golangci.yml
  • internal/onchain/bitcoin-wallet/bdk/static.go
  • internal/rpcserver/server.go
🧰 Additional context used
🧬 Code graph analysis (6)
internal/onchain/wallet_test.go (4)
internal/test/test.go (7)
  • WalletBackend (185-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • MineBlock (243-247)
pkg/boltz/currency.go (2)
  • CurrencyBtc (11-11)
  • CurrencyLiquid (12-12)
internal/onchain/bitcoin-wallet/bdk/bdk.go (1)
  • WalletCredentials (968-971)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
bdk/src/types.rs (2)
internal/onchain/liquid-wallet/wallet.go (1)
  • Testnet (136-136)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • WalletTransactionOutput (1108-1112)
  • WalletSendResult (1008-1012)
bdk/src/wallet.rs (1)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • WalletSendResult (1008-1012)
  • WalletTransactionOutput (1108-1112)
internal/onchain/bitcoin-wallet/wallet.go (3)
internal/onchain/bitcoin-wallet/bdk/bdk.go (12)
  • Backend (683-685)
  • Wallet (757-759)
  • Network (1254-1254)
  • NewBackend (687-697)
  • NetworkBitcoin (1257-1257)
  • NetworkTestnet (1258-1258)
  • NetworkRegtest (1259-1259)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
  • DeriveDefaultXpub (1410-1422)
  • Balance (928-931)
  • WalletTransaction (1052-1059)
internal/onchain/onchain.go (2)
  • ChainProvider (40-48)
  • ElectrumOptions (50-53)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
internal/test/test.go (4)
internal/onchain/bitcoin-wallet/bdk/bdk.go (4)
  • DeriveDefaultXpub (1410-1422)
  • NetworkRegtest (1259-1259)
  • NewBackend (687-697)
  • Network (1254-1254)
internal/electrum/electrum.go (1)
  • NewClient (20-52)
internal/onchain/onchain.go (1)
  • RegtestElectrumConfig (60-63)
internal/onchain/bitcoin-wallet/wallet.go (2)
  • NewBackend (33-54)
  • Config (25-31)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
internal/onchain/bitcoin-wallet/wallet.go (3)
  • NewBackend (33-54)
  • Backend (13-17)
  • Wallet (19-23)
internal/test/test.go (1)
  • WalletCredentials (64-78)
🪛 Clang (14.0.6)
internal/onchain/bitcoin-wallet/bdk/bdk.h

[error] 8-8: 'stdbool.h' file not found

(clang-diagnostic-error)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (3)
bdk/src/types.rs (1)

1-69: LGTM! Well-structured UniFFI type definitions.

The type definitions are clean and complete:

  • Error handling properly chains the full error context via error.chain()
  • Network enum now includes Signet (addressing past review feedback)
  • All structs are properly annotated for UniFFI code generation
bdk/uniffi-bindgen.rs (1)

1-3: LGTM! Standard UniFFI bindgen entry point.

The delegation to uniffi::uniffi_bindgen_main() is the standard pattern for UniFFI binding generation.

internal/onchain/bitcoin-wallet/bdk/bdk.h (1)

1-797: LGTM! Autogenerated UniFFI C header.

This file is autogenerated by UniFFI for the C/FFI bindings. The static analysis error about missing stdbool.h is a false positive—the header will be available in the actual build environment.

Comment thread bdk/src/wallet.rs
Comment thread bdk/src/wallet.rs
Comment thread internal/onchain/wallet_test.go
Comment thread internal/test/test.go
Comment thread internal/test/test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/test/test.go (1)

143-156: Fix concurrent map writes to shared result.

eg.Go goroutines write to the same map without sync; this can panic. Protect with a mutex.

 func InitTestWallet(debug bool) (map[boltz.Currency]*wallet.Wallet, error) {
@@
-  result := make(map[boltz.Currency]*wallet.Wallet)
+  result := make(map[boltz.Currency]*wallet.Wallet)
+  var mu sync.Mutex
@@
-      wallet, err := wallet.Login(WalletCredentials(currency))
+      w, err := wallet.Login(WalletCredentials(currency))
         if err != nil {
           return err
         }
         time.Sleep(2 * time.Second)
-        result[currency] = wallet
+        mu.Lock()
+        result[currency] = w
+        mu.Unlock()
         return FundWallet(currency, w)

Add import:

 import (
   "fmt"
   "math/rand/v2"
   "os"
   "os/exec"
   "strconv"
   "strings"
   "testing"
   "time"
+  "sync"
♻️ Duplicate comments (6)
internal/onchain/wallet_test.go (1)

290-301: Commented-out test block: either enable or convert to t.Skip with rationale.

Keeping dead code commented confuses maintenance. Turn it into a skipped test with a brief reason, or remove it.

bdk/src/wallet.rs (3)

62-85: Use testnet/regtest coin type (1) for default xpub derivation.

Currently hardcoded to mainnet coin type (0). Select coin type by network.

 fn derive_default_xpub(network: Network, mnemonic: String) -> Result<String, Error> {
     let secp = Secp256k1::new();
-    let derivation_path = DerivationPath::from_str("m/84h/0h/0h").context("derivation path")?;
+    let coin_type = match network {
+        Network::Bitcoin => 0,
+        Network::Testnet | Network::Regtest => 1,
+    };
+    let derivation_path = DerivationPath::from_str(&format!("m/84h/{}h/0h", coin_type))
+        .context("derivation path")?;

Based on learnings


213-224: Don’t hold wallet mutex across network I/O in sync().

Build the request under the lock, drop it, perform full_scan, then re-lock to apply/persist. Reduces contention.

-    let mut wallet = self.get_wallet()?;
-    let request = wallet.start_full_scan();
-    let update = self
+    let request = {
+        let mut wallet = self.get_wallet()?;
+        wallet.start_full_scan()
+    };
+    let update = self
         .backend
         .electrum
         .full_scan(request, 20, 50, false)
         .context("full scan")?;
-    wallet.apply_update(update).context("apply update")?;
-    self.persist(&mut wallet)?;
+    let mut wallet = self.get_wallet()?;
+    wallet.apply_update(update).context("apply update")?;
+    self.persist(&mut wallet)?;

Based on learnings


319-323: Fix pagination order (skip before take) and treat limit=0 as “no limit”.

Current take-then-skip yields wrong pages; limit=0 returns no items.

-    let transactions = wallet
-        .transactions()
-        .take(limit as usize)
-        .skip(offset as usize)
+    let effective_limit = if limit == 0 { usize::MAX } else { limit as usize };
+    let transactions = wallet
+        .transactions()
+        .skip(offset as usize)
+        .take(effective_limit)
internal/onchain/bitcoin-wallet/bdk/bdk.go (1)

1244-1252: Panic bug: wrong type in FfiDestroyerError.Destroy.

Errors are stored as pointers; matching by value misses and panics.

 func (_ FfiDestroyerError) Destroy(value *Error) {
+    if value == nil || value.err == nil {
+        return
+    }
     switch variantValue := value.err.(type) {
-    case ErrorGeneric:
-        variantValue.destroy()
+    case *ErrorGeneric:
+        variantValue.destroy()
     default:
         _ = variantValue
         panic(fmt.Sprintf("invalid error value `%v` in FfiDestroyerError.Destroy", value))
     }
 }
internal/onchain/bitcoin-wallet/wallet.go (1)

56-66: Fix nil deref in panic path.

Default branch dereferences a possibly nil pointer. Print the pointer instead.

-    default:
-        panic(fmt.Sprintf("unexpected network %v", *network))
+    default:
+        // avoid dereferencing nil in panic path
+        panic(fmt.Sprintf("unexpected network %v", network))
🧹 Nitpick comments (13)
internal/test/test.go (1)

71-73: Don’t ignore DeriveDefaultXpub error.

If derivation fails, tests silently proceed with an empty descriptor. Fail fast.

-  if currency == boltz.CurrencyBtc {
-    creds.CoreDescriptor, _ = bdk.DeriveDefaultXpub(bdk.NetworkRegtest, WalletMnemonic)
-  }
+  if currency == boltz.CurrencyBtc {
+    desc, err := bdk.DeriveDefaultXpub(bdk.NetworkRegtest, WalletMnemonic)
+    if err != nil { panic("DeriveDefaultXpub failed: " + err.Error()) }
+    creds.CoreDescriptor = desc
+  }
internal/onchain/wallet_test.go (1)

244-251: Avoid fixed sleeps; wait until tx is in mempool.

Replace time.Sleep with require.Eventually to reduce flakes.

-    // Wait a bit to ensure transaction is in mempool
-    time.Sleep(2 * time.Second)
+    // Wait until wallet sees the unconfirmed tx
+    require.Eventually(t, func() bool {
+        _ = wallet.Sync()
+        txs, _ := wallet.GetTransactions(50, 0)
+        for _, tx := range txs { if tx.Id == txId && tx.BlockHeight == 0 { return true } }
+        return false
+    }, 10*time.Second, 250*time.Millisecond)
bdk/Cargo.toml (1)

17-19: Add an explicit bin target for uniffi-bindgen.

Helps “cargo run --bin uniffi-bindgen” workflows and some generators. Optional but nice.

 [lib]
 crate-type = ["staticlib", "cdylib", "rlib"]
 name = "bdk"
+
+[[bin]]
+name = "uniffi-bindgen"
+path = "uniffi-bindgen.rs"
Makefile (2)

63-65: Prefer passing the .so to uniffi-bindgen-go.

Dynamic lib tends to be more portable for metadata extraction than .a.

-bdk-bindings: build-bdk bindgen-go
-	cd $(BDK_BINDINGS_PATH) && uniffi-bindgen-go --out-dir ../internal/onchain/bitcoin-wallet/ --library ./target/release/libbdk.a
+bdk-bindings: build-bdk bindgen-go
+	cd $(BDK_BINDINGS_PATH) && uniffi-bindgen-go --out-dir ../internal/onchain/bitcoin-wallet/ --library ./target/release/libbdk.so

114-120: Avoid stale BDK artifacts: build unconditionally.

Guarded build won’t rebuild after source changes if libbdk.a exists. Cargo is incremental; let it decide.

-build-bdk:
-ifeq ("$(wildcard internal/onchain/bitcoin-wallet/bdk/libbdk.a)","")
-	@$(call print, "Building bdk")
-	cd $(BDK_BINDINGS_PATH) && cargo build --release --lib
-	cp $(BDK_BINDINGS_PATH)/target/release/libbdk.a $(BDK_BINDINGS_PATH)/target/release/libbdk.so internal/onchain/bitcoin-wallet/bdk/
-endif
+build-bdk:
+	@$(call print, "Building bdk")
+	cd $(BDK_BINDINGS_PATH) && cargo build --release --lib
+	cp $(BDK_BINDINGS_PATH)/target/release/libbdk.a $(BDK_BINDINGS_PATH)/target/release/libbdk.so internal/onchain/bitcoin-wallet/bdk/ || true
bdk/src/wallet.rs (1)

163-165: Round fee-rate conversion to avoid underpaying by truncation.

Use precise 250x conversion and round.

-fn parse_fee_rate(sat_per_vbyte: f64) -> FeeRate {
-    FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0 / 4.0) as u64)
-}
+fn parse_fee_rate(sat_per_vbyte: f64) -> FeeRate {
+    let sp_kwu = (sat_per_vbyte * 250.0).round() as u64;
+    FeeRate::from_sat_per_kwu(sp_kwu)
+}

Based on learnings

bdk/src/types.rs (1)

47-54: Document field units/semantics for cross-language consumers.

Please add brief docs indicating:

  • timestamp is Unix seconds.
  • block_height is 0 (or similar) when unconfirmed.

This avoids subtle bugs in bindings and callers.

 #[derive(uniffi::Record)]
 pub struct WalletTransaction {
     pub id: String,
-    pub timestamp: u64,
+    /// Unix timestamp in seconds
+    pub timestamp: u64,
     pub outputs: Vec<WalletTransactionOutput>,
-    pub block_height: u32,
+    /// 0 if unconfirmed
+    pub block_height: u32,
     pub balance_change: i64,
     pub is_consolidation: bool,
 }
internal/onchain/bitcoin-wallet/wallet.go (2)

45-54: Avoid double scheme in Electrum URL.

If cfg.Electrum.Url already contains tcp:// or ssl://, this will produce tcp://tcp://... Consider normalizing.

-    url = cfg.Electrum.Url
-    if cfg.Electrum.SSL {
-        url = "ssl://" + url
-    } else {
-        url = "tcp://" + url
-    }
+    u := cfg.Electrum.Url
+    if !strings.HasPrefix(u, "tcp://") && !strings.HasPrefix(u, "ssl://") {
+        if cfg.Electrum.SSL {
+            u = "ssl://" + u
+        } else {
+            u = "tcp://" + u
+        }
+    }
+    url = u

(Add import "strings")


48-49: Use filepath.Join for portability.

String concat can be brittle across platforms.

-        cfg.DataDir+"/bdk.sqlite",
+        filepath.Join(cfg.DataDir, "bdk.sqlite"),

(Add import "path/filepath")

internal/onchain/bitcoin-wallet/bdk/bdk.go (2)

1256-1260: Add NetworkSignet to mirror Rust and avoid future mismatches.

Rust exposes a Signet variant; define the Go constant for parity even if unused today.

 const (
     NetworkBitcoin Network = 1
     NetworkTestnet Network = 2
     NetworkRegtest Network = 3
+    NetworkSignet  Network = 4
 )

67-69: Guard potential length overflow in ToGoBytes.

C.int(cb.inner.len) may overflow on 32‑bit targets. Add a bound check.

 func (cb GoRustBuffer) ToGoBytes() []byte {
-    return C.GoBytes(unsafe.Pointer(cb.inner.data), C.int(cb.inner.len))
+    if cb.inner.len > C.uint64_t(math.MaxInt32) {
+        panic("buffer too large for GoBytes")
+    }
+    return C.GoBytes(unsafe.Pointer(cb.inner.data), C.int(cb.inner.len))
 }
internal/onchain/bitcoin-wallet/bdk/bdk.h (2)

8-8: Guard stdbool include for C++/toolchains missing it.

Fixes portability and the reported clang error.

-#include <stdbool.h>
+#ifndef __cplusplus
+#  include <stdbool.h>
+#endif

3-5: Remove unprofessional autogenerated comment.

Please replace with a neutral “Autogenerated by UniFFI. Do not edit.” notice.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 667843d and a953735.

⛔ Files ignored due to path filters (1)
  • bdk/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .golangci.yml (1 hunks)
  • Makefile (2 hunks)
  • bdk/Cargo.toml (1 hunks)
  • bdk/src/lib.rs (1 hunks)
  • bdk/src/types.rs (1 hunks)
  • bdk/src/wallet.rs (1 hunks)
  • bdk/uniffi-bindgen.rs (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.h (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/dynamic.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/static.go (1 hunks)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
  • internal/onchain/wallet_test.go (2 hunks)
  • internal/rpcserver/server.go (2 hunks)
  • internal/test/test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • bdk/src/lib.rs
  • internal/onchain/bitcoin-wallet/bdk/static.go
  • internal/onchain/bitcoin-wallet/bdk/dynamic.go
🧰 Additional context used
🧬 Code graph analysis (7)
bdk/src/wallet.rs (1)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • WalletSendResult (1008-1012)
  • WalletTransactionOutput (1108-1112)
internal/test/test.go (7)
pkg/boltz/currency.go (2)
  • CurrencyBtc (11-11)
  • CurrencyLiquid (12-12)
internal/onchain/bitcoin-wallet/bdk/bdk.go (4)
  • DeriveDefaultXpub (1410-1422)
  • NetworkRegtest (1259-1259)
  • Network (1254-1254)
  • NewBackend (687-697)
internal/onchain/onchain.go (3)
  • Currency (67-70)
  • ChainProvider (40-48)
  • RegtestElectrumConfig (60-63)
internal/onchain/boltz.go (1)
  • NewBoltzChainProvider (16-18)
internal/onchain/liquid-wallet/wallet.go (4)
  • Regtest (135-135)
  • Config (40-48)
  • Persister (20-23)
  • NewBackend (68-131)
internal/onchain/bitcoin-wallet/wallet.go (2)
  • Config (25-31)
  • NewBackend (33-54)
internal/onchain/wallet.go (1)
  • WalletBackend (126-129)
internal/onchain/wallet_test.go (4)
internal/test/test.go (7)
  • WalletBackend (189-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • MineBlock (243-247)
pkg/boltz/currency.go (2)
  • CurrencyBtc (11-11)
  • CurrencyLiquid (12-12)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
internal/rpcserver/server.go (4)
internal/onchain/bitcoin-wallet/wallet.go (2)
  • Config (25-31)
  • NewBackend (33-54)
internal/config/config.go (1)
  • Config (55-98)
internal/onchain/onchain.go (1)
  • ChainProvider (40-48)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/bitcoin-wallet/wallet.go (4)
internal/onchain/bitcoin-wallet/bdk/bdk.go (12)
  • Backend (683-685)
  • Wallet (757-759)
  • Network (1254-1254)
  • NewBackend (687-697)
  • NetworkBitcoin (1257-1257)
  • NetworkTestnet (1258-1258)
  • NetworkRegtest (1259-1259)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
  • DeriveDefaultXpub (1410-1422)
  • Balance (928-931)
  • WalletTransaction (1052-1059)
internal/onchain/onchain.go (2)
  • ChainProvider (40-48)
  • ElectrumOptions (50-53)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
bdk/src/types.rs (2)
internal/onchain/liquid-wallet/wallet.go (1)
  • Testnet (136-136)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • WalletTransactionOutput (1108-1112)
  • WalletSendResult (1008-1012)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
internal/onchain/bitcoin-wallet/wallet.go (3)
  • NewBackend (33-54)
  • Backend (13-17)
  • Wallet (19-23)
internal/test/test.go (2)
  • SendToAddress (261-263)
  • WalletCredentials (64-78)
🪛 Clang (14.0.6)
internal/onchain/bitcoin-wallet/bdk/bdk.h

[error] 8-8: 'stdbool.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (4)
.golangci.yml (1)

6-6: LGTM for excluding generated bindings.

Excluding internal/onchain/bitcoin-wallet/bdk/ from linters is appropriate for generated FFI code.

bdk/uniffi-bindgen.rs (1)

1-3: LGTM.

Minimal entrypoint correctly defers to uniffi_bindgen_main().

bdk/src/types.rs (1)

21-37: Signet support and mapping look correct.

Enum parity with BDK and the From mapping are complete. Good addition.

internal/onchain/bitcoin-wallet/wallet.go (1)

175-189: No issues found. Code is correct.

The verification confirms that result[i].Outputs is of type []TransactionOutput, and the code correctly appends onchain.TransactionOutput structs with matching fields (Address, Amount, IsOurAddress). The types align perfectly with no implicit conversions or compile-time mismatches.

Comment thread internal/onchain/bitcoin-wallet/wallet.go Outdated
Comment thread internal/onchain/bitcoin-wallet/wallet.go
Comment thread internal/rpcserver/server.go
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from a953735 to 1b15a97 Compare October 20, 2025 08:47
avoid double spends
skipped the wrong tests before (new suite instead of gdk)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/onchain/wallet_test.go (2)

290-301: Address the commented-out Xpub/BTC test case.

This echoes michael1011's previous question: why is this test case commented out? If Xpub import for BTC is not yet supported, consider removing the commented code or adding a TODO comment explaining when it will be implemented. If it should work, please uncomment and verify.


224-255: Verify broadcast and txid semantics in BumpTransactionFee test.

The test variable is named newTxId but the backend may return raw transaction hex. Additionally, the test doesn't verify that:

  1. The bump actually broadcasts (no Sync() call after bumping)
  2. The replacement transaction exists in the mempool or wallet
  3. The old transaction is no longer present

Consider the approach in internal/onchain/wallet/wallet_test.go (lines 29-51) which uses Eventually to poll until the bump succeeds, or follow the previous review suggestion to decode the hex if needed and sync to verify broadcast.

Apply this diff to improve the test:

 	// Try to bump the fee
-	newTxId, err := wallet.BumpTransactionFee(txId, 3)
-	require.NoError(t, err)
-	require.NotEmpty(t, newTxId)
-	require.NotEqual(t, txId, newTxId)
+	require.Eventually(t, func() bool {
+		newTxId, err := wallet.BumpTransactionFee(txId, 3)
+		if err != nil {
+			return false
+		}
+		require.NotEmpty(t, newTxId)
+		require.NotEqual(t, txId, newTxId)
+		
+		// Sync and verify the replacement is in the wallet
+		require.NoError(t, wallet.Sync())
+		transactions, err := wallet.GetTransactions(30, 0)
+		require.NoError(t, err)
+		
+		// Verify new tx exists and old tx is gone
+		hasNew := false
+		hasOld := false
+		for _, tx := range transactions {
+			if tx.Id == newTxId {
+				hasNew = true
+			}
+			if tx.Id == txId {
+				hasOld = true
+			}
+		}
+		return hasNew && !hasOld
+	}, 15*time.Second, 250*time.Millisecond)
 
 	test.MineBlock()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 450fadc and 3a6e370.

📒 Files selected for processing (3)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
  • internal/onchain/wallet/wallet_test.go (1 hunks)
  • internal/onchain/wallet_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/onchain/bitcoin-wallet/wallet.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/onchain/wallet_test.go (5)
pkg/boltz/currency.go (2)
  • CurrencyLiquid (12-12)
  • CurrencyBtc (11-11)
internal/onchain/wallet/wallet_test.go (1)
  • TestWallet_BumpTransactionFee (30-52)
internal/test/test.go (7)
  • WalletBackend (189-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • MineBlock (243-247)
internal/onchain/bitcoin-wallet/bdk/bdk.go (1)
  • WalletCredentials (968-971)
internal/onchain/wallet.go (1)
  • WalletSendArgs (42-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (2)
internal/onchain/wallet_test.go (2)

22-22: LGTM! BTC currency testing enabled.

The addition of boltz.CurrencyBtc to the currency list correctly enables BTC wallet testing across all tests using the walletTest helper function.


303-317: The Liquid CoreDescriptor change is correct.

Git history shows this change (commit 05c39a0) was intentional: the test case was renamed from "CoreDescriptor/Liquid/NoCt" with a BTC descriptor and shouldError: true to "CoreDescriptor/Liquid" with a proper Liquid descriptor and shouldError: false.

The old test validated that BTC descriptors (wpkh without the ct wrapper) fail on Liquid. The new test validates that proper Liquid descriptors succeed. The Liquid wallet implementation correctly parses and validates the descriptor format via lwk.NewWolletDescriptor() at line 189 of liquid-wallet/wallet.go, so the behavioral change is justified.

Comment thread internal/onchain/wallet/wallet_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (8)
internal/onchain/bitcoin-wallet/bdk/bdk.h (1)

3-4: Unprofessional banner remains in generated header.

The informal banner text is still present. Replace with a neutral autogeneration notice.

internal/onchain/bitcoin-wallet/wallet.go (2)

95-106: Nil dereference in panic path remains.

The default case dereferences the network pointer in the panic message, which will panic with a nil dereference if network is nil.

Apply this diff:

 	default:
-		panic(fmt.Sprintf("unexpected network %v", *network))
+		panic(fmt.Sprintf("unexpected network %v", network))

157-166: External-call hazard: ApplyTransaction failure loses broadcast txid.

If ApplyTransaction fails after a successful broadcast, returning "", err hides the txid and can trigger unsafe retries.

Return the txid even when ApplyTransaction fails:

 func (w *Wallet) broadcastTransaction(txHex string) (string, error) {
 	txId, err := w.backend.cfg.ChainProvider.BroadcastTransaction(txHex)
 	if err != nil {
 		return "", err
 	}
 	if err := w.Wallet.ApplyTransaction(txHex); err != nil {
-		return "", err
+		return txId, fmt.Errorf("broadcast succeeded but apply failed: %w", err)
 	}
 	return txId, nil
 }
bdk/src/wallet.rs (2)

65-65: Derivation path hardcoded to mainnet coin type.

The path m/84h/0h/0h always uses coin type 0 (mainnet). Testnet and Regtest should use coin type 1.

Apply this diff:

 fn derive_default_xpub(network: Network, mnemonic: String) -> Result<String, Error> {
   let secp = Secp256k1::new();
-  let derivation_path = DerivationPath::from_str("m/84h/0h/0h").context("derivation path")?;
+  let coin_type = match network {
+      Network::Bitcoin => 0,
+      Network::Testnet | Network::Regtest => 1,
+      Network::Signet => 1,
+  };
+  let derivation_path = DerivationPath::from_str(
+      &format!("m/84h/{}h/0h", coin_type)
+  ).context("derivation path")?;

312-320: Pagination order incorrect: take before skip.

Calling take(limit) before skip(offset) produces wrong pages.

Apply this diff:

     let transactions = wallet
         .transactions()
-        .take(if limit > 0 {
-            limit as usize
-        } else {
-            usize::MAX
-        })
         .skip(offset as usize)
+        .take(if limit > 0 {
+            limit as usize
+        } else {
+            usize::MAX
+        })
internal/onchain/bitcoin-wallet/bdk/bdk.go (3)

55-58: Missing overflow check in AsReader.

unsafe.Slice expects an int but receives uint64 without bounds checking. If cb.inner.len exceeds math.MaxInt, the cast will overflow.

Apply this diff:

 func (cb GoRustBuffer) AsReader() *bytes.Reader {
+	n := int(cb.inner.len)
+	if n < 0 || uint64(n) != uint64(cb.inner.len) {
+		panic("RustBuffer length overflows int")
+	}
-	b := unsafe.Slice((*byte)(cb.inner.data), C.uint64_t(cb.inner.len))
+	b := unsafe.Slice((*byte)(cb.inner.data), n)
 	return bytes.NewReader(b)
 }

67-69: Missing overflow check in ToGoBytes.

C.GoBytes expects C.int but receives cb.inner.len cast directly. If the length exceeds math.MaxInt32, the cast will overflow.

Apply this diff:

 func (cb GoRustBuffer) ToGoBytes() []byte {
+	n := int(cb.inner.len)
+	if n < 0 || uint64(n) != uint64(cb.inner.len) {
+		panic("RustBuffer length overflows int")
+	}
-	return C.GoBytes(unsafe.Pointer(cb.inner.data), C.int(cb.inner.len))
+	return C.GoBytes(unsafe.Pointer(cb.inner.data), C.int(n))
 }

1244-1252: Type switch matches by value instead of pointer.

The case ErrorGeneric matches by value, but errors are stored as *ErrorGeneric pointers. This will always fall through to the panic.

Apply this diff:

 func (_ FfiDestroyerError) Destroy(value *Error) {
 	switch variantValue := value.err.(type) {
-	case ErrorGeneric:
+	case *ErrorGeneric:
 		variantValue.destroy()
 	default:
 		_ = variantValue
 		panic(fmt.Sprintf("invalid error value `%v` in FfiDestroyerError.Destroy", value))
 	}
 }
🧹 Nitpick comments (1)
bdk/src/wallet.rs (1)

152-154: Fee rate conversion may underpay due to truncation.

Manual conversion to sat/kwu truncates fractional sat/vB and may underpay.

Consider rounding up to preserve user intent:

 fn parse_fee_rate(sat_per_vbyte: f64) -> FeeRate {
-    FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0 / 4.0) as u64)
+    // Round up to avoid underpaying
+    let sat_per_kwu = (sat_per_vbyte * 1000.0 / 4.0).ceil() as u64;
+    FeeRate::from_sat_per_kwu(sat_per_kwu)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a6e370 and 83ed177.

📒 Files selected for processing (4)
  • bdk/src/wallet.rs (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.go (1 hunks)
  • internal/onchain/bitcoin-wallet/bdk/bdk.h (1 hunks)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
bdk/src/wallet.rs (1)
internal/onchain/bitcoin-wallet/bdk/bdk.go (9)
  • Network (1254-1254)
  • Wallet (757-759)
  • Error (1152-1154)
  • WalletCredentials (968-971)
  • ChainClient (683-685)
  • Balance (928-931)
  • WalletSendResult (1008-1012)
  • WalletTransaction (1052-1059)
  • WalletTransactionOutput (1108-1112)
internal/onchain/bitcoin-wallet/bdk/bdk.go (3)
internal/onchain/liquid-wallet/lwk/lwk.go (3)
  • NewAddress (2758-2768)
  • Mnemonic (5297-5299)
  • Address (2753-2755)
internal/test/test.go (1)
  • WalletCredentials (64-78)
internal/onchain/bitcoin-wallet/wallet.go (1)
  • Wallet (24-29)
internal/onchain/bitcoin-wallet/wallet.go (4)
internal/onchain/onchain.go (3)
  • ElectrumOptions (50-53)
  • ChainProvider (40-48)
  • RegtestElectrumConfig (60-63)
internal/onchain/bitcoin-wallet/bdk/bdk.go (13)
  • Wallet (757-759)
  • Network (1254-1254)
  • ChainClient (683-685)
  • NewChainClient (687-697)
  • NetworkBitcoin (1257-1257)
  • NetworkTestnet (1258-1258)
  • NetworkRegtest (1259-1259)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
  • DeriveDefaultXpub (1411-1423)
  • Balance (928-931)
  • WalletSendResult (1008-1012)
  • Error (1152-1154)
pkg/boltz/currency.go (1)
  • CurrencyBtc (11-11)
internal/onchain/wallet.go (2)
  • WalletSendArgs (42-47)
  • DefaultTransactionsLimit (11-11)
🪛 Clang (14.0.6)
internal/onchain/bitcoin-wallet/bdk/bdk.h

[error] 8-8: 'stdbool.h' file not found

(clang-diagnostic-error)

Comment thread bdk/src/wallet.rs
Comment thread internal/onchain/bitcoin-wallet/wallet.go Outdated
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from f683819 to 369397a Compare October 25, 2025 11:06
Comment thread bdk/src/types.rs
Comment on lines +21 to +36
#[derive(uniffi::Enum)]
pub enum Network {
Bitcoin,
Testnet,
Regtest,
}

impl From<Network> for BdkNetwork {
fn from(network: Network) -> Self {
match network {
Network::Bitcoin => BdkNetwork::Bitcoin,
Network::Testnet => BdkNetwork::Testnet,
Network::Regtest => BdkNetwork::Regtest,
}
}
}

@michael1011 michael1011 Oct 27, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend does support signet but we don't have a deployment there. And the client doesn't support signet in many places, so whatever. Doesn't matter

Comment thread bdk/src/wallet.rs
};

let descriptor_pubkey = DescriptorPublicKey::MultiXPub(multi);
Ok(Wpkh::new(descriptor_pubkey).context("wpkh")?.to_string())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We agreed to default to P2WPKH

Comment thread bdk/src/wallet.rs Outdated
Comment thread internal/onchain/wallet_test.go Outdated
Comment on lines +290 to +301
/*
{
name: "Xpub/BTC",
credentials: onchain.WalletCredentials{
WalletInfo: onchain.WalletInfo{
Currency: boltz.CurrencyBtc,
},
Xpub: "vpub5XzEwP9YWe4cJD6pB3njrMgWahQbzHhfGAyuErnswtPuzm6QdLqHH79DSZ6YW3McdE1pwxvr7wHU2nMtVbPZ1jW4tqg8ggx4ZV19U7i69pd",
},
shouldError: false,
},
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that still commented out?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
bdk/src/wallet.rs (2)

207-217: Major: Wallet mutex held during network I/O blocks other operations.

Despite being marked as addressed in a previous review, the lock is still held across the full_scan network call (lines 208-215). This blocks all other wallet operations during synchronization, which can take several seconds.

Apply this diff to release the lock during network I/O:

 pub fn sync(&self, chain_client: Arc<ChainClient>) -> Result<(), Error> {
-    let mut wallet = self.get_wallet()?;
-    let request = wallet.start_full_scan();
-    let update = chain_client
+    let request = {
+        let mut wallet = self.get_wallet()?;
+        wallet.start_full_scan()
+    };
+    let update = chain_client
         .electrum
         .full_scan(request, 20, 50, false)
         .context("full scan")?;
+    let mut wallet = self.get_wallet()?;
     wallet.apply_update(update).context("apply update")?;
     self.persist(&mut wallet)?;
     Ok(())
 }

Based on learnings


62-85: Critical: Derivation path uses mainnet coin type for all networks.

Line 65 hardcodes m/84h/0h/0h, which always uses coin type 0 (mainnet). Per BIP44, testnet and regtest should use coin type 1. This was flagged in a previous review but remains unresolved.

Apply this diff to fix the coin-type selection:

 fn derive_default_xpub(network: Network, mnemonic: String) -> Result<String, Error> {
   let secp = Secp256k1::new();
-  let derivation_path = DerivationPath::from_str("m/84h/0h/0h").context("derivation path")?;
+  let coin_type = match network {
+      Network::Bitcoin => 0,
+      Network::Testnet | Network::Regtest => 1,
+  };
+  let derivation_path = DerivationPath::from_str(
+      &format!("m/84h/{}h/0h", coin_type)
+  ).context("derivation path")?;
🧹 Nitpick comments (1)
bdk/src/wallet.rs (1)

152-154: Consider rounding up fractional fee rates to avoid underpaying.

The current cast to u64 truncates fractional sat/vB values. For example, 1.5 sat/vB becomes 375 sat/kwu (floored) instead of 376 (rounded up). While the math is correct, underpaying fees could cause transaction delays.

Apply this diff to round up:

 fn parse_fee_rate(sat_per_vbyte: f64) -> FeeRate {
-    FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0 / 4.0) as u64)
+    FeeRate::from_sat_per_kwu((sat_per_vbyte * 1000.0 / 4.0).ceil() as u64)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 369397a and 948efee.

📒 Files selected for processing (2)
  • bdk/src/wallet.rs (1 hunks)
  • internal/onchain/wallet_test.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/onchain/wallet_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
bdk/src/wallet.rs (1)
internal/onchain/bitcoin-wallet/bdk/bdk.go (9)
  • Network (1254-1254)
  • Wallet (757-759)
  • Error (1152-1154)
  • WalletCredentials (968-971)
  • ChainClient (683-685)
  • Balance (928-931)
  • WalletSendResult (1008-1012)
  • WalletTransaction (1052-1059)
  • WalletTransactionOutput (1108-1112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (10)
bdk/src/wallet.rs (10)

1-60: LGTM: Helper functions are well-structured.

The mnemonic-to-xprv and key derivation helpers are implemented correctly with appropriate error handling.


87-133: LGTM: Descriptor derivation logic is sound.

The credential handling and descriptor derivation correctly manages optional mnemonics and builds the keymap appropriately.


135-150: LGTM: ChainClient wrapper is straightforward.

The Electrum client integration is clean and properly handles connection initialization.


156-205: LGTM: Wallet initialization is robust.

The constructor properly handles both wallet loading and creation scenarios with appropriate descriptor setup and database persistence.


219-225: LGTM: Transaction application is correct.

Properly deserializes and applies unconfirmed transactions to the wallet state.


244-258: LGTM: Address generation and balance retrieval are correct.

Both methods properly use the wallet API and persist state when necessary.


305-350: LGTM: Pagination is now correct.

The skip() before take() ordering (line 314) properly implements pagination. This addresses the issue raised in a previous review.


353-366: LGTM: Helper methods are straightforward.

The mutex management in get_wallet and persist is appropriate for thread-safe wallet access.


260-303: Broadcasting is properly implemented in the Go layer.

The Go wrapper at internal/onchain/bitcoin-wallet/wallet.go calls sendToAddress which invokes the Rust SendToAddress binding and then automatically broadcasts the returned transaction hex via broadcastTransaction, ensuring consistency with the Rust method's API design.


227-242: No action required—broadcasting is already implemented.

The Go wrapper at internal/onchain/bitcoin-wallet/wallet.go:168–174 already broadcasts the bumped transaction via w.broadcastTransaction(txHex) before returning. This follows the same pattern as other send methods (e.g., sendToAddress at line 200), ensuring API consistency. The Rust layer correctly returns raw hex; the Go layer correctly handles broadcasting.

Likely an incorrect or invalid review comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/onchain/wallet_test.go (1)

234-265: Address the concerns from previous reviews and improve test robustness.

This test still has the issues flagged in past review comments:

  1. Line 258: The variable is named newTxId but the backend may return raw transaction hex rather than a txid. The test only checks newTxId != txId, which could pass trivially.
  2. Line 254-255: Hard-coded sleep is fragile; prefer require.Eventually to poll mempool state.
  3. Line 263: Mining a block without verifying that the bumped transaction was actually confirmed or that the original transaction was replaced.

Consider this more robust approach:

-	// Wait a bit to ensure transaction is in mempool
-	time.Sleep(2 * time.Second)
-
-	// Try to bump the fee
-	newTxId, err := wallet.BumpTransactionFee(txId, 3)
-	require.NoError(t, err)
-	require.NotEmpty(t, newTxId)
-	require.NotEqual(t, txId, newTxId)
-
-	test.MineBlock()
+	// Wait for transaction to appear in mempool
+	require.Eventually(t, func() bool {
+		require.NoError(t, wallet.Sync())
+		txs, _ := wallet.GetTransactions(0, 0)
+		return slices.ContainsFunc(txs, func(tx onchain.Transaction) bool {
+			return tx.Id == txId && tx.Confirmations == 0
+		})
+	}, 10*time.Second, 250*time.Millisecond)
+
+	// Bump the fee
+	newTxResult, err := wallet.BumpTransactionFee(txId, 3)
+	require.NoError(t, err)
+	require.NotEmpty(t, newTxResult)
+	
+	// If backend returns hex, decode to get txid; otherwise use as-is
+	// Verify the bumped transaction differs from original
+	require.NotEqual(t, txId, newTxResult)
+	
+	// Verify the bumped transaction is in mempool
+	require.Eventually(t, func() bool {
+		require.NoError(t, wallet.Sync())
+		txs, _ := wallet.GetTransactions(0, 0)
+		return slices.ContainsFunc(txs, func(tx onchain.Transaction) bool {
+			return tx.Id == newTxResult && tx.Confirmations == 0
+		})
+	}, 10*time.Second, 250*time.Millisecond)
+	
+	test.MineBlock()
+	
+	// Verify the bumped transaction is confirmed
+	require.Eventually(t, func() bool {
+		require.NoError(t, wallet.Sync())
+		txs, _ := wallet.GetTransactions(0, 0)
+		return slices.ContainsFunc(txs, func(tx onchain.Transaction) bool {
+			return tx.Id == newTxResult && tx.Confirmations > 0
+		})
+	}, 10*time.Second, 250*time.Millisecond)
🧹 Nitpick comments (1)
internal/onchain/wallet_test.go (1)

414-414: Clarify the intent of ignoring ValidateWalletCredentials error.

The error from ValidateWalletCredentials is explicitly ignored. Looking at the implementation, this function has a side effect of populating CoreDescriptor when it's empty. However, ignoring the error is confusing because some test cases with invalid credentials (e.g., "wrong wrong" mnemonic) would fail validation but are expected to fail later in NewWallet.

Consider one of these approaches for clarity:

Option 1: Only call ValidateWalletCredentials for test cases expected to succeed:

 	backend := test.WalletBackend(t, tc.currency)
 	tc.credentials.WalletInfo = test.WalletInfo(tc.currency)
-	_ = onchain.ValidateWalletCredentials(backend, &tc.credentials)
+	if !tc.shouldError {
+		require.NoError(t, onchain.ValidateWalletCredentials(backend, &tc.credentials))
+	}
 	wallet, err := backend.NewWallet(&tc.credentials)

Option 2: Add a comment explaining why the error is ignored:

+	// Populate CoreDescriptor if missing; ignore errors as some test cases expect NewWallet to fail
 	_ = onchain.ValidateWalletCredentials(backend, &tc.credentials)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 948efee and 3e595df.

📒 Files selected for processing (1)
  • internal/onchain/wallet_test.go (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/onchain/wallet_test.go (5)
pkg/boltz/currency.go (2)
  • CurrencyLiquid (12-12)
  • CurrencyBtc (11-11)
internal/onchain/wallet/wallet_test.go (1)
  • TestWallet_BumpTransactionFee (30-52)
internal/test/test.go (8)
  • WalletBackend (189-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • MineBlock (243-247)
  • WalletMnemonic (51-51)
internal/onchain/bitcoin-wallet/bdk/bdk.go (2)
  • WalletCredentials (968-971)
  • Error (1152-1154)
internal/onchain/wallet.go (2)
  • WalletSendArgs (42-47)
  • ValidateWalletCredentials (131-146)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (2)
internal/onchain/wallet_test.go (2)

22-22: LGTM! Good test coverage expansion.

Extending the test matrix to include both Liquid and BTC currencies is a solid approach to ensure wallet functionality works across both chains.


352-359: The test inconsistency is confirmed and appears intentional—verify the design decision.

The two wallet backends have different validation strategies:

  • BTC: The bdk library (Rust layer) validates that a provided descriptor matches the mnemonic's derived keys. When both credentials are provided with mismatched pubkeys, bdk.NewWallet() returns an error. Test expects shouldError: true

  • Liquid: The lwk library validates descriptor syntax only via lwk.NewWolletDescriptor(), but does not validate that the descriptor's pubkeys match any provided mnemonic. The signer and descriptor are treated independently. Test expects shouldError: false

This difference allows Liquid wallets to import descriptors from external sources (hardware wallets, etc.) even when a mnemonic is also provided—the two don't need to derive the same keys.

Action: Confirm whether this design difference is intentional. If Liquid should enforce matching descriptors like BTC, update line 358 to shouldError: true. If the permissive behavior is correct, consider documenting why BTC and Liquid differ.

Comment on lines +204 to +205
first := transactions[0]
count := len(transactions)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add bounds check to prevent panic.

The test directly accesses transactions[0] without verifying the slice is non-empty. While walletTest funds the wallet, there's no guarantee transactions appear in GetTransactions before this assertion.

Apply this diff to add a defensive check:

+	require.NotEmpty(t, transactions, "wallet should have transactions after funding")
 	first := transactions[0]
 	count := len(transactions)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
first := transactions[0]
count := len(transactions)
require.NotEmpty(t, transactions, "wallet should have transactions after funding")
first := transactions[0]
count := len(transactions)
🤖 Prompt for AI Agents
In internal/onchain/wallet_test.go around lines 204-205, the test accesses
transactions[0] without verifying the slice has elements; add a defensive bounds
check by validating len(transactions) > 0 before indexing, and if it's zero call
t.Fatalf (or t.Fatalf with a helpful message) so the test fails cleanly instead
of panicking; only proceed to inspect transactions[0] after that check.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/onchain/wallet.go (1)

136-138: Xpub deprecation logic is correct.

The guard properly deprecates Xpub-only credentials and directs users to the newer CoreDescriptor/Mnemonic approach, aligning with the BDK integration.

Consider simplifying the error message punctuation:

-			return errors.New("xpub is deprecated, use core descriptor and / or mnemonic")
+			return errors.New("xpub is deprecated, use core descriptor or mnemonic")

The "or" is clearer since either credential type is sufficient (Mnemonic auto-derives the descriptor at lines 141-147).

This is a breaking change for existing Xpub-only configurations. Verify that migration documentation or upgrade notes explain how users should transition from Xpub to CoreDescriptor/Mnemonic-based credentials.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e595df and 0a7fe7a.

📒 Files selected for processing (1)
  • internal/onchain/wallet.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)

@michael1011 michael1011 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@jackstar12

jackstar12 commented Oct 28, 2025

Copy link
Copy Markdown
Member Author

TODO:

  • fix flaky bump tx test
  • expose boltz electrum fallback @michael1011

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/onchain/bitcoin-wallet/wallet.go (2)

96-107: Fix nil dereference in panic path.

If the network pointer is nil, line 105 will panic on dereference before the panic message can be formatted.

Apply this diff:

 	default:
-		panic(fmt.Sprintf("unexpected network %v", *network))
+		panic(fmt.Sprintf("unexpected network %v", network))

158-167: External-call hazard: broadcast success but apply failure loses txid.

After successful broadcast at line 159, if ApplyTransaction fails at line 163, the method returns "", err, hiding the fact that the transaction was already broadcast. This can lead to unsafe retries and double-spends.

Apply this diff to return the txid even when ApplyTransaction fails:

 func (w *Wallet) broadcastTransaction(txHex string) (string, error) {
 	txId, err := w.backend.cfg.ChainProvider.BroadcastTransaction(txHex)
 	if err != nil {
 		return "", err
 	}
 	if err := w.Wallet.ApplyTransaction(txHex); err != nil {
-		return "", err
+		// Transaction already broadcast; return txId to prevent unsafe retries
+		logger.Errorf("Failed to apply transaction %s to wallet state: %v", txId, err)
+		return txId, fmt.Errorf("transaction broadcast but apply failed: %w", err)
 	}
 	return txId, nil
 }
🧹 Nitpick comments (1)
internal/onchain/onchain.go (1)

55-60: LGTM! Consider refactoring to reduce duplication.

The String() method is correct and provides a clean way to format Electrum connection strings.

Note: The logic in internal/onchain/bitcoin-wallet/wallet.go:39-45 (newChainClient) duplicates this formatting. Consider refactoring newChainClient to use this method:

 func newChainClient(electrum *onchain.ElectrumOptions) (*bdk.ChainClient, error) {
-	url := electrum.Url
-	if electrum.SSL {
-		url = "ssl://" + url
-	} else {
-		url = "tcp://" + url
-	}
+	url := electrum.String()
 	logger.Debugf("Connecting to electrum server: %s", url)
 	return bdk.NewChainClient(url)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a7fe7a and aff1936.

📒 Files selected for processing (2)
  • internal/onchain/bitcoin-wallet/wallet.go (1 hunks)
  • internal/onchain/onchain.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-11T16:16:33.431Z
Learnt from: jackstar12
Repo: BoltzExchange/boltz-client PR: 502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.

Applied to files:

  • internal/onchain/bitcoin-wallet/wallet.go
🧬 Code graph analysis (1)
internal/onchain/bitcoin-wallet/wallet.go (4)
internal/onchain/onchain.go (5)
  • ElectrumOptions (50-53)
  • ChainProvider (40-48)
  • RegtestElectrumConfig (67-70)
  • Id (21-21)
  • Output (35-38)
internal/onchain/bitcoin-wallet/bdk/bdk.go (13)
  • Wallet (757-759)
  • Network (1254-1254)
  • ChainClient (683-685)
  • NewChainClient (687-697)
  • NetworkBitcoin (1257-1257)
  • NetworkTestnet (1258-1258)
  • NetworkRegtest (1259-1259)
  • NewWallet (761-771)
  • WalletCredentials (968-971)
  • DeriveDefaultXpub (1411-1423)
  • Balance (928-931)
  • WalletSendResult (1008-1012)
  • Error (1152-1154)
internal/logger/logger.go (3)
  • Debugf (121-123)
  • Infof (113-115)
  • Errorf (97-99)
internal/onchain/wallet.go (2)
  • WalletSendArgs (42-47)
  • DefaultTransactionsLimit (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
  • GitHub Check: Lint
🔇 Additional comments (3)
internal/onchain/bitcoin-wallet/wallet.go (3)

50-79: LGTM! Boltz fallback server properly configured.

The NewBackend constructor correctly sets up Electrum server defaults and adds a fallback server for mainnet.


193-202: LGTM! Proper locking for atomic send operation.

The sendLock ensures atomicity from transaction construction through broadcast and state application, consistent with the caller-controlled locking pattern used elsewhere in the codebase.

Based on learnings


239-265: LGTM! Transaction mapping logic is correct.

The GetTransactions method properly maps BDK transaction types to onchain abstractions, with appropriate default limit handling and output mapping.

Comment thread internal/onchain/bitcoin-wallet/wallet.go
@jackstar12 jackstar12 force-pushed the feat/bdk-rust-bindings branch from 84af795 to fb7eb72 Compare November 3, 2025 10:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/onchain/wallet_test.go (2)

205-225: Guard against empty transaction list before indexing.

Line 205 still reads transactions[0] without verifying the slice has elements. If GetTransactions ever returns an empty slice, this panics and the test aborts. Please restore the missing require.NotEmpty(t, transactions, ...) (or equivalent) before touching transactions[0]. This matches the earlier feedback that remains unresolved.

 	transactions, err := wallet.GetTransactions(0, 0)
 	require.NoError(t, err)
 	require.NotNil(t, transactions)
+	require.NotEmpty(t, transactions, "wallet should have transactions after funding")
 
 	first := transactions[0]

255-261: Strengthen bump-fee assertions (still treating hex as txid).

The bump test continues to accept any non-empty string different from txId. Our backend returns the raw tx hex, so the assertion remains vacuous—the bump can fail to broadcast and this still passes. Please decode the returned hex to a txid (or have the backend return the txid) and assert the replacement actually exists in the wallet/mempool after a sync, per the prior review.

-		require.Eventually(t, func() bool {
-			newTxId, err := wallet.BumpTransactionFee(txId, 3)
+		require.Eventually(t, func() bool {
+			newTxHex, err := wallet.BumpTransactionFee(txId, 3)
 			if err != nil {
 				return false
 			}
-			return newTxId != txId
+			require.NotEmpty(t, newTxHex)
+			derived := test.BtcCli(fmt.Sprintf("decoderawtransaction %s | jq -r .txid", strconv.Quote(newTxHex)))
+			require.NotEmpty(t, derived)
+			require.NotEqual(t, txId, derived)
+			require.NoError(t, wallet.Sync())
+			txns, err := wallet.GetTransactions(10, 0)
+			require.NoError(t, err)
+			return slices.ContainsFunc(txns, func(tx onchain.Transaction) bool { return tx.Id == derived })
 		}, 10*time.Second, 250*time.Millisecond)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5665fd8 and df2cc7c.

📒 Files selected for processing (1)
  • internal/onchain/wallet_test.go (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/onchain/wallet_test.go (4)
pkg/boltz/currency.go (2)
  • CurrencyLiquid (12-12)
  • CurrencyBtc (11-11)
internal/onchain/wallet/wallet_test.go (1)
  • TestWallet_BumpTransactionFee (30-52)
internal/test/test.go (8)
  • WalletBackend (189-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • MineBlock (243-247)
  • WalletMnemonic (51-51)
internal/onchain/wallet.go (2)
  • WalletSendArgs (42-47)
  • ValidateWalletCredentials (132-150)
🪛 GitHub Actions: Lint
internal/onchain/wallet_test.go

[error] 150-150: undefined: balance

🪛 GitHub Check: Lint
internal/onchain/wallet_test.go

[failure] 162-162:
undefined: balance (typecheck)


[failure] 161-161:
undefined: balance


[failure] 159-159:
undefined: balance

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)

Comment thread internal/onchain/wallet_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
internal/onchain/wallet_test.go (2)

205-206: Add bounds check before accessing transactions[0].

The test directly accesses transactions[0] without verifying the slice is non-empty. While walletTest with funded=true should ensure transactions exist, there's no guarantee they appear in GetTransactions before this line.

Apply this diff to add a defensive check:

+	require.NotEmpty(t, transactions, "wallet should have transactions after funding")
 	first := transactions[0]
 	count := len(transactions)

235-265: Verify broadcast and confirm the bumped transaction.

The test polls for a successful BumpTransactionFee call but doesn't verify:

  1. The bumped transaction was actually broadcast to the mempool
  2. The returned value semantics (is it a txid or raw hex?)
  3. The bumped transaction is confirmed after mining

Consider these improvements:

  • After the Eventually block succeeds, sync the wallet and verify the new transaction appears in GetTransactions
  • After mining the block (line 263), verify the bumped transaction is confirmed and the original is no longer in the mempool
  • If the backend returns raw hex rather than a txid, either decode the hex to get the txid for assertions, or rename the variable to newTxHex for clarity
🧹 Nitpick comments (1)
internal/onchain/wallet_test.go (1)

414-414: Clarify why ValidateWalletCredentials result is discarded.

The validation function is called but its error return is discarded with _. If validation is meant to prevent invalid credentials from reaching NewWallet, the error should be checked. If validation is optional/advisory here, consider adding a comment explaining why the result is ignored.

If validation errors should be checked:

-			_ = onchain.ValidateWalletCredentials(backend, &tc.credentials)
+			validateErr := onchain.ValidateWalletCredentials(backend, &tc.credentials)
+			if tc.shouldError && validateErr != nil {
+				t.Log("Validation error:", validateErr)
+			}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df2cc7c and 82bca53.

📒 Files selected for processing (1)
  • internal/onchain/wallet_test.go (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/onchain/wallet_test.go (4)
pkg/boltz/currency.go (2)
  • CurrencyLiquid (12-12)
  • CurrencyBtc (11-11)
internal/test/test.go (8)
  • MineBlock (243-247)
  • WalletBackend (189-207)
  • WalletCredentials (64-78)
  • FundWallet (80-122)
  • GetCli (217-223)
  • GetNewAddress (257-259)
  • SendToAddress (261-263)
  • WalletMnemonic (51-51)
internal/onchain/wallet/wallet_test.go (1)
  • TestWallet_BumpTransactionFee (30-52)
internal/onchain/wallet.go (2)
  • WalletSendArgs (42-47)
  • ValidateWalletCredentials (132-150)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (3)
internal/onchain/wallet_test.go (3)

21-21: LGTM! BTC support added to test suite.

The addition of BTC currency enables comprehensive testing of the new BDK-backed Bitcoin wallet alongside the existing Liquid wallet tests.


154-163: LGTM! Balance verification after SendAll is correct.

The test properly mines a block and polls for the wallet balance to reach zero, confirming that SendAll depleted the wallet. The balance variable is correctly declared on line 150.


418-421: LGTM! Logging improves test debuggability.

The added t.Log calls for error messages and descriptors will help diagnose test failures.

@michael1011 michael1011 merged commit 4390ea8 into master Nov 4, 2025
3 checks passed
@michael1011 michael1011 deleted the feat/bdk-rust-bindings branch November 4, 2025 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants