Skip to content

Commit 735f94f

Browse files
benthecarmanclaude
andcommitted
Update ldk-node and rust-lightning
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent eedd12a commit 735f94f

16 files changed

Lines changed: 272 additions & 78 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ codegen-units = 1 # Reduce number of codegen units to increase optimizations.
1616
panic = 'abort' # Abort on panic
1717

1818
[workspace.dependencies]
19-
bitcoin-payment-instructions = { version = "0.6.0" }
20-
lightning = { version = "0.2.0" }
21-
lightning-invoice = { version = "0.34.0" }
19+
bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "679dac50cc0d81ec4d31da94b93d467e5308f16a" }
20+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "369a2cf9c8ef810deea0cd2b4cf6ed0691b78144" }
21+
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "369a2cf9c8ef810deea0cd2b4cf6ed0691b78144" }
2222

2323
[profile.release]
2424
panic = "abort"

graduated-rebalancer/src/lib.rs

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
1010
use bitcoin_payment_instructions::amount::Amount;
1111
use bitcoin_payment_instructions::PaymentMethod;
12-
use lightning::bitcoin::hashes::Hash;
1312
use lightning::bitcoin::hex::DisplayHex;
1413
use lightning::bitcoin::OutPoint;
1514
use lightning::util::logger::Logger;
@@ -278,7 +277,7 @@ where
278277
self.logger,
279278
"Attempting to pay invoice {inv} to rebalance for {transfer_amt:?}",
280279
);
281-
let expected_hash = *inv.payment_hash();
280+
let expected_hash = inv.payment_hash();
282281
match self.trusted.pay(PaymentMethod::LightningBolt11(inv), transfer_amt).await {
283282
Ok(rebalance_id) => {
284283
log_debug!(
@@ -295,29 +294,23 @@ where
295294
})
296295
.await;
297296

298-
let ln_payment = match self
299-
.ln_wallet
300-
.await_payment_receipt(expected_hash.to_byte_array())
301-
.await
302-
{
303-
Some(receipt) => receipt,
304-
None => {
305-
log_error!(self.logger, "Failed to receive rebalance payment!");
306-
return;
307-
},
308-
};
309-
310-
let trusted_payment = match self
311-
.trusted
312-
.await_payment_success(expected_hash.to_byte_array())
313-
.await
314-
{
315-
Some(success) => success,
316-
None => {
317-
log_error!(self.logger, "Failed to send rebalance payment!");
318-
return;
319-
},
320-
};
297+
let ln_payment =
298+
match self.ln_wallet.await_payment_receipt(expected_hash.0).await {
299+
Some(receipt) => receipt,
300+
None => {
301+
log_error!(self.logger, "Failed to receive rebalance payment!");
302+
return;
303+
},
304+
};
305+
306+
let trusted_payment =
307+
match self.trusted.await_payment_success(expected_hash.0).await {
308+
Some(success) => success,
309+
None => {
310+
log_error!(self.logger, "Failed to send rebalance payment!");
311+
return;
312+
},
313+
};
321314

322315
log_info!(
323316
self.logger,

orange-sdk/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2024"
55
authors = ["benthecarman <benthecarman@live.com>"]
66
documentation = "https://docs.rs/orange-sdk/"
77
license = "MIT OR Apache-2.0"
8-
keywords = [ "lightning", "bitcoin", "spark", "cashu" ]
8+
keywords = ["lightning", "bitcoin", "spark", "cashu"]
99
readme = "../README.md"
1010

1111
[lib]
@@ -23,7 +23,7 @@ _cashu-tests = ["_test-utils", "cdk-ldk-node", "cdk/mint", "cdk-sqlite", "cdk-ax
2323
[dependencies]
2424
graduated-rebalancer = { path = "../graduated-rebalancer", version = "0.1.0" }
2525

26-
ldk-node = { version = "0.7.0" }
26+
ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "109978de1f57fc3b3f23f241f238b97d9deaa56a" }
2727
lightning-macros = "0.2.0"
2828
bitcoin-payment-instructions = { workspace = true, features = ["http"] }
2929
chrono = { version = "0.4", default-features = false }

orange-sdk/src/dyn_store.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
//! Object-safe wrapper around ldk-node's `KVStore` + `KVStoreSync` traits.
2+
//!
3+
//! `lightning`'s `KVStore` returns `impl Future` from its methods, which makes the trait not
4+
//! object-safe — orange-sdk can't share a backend across components as `Arc<dyn KVStore>`. The
5+
//! supertrait `SyncAndAsyncKVStore` that ldk-node exposes inherits the same problem, and even
6+
//! if it didn't, no `Deref` blanket impl exists for `KVStoreSync`, so `Arc<...>` doesn't satisfy
7+
//! it on its own.
8+
//!
9+
//! This module defines `DynStore`, an object-safe trait covering both sync and async kv
10+
//! methods (async ones return boxed futures), with a blanket impl over any concrete type that
11+
//! implements `KVStore + KVStoreSync`. The whole crate stores backends as
12+
//! `Arc<dyn DynStore>`; conversion to a value ldk-node accepts happens at the call site
13+
//! through a thin newtype that delegates both traits back to `DynStore`.
14+
15+
use std::future::Future;
16+
use std::pin::Pin;
17+
use std::sync::Arc;
18+
19+
use ldk_node::lightning::io;
20+
use ldk_node::lightning::util::persist::{KVStore, KVStoreSync};
21+
22+
/// Object-safe view of a `KVStore + KVStoreSync` backend. Async methods return boxed
23+
/// futures so the trait can be used through `dyn`.
24+
pub(crate) trait DynStore: Send + Sync + 'static {
25+
fn read_async(
26+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str,
27+
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, io::Error>> + Send + 'static>>;
28+
29+
fn write_async(
30+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec<u8>,
31+
) -> Pin<Box<dyn Future<Output = Result<(), io::Error>> + Send + 'static>>;
32+
33+
fn remove_async(
34+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool,
35+
) -> Pin<Box<dyn Future<Output = Result<(), io::Error>> + Send + 'static>>;
36+
37+
fn list_async(
38+
&self, primary_namespace: &str, secondary_namespace: &str,
39+
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, io::Error>> + Send + 'static>>;
40+
41+
fn read_sync(
42+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str,
43+
) -> Result<Vec<u8>, io::Error>;
44+
45+
fn write_sync(
46+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: Vec<u8>,
47+
) -> Result<(), io::Error>;
48+
49+
fn remove_sync(
50+
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool,
51+
) -> Result<(), io::Error>;
52+
53+
fn list_sync(
54+
&self, primary_namespace: &str, secondary_namespace: &str,
55+
) -> Result<Vec<String>, io::Error>;
56+
}
57+
58+
impl<T> DynStore for T
59+
where
60+
T: KVStore + KVStoreSync + Send + Sync + 'static,
61+
{
62+
fn read_async(
63+
&self, p: &str, s: &str, k: &str,
64+
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, io::Error>> + Send + 'static>> {
65+
Box::pin(<T as KVStore>::read(self, p, s, k))
66+
}
67+
68+
fn write_async(
69+
&self, p: &str, s: &str, k: &str, buf: Vec<u8>,
70+
) -> Pin<Box<dyn Future<Output = Result<(), io::Error>> + Send + 'static>> {
71+
Box::pin(<T as KVStore>::write(self, p, s, k, buf))
72+
}
73+
74+
fn remove_async(
75+
&self, p: &str, s: &str, k: &str, lazy: bool,
76+
) -> Pin<Box<dyn Future<Output = Result<(), io::Error>> + Send + 'static>> {
77+
Box::pin(<T as KVStore>::remove(self, p, s, k, lazy))
78+
}
79+
80+
fn list_async(
81+
&self, p: &str, s: &str,
82+
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, io::Error>> + Send + 'static>> {
83+
Box::pin(<T as KVStore>::list(self, p, s))
84+
}
85+
86+
fn read_sync(&self, p: &str, s: &str, k: &str) -> Result<Vec<u8>, io::Error> {
87+
<T as KVStoreSync>::read(self, p, s, k)
88+
}
89+
90+
fn write_sync(&self, p: &str, s: &str, k: &str, buf: Vec<u8>) -> Result<(), io::Error> {
91+
<T as KVStoreSync>::write(self, p, s, k, buf)
92+
}
93+
94+
fn remove_sync(&self, p: &str, s: &str, k: &str, lazy: bool) -> Result<(), io::Error> {
95+
<T as KVStoreSync>::remove(self, p, s, k, lazy)
96+
}
97+
98+
fn list_sync(&self, p: &str, s: &str) -> Result<Vec<String>, io::Error> {
99+
<T as KVStoreSync>::list(self, p, s)
100+
}
101+
}
102+
103+
// Make `Arc<dyn DynStore>` itself implement `KVStore` + `KVStoreSync` so the same handle
104+
// orange-sdk shares internally can be handed to ldk-node's `build_with_store`. We give it
105+
// `KVStore::read` etc. by forwarding to the boxed-future variants on the trait, and the
106+
// sync trait by forwarding to the sync variants.
107+
//
108+
// Note: we impl on `dyn DynStore` (which is local), not `Arc` directly — that gives us
109+
// `&dyn DynStore: KVStore + KVStoreSync` and, via lightning's `Deref` blanket impl for
110+
// `KVStore`, `Arc<dyn DynStore>: KVStore`. For `KVStoreSync` (no `Deref` blanket exists)
111+
// callers wrap the `Arc` in `LdkNodeStore` below before handing it to ldk-node.
112+
impl KVStore for dyn DynStore {
113+
fn read(
114+
&self, p: &str, s: &str, k: &str,
115+
) -> impl Future<Output = Result<Vec<u8>, io::Error>> + Send + 'static {
116+
self.read_async(p, s, k)
117+
}
118+
fn write(
119+
&self, p: &str, s: &str, k: &str, buf: Vec<u8>,
120+
) -> impl Future<Output = Result<(), io::Error>> + Send + 'static {
121+
self.write_async(p, s, k, buf)
122+
}
123+
fn remove(
124+
&self, p: &str, s: &str, k: &str, lazy: bool,
125+
) -> impl Future<Output = Result<(), io::Error>> + Send + 'static {
126+
self.remove_async(p, s, k, lazy)
127+
}
128+
fn list(
129+
&self, p: &str, s: &str,
130+
) -> impl Future<Output = Result<Vec<String>, io::Error>> + Send + 'static {
131+
self.list_async(p, s)
132+
}
133+
}
134+
135+
impl KVStoreSync for dyn DynStore {
136+
fn read(&self, p: &str, s: &str, k: &str) -> Result<Vec<u8>, io::Error> {
137+
self.read_sync(p, s, k)
138+
}
139+
fn write(&self, p: &str, s: &str, k: &str, buf: Vec<u8>) -> Result<(), io::Error> {
140+
self.write_sync(p, s, k, buf)
141+
}
142+
fn remove(&self, p: &str, s: &str, k: &str, lazy: bool) -> Result<(), io::Error> {
143+
self.remove_sync(p, s, k, lazy)
144+
}
145+
fn list(&self, p: &str, s: &str) -> Result<Vec<String>, io::Error> {
146+
self.list_sync(p, s)
147+
}
148+
}
149+
150+
/// Cloneable handle wrapping `Arc<dyn DynStore>` that satisfies ldk-node's
151+
/// `SyncAndAsyncKVStore + Send + Sync + 'static` bound on `build_with_store`. Both trait
152+
/// impls just forward to the underlying `dyn DynStore`.
153+
#[derive(Clone)]
154+
pub(crate) struct LdkNodeStore(pub(crate) Arc<dyn DynStore>);
155+
156+
impl KVStore for LdkNodeStore {
157+
fn read(
158+
&self, p: &str, s: &str, k: &str,
159+
) -> impl Future<Output = Result<Vec<u8>, io::Error>> + Send + 'static {
160+
self.0.read_async(p, s, k)
161+
}
162+
fn write(
163+
&self, p: &str, s: &str, k: &str, buf: Vec<u8>,
164+
) -> impl Future<Output = Result<(), io::Error>> + Send + 'static {
165+
self.0.write_async(p, s, k, buf)
166+
}
167+
fn remove(
168+
&self, p: &str, s: &str, k: &str, lazy: bool,
169+
) -> impl Future<Output = Result<(), io::Error>> + Send + 'static {
170+
self.0.remove_async(p, s, k, lazy)
171+
}
172+
fn list(
173+
&self, p: &str, s: &str,
174+
) -> impl Future<Output = Result<Vec<String>, io::Error>> + Send + 'static {
175+
self.0.list_async(p, s)
176+
}
177+
}
178+
179+
impl KVStoreSync for LdkNodeStore {
180+
fn read(&self, p: &str, s: &str, k: &str) -> Result<Vec<u8>, io::Error> {
181+
self.0.read_sync(p, s, k)
182+
}
183+
fn write(&self, p: &str, s: &str, k: &str, buf: Vec<u8>) -> Result<(), io::Error> {
184+
self.0.write_sync(p, s, k, buf)
185+
}
186+
fn remove(&self, p: &str, s: &str, k: &str, lazy: bool) -> Result<(), io::Error> {
187+
self.0.remove_sync(p, s, k, lazy)
188+
}
189+
fn list(&self, p: &str, s: &str) -> Result<Vec<String>, io::Error> {
190+
self.0.list_sync(p, s)
191+
}
192+
}

