Skip to content

Commit d4ae971

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 e83dcca commit d4ae971

4 files changed

Lines changed: 130 additions & 16 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
@@ -679,6 +679,15 @@ impl Wallet {
679679
transaction: Option<Transaction>,
680680
}
681681

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

orange-sdk/src/rebalancer.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,14 @@ impl graduated_rebalancer::EventHandler for OrangeRebalanceEventHandler {
293293
trigger_id,
294294
trusted_rebalance_payment_id,
295295
amount_msat,
296+
payment_hash,
296297
} => {
297298
let metadata = TxMetadata {
298-
ty: TxType::PendingRebalance {},
299+
ty: TxType::PendingRebalance {
300+
payment_triggering_transfer: Some(PaymentId::Trusted(trigger_id)),
301+
trusted_payment: Some(trusted_rebalance_payment_id),
302+
payment_hash: Some(payment_hash),
303+
},
299304
time: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(),
300305
};
301306
self.tx_metadata
@@ -317,6 +322,7 @@ impl graduated_rebalancer::EventHandler for OrangeRebalanceEventHandler {
317322
trigger_id,
318323
trusted_rebalance_payment_id: rebalance_id,
319324
ln_rebalance_payment_id: lightning_id,
325+
payment_hash: _,
320326
amount_msat,
321327
fee_msat,
322328
} => {

orange-sdk/src/store.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ 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;
22-
use ldk_node::lightning::util::persist::KVStore;
21+
use ldk_node::lightning::types::payment::{PaymentHash, PaymentPreimage};
22+
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};
2525
use ldk_node::payment::PaymentDetails;
@@ -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)