Skip to content

Commit bb7ca31

Browse files
author
Matt Corallo
committed
Handle internal transfers completed after shutdown
Currently we only process setting `TxMetadata` for internal transfers if they complete in-line while the app is open and running the rebalance logic. If, however, an internal transfer is initiated but then does not complete the metadata is lost and instead our transaction list includes a LN payment received and no information about the trusted transaction at all. Instead, here, we track enough information in `TxType::PendingRebalance` to match the trusted transfer with an LN transaction and then do so in `list_transactions`, updating metadata as appropriate if we find a match. We handle upgrades from previous versions of Orange gracefully, though I'm not sure if we really need to do that yet.
1 parent 648c16f commit bb7ca31

4 files changed

Lines changed: 129 additions & 15 deletions

File tree

graduated-rebalancer/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use bitcoin_payment_instructions::PaymentMethod;
1212
use lightning::bitcoin::hashes::Hash;
1313
use lightning::bitcoin::hex::DisplayHex;
1414
use lightning::bitcoin::OutPoint;
15+
use lightning::types::payment::PaymentHash;
1516
use lightning::util::logger::Logger;
1617
use lightning::{log_debug, log_error, log_info};
1718
use lightning_invoice::Bolt11Invoice;
@@ -159,6 +160,12 @@ pub enum RebalancerEvent {
159160
trigger_id: [u8; 32],
160161
/// Trusted wallet payment ID for the rebalance
161162
trusted_rebalance_payment_id: [u8; 32],
163+
/// The [`PaymentHash`] of this rebalance payment.
164+
///
165+
/// Note that if you're using LDK only we have the information required to make a
166+
/// payment for this hash, meaning that any payments claimable by our lightning
167+
/// wallet are related to this rebalance.
168+
payment_hash: PaymentHash,
162169
/// Amount being rebalanced in millisatoshis
163170
amount_msat: u64,
164171
},
@@ -170,6 +177,12 @@ pub enum RebalancerEvent {
170177
trusted_rebalance_payment_id: [u8; 32],
171178
/// Lightning payment ID for the rebalance
172179
ln_rebalance_payment_id: [u8; 32],
180+
/// The [`PaymentHash`] of this rebalance payment.
181+
///
182+
/// Note that if you're using LDK only we have the information required to make a
183+
/// payment for this hash, meaning that any payments claimable by our lightning
184+
/// wallet are related to this rebalance.
185+
payment_hash: PaymentHash,
173186
/// Amount rebalanced in millisatoshis
174187
amount_msat: u64,
175188
/// Total fee paid in millisatoshis
@@ -281,6 +294,7 @@ where
281294
.handle_event(RebalancerEvent::RebalanceInitiated {
282295
trigger_id: params.id,
283296
trusted_rebalance_payment_id: rebalance_id,
297+
payment_hash: PaymentHash(expected_hash.to_byte_array()),
284298
amount_msat: transfer_amt.milli_sats(),
285299
})
286300
.await;
@@ -322,6 +336,7 @@ where
322336
trusted_rebalance_payment_id: rebalance_id,
323337
ln_rebalance_payment_id: ln_payment.id,
324338
amount_msat: transfer_amt.milli_sats(),
339+
payment_hash: PaymentHash(expected_hash.to_byte_array()),
325340
fee_msat: ln_payment.fee_paid_msat.unwrap_or_default()
326341
+ trusted_payment.fee_paid_msat.unwrap_or_default(),
327342
})

orange-sdk/src/lib.rs

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,15 @@ impl Wallet {
680680
transaction: Option<Transaction>,
681681
}
682682

