Skip to content

bark#43

Open
ntheile wants to merge 1 commit into
masterfrom
bark
Open

bark#43
ntheile wants to merge 1 commit into
masterfrom
bark

Conversation

@ntheile

@ntheile ntheile commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Add Bark node.

see:

Summary by CodeRabbit

  • New Features
    • Added a new Lightning node implementation with encrypted wallet backup and restore using secret-based protection.
    • Support for creating and paying Lightning invoices with payment verification.
    • Transaction listing and lookup with search and filtering capabilities.
    • Wallet synchronization and state management.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a complete Bark wallet integration as a new Lightning node backend. It introduces encrypted backup/restore with AES-256-GCM, BOLT11 invoice and payment operations, wallet configuration and initialization, and module-level exports for cross-language bindings via UniFFI.

Changes

Bark Wallet Integration

Layer / File(s) Summary
Dependencies and type definitions
crates/lni/Cargo.toml, crates/lni/bark/types.rs
Add runtime dependencies for bark-wallet, aes-gcm, and pbkdf2; define BarkBackupInfo (metadata and snapshot hash) and BarkBackup (encrypted payload) with cross-language bindings and redacted debug output.
BarkNode configuration and initialization
crates/lni/bark/lib.rs (config, impl)
BarkConfig structure with mnemonic, storage directory, network selection, and wallet creation flags; BarkNode::new constructor that validates configuration, opens SQLite storage, and opens or creates the Bark wallet; unit tests for config defaults and network parsing.
Lightning API implementation
crates/lni/bark/api.rs
Sats↔msats conversion and validation helpers; movement_to_transaction to map Bark movements to shared Transaction type; invoice creation (BOLT11 only), payment, lookup, and listing with search/timestamp/pagination filters; BOLT12 offer stubs returning "not implemented"; invoice-event callback stub.
Encrypted backup and restore
crates/lni/bark/backup.rs
Encrypted backup creation via AES-256-GCM with PBKDF2-derived keys; restore flow with envelope/metadata validation and deterministic snapshot integrity checks; staging-based atomic file installation with checksum verification and overwrite guards; unit tests for round-trip restore, wrong-secret rejection, overwrite refusal, and debug redaction.
Module integration and exports
crates/lni/bark/{lib.rs (wiring), lib.rs (top-level), crates/lni/lib.rs
Expose BarkNode backup/restore methods; wire Lightning API calls through BarkNode; add bark module with submodules and type re-exports; implement UniFFI-gated create_bark_node factory; extend debug redaction tests to verify BarkConfig secrets are redacted.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BarkNode
  participant Wallet
  participant Storage
  Client->>BarkNode: BarkNode::new(config)
  BarkNode->>Storage: create/validate directory
  BarkNode->>Wallet: open or create wallet
  Wallet-->>BarkNode: Arc<Wallet>
  BarkNode-->>Client: initialized BarkNode
  
  Client->>BarkNode: create_invoice(amount)
  BarkNode->>Wallet: create Bolt11
  Wallet-->>BarkNode: invoice + hash
  BarkNode-->>Client: Transaction
  
  Client->>BarkNode: pay_invoice(invoice)
  BarkNode->>Wallet: pay_lightning_invoice
  Wallet-->>BarkNode: send state + preimage
  BarkNode-->>Client: PayInvoiceResponse
  
  Client->>BarkNode: create_backup(secret)
  BarkNode->>Storage: collect + checksum files
  BarkNode->>Storage: encrypt (AES-256-GCM + PBKDF2)
  BarkNode-->>Client: BarkBackup (Base64 envelope)
  
  Client->>BarkNode: restore_from_backup(backup, secret)
  BarkNode->>Storage: decrypt + validate envelope
  BarkNode->>Storage: staging write + verify
  BarkNode->>Storage: atomic replace + reopen wallet
  BarkNode-->>Client: restored BarkNode
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit hops into the Bark, with keys in paw
AES and PBKDF standing guard—no flaw
Invoices created, payments sent with care
Wallets backup up in encrypted air ✨
Lightning flows through a furry node, fair!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'bark' is a single word that refers to the main component being added (Bark wallet integration), but it lacks specificity about what was actually implemented and fails to convey the nature of the changes. Clarify the title to better describe the change, such as 'Add Bark wallet Lightning node integration' or 'Implement Bark wallet support with backup/restore functionality'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bark

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbd29a07bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/lni/bark/api.rs
Comment on lines +192 to +196
let invoice = wallet
.pay_lightning_invoice(params.invoice, amount, true)
.await
.map_err(|e| ApiError::Api {
reason: e.to_string(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce caller fee limits before paying

When PayInvoiceParams includes fee_limit_msat or fee_limit_percentage, this path still calls Bark's payment API without reading either cap, so a caller that set a maximum fee can still have the invoice paid with whatever fee Bark selects. This affects Bark payments where the app relies on LNI's shared fee-limit fields to prevent unexpectedly expensive sends; either pass an equivalent limit if the SDK supports it or reject unsupported fee-limit parameters instead of silently ignoring them.

Useful? React with 👍 / 👎.

Comment thread crates/lni/bark/api.rs
reason: e.to_string(),
})?;

if let Some(search) = params.search {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor payment_hash in transaction lists

ListTransactionsParams has a dedicated payment_hash filter, but Bark only applies search, so list_transactions with payment_hash set and search unset returns the entire wallet history. This breaks callers that use the shared API to fetch transactions for one payment hash and can expose unrelated transactions in the response; include params.payment_hash in the retain predicate before paginating.

Useful? React with 👍 / 👎.

Comment thread crates/lni/bark/api.rs
));
}

