mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Split HTLC tracking into separate Inbound/Outbound types
This isnt as simplifying as I'd hoped, but still increases compile-time checking, which is nice, and removes one of two panic!()s.
This commit is contained in:
parent
bde48b2c15
commit
3b49837862
1 changed files with 231 additions and 207 deletions
|
@ -87,55 +87,22 @@ impl ChannelKeys {
|
|||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum HTLCState {
|
||||
enum InboundHTLCState {
|
||||
/// Added by remote, to be included in next local commitment tx.
|
||||
/// Implies HTLCOutput::outbound: false
|
||||
RemoteAnnounced,
|
||||
/// Included in a received commitment_signed message (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// accept this HTLC. Implies AwaitingRemoteRevoke.
|
||||
/// We also have not yet included this HTLC in a commitment_signed message, and are waiting on
|
||||
/// a remote revoke_and_ack on a previous state before we can do so.
|
||||
/// Implies HTLCOutput::outbound: false
|
||||
AwaitingRemoteRevokeToAnnounce,
|
||||
/// Included in a received commitment_signed message (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// accept this HTLC. Implies AwaitingRemoteRevoke.
|
||||
/// We have included this HTLC in our latest commitment_signed and are now just waiting on a
|
||||
/// revoke_and_ack.
|
||||
/// Implies HTLCOutput::outbound: true
|
||||
AwaitingAnnouncedRemoteRevoke,
|
||||
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
|
||||
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
|
||||
/// we will promote to Committed (note that they may not accept it until the next time we
|
||||
/// revoke, but we dont really care about that:
|
||||
/// * they've revoked, so worst case we can announce an old state and get our (option on)
|
||||
/// money back (though we wont), and,
|
||||
/// * we'll send them a revoke when they send a commitment_signed, and since only they're
|
||||
/// allowed to remove it, the "can only be removed once committed on both sides" requirement
|
||||
/// doesn't matter to us and its up to them to enforce it, worst-case they jump ahead but
|
||||
/// we'll never get out of sync).
|
||||
/// Implies HTLCOutput::outbound: true
|
||||
LocalAnnounced,
|
||||
Committed,
|
||||
/// Remote removed this (outbound) HTLC. We're waiting on their commitment_signed to finalize
|
||||
/// the change (though they'll need to revoke before we fail the payment).
|
||||
/// Implies HTLCOutput::outbound: true
|
||||
RemoteRemoved,
|
||||
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
|
||||
/// We also have not yet removed this HTLC in a commitment_signed message, and are waiting on a
|
||||
/// remote revoke_and_ack on a previous state before we can do so.
|
||||
/// Implies HTLCOutput::outbound: true
|
||||
AwaitingRemoteRevokeToRemove,
|
||||
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
|
||||
/// We have removed this HTLC in our latest commitment_signed and are now just waiting on a
|
||||
/// revoke_and_ack to drop completely.
|
||||
/// Implies HTLCOutput::outbound: true
|
||||
AwaitingRemovedRemoteRevoke,
|
||||
/// Removed by us and a new commitment_signed was sent (if we were AwaitingRemoteRevoke when we
|
||||
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
|
||||
/// we'll drop it.
|
||||
|
@ -146,32 +113,69 @@ enum HTLCState {
|
|||
/// ChannelMonitor::would_broadcast_at_height) so we actually remove the HTLC from our own
|
||||
/// local state before then, once we're sure that the next commitment_signed and
|
||||
/// ChannelMonitor::provide_latest_local_commitment_tx_info will not include this HTLC.
|
||||
/// Implies HTLCOutput::outbound: false
|
||||
LocalRemoved,
|
||||
}
|
||||
|
||||
struct HTLCOutput { //TODO: Refactor into Outbound/InboundHTLCOutput (will save memory and fewer panics)
|
||||
outbound: bool, // ie to an HTLC-Timeout transaction
|
||||
struct InboundHTLCOutput {
|
||||
htlc_id: u64,
|
||||
amount_msat: u64,
|
||||
cltv_expiry: u32,
|
||||
payment_hash: [u8; 32],
|
||||
state: HTLCState,
|
||||
/// If we're in a Remote* removed state, set if they failed, otherwise None
|
||||
fail_reason: Option<HTLCFailReason>,
|
||||
/// If we're in LocalRemoved*, set to true if we fulfilled the HTLC, and can claim money
|
||||
state: InboundHTLCState,
|
||||
/// If we're in LocalRemoved, set to true if we fulfilled the HTLC, and can claim money
|
||||
local_removed_fulfilled: bool,
|
||||
/// state pre-committed Remote* implies pending_forward_state, otherwise it must be None
|
||||
/// state pre-Committed implies pending_forward_state, otherwise it must be None
|
||||
pending_forward_state: Option<PendingHTLCStatus>,
|
||||
}
|
||||
|
||||
impl HTLCOutput {
|
||||
fn get_in_commitment(&self, offered: bool) -> HTLCOutputInCommitment {
|
||||
#[derive(PartialEq)]
|
||||
enum OutboundHTLCState {
|
||||
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
|
||||
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
|
||||
/// we will promote to Committed (note that they may not accept it until the next time we
|
||||
/// revoke, but we dont really care about that:
|
||||
/// * they've revoked, so worst case we can announce an old state and get our (option on)
|
||||
/// money back (though we wont), and,
|
||||
/// * we'll send them a revoke when they send a commitment_signed, and since only they're
|
||||
/// allowed to remove it, the "can only be removed once committed on both sides" requirement
|
||||
/// doesn't matter to us and its up to them to enforce it, worst-case they jump ahead but
|
||||
/// we'll never get out of sync).
|
||||
LocalAnnounced,
|
||||
Committed,
|
||||
/// Remote removed this (outbound) HTLC. We're waiting on their commitment_signed to finalize
|
||||
/// the change (though they'll need to revoke before we fail the payment).
|
||||
RemoteRemoved,
|
||||
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
|
||||
/// We also have not yet removed this HTLC in a commitment_signed message, and are waiting on a
|
||||
/// remote revoke_and_ack on a previous state before we can do so.
|
||||
AwaitingRemoteRevokeToRemove,
|
||||
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
|
||||
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
|
||||
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
|
||||
/// We have removed this HTLC in our latest commitment_signed and are now just waiting on a
|
||||
/// revoke_and_ack to drop completely.
|
||||
AwaitingRemovedRemoteRevoke,
|
||||
}
|
||||
|
||||
struct OutboundHTLCOutput {
|
||||
htlc_id: u64,
|
||||
amount_msat: u64,
|
||||
cltv_expiry: u32,
|
||||
payment_hash: [u8; 32],
|
||||
state: OutboundHTLCState,
|
||||
/// If we're in a removed state, set if they failed, otherwise None
|
||||
fail_reason: Option<HTLCFailReason>,
|
||||
}
|
||||
|
||||
macro_rules! get_htlc_in_commitment {
|
||||
($htlc: expr, $offered: expr) => {
|
||||
HTLCOutputInCommitment {
|
||||
offered: offered,
|
||||
amount_msat: self.amount_msat,
|
||||
cltv_expiry: self.cltv_expiry,
|
||||
payment_hash: self.payment_hash,
|
||||
offered: $offered,
|
||||
amount_msat: $htlc.amount_msat,
|
||||
cltv_expiry: $htlc.cltv_expiry,
|
||||
payment_hash: $htlc.payment_hash,
|
||||
transaction_output_index: 0
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +266,8 @@ pub struct Channel {
|
|||
cur_local_commitment_transaction_number: u64,
|
||||
cur_remote_commitment_transaction_number: u64,
|
||||
value_to_self_msat: u64, // Excluding all pending_htlcs, excluding fees
|
||||
pending_htlcs: Vec<HTLCOutput>,
|
||||
pending_inbound_htlcs: Vec<InboundHTLCOutput>,
|
||||
pending_outbound_htlcs: Vec<OutboundHTLCOutput>,
|
||||
holding_cell_htlc_updates: Vec<HTLCUpdateAwaitingACK>,
|
||||
next_local_htlc_id: u64,
|
||||
next_remote_htlc_id: u64,
|
||||
|
@ -421,7 +426,8 @@ impl Channel {
|
|||
cur_local_commitment_transaction_number: (1 << 48) - 1,
|
||||
cur_remote_commitment_transaction_number: (1 << 48) - 1,
|
||||
value_to_self_msat: channel_value_satoshis * 1000 - push_msat,
|
||||
pending_htlcs: Vec::new(),
|
||||
pending_inbound_htlcs: Vec::new(),
|
||||
pending_outbound_htlcs: Vec::new(),
|
||||
holding_cell_htlc_updates: Vec::new(),
|
||||
next_local_htlc_id: 0,
|
||||
next_remote_htlc_id: 0,
|
||||
|
@ -578,7 +584,8 @@ impl Channel {
|
|||
cur_local_commitment_transaction_number: (1 << 48) - 1,
|
||||
cur_remote_commitment_transaction_number: (1 << 48) - 1,
|
||||
value_to_self_msat: msg.push_msat,
|
||||
pending_htlcs: Vec::new(),
|
||||
pending_inbound_htlcs: Vec::new(),
|
||||
pending_outbound_htlcs: Vec::new(),
|
||||
holding_cell_htlc_updates: Vec::new(),
|
||||
next_local_htlc_id: 0,
|
||||
next_remote_htlc_id: 0,
|
||||
|
@ -686,66 +693,83 @@ impl Channel {
|
|||
ins
|
||||
};
|
||||
|
||||
let mut txouts: Vec<(TxOut, Option<HTLCOutputInCommitment>)> = Vec::with_capacity(self.pending_htlcs.len() + 2);
|
||||
let mut txouts: Vec<(TxOut, Option<HTLCOutputInCommitment>)> = Vec::with_capacity(self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len() + 2);
|
||||
|
||||
let dust_limit_satoshis = if local { self.our_dust_limit_satoshis } else { self.their_dust_limit_satoshis };
|
||||
let mut remote_htlc_total_msat = 0;
|
||||
let mut local_htlc_total_msat = 0;
|
||||
let mut value_to_self_msat_offset = 0;
|
||||
|
||||
for ref htlc in self.pending_htlcs.iter() {
|
||||
macro_rules! add_htlc_output {
|
||||
($htlc: expr, $outbound: expr) => {
|
||||
if $outbound == local { // "offered HTLC output"
|
||||
if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_TIMEOUT_TX_WEIGHT / 1000) {
|
||||
let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
|
||||
txouts.push((TxOut {
|
||||
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
|
||||
value: $htlc.amount_msat / 1000
|
||||
}, Some(htlc_in_tx)));
|
||||
}
|
||||
} else {
|
||||
if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_SUCCESS_TX_WEIGHT / 1000) {
|
||||
let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
|
||||
txouts.push((TxOut { // "received HTLC output"
|
||||
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
|
||||
value: $htlc.amount_msat / 1000
|
||||
}, Some(htlc_in_tx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ref htlc in self.pending_inbound_htlcs.iter() {
|
||||
let include = match htlc.state {
|
||||
HTLCState::RemoteAnnounced => !generated_by_local,
|
||||
HTLCState::AwaitingRemoteRevokeToAnnounce => !generated_by_local,
|
||||
HTLCState::AwaitingAnnouncedRemoteRevoke => true,
|
||||
HTLCState::LocalAnnounced => generated_by_local,
|
||||
HTLCState::Committed => true,
|
||||
HTLCState::RemoteRemoved => generated_by_local,
|
||||
HTLCState::AwaitingRemoteRevokeToRemove => generated_by_local,
|
||||
HTLCState::AwaitingRemovedRemoteRevoke => false,
|
||||
HTLCState::LocalRemoved => !generated_by_local,
|
||||
InboundHTLCState::RemoteAnnounced => !generated_by_local,
|
||||
InboundHTLCState::AwaitingRemoteRevokeToAnnounce => !generated_by_local,
|
||||
InboundHTLCState::AwaitingAnnouncedRemoteRevoke => true,
|
||||
InboundHTLCState::Committed => true,
|
||||
InboundHTLCState::LocalRemoved => !generated_by_local,
|
||||
};
|
||||
|
||||
if include {
|
||||
if htlc.outbound == local { // "offered HTLC output"
|
||||
if htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_TIMEOUT_TX_WEIGHT / 1000) {
|
||||
let htlc_in_tx = htlc.get_in_commitment(true);
|
||||
txouts.push((TxOut {
|
||||
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
|
||||
value: htlc.amount_msat / 1000
|
||||
}, Some(htlc_in_tx)));
|
||||
}
|
||||
} else {
|
||||
if htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_SUCCESS_TX_WEIGHT / 1000) {
|
||||
let htlc_in_tx = htlc.get_in_commitment(false);
|
||||
txouts.push((TxOut { // "received HTLC output"
|
||||
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
|
||||
value: htlc.amount_msat / 1000
|
||||
}, Some(htlc_in_tx)));
|
||||
}
|
||||
};
|
||||
if htlc.outbound {
|
||||
local_htlc_total_msat += htlc.amount_msat;
|
||||
} else {
|
||||
remote_htlc_total_msat += htlc.amount_msat;
|
||||
}
|
||||
add_htlc_output!(htlc, false);
|
||||
remote_htlc_total_msat += htlc.amount_msat;
|
||||
} else {
|
||||
match htlc.state {
|
||||
HTLCState::AwaitingRemoteRevokeToRemove|HTLCState::AwaitingRemovedRemoteRevoke => {
|
||||
InboundHTLCState::LocalRemoved => {
|
||||
if generated_by_local && htlc.local_removed_fulfilled {
|
||||
value_to_self_msat_offset += htlc.amount_msat as i64;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ref htlc in self.pending_outbound_htlcs.iter() {
|
||||
let include = match htlc.state {
|
||||
OutboundHTLCState::LocalAnnounced => generated_by_local,
|
||||
OutboundHTLCState::Committed => true,
|
||||
OutboundHTLCState::RemoteRemoved => generated_by_local,
|
||||
OutboundHTLCState::AwaitingRemoteRevokeToRemove => generated_by_local,
|
||||
OutboundHTLCState::AwaitingRemovedRemoteRevoke => false,
|
||||
};
|
||||
|
||||
if include {
|
||||
add_htlc_output!(htlc, true);
|
||||
local_htlc_total_msat += htlc.amount_msat;
|
||||
} else {
|
||||
match htlc.state {
|
||||
OutboundHTLCState::AwaitingRemoteRevokeToRemove|OutboundHTLCState::AwaitingRemovedRemoteRevoke => {
|
||||
if htlc.fail_reason.is_none() {
|
||||
value_to_self_msat_offset -= htlc.amount_msat as i64;
|
||||
}
|
||||
},
|
||||
HTLCState::RemoteRemoved => {
|
||||
OutboundHTLCState::RemoteRemoved => {
|
||||
if !generated_by_local && htlc.fail_reason.is_none() {
|
||||
value_to_self_msat_offset -= htlc.amount_msat as i64;
|
||||
}
|
||||
},
|
||||
HTLCState::LocalRemoved => {
|
||||
if generated_by_local && htlc.local_removed_fulfilled {
|
||||
value_to_self_msat_offset += htlc.amount_msat as i64;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -820,7 +844,8 @@ impl Channel {
|
|||
ins
|
||||
};
|
||||
|
||||
assert!(self.pending_htlcs.is_empty());
|
||||
assert!(self.pending_inbound_htlcs.is_empty());
|
||||
assert!(self.pending_outbound_htlcs.is_empty());
|
||||
let mut txouts: Vec<(TxOut, ())> = Vec::new();
|
||||
|
||||
let mut total_fee_satoshis = proposed_total_fee_satoshis;
|
||||
|
@ -1010,9 +1035,9 @@ impl Channel {
|
|||
sha.result(&mut payment_hash_calc);
|
||||
|
||||
let mut pending_idx = std::usize::MAX;
|
||||
for (idx, htlc) in self.pending_htlcs.iter().enumerate() {
|
||||
if !htlc.outbound && htlc.payment_hash == payment_hash_calc &&
|
||||
htlc.state != HTLCState::LocalRemoved {
|
||||
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
|
||||
if htlc.payment_hash == payment_hash_calc &&
|
||||
htlc.state != InboundHTLCState::LocalRemoved {
|
||||
if let Some(PendingHTLCStatus::Fail(_)) = htlc.pending_forward_state {
|
||||
} else {
|
||||
if pending_idx != std::usize::MAX {
|
||||
|
@ -1055,11 +1080,11 @@ impl Channel {
|
|||
}
|
||||
|
||||
let htlc_id = {
|
||||
let htlc = &mut self.pending_htlcs[pending_idx];
|
||||
if htlc.state == HTLCState::Committed {
|
||||
htlc.state = HTLCState::LocalRemoved;
|
||||
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
|
||||
if htlc.state == InboundHTLCState::Committed {
|
||||
htlc.state = InboundHTLCState::LocalRemoved;
|
||||
htlc.local_removed_fulfilled = true;
|
||||
} else if htlc.state == HTLCState::RemoteAnnounced || htlc.state == HTLCState::AwaitingRemoteRevokeToAnnounce || htlc.state == HTLCState::AwaitingAnnouncedRemoteRevoke {
|
||||
} else if htlc.state == InboundHTLCState::RemoteAnnounced || htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce || htlc.state == InboundHTLCState::AwaitingAnnouncedRemoteRevoke {
|
||||
// Theoretically we can hit this if we get the preimage on an HTLC prior to us
|
||||
// having forwarded it to anyone. This implies that the sender is busted as someone
|
||||
// else knows the preimage, but handling this case and implementing the logic to
|
||||
|
@ -1068,7 +1093,7 @@ impl Channel {
|
|||
// channel_monitor and pretend we didn't just see the preimage.
|
||||
return Ok((None, Some(self.channel_monitor.clone())));
|
||||
} else {
|
||||
// LocalRemoved/LocalRemovedAwaitingCOmmitment handled in the search loop
|
||||
// LocalRemoved handled in the search loop
|
||||
panic!("Have an inbound HTLC when not awaiting remote revoke that had a garbage state");
|
||||
}
|
||||
htlc.htlc_id
|
||||
|
@ -1124,11 +1149,11 @@ impl Channel {
|
|||
|
||||
let mut htlc_id = 0;
|
||||
let mut htlc_amount_msat = 0;
|
||||
for htlc in self.pending_htlcs.iter_mut() {
|
||||
if !htlc.outbound && htlc.payment_hash == *payment_hash_arg {
|
||||
if htlc.state == HTLCState::Committed {
|
||||
htlc.state = HTLCState::LocalRemoved;
|
||||
} else if htlc.state == HTLCState::RemoteAnnounced {
|
||||
for htlc in self.pending_inbound_htlcs.iter_mut() {
|
||||
if htlc.payment_hash == *payment_hash_arg {
|
||||
if htlc.state == InboundHTLCState::Committed {
|
||||
htlc.state = InboundHTLCState::LocalRemoved;
|
||||
} else if htlc.state == InboundHTLCState::RemoteAnnounced {
|
||||
if let Some(PendingHTLCStatus::Forward(_)) = htlc.pending_forward_state {
|
||||
panic!("Somehow forwarded HTLC prior to remote revocation!");
|
||||
} else {
|
||||
|
@ -1137,7 +1162,7 @@ impl Channel {
|
|||
// we'll fail this one as soon as remote commits to it.
|
||||
continue;
|
||||
}
|
||||
} else if htlc.state == HTLCState::LocalRemoved {
|
||||
} else if htlc.state == InboundHTLCState::LocalRemoved {
|
||||
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given payment preimage", action: None});
|
||||
} else {
|
||||
panic!("Have an inbound HTLC when not awaiting remote revoke that had a garbage state");
|
||||
|
@ -1361,30 +1386,34 @@ impl Channel {
|
|||
/// totals, though there is little reason to outside of further avoiding any race condition
|
||||
/// issues.
|
||||
fn get_pending_htlc_stats(&self, for_remote_update_check: bool) -> (u32, u32, u64, u64) {
|
||||
//TODO: Can probably split this into inbound/outbound
|
||||
let mut inbound_htlc_count: u32 = 0;
|
||||
let mut outbound_htlc_count: u32 = 0;
|
||||
let mut htlc_outbound_value_msat = 0;
|
||||
let mut htlc_inbound_value_msat = 0;
|
||||
for ref htlc in self.pending_htlcs.iter() {
|
||||
for ref htlc in self.pending_inbound_htlcs.iter() {
|
||||
match htlc.state {
|
||||
HTLCState::RemoteAnnounced => {},
|
||||
HTLCState::AwaitingRemoteRevokeToAnnounce => {},
|
||||
HTLCState::AwaitingAnnouncedRemoteRevoke => {},
|
||||
HTLCState::LocalAnnounced => { if for_remote_update_check { continue; } },
|
||||
HTLCState::Committed => {},
|
||||
HTLCState::RemoteRemoved => { if for_remote_update_check { continue; } },
|
||||
HTLCState::AwaitingRemoteRevokeToRemove => { if for_remote_update_check { continue; } },
|
||||
HTLCState::AwaitingRemovedRemoteRevoke => { if for_remote_update_check { continue; } },
|
||||
HTLCState::LocalRemoved => {},
|
||||
}
|
||||
if !htlc.outbound {
|
||||
inbound_htlc_count += 1;
|
||||
htlc_inbound_value_msat += htlc.amount_msat;
|
||||
} else {
|
||||
outbound_htlc_count += 1;
|
||||
htlc_outbound_value_msat += htlc.amount_msat;
|
||||
InboundHTLCState::RemoteAnnounced => {},
|
||||
InboundHTLCState::AwaitingRemoteRevokeToAnnounce => {},
|
||||
InboundHTLCState::AwaitingAnnouncedRemoteRevoke => {},
|
||||
InboundHTLCState::Committed => {},
|
||||
InboundHTLCState::LocalRemoved => {},
|
||||
}
|
||||
inbound_htlc_count += 1;
|
||||
htlc_inbound_value_msat += htlc.amount_msat;
|
||||
}
|
||||
for ref htlc in self.pending_outbound_htlcs.iter() {
|
||||
match htlc.state {
|
||||
OutboundHTLCState::LocalAnnounced => { if for_remote_update_check { continue; } },
|
||||
OutboundHTLCState::Committed => {},
|
||||
OutboundHTLCState::RemoteRemoved => { if for_remote_update_check { continue; } },
|
||||
OutboundHTLCState::AwaitingRemoteRevokeToRemove => { if for_remote_update_check { continue; } },
|
||||
OutboundHTLCState::AwaitingRemovedRemoteRevoke => { if for_remote_update_check { continue; } },
|
||||
}
|
||||
outbound_htlc_count += 1;
|
||||
htlc_outbound_value_msat += htlc.amount_msat;
|
||||
}
|
||||
|
||||
(inbound_htlc_count, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat)
|
||||
}
|
||||
|
||||
|
@ -1425,14 +1454,12 @@ impl Channel {
|
|||
|
||||
// Now update local state:
|
||||
self.next_remote_htlc_id += 1;
|
||||
self.pending_htlcs.push(HTLCOutput {
|
||||
outbound: false,
|
||||
self.pending_inbound_htlcs.push(InboundHTLCOutput {
|
||||
htlc_id: msg.htlc_id,
|
||||
amount_msat: msg.amount_msat,
|
||||
payment_hash: msg.payment_hash,
|
||||
cltv_expiry: msg.cltv_expiry,
|
||||
state: HTLCState::RemoteAnnounced,
|
||||
fail_reason: None,
|
||||
state: InboundHTLCState::RemoteAnnounced,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: Some(pending_forward_state),
|
||||
});
|
||||
|
@ -1443,8 +1470,8 @@ impl Channel {
|
|||
/// Removes an outbound HTLC which has been commitment_signed by the remote end
|
||||
#[inline]
|
||||
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<[u8; 32], HandleError> {
|
||||
for htlc in self.pending_htlcs.iter_mut() {
|
||||
if htlc.outbound && htlc.htlc_id == htlc_id {
|
||||
for htlc in self.pending_outbound_htlcs.iter_mut() {
|
||||
if htlc.htlc_id == htlc_id {
|
||||
match check_preimage {
|
||||
None => {},
|
||||
Some(payment_hash) =>
|
||||
|
@ -1452,15 +1479,15 @@ impl Channel {
|
|||
return Err(HandleError{err: "Remote tried to fulfill HTLC with an incorrect preimage", action: None});
|
||||
}
|
||||
};
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
return Err(HandleError{err: "Remote tried to fulfill HTLC before it had been committed", action: None});
|
||||
} else if htlc.state == HTLCState::Committed {
|
||||
htlc.state = HTLCState::RemoteRemoved;
|
||||
htlc.fail_reason = fail_reason;
|
||||
} else if htlc.state == HTLCState::AwaitingRemoteRevokeToRemove || htlc.state == HTLCState::AwaitingRemovedRemoteRevoke || htlc.state == HTLCState::RemoteRemoved {
|
||||
return Err(HandleError{err: "Remote tried to fulfill HTLC that they'd already fulfilled", action: None});
|
||||
} else {
|
||||
panic!("Got a non-outbound state on an outbound HTLC");
|
||||
match htlc.state {
|
||||
OutboundHTLCState::LocalAnnounced =>
|
||||
return Err(HandleError{err: "Remote tried to fulfill HTLC before it had been committed", action: None}),
|
||||
OutboundHTLCState::Committed => {
|
||||
htlc.state = OutboundHTLCState::RemoteRemoved;
|
||||
htlc.fail_reason = fail_reason;
|
||||
},
|
||||
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
|
||||
return Err(HandleError{err: "Remote tried to fulfill HTLC that they'd already fulfilled", action: None}),
|
||||
}
|
||||
return Ok(htlc.payment_hash.clone());
|
||||
}
|
||||
|
@ -1543,12 +1570,15 @@ impl Channel {
|
|||
self.channel_monitor.provide_latest_local_commitment_tx_info(local_commitment_tx.0, local_keys, self.feerate_per_kw, htlcs_and_sigs);
|
||||
|
||||
let mut need_our_commitment = false;
|
||||
for htlc in self.pending_htlcs.iter_mut() {
|
||||
if htlc.state == HTLCState::RemoteAnnounced {
|
||||
htlc.state = HTLCState::AwaitingRemoteRevokeToAnnounce;
|
||||
for htlc in self.pending_inbound_htlcs.iter_mut() {
|
||||
if htlc.state == InboundHTLCState::RemoteAnnounced {
|
||||
htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce;
|
||||
need_our_commitment = true;
|
||||
} else if htlc.state == HTLCState::RemoteRemoved {
|
||||
htlc.state = HTLCState::AwaitingRemoteRevokeToRemove;
|
||||
}
|
||||
}
|
||||
for htlc in self.pending_outbound_htlcs.iter_mut() {
|
||||
if htlc.state == OutboundHTLCState::RemoteRemoved {
|
||||
htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove;
|
||||
need_our_commitment = true;
|
||||
}
|
||||
}
|
||||
|
@ -1673,13 +1703,16 @@ impl Channel {
|
|||
let mut require_commitment = false;
|
||||
let mut value_to_self_msat_diff: i64 = 0;
|
||||
// We really shouldnt have two passes here, but retain gives a non-mutable ref (Rust bug)
|
||||
self.pending_htlcs.retain(|htlc| {
|
||||
if htlc.state == HTLCState::LocalRemoved {
|
||||
self.pending_inbound_htlcs.retain(|htlc| {
|
||||
if htlc.state == InboundHTLCState::LocalRemoved {
|
||||
if htlc.local_removed_fulfilled {
|
||||
value_to_self_msat_diff += htlc.amount_msat as i64;
|
||||
}
|
||||
false
|
||||
} else if htlc.state == HTLCState::AwaitingRemovedRemoteRevoke {
|
||||
} else { true }
|
||||
});
|
||||
self.pending_outbound_htlcs.retain(|htlc| {
|
||||
if htlc.state == OutboundHTLCState::AwaitingRemovedRemoteRevoke {
|
||||
if let Some(reason) = htlc.fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
|
||||
revoked_htlcs.push((htlc.payment_hash, reason));
|
||||
} else {
|
||||
|
@ -1689,16 +1722,14 @@ impl Channel {
|
|||
false
|
||||
} else { true }
|
||||
});
|
||||
for htlc in self.pending_htlcs.iter_mut() {
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
htlc.state = HTLCState::Committed;
|
||||
} else if htlc.state == HTLCState::AwaitingRemoteRevokeToAnnounce {
|
||||
htlc.state = HTLCState::AwaitingAnnouncedRemoteRevoke;
|
||||
for htlc in self.pending_inbound_htlcs.iter_mut() {
|
||||
if htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce {
|
||||
htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke;
|
||||
require_commitment = true;
|
||||
} else if htlc.state == HTLCState::AwaitingAnnouncedRemoteRevoke {
|
||||
} else if htlc.state == InboundHTLCState::AwaitingAnnouncedRemoteRevoke {
|
||||
match htlc.pending_forward_state.take().unwrap() {
|
||||
PendingHTLCStatus::Fail(fail_msg) => {
|
||||
htlc.state = HTLCState::LocalRemoved;
|
||||
htlc.state = InboundHTLCState::LocalRemoved;
|
||||
require_commitment = true;
|
||||
match fail_msg {
|
||||
HTLCFailureMsg::Relay(msg) => update_fail_htlcs.push(msg),
|
||||
|
@ -1707,11 +1738,16 @@ impl Channel {
|
|||
},
|
||||
PendingHTLCStatus::Forward(forward_info) => {
|
||||
to_forward_infos.push(forward_info);
|
||||
htlc.state = HTLCState::Committed;
|
||||
htlc.state = InboundHTLCState::Committed;
|
||||
}
|
||||
}
|
||||
} else if htlc.state == HTLCState::AwaitingRemoteRevokeToRemove {
|
||||
htlc.state = HTLCState::AwaitingRemovedRemoteRevoke;
|
||||
}
|
||||
}
|
||||
for htlc in self.pending_outbound_htlcs.iter_mut() {
|
||||
if htlc.state == OutboundHTLCState::LocalAnnounced {
|
||||
htlc.state = OutboundHTLCState::Committed;
|
||||
} else if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
|
||||
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
|
||||
require_commitment = true;
|
||||
}
|
||||
}
|
||||
|
@ -1762,8 +1798,8 @@ impl Channel {
|
|||
self.channel_update_count += 1;
|
||||
return Ok((None, None, Vec::new()));
|
||||
}
|
||||
for htlc in self.pending_htlcs.iter() {
|
||||
if htlc.state == HTLCState::RemoteAnnounced {
|
||||
for htlc in self.pending_inbound_htlcs.iter() {
|
||||
if htlc.state == InboundHTLCState::RemoteAnnounced {
|
||||
return Err(HandleError{err: "Got shutdown with remote pending HTLCs", action: None});
|
||||
}
|
||||
}
|
||||
|
@ -1789,7 +1825,7 @@ impl Channel {
|
|||
|
||||
let our_closing_script = self.get_closing_scriptpubkey();
|
||||
|
||||
let (proposed_feerate, proposed_fee, our_sig) = if self.channel_outbound && self.pending_htlcs.is_empty() {
|
||||
let (proposed_feerate, proposed_fee, our_sig) = if self.channel_outbound && self.pending_inbound_htlcs.is_empty() && self.pending_outbound_htlcs.is_empty() {
|
||||
let mut proposed_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
|
||||
if self.feerate_per_kw > proposed_feerate {
|
||||
proposed_feerate = self.feerate_per_kw;
|
||||
|
@ -1822,8 +1858,8 @@ impl Channel {
|
|||
_ => true
|
||||
}
|
||||
});
|
||||
for htlc in self.pending_htlcs.iter() {
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
for htlc in self.pending_outbound_htlcs.iter() {
|
||||
if htlc.state == OutboundHTLCState::LocalAnnounced {
|
||||
return Ok((None, None, dropped_outbound_htlcs));
|
||||
}
|
||||
}
|
||||
|
@ -1839,7 +1875,7 @@ impl Channel {
|
|||
|
||||
self.channel_state |= ChannelState::LocalShutdownSent as u32;
|
||||
self.channel_update_count += 1;
|
||||
if self.pending_htlcs.is_empty() && self.channel_outbound {
|
||||
if self.pending_inbound_htlcs.is_empty() && self.pending_outbound_htlcs.is_empty() && self.channel_outbound {
|
||||
// There are no more HTLCs and we're the funder, this means we start the closing_signed
|
||||
// dance with an initial fee proposal!
|
||||
self.last_sent_closing_fee = Some((proposed_feerate.unwrap(), proposed_fee.unwrap()));
|
||||
|
@ -1857,7 +1893,7 @@ impl Channel {
|
|||
if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK != BOTH_SIDES_SHUTDOWN_MASK {
|
||||
return Err(HandleError{err: "Remote end sent us a closing_signed before both sides provided a shutdown", action: None});
|
||||
}
|
||||
if !self.pending_htlcs.is_empty() {
|
||||
if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() {
|
||||
return Err(HandleError{err: "Remote end sent us a closing_signed while there were still pending HTLCs", action: None});
|
||||
}
|
||||
if msg.fee_satoshis > 21000000 * 10000000 {
|
||||
|
@ -2362,16 +2398,13 @@ impl Channel {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
self.pending_htlcs.push(HTLCOutput {
|
||||
outbound: true,
|
||||
self.pending_outbound_htlcs.push(OutboundHTLCOutput {
|
||||
htlc_id: self.next_local_htlc_id,
|
||||
amount_msat: amount_msat,
|
||||
payment_hash: payment_hash.clone(),
|
||||
cltv_expiry: cltv_expiry,
|
||||
state: HTLCState::LocalAnnounced,
|
||||
state: OutboundHTLCState::LocalAnnounced,
|
||||
fail_reason: None,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None
|
||||
});
|
||||
|
||||
let res = msgs::UpdateAddHTLC {
|
||||
|
@ -2399,8 +2432,8 @@ impl Channel {
|
|||
panic!("Cannot create commitment tx until remote revokes their previous commitment");
|
||||
}
|
||||
let mut have_updates = false; // TODO initialize with "have we sent a fee update?"
|
||||
for htlc in self.pending_htlcs.iter() {
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
for htlc in self.pending_outbound_htlcs.iter() {
|
||||
if htlc.state == OutboundHTLCState::LocalAnnounced {
|
||||
have_updates = true;
|
||||
}
|
||||
if have_updates { break; }
|
||||
|
@ -2417,11 +2450,14 @@ impl Channel {
|
|||
// We can upgrade the status of some HTLCs that are waiting on a commitment, even if we
|
||||
// fail to generate this, we still are at least at a position where upgrading their status
|
||||
// is acceptable.
|
||||
for htlc in self.pending_htlcs.iter_mut() {
|
||||
if htlc.state == HTLCState::AwaitingRemoteRevokeToAnnounce {
|
||||
htlc.state = HTLCState::AwaitingAnnouncedRemoteRevoke;
|
||||
} else if htlc.state == HTLCState::AwaitingRemoteRevokeToRemove {
|
||||
htlc.state = HTLCState::AwaitingRemovedRemoteRevoke;
|
||||
for htlc in self.pending_inbound_htlcs.iter_mut() {
|
||||
if htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce {
|
||||
htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke;
|
||||
}
|
||||
}
|
||||
for htlc in self.pending_outbound_htlcs.iter_mut() {
|
||||
if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
|
||||
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2469,8 +2505,8 @@ impl Channel {
|
|||
/// Begins the shutdown process, getting a message for the remote peer and returning all
|
||||
/// holding cell HTLCs for payment failure.
|
||||
pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<[u8; 32]>), HandleError> {
|
||||
for htlc in self.pending_htlcs.iter() {
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
for htlc in self.pending_outbound_htlcs.iter() {
|
||||
if htlc.state == OutboundHTLCState::LocalAnnounced {
|
||||
return Err(HandleError{err: "Cannot begin shutdown with pending HTLCs, call send_commitment first", action: None});
|
||||
}
|
||||
}
|
||||
|
@ -2529,8 +2565,8 @@ impl Channel {
|
|||
}
|
||||
}
|
||||
|
||||
for htlc in self.pending_htlcs.drain(..) {
|
||||
if htlc.state == HTLCState::LocalAnnounced {
|
||||
for htlc in self.pending_outbound_htlcs.drain(..) {
|
||||
if htlc.state == OutboundHTLCState::LocalAnnounced {
|
||||
dropped_outbound_htlcs.push(htlc.payment_hash);
|
||||
}
|
||||
//TODO: Do something with the remaining HTLCs
|
||||
|
@ -2554,7 +2590,7 @@ mod tests {
|
|||
use bitcoin::blockdata::script::Script;
|
||||
use bitcoin::blockdata::transaction::Transaction;
|
||||
use hex;
|
||||
use ln::channel::{Channel,ChannelKeys,HTLCOutput,HTLCState,HTLCOutputInCommitment,TxCreationKeys};
|
||||
use ln::channel::{Channel,ChannelKeys,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,TxCreationKeys};
|
||||
use ln::channel::MAX_FUNDING_SATOSHIS;
|
||||
use ln::chan_utils;
|
||||
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
|
||||
|
@ -2693,15 +2729,13 @@ mod tests {
|
|||
"02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de84311054a56a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c383693901483045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220");
|
||||
}
|
||||
|
||||
chan.pending_htlcs.push({
|
||||
let mut out = HTLCOutput{
|
||||
chan.pending_inbound_htlcs.push({
|
||||
let mut out = InboundHTLCOutput{
|
||||
htlc_id: 0,
|
||||
outbound: false,
|
||||
amount_msat: 1000000,
|
||||
cltv_expiry: 500,
|
||||
payment_hash: [0; 32],
|
||||
state: HTLCState::Committed,
|
||||
fail_reason: None,
|
||||
state: InboundHTLCState::Committed,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None,
|
||||
};
|
||||
|
@ -2710,15 +2744,13 @@ mod tests {
|
|||
sha.result(&mut out.payment_hash);
|
||||
out
|
||||
});
|
||||
chan.pending_htlcs.push({
|
||||
let mut out = HTLCOutput{
|
||||
chan.pending_inbound_htlcs.push({
|
||||
let mut out = InboundHTLCOutput{
|
||||
htlc_id: 1,
|
||||
outbound: false,
|
||||
amount_msat: 2000000,
|
||||
cltv_expiry: 501,
|
||||
payment_hash: [0; 32],
|
||||
state: HTLCState::Committed,
|
||||
fail_reason: None,
|
||||
state: InboundHTLCState::Committed,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None,
|
||||
};
|
||||
|
@ -2727,49 +2759,41 @@ mod tests {
|
|||
sha.result(&mut out.payment_hash);
|
||||
out
|
||||
});
|
||||
chan.pending_htlcs.push({
|
||||
let mut out = HTLCOutput{
|
||||
chan.pending_outbound_htlcs.push({
|
||||
let mut out = OutboundHTLCOutput{
|
||||
htlc_id: 2,
|
||||
outbound: true,
|
||||
amount_msat: 2000000,
|
||||
cltv_expiry: 502,
|
||||
payment_hash: [0; 32],
|
||||
state: HTLCState::Committed,
|
||||
state: OutboundHTLCState::Committed,
|
||||
fail_reason: None,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None,
|
||||
};
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(&hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap());
|
||||
sha.result(&mut out.payment_hash);
|
||||
out
|
||||
});
|
||||
chan.pending_htlcs.push({
|
||||
let mut out = HTLCOutput{
|
||||
chan.pending_outbound_htlcs.push({
|
||||
let mut out = OutboundHTLCOutput{
|
||||
htlc_id: 3,
|
||||
outbound: true,
|
||||
amount_msat: 3000000,
|
||||
cltv_expiry: 503,
|
||||
payment_hash: [0; 32],
|
||||
state: HTLCState::Committed,
|
||||
state: OutboundHTLCState::Committed,
|
||||
fail_reason: None,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None,
|
||||
};
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(&hex::decode("0303030303030303030303030303030303030303030303030303030303030303").unwrap());
|
||||
sha.result(&mut out.payment_hash);
|
||||
out
|
||||
});
|
||||
chan.pending_htlcs.push({
|
||||
let mut out = HTLCOutput{
|
||||
chan.pending_inbound_htlcs.push({
|
||||
let mut out = InboundHTLCOutput{
|
||||
htlc_id: 4,
|
||||
outbound: false,
|
||||
amount_msat: 4000000,
|
||||
cltv_expiry: 504,
|
||||
payment_hash: [0; 32],
|
||||
state: HTLCState::Committed,
|
||||
fail_reason: None,
|
||||
state: InboundHTLCState::Committed,
|
||||
local_removed_fulfilled: false,
|
||||
pending_forward_state: None,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue