Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,19 @@ fn build_with_store_internal(
},
};

let event_queue =
match runtime.block_on(read_event_queue(Arc::clone(&kv_store), Arc::clone(&logger))) {
Ok(event_queue) => Arc::new(event_queue),
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Arc::new(EventQueue::new(Arc::clone(&kv_store), Arc::clone(&logger)))
} else {
log_error!(logger, "Failed to read event queue from store: {}", e);
return Err(BuildError::ReadFailed);
}
},
};

let wallet = Arc::new(Wallet::new(
bdk_wallet,
wallet_persister,
Expand All @@ -1751,6 +1764,7 @@ fn build_with_store_internal(
Arc::clone(&payment_store),
Arc::clone(&runtime),
Arc::clone(&config),
Arc::clone(&event_queue),
Arc::clone(&logger),
Arc::clone(&pending_payment_store),
));
Expand Down Expand Up @@ -1853,7 +1867,6 @@ fn build_with_store_internal(
external_scores_res,
channel_manager_bytes_res,
sweeper_bytes_res,
event_queue_res,
peer_info_res,
) = runtime.block_on(async move {
tokio::join!(
Expand All @@ -1866,7 +1879,6 @@ fn build_with_store_internal(
CHANNEL_MANAGER_PERSISTENCE_KEY,
),
output_sweeper_future,
read_event_queue(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)),
read_peer_info(Arc::clone(&kv_store_ref), Arc::clone(&logger_ref)),
)
});
Expand Down Expand Up @@ -2212,18 +2224,6 @@ fn build_with_store_internal(
},
};

let event_queue = match event_queue_res {
Ok(event_queue) => Arc::new(event_queue),
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Arc::new(EventQueue::new(Arc::clone(&kv_store), Arc::clone(&logger)))
} else {
log_error!(logger, "Failed to read event queue from store: {}", e);
return Err(BuildError::ReadFailed);
}
},
};