683+
let mut completed_internal_transfers = HashMap::new();
684+
#[derive(Debug)]
685+
struct CompletedInternalTransfer {
686+
trusted_transfer_id: [u8; 32],
687+
payment_triggering_transfer: PaymentId,
688+
time: Duration,
689+
ln_transfer_id: Option<[u8; 32]>,
690+
}
691+
683692
for payment in trusted_payments {
684693
if let Some(tx_metadata) = tx_metadata.get(&PaymentId::Trusted(payment.id)) {
685694
match &tx_metadata.ty {
@@ -732,7 +741,32 @@ impl Wallet {
732741
time_since_epoch: tx_metadata.time,
733742
});
734743
},
735-
TxType::PendingRebalance { .. } => {
744+
TxType::PendingRebalance {
745+
trusted_payment,
746+
payment_triggering_transfer,
747+
payment_hash,
748+
} => {
749+
if let Some(trusted_id) = trusted_payment {
750+
debug_assert_eq!(*trusted_id, payment.id);
751+
}
752+
if payment.status == TxStatus::Completed {
753+
if trusted_payment.is_some()
754+
&& payment_triggering_transfer.is_some()
755+
&& payment_hash.is_some()
756+
{
757+
let old_val = completed_internal_transfers.insert(
758+
payment_hash.unwrap(),
759+
CompletedInternalTransfer {
760+
trusted_transfer_id: trusted_payment.unwrap(),
761+
payment_triggering_transfer: payment_triggering_transfer
762+
.unwrap(),
763+
time: payment.time_since_epoch,
764+
ln_transfer_id: None,
765+
},
766+
);
767+
debug_assert!(old_val.is_none());
768+
}
769+
}
736770
// Pending rebalances are not shown in the transaction list.
737771
continue;
738772
},
@@ -880,15 +914,63 @@ impl Wallet {
880914
// failed rebalances.
881915
continue;
882916
}
883-
res.push(Transaction {
884-
id: PaymentId::SelfCustodial(payment.id.0),
885-
status,
886-
outbound: payment.direction == PaymentDirection::Outbound,
887-
amount: payment.amount_msat.map(|a| Amount::from_milli_sats(a).unwrap()),
888-
fee,
889-
payment_type: (&payment).into(),
890-
time_since_epoch: Duration::from_secs(payment.latest_update_timestamp),
891-
})
917+
918+
let payment_hash = match payment.kind {
919+
PaymentKind::Onchain { .. } => None,
920+
PaymentKind::Bolt11 { hash, .. } => Some(hash),
921+
PaymentKind::Bolt11Jit { hash, .. } => Some(hash),
922+
PaymentKind::Bolt12Offer { hash, .. } => hash,
923+
PaymentKind::Bolt12Refund { hash, .. } => hash,
924+
PaymentKind::Spontaneous { hash, .. } => Some(hash),
925+
};
926+
927+
if let Some(info) =
928+
payment_hash.map(|hash| completed_internal_transfers.get_mut(&hash)).flatten()
929+
{
930+
info.ln_transfer_id = Some(payment.id.0);
931+
} else {
932+
res.push(Transaction {
933+
id: PaymentId::SelfCustodial(payment.id.0),
934+
status,
935+
outbound: payment.direction == PaymentDirection::Outbound,
936+
amount: payment.amount_msat.map(|a| Amount::from_milli_sats(a).unwrap()),
937+
fee,
938+
payment_type: (&payment).into(),
939+
time_since_epoch: Duration::from_secs(payment.latest_update_timestamp),
940+
})
941+
}
942+
}
943+
}
944+
945+
std::mem::drop(tx_metadata);
946+
for (_, info) in completed_internal_transfers {
947+
debug_assert!(info.ln_transfer_id.is_some());
948+
if let Some(lightning_payment) = info.ln_transfer_id {
949+
log_info!(
950+
self.inner.logger,
951+
"Setting metadata for background-completed internal transfer with from trusted transaction {:?} to LN transaction {:?} triggered by transaction {}",
952+
info.trusted_transfer_id,
953+
lightning_payment,
954+
info.payment_triggering_transfer
955+
);
956+
let metadata = TxMetadata {
957+
ty: TxType::TrustedToLightning {
958+
trusted_payment: info.trusted_transfer_id,
959+
lightning_payment,
960+
payment_triggering_transfer: info.payment_triggering_transfer,
961+
},
962+
time: info.time,
963+
};
964+
self.inner
965+
.tx_metadata
966+
.set_tx_caused_rebalance(&info.payment_triggering_transfer)
967+
.expect("Failed to write metadata for rebalance transaction");
968+
self.inner
969+
.tx_metadata
970+
.upsert(PaymentId::Trusted(info.trusted_transfer_id), metadata);
971+
self.inner
972+
.tx_metadata
973+
.insert(PaymentId::SelfCustodial(lightning_payment), metadata);
892974
}
893975
}
894976

orange-sdk/src/rebalancer.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,14 @@ impl graduated_rebalancer::EventHandler for OrangeRebalanceEventHandler {
289289
trigger_id,
290290
trusted_rebalance_payment_id,
291291
amount_msat,
292+
payment_hash,
292293
} => {
293294
let metadata = TxMetadata {
294-
ty: TxType::PendingRebalance {},
295+
ty: TxType::PendingRebalance {
296+
payment_triggering_transfer: Some(PaymentId::Trusted(trigger_id)),
297+
trusted_payment: Some(trusted_rebalance_payment_id),
298+
payment_hash: Some(payment_hash),
299+
},
295300
time: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(),
296301
};
297302
self.tx_metadata
@@ -312,6 +317,7 @@ impl graduated_rebalancer::EventHandler for OrangeRebalanceEventHandler {
312317
trigger_id,
313318
trusted_rebalance_payment_id: rebalance_id,
314319
ln_rebalance_payment_id: lightning_id,
320+
payment_hash: _,
315321
amount_msat,
316322
fee_msat,
317323
} => {

orange-sdk/src/store.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use ldk_node::bitcoin::Txid;
1818
use ldk_node::bitcoin::hex::{DisplayHex, FromHex};
1919
use ldk_node::lightning::io;
2020
use ldk_node::lightning::ln::msgs::DecodeError;
21-
use ldk_node::lightning::types::payment::PaymentPreimage;
21+
use ldk_node::lightning::types::payment::{PaymentHash, PaymentPreimage};
2222
use ldk_node::lightning::util::persist::{KVStore, KVStoreSync};
2323
use ldk_node::lightning::util::ser::{Readable, Writeable, Writer};
2424
use ldk_node::lightning::{impl_writeable_tlv_based, impl_writeable_tlv_based_enum};
@@ -254,14 +254,21 @@ pub(crate) enum TxType {
254254
Payment {
255255
ty: PaymentType,
256256
},
257-
PendingRebalance {},
257+
PendingRebalance {
258+
// Note that while all of these fields are `Option`al, they are always
259+
// filled in by any released version of Orange. They were added after
260+
// some initial beta testing, however.
261+
trusted_payment: Option<[u8; 32]>,
262+
payment_triggering_transfer: Option<PaymentId>,
263+
payment_hash: Option<PaymentHash>,
264+
},
258265
}
259266

260267
impl TxType {
261268
pub(crate) fn is_rebalance(&self) -> bool {
262269
matches!(
263270
self,
264-
TxType::PendingRebalance {}
271+
TxType::PendingRebalance { .. }
265272
| TxType::TrustedToLightning { .. }
266273
| TxType::OnchainToLightning { .. }
267274
)
@@ -280,7 +287,11 @@ impl_writeable_tlv_based_enum!(TxType,
280287
},
281288
(2, PaymentTriggeringTransferLightning) => { (0, ty, required), },
282289
(3, Payment) => { (0, ty, required), },
283-
(4, PendingRebalance) => {},
290+
(4, PendingRebalance) => {
291+
(1, trusted_payment, option),
292+
(3, payment_triggering_transfer, option),
293+
(5, payment_hash, option),
294+
},
284295
);
285296

286297
#[derive(Debug, Copy, Clone)]

0 commit comments

Comments
 (0)