use crypto::hkdf::{hkdf_extract,hkdf_expand};
use ln::msgs;
-use ln::msgs::{ErrorAction, HandleError};
+use ln::msgs::{ErrorAction, HandleError, RAACommitmentOrder};
use ln::channelmonitor::ChannelMonitor;
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, PendingForwardHTLCInfo, HTLCFailReason, HTLCFailureMsg};
use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
state: InboundHTLCState,
}
-#[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
/// 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,
+ /// Note that we Box the OnionPacket as its rather large and we don't want to blow up
+ /// OutboundHTLCOutput's size just for a temporary bit
+ LocalAnnounced(Box<msgs::OnionPacket>),
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).
ShutdownComplete = 2048,
}
const BOTH_SIDES_SHUTDOWN_MASK: u32 = (ChannelState::LocalShutdownSent as u32 | ChannelState::RemoteShutdownSent as u32);
+const MULTI_STATE_FLAGS: u32 = (BOTH_SIDES_SHUTDOWN_MASK | ChannelState::PeerDisconnected as u32);
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
for ref htlc in self.pending_outbound_htlcs.iter() {
let include = match htlc.state {
- OutboundHTLCState::LocalAnnounced => generated_by_local,
+ OutboundHTLCState::LocalAnnounced(_) => generated_by_local,
OutboundHTLCState::Committed => true,
OutboundHTLCState::RemoteRemoved => generated_by_local,
OutboundHTLCState::AwaitingRemoteRevokeToRemove => generated_by_local,
Ok(())
}
- /// Removes an outbound HTLC which has been commitment_signed by the remote end
+ /// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
}
};
match htlc.state {
- OutboundHTLCState::LocalAnnounced =>
- return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
+ OutboundHTLCState::LocalAnnounced(_) =>
+ return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
- return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
+ return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::RemoteRemoved {
+ if let OutboundHTLCState::RemoteRemoved = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove;
need_our_commitment = true;
}
} else { true }
});
self.pending_outbound_htlcs.retain(|htlc| {
- if htlc.state == OutboundHTLCState::AwaitingRemovedRemoteRevoke {
+ if let OutboundHTLCState::AwaitingRemovedRemoteRevoke = htlc.state {
if let Some(reason) = htlc.fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
htlc.state = OutboundHTLCState::Committed;
- } else if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
+ } else if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
require_commitment = true;
}
if !self.channel_outbound {
panic!("Cannot send fee from inbound channel");
}
-
if !self.is_usable() {
panic!("Cannot update fee until channel is fully established and we haven't started shutting down");
}
+ if !self.is_live() {
+ panic!("Cannot update fee while peer is disconnected (ChannelManager should have caught this)");
+ }
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
self.holding_cell_update_fee = Some(feerate_per_kw);
});
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::RemoteRemoved {
+ if let OutboundHTLCState::RemoteRemoved = htlc.state {
// They sent us an update to remove this but haven't yet sent the corresponding
// commitment_signed, we need to move it back to Committed and they can re-send
// the update upon reconnection.
/// May panic if some calls other than message-handling calls (which will all Err immediately)
/// have been called between remove_uncommitted_htlcs_and_mark_paused and this call.
- pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>), ChannelError> {
+ pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>, RAACommitmentOrder), ChannelError> {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
// While BOLT 2 doesn't indicate explicitly we should error this channel here, it
// almost certainly indicates we are going to end up out-of-sync in some way, so we
if msg.next_local_commitment_number == 0 || msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
msg.next_remote_commitment_number == 0 || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER {
- return Err(ChannelError::Close("Peer send garbage channel_reestablish"));
+ return Err(ChannelError::Close("Peer sent a garbage channel_reestablish"));
}
// Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
// remaining cases either succeed or ErrorMessage-fail).
self.channel_state &= !(ChannelState::PeerDisconnected as u32);
- let mut required_revoke = None;
- if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
+ let required_revoke = if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
// Remote isn't waiting on any RevokeAndACK from us!
// Note that if we need to repeat our FundingLocked we'll do that in the next if block.
+ None
} else if msg.next_remote_commitment_number == (INITIAL_COMMITMENT_NUMBER - 1) - self.cur_local_commitment_transaction_number {
let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &self.build_local_commitment_secret(self.cur_local_commitment_transaction_number));
let per_commitment_secret = chan_utils::build_commitment_secret(self.local_keys.commitment_seed, self.cur_local_commitment_transaction_number + 2);
- required_revoke = Some(msgs::RevokeAndACK {
+ Some(msgs::RevokeAndACK {
channel_id: self.channel_id,
per_commitment_secret,
next_per_commitment_point,
- });
+ })
} else {
return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction"));
- }
+ };
- if msg.next_local_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_remote_commitment_transaction_number {
- if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
- log_debug!(self, "Reconnected channel {} with no lost commitment txn", log_bytes!(self.channel_id()));
- if msg.next_local_commitment_number == 1 && msg.next_remote_commitment_number == 1 {
- let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
- let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret);
- return Ok((Some(msgs::FundingLocked {
- channel_id: self.channel_id(),
- next_per_commitment_point: next_per_commitment_point,
- }), None, None, None));
- }
+ // We increment cur_remote_commitment_transaction_number only upon receipt of
+ // revoke_and_ack, not on sending commitment_signed, so we add one if have
+ // AwaitingRemoteRevoke set, which indicates we sent a commitment_signed but haven't gotten
+ // the corresponding revoke_and_ack back yet.
+ let our_next_remote_commitment_number = INITIAL_COMMITMENT_NUMBER - self.cur_remote_commitment_transaction_number + if (self.channel_state & ChannelState::AwaitingRemoteRevoke as u32) != 0 { 1 } else { 0 };
+
+ let resend_funding_locked = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number == 1 {
+ let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
+ let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret);
+ Some(msgs::FundingLocked {
+ channel_id: self.channel_id(),
+ next_per_commitment_point: next_per_commitment_point,
+ })
+ } else { None };
+
+ let order = RAACommitmentOrder::RevokeAndACKFirst;
+
+ if msg.next_local_commitment_number == our_next_remote_commitment_number {
+ if required_revoke.is_some() {
+ log_debug!(self, "Reconnected channel {} with only lost outbound RAA", log_bytes!(self.channel_id()));
+ } else {
+ log_debug!(self, "Reconnected channel {} with no loss", log_bytes!(self.channel_id()));
}
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == 0 {
panic!("Got non-channel-failing result from free_holding_cell_htlcs");
}
},
- Ok(Some((commitment_update, channel_monitor))) => return Ok((None, required_revoke, Some(commitment_update), Some(channel_monitor))),
- Ok(None) => return Ok((None, required_revoke, None, None)),
+ Ok(Some((commitment_update, channel_monitor))) => return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(channel_monitor), order)),
+ Ok(None) => return Ok((resend_funding_locked, required_revoke, None, None, order)),
}
} else {
- return Ok((None, required_revoke, None, None));
+ return Ok((resend_funding_locked, required_revoke, None, None, order));
+ }
+ } else if msg.next_local_commitment_number == our_next_remote_commitment_number - 1 {
+ if required_revoke.is_some() {
+ log_debug!(self, "Reconnected channel {} with lost outbound RAA and lost remote commitment tx", log_bytes!(self.channel_id()));
+ } else {
+ log_debug!(self, "Reconnected channel {} with only lost remote commitment tx", log_bytes!(self.channel_id()));
+ }
+ let mut update_add_htlcs = Vec::new();
+ let mut update_fulfill_htlcs = Vec::new();
+ let mut update_fail_htlcs = Vec::new();
+ let mut update_fail_malformed_htlcs = Vec::new();
+
+ for htlc in self.pending_outbound_htlcs.iter() {
+ if let &OutboundHTLCState::LocalAnnounced(ref onion_packet) = &htlc.state {
+ update_add_htlcs.push(msgs::UpdateAddHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ amount_msat: htlc.amount_msat,
+ payment_hash: htlc.payment_hash,
+ cltv_expiry: htlc.cltv_expiry,
+ onion_routing_packet: (**onion_packet).clone(),
+ });
+ }
}
- } else if msg.next_local_commitment_number == (INITIAL_COMMITMENT_NUMBER - 1) - self.cur_remote_commitment_transaction_number {
- return Ok((None, required_revoke,
+
+ for htlc in self.pending_inbound_htlcs.iter() {
+ if let &InboundHTLCState::LocalRemoved(ref reason) = &htlc.state {
+ match reason {
+ &InboundHTLCRemovalReason::FailRelay(ref err_packet) => {
+ update_fail_htlcs.push(msgs::UpdateFailHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ reason: err_packet.clone()
+ });
+ },
+ &InboundHTLCRemovalReason::FailMalformed((ref sha256_of_onion, ref failure_code)) => {
+ update_fail_malformed_htlcs.push(msgs::UpdateFailMalformedHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ sha256_of_onion: sha256_of_onion.clone(),
+ failure_code: failure_code.clone(),
+ });
+ },
+ &InboundHTLCRemovalReason::Fulfill(ref payment_preimage) => {
+ update_fulfill_htlcs.push(msgs::UpdateFulfillHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ payment_preimage: payment_preimage.clone(),
+ });
+ },
+ }
+ }
+ }
+
+ return Ok((resend_funding_locked, required_revoke,
Some(msgs::CommitmentUpdate {
- update_add_htlcs: Vec::new(),
- update_fulfill_htlcs: Vec::new(),
- update_fail_htlcs: Vec::new(),
- update_fail_malformed_htlcs: Vec::new(),
- update_fee: None,
+ update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, update_fail_malformed_htlcs,
+ update_fee: None, //TODO: We need to support re-generating any update_fees in the last commitment_signed!
commitment_signed: self.send_commitment_no_state_update().expect("It looks like we failed to re-generate a commitment_signed we had previously sent?").0,
- }), None));
+ }), None, order));
} else {
return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction"));
}
}
});
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
return Ok((None, None, dropped_outbound_htlcs));
}
}
self.our_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown)
+ pub fn get_their_htlc_minimum_msat(&self) -> u64 {
+ self.our_htlc_minimum_msat
+ }
+
pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
/// apply - no calls may be made except those explicitly stated to be allowed post-shutdown.
/// Only returns an ErrorAction of DisconnectPeer, if Err.
pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Result<Option<msgs::FundingLocked>, HandleError> {
- let non_shutdown_state = self.channel_state & (!BOTH_SIDES_SHUTDOWN_MASK);
+ let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
if self.funding_tx_confirmations > 0 {
if header.bitcoin_hash() != self.last_block_connected {
self.last_block_connected = header.bitcoin_hash();
self.channel_state |= ChannelState::OurFundingLocked as u32;
true
} else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) {
- self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & BOTH_SIDES_SHUTDOWN_MASK);
+ self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS);
self.channel_update_count += 1;
true
- } else if self.channel_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) {
+ } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) {
// We got a reorg but not enough to trigger a force close, just update
// funding_tx_confirmed_in and return.
false
amount_msat: amount_msat,
payment_hash: payment_hash.clone(),
cltv_expiry: cltv_expiry,
- state: OutboundHTLCState::LocalAnnounced,
+ state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())),
source,
fail_reason: None,
});
}
let mut have_updates = self.pending_update_fee.is_some();
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
have_updates = true;
}
if have_updates { break; }
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
+ if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
}
}
/// holding cell HTLCs for payment failure.
pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, [u8; 32])>), APIError> {
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first"});
}
}