Skip to content

Commit e67a85d

Browse files
committed
WIP: splice in
1 parent 5915a3d commit e67a85d

9 files changed

Lines changed: 306 additions & 22 deletions

File tree

examples/cli/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ impl WalletState {
192192
fee_msat
193193
);
194194
},
195+
Event::SplicePending { new_funding_txo, .. } => {
196+
println!(
197+
"{} Splice pending: {}",
198+
"🔄".bright_yellow(),
199+
new_funding_txo
200+
);
201+
},
195202
}
196203

197204
w.event_handled().unwrap();

graduated-rebalancer/src/lib.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ pub trait LightningWallet: Send + Sync {
108108
&self, payment_hash: [u8; 32],
109109
) -> Pin<Box<dyn Future<Output = Option<ReceivedLightningPayment>> + Send + '_>>;
110110

111+
/// Check if we already have a channel with the LSP
112+
fn has_channel_with_lsp(&self) -> bool;
113+
111114
/// Open a channel with the LSP using on-chain funds
112115
fn open_channel_with_lsp(
113116
&self, amt: Amount,
@@ -117,6 +120,16 @@ pub trait LightningWallet: Send + Sync {
117120
fn await_channel_pending(
118121
&self, channel_id: u128,
119122
) -> Pin<Box<dyn Future<Output = OutPoint> + Send + '_>>;
123+
124+
/// Splice funds from on-chain to an existing channel with the LSP
125+
fn splice_to_lsp_channel(
126+
&self, amt: Amount,
127+
) -> Pin<Box<dyn Future<Output = Result<u128, Self::Error>> + Send + '_>>;
128+
129+
/// Wait for a splice pending notification, returns the splice outpoint
130+
fn await_splice_pending(
131+
&self, channel_id: u128,
132+
) -> Pin<Box<dyn Future<Output = OutPoint> + Send + '_>>;
120133
}
121134

122135
/// Represents a payment from the lightning wallet
@@ -313,32 +326,51 @@ where
313326
}
314327
}
315328

316-
/// Perform on-chain to lightning rebalance by opening a channel
329+
/// Perform on-chain to lightning rebalance by opening a channel or splicing into an existing one
317330
async fn do_onchain_rebalance(&self, params: TriggerParams) {
318-
// This should open a channel with the LSP using available on-chain funds
319-
320331
let _ = self.balance_mutex.lock().await;
321332

322-
log_info!(self.logger, "Opening channel with LSP with on-chain funds");
333+
let (channel_outpoint, user_channel_id) = if self.ln_wallet.has_channel_with_lsp() {
334+
log_info!(self.logger, "Splicing into channel with LSP with on-chain funds");
323335

324-
// todo for now we can only open a channel, eventually move to splicing
325-
let user_chan_id = match self.ln_wallet.open_channel_with_lsp(params.amount).await {
326-
Ok(chan_id) => chan_id,
327-
Err(e) => {
328-
log_error!(self.logger, "Failed to open channel with LSP: {e:?}");
329-
return;
330-
},
331-
};
336+
let user_chan_id = match self.ln_wallet.splice_to_lsp_channel(params.amount).await {
337+
Ok(chan_id) => chan_id,
338+
Err(e) => {
339+
log_error!(self.logger, "Failed to open channel with LSP: {e:?}");
340+
return;
341+
},
342+
};
343+
344+
log_info!(self.logger, "Initiated splice opened with LSP");
345+
346+
let channel_outpoint = self.ln_wallet.await_splice_pending(user_chan_id).await;
347+
348+
log_info!(self.logger, "Splice initiated at: {channel_outpoint}");
349+
350+
(channel_outpoint, user_chan_id)
351+
} else {
352+
log_info!(self.logger, "Opening channel with LSP with on-chain funds");
332353

333-
log_info!(self.logger, "Initiated channel opened with LSP");
354+
let user_chan_id = match self.ln_wallet.open_channel_with_lsp(params.amount).await {
355+
Ok(chan_id) => chan_id,
356+
Err(e) => {
357+
log_error!(self.logger, "Failed to open channel with LSP: {e:?}");
358+
return;
359+
},
360+
};
361+
362+
log_info!(self.logger, "Initiated channel opened with LSP");
334363

335-
let channel_outpoint = self.ln_wallet.await_channel_pending(user_chan_id).await;
364+
let channel_outpoint = self.ln_wallet.await_channel_pending(user_chan_id).await;
336365

337-
log_info!(self.logger, "Channel open succeeded at: {channel_outpoint}",);
366+
log_info!(self.logger, "Channel open succeeded at: {channel_outpoint}");
367+
368+
(channel_outpoint, user_chan_id)
369+
};
338370

339371
self.event_handler.handle_event(RebalancerEvent::OnChainRebalanceInitiated {
340372
trigger_id: params.id,
341-
user_channel_id: user_chan_id,
373+
user_channel_id,
342374
channel_outpoint,
343375
});
344376
}