let peer_store = match peer_info_res {
Ok(peer_store) => Arc::new(peer_store),
Err(e) => {
Expand Down
56 changes: 55 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex};

use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{Amount, OutPoint};
use bitcoin::{Amount, BlockHash, OutPoint, Txid};
use lightning::blinded_path::message::NextMessageHop;
use lightning::events::bump_transaction::BumpTransactionEvent;
#[cfg(not(feature = "uniffi"))]
Expand Down Expand Up @@ -271,6 +271,46 @@ pub enum Event {
/// This will be `None` for events serialized by LDK Node v0.2.1 and prior.
reason: Option<ClosureReason>,
},
/// A sent on-chain payment was successful.
///
/// This is only emitted for wallet transactions which were not classified as channel
/// funding, splices, closes, sweeps, or other LDK-driven chain activity.
///
/// It's guaranteed to have reached at least [`ANTI_REORG_DELAY`] confirmations.
///
/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
OnchainPaymentSuccessful {
/// A local identifier used to track the payment.
payment_id: PaymentId,
/// The transaction identifier.
txid: Txid,
/// The value, in thousandths of a satoshi, that was sent.
amount_msat: u64,
/// The hash of the block in which the transaction was confirmed.
block_hash: BlockHash,
/// The height at which the block was confirmed.
block_height: u32,
},
/// An on-chain payment has been received.
///
/// This is only emitted for wallet transactions which were not classified as channel
/// funding, splices, closes, sweeps, or other LDK-driven chain activity.
///
/// It's guaranteed to have reached at least [`ANTI_REORG_DELAY`] confirmations.
///
/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
OnchainPaymentReceived {
/// A local identifier used to track the payment.
payment_id: PaymentId,
/// The transaction identifier.
txid: Txid,
/// The value, in thousandths of a satoshi, that has been received.
amount_msat: u64,
/// The hash of the block in which the transaction was confirmed.
block_hash: BlockHash,
/// The height at which the block was confirmed.
block_height: u32,
},
/// A channel splice has been negotiated and the funding transaction is pending
/// confirmation on-chain.
SpliceNegotiated {
Expand Down Expand Up @@ -374,6 +414,20 @@ impl_writeable_tlv_based_enum!(Event,
(5, user_channel_id, required),
// TLV 7 (abandoned_funding_txo) may be set for LDK Node v0.7.
},
(10, OnchainPaymentSuccessful) => {
(0, payment_id, required),
(2, txid, required),
(4, amount_msat, required),
(6, block_hash, required),
(8, block_height, required),
},
(11, OnchainPaymentReceived) => {
(0, payment_id, required),
(2, txid, required),
(4, amount_msat, required),
(6, block_hash, required),
(8, block_height, required),
},
);

pub struct EventQueue<L: Deref>
Expand Down
82 changes: 74 additions & 8 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use lightning_invoice::RawBolt11Invoice;
use persist::KVStoreWalletPersister;

use crate::config::Config;
use crate::event::{Event, EventQueue};
use crate::fee_estimator::{ConfirmationTarget, FeeEstimator, OnchainFeeEstimator};
use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use crate::payment::store::ConfirmationStatus;
Expand Down Expand Up @@ -91,6 +92,7 @@ pub(crate) struct Wallet {
payment_store: Arc<PaymentStore>,
runtime: Arc<Runtime>,
config: Arc<Config>,
event_queue: Arc<EventQueue<Arc<Logger>>>,
logger: Arc<Logger>,
pending_payment_store: Arc<PendingPaymentStore>,
}
Expand All @@ -101,7 +103,8 @@ impl Wallet {
wallet_persister: KVStoreWalletPersister, broadcaster: Arc<Broadcaster>,
fee_estimator: Arc<OnchainFeeEstimator>, chain_source: Arc<ChainSource>,
payment_store: Arc<PaymentStore>, runtime: Arc<Runtime>, config: Arc<Config>,
logger: Arc<Logger>, pending_payment_store: Arc<PendingPaymentStore>,
event_queue: Arc<EventQueue<Arc<Logger>>>, logger: Arc<Logger>,
pending_payment_store: Arc<PendingPaymentStore>,
) -> Self {
let inner = Mutex::new(wallet);
let persister = Mutex::new(wallet_persister);
Expand All @@ -114,6 +117,7 @@ impl Wallet {
payment_store,
runtime,
config,
event_queue,
logger,
pending_payment_store,
}
Expand Down Expand Up @@ -274,11 +278,19 @@ impl Wallet {
confirmation_status,
);

self.runtime.block_on(self.payment_store.insert_or_update(payment.clone()))?;
let updated = self
.runtime
.block_on(self.payment_store.insert_or_update(payment.clone()))?;
let stored_payment =
self.payment_store.get(&payment_id).unwrap_or_else(|| payment.clone());

if updated && payment_status == PaymentStatus::Succeeded {
self.emit_onchain_payment_event(&stored_payment)?;
}

if payment_status == PaymentStatus::Pending {
let pending_payment =
self.create_pending_payment_from_tx(payment, Vec::new());
self.create_pending_payment_from_tx(stored_payment, Vec::new());

self.runtime.block_on(
self.pending_payment_store.insert_or_update(pending_payment),
Expand All @@ -300,17 +312,25 @@ impl Wallet {
let mut unconfirmed_outbound_txids: Vec<Txid> = Vec::new();

for mut payment in pending_payments {
match payment.details.kind {
match &payment.details.kind {
PaymentKind::Onchain {
status: ConfirmationStatus::Confirmed { height, .. },
..
} => {
let payment_id = payment.details.id;
if new_tip.height >= height + ANTI_REORG_DELAY - 1 {
if new_tip.height >= *height + ANTI_REORG_DELAY - 1 {
payment.details.status = PaymentStatus::Succeeded;
self.runtime.block_on(
self.payment_store.insert_or_update(payment.details),
let updated = self.runtime.block_on(
self.payment_store
.insert_or_update(payment.details.clone()),
)?;
let stored_payment = self
.payment_store
.get(&payment_id)
.unwrap_or_else(|| payment.details.clone());
if updated {
self.emit_onchain_payment_event(&stored_payment)?;
}
self.runtime
.block_on(self.pending_payment_store.remove(&payment_id))?;
}
Expand All @@ -320,7 +340,7 @@ impl Wallet {
status: ConfirmationStatus::Unconfirmed,
..
} if payment.details.direction == PaymentDirection::Outbound => {
unconfirmed_outbound_txids.push(txid);
unconfirmed_outbound_txids.push(*txid);
},
_ => {},
}
Expand Down Expand Up @@ -1443,6 +1463,52 @@ impl Wallet {
PendingPaymentDetails::new(payment, conflicting_txids, Vec::new())
}

fn emit_onchain_payment_event(&self, payment: &PaymentDetails) -> Result<(), Error> {
if payment.status != PaymentStatus::Succeeded {
return Ok(());
}

let (txid, block_hash, block_height) = match &payment.kind {
PaymentKind::Onchain {
txid,
status: ConfirmationStatus::Confirmed { block_hash, height, .. },
tx_type: None,
} => (*txid, *block_hash, *height),
_ => return Ok(()),
};

let Some(amount_msat) = payment.amount_msat else {
log_error!(
self.logger,
"Skipping on-chain payment event for {} due to missing amount",
payment.id
);
return Ok(());
};

let event = match payment.direction {
PaymentDirection::Outbound => Event::OnchainPaymentSuccessful {
payment_id: payment.id,
txid,
amount_msat,
block_hash,
block_height,
},
PaymentDirection::Inbound => Event::OnchainPaymentReceived {
payment_id: payment.id,
txid,
amount_msat,
block_hash,
block_height,
},
};

self.runtime.block_on(self.event_queue.add_event(event)).map_err(|e| {
log_error!(self.logger, "Failed to push on-chain payment event: {}", e);
Error::PersistenceFailed
})
}

fn find_payment_by_txid(&self, target_txid: Txid) -> Option<PaymentId> {
let direct_payment_id = PaymentId(target_txid.to_byte_array());
if self.pending_payment_store.contains_key(&direct_payment_id) {
Expand Down
Loading
Loading