orange-sdk/src/event.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::logging::Logger;
22
use crate::store::{self, PaymentId};
33

4+
use crate::dyn_store::DynStore;
45
use ldk_node::bitcoin::secp256k1::PublicKey;
56
use ldk_node::bitcoin::{OutPoint, Txid};
67
use ldk_node::lightning::events::{ClosureReason, PaymentFailureReason};
@@ -11,7 +12,7 @@ use ldk_node::lightning::util::ser::{Writeable, Writer};
1112
use ldk_node::lightning::{impl_writeable_tlv_based_enum, log_debug, log_error, log_warn};
1213
use ldk_node::lightning_types::payment::{PaymentHash, PaymentPreimage};
1314
use ldk_node::payment::{ConfirmationStatus, PaymentKind};
14-
use ldk_node::{CustomTlvRecord, DynStore, UserChannelId};
15+
use ldk_node::{CustomTlvRecord, UserChannelId};
1516

1617
use std::collections::VecDeque;
1718
use std::sync::Arc;
@@ -207,12 +208,12 @@ impl_writeable_tlv_based_enum!(Event,
207208
pub struct EventQueue {
208209
queue: Arc<Mutex<VecDeque<Event>>>,
209210
waker: Arc<Mutex<Option<Waker>>>,
210-
kv_store: Arc<DynStore>,
211+
kv_store: Arc<dyn DynStore>,
211212
logger: Arc<Logger>,
212213
}
213214

214215
impl EventQueue {
215-
pub(crate) fn new(kv_store: Arc<DynStore>, logger: Arc<Logger>) -> Self {
216+
pub(crate) fn new(kv_store: Arc<dyn DynStore>, logger: Arc<Logger>) -> Self {
216217
let queue = Arc::new(Mutex::new(VecDeque::new()));
217218
let waker = Arc::new(Mutex::new(None));
218219
Self { queue, waker, kv_store, logger }
@@ -335,6 +336,7 @@ impl LdkEventHandler {
335336
payment_hash,
336337
payment_preimage,
337338
fee_paid_msat,
339+
bolt12_invoice: _,
338340
} => {
339341
let preimage = payment_preimage.unwrap(); // safe
340342
let payment_id = PaymentId::SelfCustodial(payment_id.unwrap().0); // safe

orange-sdk/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ use ldk_node::lightning::util::logger::Logger as _;
2727
use ldk_node::lightning::{log_debug, log_error, log_info, log_trace, log_warn};
2828
use ldk_node::lightning_invoice::Bolt11Invoice;
2929
use ldk_node::payment::{PaymentDirection, PaymentKind};
30-
use ldk_node::{BuildError, ChannelDetails, DynStore, NodeError};
30+
use ldk_node::{BuildError, ChannelDetails, NodeError};
31+
32+
use crate::dyn_store::DynStore;
3133

3234
use std::collections::HashMap;
3335
use std::fmt::{self, Debug, Write};
3436
use std::sync::Arc;
3537
use std::time::{Duration, SystemTime};
3638

39+
mod dyn_store;
3740
mod event;
3841
#[cfg(feature = "uniffi")]
3942
mod ffi;
@@ -112,7 +115,7 @@ struct WalletImpl {
112115
/// Metadata store for tracking transactions.
113116
tx_metadata: TxMetadataStore,
114117
/// Key-value store for persistent storage.
115-
store: Arc<DynStore>,
118+
store: Arc<dyn DynStore>,
116119
/// Logger for logging wallet operations.
117120
logger: Arc<Logger>,
118121
/// The Tokio runtime for asynchronous operations.
@@ -519,7 +522,7 @@ impl Wallet {
519522
BuildError::RuntimeSetupFailed
520523
})?);
521524

522-
let store: Arc<DynStore> = match &config.storage_config {
525+
let store: Arc<dyn DynStore> = match &config.storage_config {
523526
StorageConfig::LocalSQLite(path) => {
524527
Arc::new(SqliteStore::new(path.into(), Some("orange.sqlite".to_owned()), None)?)
525528
},

0 commit comments

Comments
 (0)