let sats = u64::try_from(msats / 1000)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject sub-satoshi Bark amounts

For Bark, bitcoin::Amount is sat-denominated, but this conversion truncates amount_msats / 1000; for example, amount_msats = 1999 creates or pays only 1 sat while the returned Transaction still reports 1999 msat. This affects any caller that supplies a millisatoshi amount not divisible by 1000, leading to undercharged invoices or underpaid zero-amount invoices instead of an explicit unsupported-precision error.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/lni/bark/backup.rs (1)

395-408: ⚖️ Poor tradeoff

Non-atomic directory replacement has a data-loss window.

Between remove_dir_all(storage_dir) on line 396 and rename(&staging_dir, storage_dir) on line 403, a crash or power failure would leave the user without wallet data. On POSIX systems, this could be mitigated by renaming the old directory aside before the final rename, then removing it afterward; on Windows, atomic directory swaps are harder.

If this is an acceptable trade-off for the target platforms, consider documenting it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lni/bark/backup.rs` around lines 395 - 408, Current replace logic
deletes storage_dir then renames staging_dir into its place, leaving a data-loss
window; change to a safe three-step swap: first rename existing storage_dir to a
unique backup name (e.g., storage_dir_bak with timestamp/UUID) rather than
remove it, then rename staging_dir to storage_dir, and only after successful
rename remove the backup; ensure all operations that can fail (renaming old,
installing staging, removing backup) map errors to ApiError and attempt
best-effort cleanup of staging_dir and the backup on failure (references:
storage_dir, staging_dir and the rename/remove_dir_all calls), and consider
platform-specific fallbacks for Windows if atomic semantics aren’t available.
crates/lni/bark/lib.rs (1)

98-100: ⚡ Quick win

Normalize network labels before passing them to API methods.

network_label() returns raw user input ("mainnet" or "bitcoin"), while backup/restore paths already canonicalize labels. Canonicalizing here too avoids alias-driven output drift for equivalent network configs.

Suggested refactor
-    fn network_label(&self) -> &str {
-        self.network.as_deref().unwrap_or("mainnet")
-    }
+    fn network_label(&self) -> Result<&'static str, ApiError> {
+        Ok(canonical_network_label(self.get_network()?))
+    }

@@
-        crate::bark::api::get_info(self.wallet.clone(), self.config.network_label()).await
+        crate::bark::api::get_info(self.wallet.clone(), self.config.network_label()?).await

Also applies to: 255-257

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lni/bark/lib.rs` around lines 98 - 100, network_label() currently
returns raw user input and should return a canonicalized network label like the
backup/restore code does; update fn network_label(&self) -> &str to return the
canonicalized form (call the same canonicalization helper used by
backup/restore, e.g., canonicalize_network_label/normalize_network_label) so
aliases (e.g., "bitcoin") map to the canonical "mainnet", and apply the same
change to the other occurrences noted around lines 255-257 to ensure all API
calls receive normalized labels.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/lni/bark/lib.rs`:
- Around line 150-167: The current match falls back to Wallet::create for any
Wallet::open error; change this so you only call Wallet::create when the open
error indicates an uninitialized/missing wallet (the “Wallet is not initialised”
path) and config.create_if_missing is true. Concretely, in the match on
Wallet::open, restrict the Err arm to the specific error variant or condition
(e.g., WalletError::NotInitialised or matching the “Wallet is not initialised”
message) and leave all other Err(open_error) cases to return or propagate the
original error so you don't attempt create on unrelated failures; keep the call
to Wallet::create exactly as currently structured when the uninitialized
condition is met.

---

Nitpick comments:
In `@crates/lni/bark/backup.rs`:
- Around line 395-408: Current replace logic deletes storage_dir then renames
staging_dir into its place, leaving a data-loss window; change to a safe
three-step swap: first rename existing storage_dir to a unique backup name
(e.g., storage_dir_bak with timestamp/UUID) rather than remove it, then rename
staging_dir to storage_dir, and only after successful rename remove the backup;
ensure all operations that can fail (renaming old, installing staging, removing
backup) map errors to ApiError and attempt best-effort cleanup of staging_dir
and the backup on failure (references: storage_dir, staging_dir and the
rename/remove_dir_all calls), and consider platform-specific fallbacks for
Windows if atomic semantics aren’t available.

In `@crates/lni/bark/lib.rs`:
- Around line 98-100: network_label() currently returns raw user input and
should return a canonicalized network label like the backup/restore code does;
update fn network_label(&self) -> &str to return the canonicalized form (call
the same canonicalization helper used by backup/restore, e.g.,
canonicalize_network_label/normalize_network_label) so aliases (e.g., "bitcoin")
map to the canonical "mainnet", and apply the same change to the other
occurrences noted around lines 255-257 to ensure all API calls receive
normalized labels.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 064c12cd-f48f-4266-8f9b-93e7a486f819

📥 Commits

Reviewing files that changed from the base of the PR and between 45a1d91 and cbd29a0.

📒 Files selected for processing (6)
  • crates/lni/Cargo.toml
  • crates/lni/bark/api.rs
  • crates/lni/bark/backup.rs
  • crates/lni/bark/lib.rs
  • crates/lni/bark/types.rs
  • crates/lni/lib.rs

Comment thread crates/lni/bark/lib.rs
Comment on lines +150 to +167
let wallet = match Wallet::open(
&mnemonic,
db.clone(),
bark_config.clone(),
Box::new(MemoryLockManager::new()),
)
.await
{
Ok(wallet) => wallet,
Err(open_error) if config.create_if_missing.unwrap_or(true) => Wallet::create(
&mnemonic,
network,
bark_config,
db,
Box::new(MemoryLockManager::new()),
config.force_create.unwrap_or(false),
)
.await

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

For bark-wallet v0.2.3, which Wallet::open error variant specifically means "wallet missing/uninitialized", and under what conditions can Wallet::create reinitialize or overwrite existing wallet state?

💡 Result:

In the bark-wallet library, the Wallet::open method does not return a specific error variant for "wallet missing" or "uninitialized." Instead, it uses the anyhow::Result type to handle errors [1][2][3]. When opening a database that contains no wallet properties, the method explicitly returns an error context message: "Wallet is not initialised" [1][2][3]. Regarding Wallet::create, the implementation includes a check to see if properties already exist in the provided database [3]. If db.read_properties returns existing data, the method will return an error (bail!) stating that it "cannot overwrite already existing config" [3]. Therefore, under standard conditions, Wallet::create cannot reinitialize or overwrite an existing wallet state; it is designed to fail if a wallet is already present [3]. There is a force parameter (often used in the CLI or specific internal flows) which may be used to override this behavior, but the standard Wallet::create function prevents accidental destruction of existing wallet state [3].

Citations:


🌐 Web query:

bark-wallet v0.2.3 Wallet::create implementation: what happens when db.read_properties returns an error (e.g., corruption/unreadable), and how does the force parameter change overwrite/reinitialize behavior?

💡 Result:

In bark-wallet v0.2.3, Wallet::create loads/initializes wallet state based on what’s already in the Bark persister database; Wallet::open explicitly shows how failures in db.read_properties are handled, and the create force flag affects only the “server connection” part of the workflow. 1) What happens if db.read_properties returns an error (corruption/unreadable) - The wallet-open path calls db.read_properties and immediately errors out if properties can’t be read/parsed. - Concretely, Wallet::open does: let properties = db.read_properties?.context("Wallet is not initialised")?; so any error returned by db.read_properties (including unreadable/corrupt DB) is propagated as a failing anyhow::Result (it does not silently fall back to creating a new wallet from scratch). 2) How the force parameter changes overwrite/reinitialize behavior - The docs comment/implementation text around Wallet::create states: “The force flag will allow you to create the wallet even if a connection to the Ark server cannot be established, it will not overwrite a wallet which has already been created.” [1] - That means force affects whether Wallet::create can proceed when the Ark server is unreachable (e.g., offline/connection failure). It does not change behavior to “overwrite/reinitialize” an already-initialized wallet in the DB. Practical implication combining both points - If the database exists but db.read_properties fails (e.g., corruption/unreadable), Wallet::open will fail rather than reinitializing. Wallet::create’s force does not promise overwrite/reinitialize semantics for an already-created wallet; its purpose is avoiding server-connection failure gating, not ignoring/repairing a broken persister state. [1] If you want, tell me whether you mean Wallet::create or Wallet::open in your scenario (some apps “create if missing, otherwise open”), and I can trace the exact control flow for that specific call chain in your version.

Citations:


🌐 Web query:

bark-wallet v0.2.3 Wallet::create control flow when db.read_properties returns an Err (corrupt/unreadable DB): does Wallet::create fail, or does it treat it as "not initialised" and proceed to create/write wallet state anyway?

💡 Result:

In bark-wallet version 0.2.3 (and generally within the library's design), the Wallet::create function does not silently treat an error from db.read_properties as an "uninitialized" state; instead, it propagates the error, causing the function to fail [1][2]. When Wallet::create is called, it relies on the database client to manage state [3]. If the database is corrupt or unreadable, the call to db.read_properties will return an Err (e.g., a database error from the underlying SQL driver). Because Wallet::create uses the Rust? operator or explicit error handling contexts (such as.context("...") in the codebase), any failure at the database level results in the immediate termination of the Wallet::create execution flow, returning an anyhow::Result::Err [1][2]. The library makes a clear distinction between an "uninitialized" wallet (where the DB might be empty or missing specific properties) and a system or database error (where the DB exists but cannot be read due to corruption) [2][4]. If an error occurs during database interaction, the library does not attempt to proceed with writing a new wallet state, as doing so could potentially overwrite or further corrupt existing data [2]. To successfully create or open a wallet, the database must be accessible and readable [2][5].

Citations:


Only fall back to Wallet::create when Wallet::open fails due to an uninitialized/missing wallet.
crates/lni/bark/lib.rs currently calls Wallet::create for any Wallet::open error when config.create_if_missing is true. In bark-wallet, “uninitialized/missing” is represented by the “Wallet is not initialised” path, while Wallet::create won’t overwrite an already-created wallet and will fail fast if the DB can’t be read. Narrowing the fallback to the uninitialized/missing error preserves non-missing error reporting and avoids unnecessary create attempts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lni/bark/lib.rs` around lines 150 - 167, The current match falls back
to Wallet::create for any Wallet::open error; change this so you only call
Wallet::create when the open error indicates an uninitialized/missing wallet
(the “Wallet is not initialised” path) and config.create_if_missing is true.
Concretely, in the match on Wallet::open, restrict the Err arm to the specific
error variant or condition (e.g., WalletError::NotInitialised or matching the
“Wallet is not initialised” message) and leave all other Err(open_error) cases
to return or propagate the original error so you don't attempt create on
unrelated failures; keep the call to Wallet::create exactly as currently
structured when the uninitialized condition is met.

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.

1 participant