orange-sdk/src/event.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ pub enum Event {
131131
/// The fee paid, in msats, for the rebalance payment.
132132
fee_msat: u64,
133133
},
134+
/// We have initiated a splice and are waiting for it to confirm.
135+
SplicePending {
136+
/// The `channel_id` of the channel.
137+
channel_id: ChannelId,
138+
/// The `user_channel_id` of the channel.
139+
user_channel_id: UserChannelId,
140+
/// The `node_id` of the channel counterparty.
141+
counterparty_node_id: PublicKey,
142+
/// The outpoint of the channel's splice funding transaction.
143+
new_funding_txo: OutPoint,
144+
},
134145
}
135146

136147
impl_writeable_tlv_based_enum!(Event,
@@ -182,6 +193,12 @@ impl_writeable_tlv_based_enum!(Event,
182193
(6, amount_msat, required),
183194
(8, fee_msat, required),
184195
},
196+
(8, SplicePending) => {
197+
(1, channel_id, required),
198+
(3, counterparty_node_id, required),
199+
(5, user_channel_id, required),
200+
(7, new_funding_txo, required),
201+
},
185202
);
186203

187204
/// A queue for events emitted by the [`Wallet`].
@@ -301,6 +318,7 @@ pub(crate) struct LdkEventHandler {
301318
pub(crate) tx_metadata: store::TxMetadataStore,
302319
pub(crate) payment_receipt_sender: watch::Sender<()>,
303320
pub(crate) channel_pending_sender: watch::Sender<u128>,
321+
pub(crate) splice_pending_sender: watch::Sender<u128>,
304322
pub(crate) logger: Arc<Logger>,
305323
}
306324

@@ -415,11 +433,28 @@ impl LdkEventHandler {
415433
return;
416434
}
417435
},
418-
ldk_node::Event::SplicePending { .. } => {
419-
log_debug!(self.logger, "Received SplicePending event");
436+
ldk_node::Event::SplicePending {
437+
channel_id,
438+
user_channel_id,
439+
counterparty_node_id,
440+
new_funding_txo,
441+
} => {
442+
log_debug!(self.logger, "Received SplicePending event {event:?}");
443+
let _ = self.splice_pending_sender.send(user_channel_id.0);
444+
445+
if let Err(e) = self.event_queue.add_event(Event::SplicePending {
446+
channel_id,
447+
user_channel_id,
448+
counterparty_node_id,
449+
new_funding_txo,
450+
}) {
451+
log_error!(self.logger, "Failed to add SplicePending event: {e:?}");
452+
return;
453+
}
420454
},
421455
ldk_node::Event::SpliceFailed { .. } => {
422-
log_debug!(self.logger, "Received SpliceFailed event");
456+
println!("===========splice failed============");
457+
log_warn!(self.logger, "Received SpliceFailed event: {event:?}");
423458
},
424459
}
425460

orange-sdk/src/ffi/orange/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,17 @@ pub enum Event {
262262
/// The fee paid, in msats, for the rebalance payment.
263263
fee_msat: u64,
264264
},
265+
/// We have initiated a splice and are waiting for it to confirm.
266+
SplicePending {
267+
/// The `channel_id` of the channel.
268+
channel_id: Vec<u8>,
269+
/// The `user_channel_id` of the channel.
270+
user_channel_id: Vec<u8>,
271+
/// The `node_id` of the channel counterparty.
272+
counterparty_node_id: Vec<u8>,
273+
/// The outpoint of the channel's splice funding transaction.
274+
new_funding_txo: String,
275+
},
265276
}
266277

267278
impl From<OrangeEvent> for Event {
@@ -349,6 +360,17 @@ impl From<OrangeEvent> for Event {
349360
amount_msat,
350361
fee_msat,
351362
},
363+
OrangeEvent::SplicePending {
364+
channel_id,
365+
user_channel_id,
366+
counterparty_node_id,
367+
new_funding_txo,
368+
} => Event::SplicePending {
369+
channel_id: channel_id.0.to_vec(),
370+
user_channel_id: user_channel_id.0.to_be_bytes().to_vec(),
371+
counterparty_node_id: counterparty_node_id.serialize().to_vec(),
372+
new_funding_txo: new_funding_txo.to_string(),
373+
},
352374
}
353375
}
354376
}

orange-sdk/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,11 +665,14 @@ impl Wallet {
665665
/// Lists the transactions which have been made.
666666
pub async fn list_transactions(&self) -> Result<Vec<Transaction>, WalletError> {
667667
let trusted_payments = self.inner.trusted.list_payments().await?;
668-
let lightning_payments = self.inner.ln_wallet.list_payments();
668+
let mut lightning_payments = self.inner.ln_wallet.list_payments();
669+
lightning_payments.sort_by_key(|l| l.latest_update_timestamp);
669670

670671
let mut res = Vec::with_capacity(trusted_payments.len() + lightning_payments.len());
671672
let tx_metadata = self.inner.tx_metadata.read();
672673

674+
println!("\n\n=======================");
675+
673676
let mut internal_transfers = HashMap::new();
674677
#[derive(Debug, Default)]
675678
struct InternalTransfer {
@@ -773,6 +776,7 @@ impl Wallet {
773776
});
774777
}
775778
}
779+
println!("ln payments: {:#?}", lightning_payments);
776780
for payment in lightning_payments {
777781
use ldk_node::payment::PaymentDirection;
778782
let lightning_receive_fee = match payment.kind {
@@ -792,6 +796,7 @@ impl Wallet {
792796
),
793797
};
794798
if let Some(tx_metadata) = tx_metadata.get(&PaymentId::SelfCustodial(payment.id.0)) {
799+
println!("Found metadata for lightning payment {} got {:?}", payment.id, tx_metadata.ty);
795800
match &tx_metadata.ty {
796801
TxType::TrustedToLightning {
797802
trusted_payment: _,
@@ -814,6 +819,7 @@ impl Wallet {
814819
.or_insert(InternalTransfer::default());
815820
if &payment.id.0 == channel_txid.as_byte_array() {
816821
debug_assert!(entry.send_fee.is_none());
822+
println!("onchain to ln fee: {:?}", payment.fee_paid_msat);
817823
entry.send_fee = payment
818824
.fee_paid_msat
819825
.map(|fee| Amount::from_milli_sats(fee).expect("Must be valid"));
@@ -831,6 +837,8 @@ impl Wallet {
831837
});
832838
debug_assert!(entry.transaction.is_none());
833839

840+
println!("trigger: {:?}", payment.fee_paid_msat);
841+
834842
entry.transaction = Some(Transaction {
835843
id: PaymentId::SelfCustodial(payment.id.0),
836844
status: payment.status.into(),

orange-sdk/src/lightning_wallet.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ pub(crate) struct LightningWalletBalance {
3838

3939
pub(crate) struct LightningWalletImpl {
4040
pub(crate) ldk_node: Arc<ldk_node::Node>,
41+
logger: Arc<Logger>,
4142
payment_receipt_flag: watch::Receiver<()>,
4243
channel_pending_receipt_flag: watch::Receiver<u128>,
44+
splice_pending_receipt_flag: watch::Receiver<u128>,
4345
lsp_node_id: PublicKey,
4446
lsp_socket_addr: SocketAddress,
4547
}
@@ -163,18 +165,22 @@ impl LightningWallet {
163165
let ldk_node = Arc::new(builder.build_with_store(Arc::clone(&store))?);
164166
let (payment_receipt_sender, payment_receipt_flag) = watch::channel(());
165167
let (channel_pending_sender, channel_pending_receipt_flag) = watch::channel(0);
168+
let (splice_pending_sender, splice_pending_receipt_flag) = watch::channel(0);
166169
let ev_handler = Arc::new(LdkEventHandler {
167170
event_queue,
168171
ldk_node: Arc::clone(&ldk_node),
169172
tx_metadata,
170173
payment_receipt_sender,
171174
channel_pending_sender,
172-
logger,
175+
splice_pending_sender,
176+
logger: Arc::clone(&logger),
173177
});
174178
let inner = Arc::new(LightningWalletImpl {
175179
ldk_node,
180+
logger,
176181
payment_receipt_flag,
177182
channel_pending_receipt_flag,
183+
splice_pending_receipt_flag,
178184
lsp_node_id,
179185
lsp_socket_addr,
180186
});
@@ -204,6 +210,12 @@ impl LightningWallet {
204210
flag.wait_for(|t| t == &channel_id).await.expect("channel pending not received");
205211
}
206212

213+
pub(crate) async fn await_splice_pending(&self, channel_id: u128) {
214+
let mut flag = self.inner.splice_pending_receipt_flag.clone();
215+
flag.mark_unchanged();
216+
flag.wait_for(|t| t == &channel_id).await.expect("splice pending not received");
217+
}
218+
207219
pub(crate) fn get_on_chain_address(&self) -> Result<Address, NodeError> {
208220
self.inner.ldk_node.onchain_payment().new_address()
209221
}
@@ -295,6 +307,32 @@ impl LightningWallet {
295307
}
296308
}
297309

310+
pub(crate) async fn splice_balance_into_channel(
311+
&self, amount: Amount,
312+
) -> Result<UserChannelId, NodeError> {
313+
// find existing channel to splice into
314+
let channels = self.inner.ldk_node.list_channels();
315+
let channel = channels.iter().find(|c| c.counterparty_node_id == self.inner.lsp_node_id);
316+
317+
// todo fix this, for now leave some onchain balance for fees
318+
let amt = amount.saturating_sub(Amount::from_sats(10_000).unwrap());
319+
320+
match channel {
321+
Some(chan) => {
322+
self.inner.ldk_node.splice_in(
323+
&chan.user_channel_id,
324+
chan.counterparty_node_id,
325+
amt.sats_rounding_up(),
326+
)?;
327+
Ok(chan.user_channel_id)
328+
},
329+
None => {
330+
log_error!(self.inner.logger, "No existing channel to splice into");
331+
Err(NodeError::WalletOperationFailed)
332+
},
333+
}
334+
}
335+
298336
pub(crate) async fn open_channel_with_lsp(&self) -> Result<UserChannelId, NodeError> {
299337
let bal = self.inner.ldk_node.list_balances().spendable_onchain_balance_sats;
300338

@@ -407,6 +445,11 @@ impl graduated_rebalancer::LightningWallet for LightningWallet {
407445
})
408446
}
409447

448+
fn has_channel_with_lsp(&self) -> bool {
449+
let channels = self.inner.ldk_node.list_channels();
450+
channels.iter().any(|c| c.counterparty_node_id == self.inner.lsp_node_id)
451+
}
452+
410453
fn open_channel_with_lsp(
411454
&self, _amt: Amount,
412455
) -> Pin<Box<dyn Future<Output = Result<u128, Self::Error>> + Send + '_>> {
@@ -435,6 +478,38 @@ impl graduated_rebalancer::LightningWallet for LightningWallet {
435478
}
436479
})
437480
}
481+
482+
fn splice_to_lsp_channel(
483+
&self, amt: Amount,
484+
) -> Pin<Box<dyn Future<Output = Result<u128, Self::Error>> + Send + '_>> {
485+
Box::pin(async move { self.splice_balance_into_channel(amt).await.map(|c| c.0) })
486+
}
487+
488+
fn await_splice_pending(
489+
&self, channel_id: u128,
490+
) -> Pin<Box<dyn Future<Output = OutPoint> + Send + '_>> {
491+
Box::pin(async move {
492+
// todo since we can't see if we have any active splices, we just await the next splice pending event
493+
// this is kinda race-y hopefully we can fix
494+
self.await_splice_pending(channel_id).await;
495+
loop {
496+
let channels = self.inner.ldk_node.list_channels();
497+
let chan = channels
498+
.into_iter()
499+
.find(|c| c.user_channel_id.0 == channel_id && c.funding_txo.is_some());
500+
match chan {
501+
Some(c) => {
502+
println!("\nRETURNING HERE\n");
503+
return c.funding_txo.expect("channel has no funding txo")
504+
},
505+
None => {
506+
self.await_splice_pending(channel_id).await;
507+
// Wait for the next channel pending event
508+
},
509+
}
510+
}
511+
})
512+
}
438513
}
439514

440515
impl From<PaymentStatus> for TxStatus {

0 commit comments

Comments
 (0)