use util::ser::{Writeable, Writer, Readable};
use chain::transaction::OutPoint;
-use ln::chan_utils;
+use ln::{chan_utils, PaymentPreimage};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use ln::msgs::UnsignedChannelAnnouncement;
use ln::script::ShutdownScript;
/// secret won't leave us without a broadcastable holder transaction.
/// Policy checks should be implemented in this function, including checking the amount
/// sent to us and checking the HTLCs.
- fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>;
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
/// Gets the holder's channel public keys and basepoints
fn pubkeys(&self) -> &ChannelPublicKeys;
/// Gets an arbitrary identifier describing the set of keys which are provided back to you in
///
/// Policy checks should be implemented in this function, including checking the amount
/// sent to us and checking the HTLCs.
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
//
// TODO: Document the things someone using this interface should enforce before signing.
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
/// Validate the counterparty's revocation.
///
/// This is required in order for the signer to make sure that the state has moved
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
}
- fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
Ok(())
}
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let trusted_tx = commitment_tx.trust();
let keys = trusted_tx.keys();
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(Option<HTLCFailReason>),
+ RemoteRemoved(OutboundHTLCOutcome),
/// 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(Option<HTLCFailReason>),
+ AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome),
/// 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(Option<HTLCFailReason>),
+ AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
+}
+
+#[derive(Clone)]
+enum OutboundHTLCOutcome {
+ Success(Option<PaymentPreimage>),
+ Failure(HTLCFailReason),
+}
+
+impl From<Option<HTLCFailReason>> for OutboundHTLCOutcome {
+ fn from(o: Option<HTLCFailReason>) -> Self {
+ match o {
+ None => OutboundHTLCOutcome::Success(None),
+ Some(r) => OutboundHTLCOutcome::Failure(r)
+ }
+ }
+}
+
+impl<'a> Into<Option<&'a HTLCFailReason>> for &'a OutboundHTLCOutcome {
+ fn into(self) -> Option<&'a HTLCFailReason> {
+ match self {
+ OutboundHTLCOutcome::Success(_) => None,
+ OutboundHTLCOutcome::Failure(ref r) => Some(r)
+ }
+ }
}
struct OutboundHTLCOutput {
htlcs_included: Vec<(HTLCOutputInCommitment, Option<&'a HTLCSource>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
local_balance_msat: u64, // local balance before fees but considering dust limits
remote_balance_msat: u64, // remote balance before fees but considering dust limits
+ preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
}
/// Used when calculating whether we or the remote can afford an additional HTLC.
}
}
+ let mut preimages: Vec<PaymentPreimage> = Vec::new();
+
for ref htlc in self.pending_outbound_htlcs.iter() {
let (include, state_name) = match htlc.state {
OutboundHTLCState::LocalAnnounced(_) => (generated_by_local, "LocalAnnounced"),
OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) => (false, "AwaitingRemovedRemoteRevoke"),
};
+ let preimage_opt = match htlc.state {
+ OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(p)) => p,
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(p)) => p,
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(p)) => p,
+ _ => None,
+ };
+
+ if let Some(preimage) = preimage_opt {
+ preimages.push(preimage);
+ }
+
if include {
add_htlc_output!(htlc, true, Some(&htlc.source), state_name);
local_htlc_total_msat += htlc.amount_msat;
} else {
log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name);
match htlc.state {
- OutboundHTLCState::AwaitingRemoteRevokeToRemove(None)|OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) => {
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_))|OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => {
value_to_self_msat_offset -= htlc.amount_msat as i64;
},
- OutboundHTLCState::RemoteRemoved(None) => {
+ OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => {
if !generated_by_local {
value_to_self_msat_offset -= htlc.amount_msat as i64;
}
htlcs_included,
local_balance_msat: value_to_self_msat as u64,
remote_balance_msat: value_to_remote_msat as u64,
+ preimages
}
}
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
log_bytes!(self.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
- let counterparty_signature = self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx)
+ let counterparty_signature = self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
self.counterparty_funding_pubkey()
);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
// Now that we're past error-generating stuff, update our local state:
self.counterparty_funding_pubkey()
);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
// transaction).
let mut removed_outbound_total_msat = 0;
for ref htlc in self.pending_outbound_htlcs.iter() {
- if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(None) = htlc.state {
+ if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) = htlc.state {
removed_outbound_total_msat += htlc.amount_msat;
- } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) = htlc.state {
+ } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) = htlc.state {
removed_outbound_total_msat += htlc.amount_msat;
}
}
/// 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<PaymentHash>, fail_reason: Option<HTLCFailReason>) -> Result<&OutboundHTLCOutput, ChannelError> {
+ fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<PaymentPreimage>, fail_reason: Option<HTLCFailReason>) -> Result<&OutboundHTLCOutput, ChannelError> {
+ assert!(!(check_preimage.is_some() && fail_reason.is_some()), "cannot fail while we have a preimage");
for htlc in self.pending_outbound_htlcs.iter_mut() {
if htlc.htlc_id == htlc_id {
- match check_preimage {
- None => {},
- Some(payment_hash) =>
+ let outcome = match check_preimage {
+ None => fail_reason.into(),
+ Some(payment_preimage) => {
+ let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
if payment_hash != htlc.payment_hash {
return Err(ChannelError::Close(format!("Remote tried to fulfill HTLC ({}) with an incorrect preimage", htlc_id)));
}
+ OutboundHTLCOutcome::Success(Some(payment_preimage))
+ }
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) before it had been committed", htlc_id))),
OutboundHTLCState::Committed => {
- htlc.state = OutboundHTLCState::RemoteRemoved(fail_reason);
+ htlc.state = OutboundHTLCState::RemoteRemoved(outcome);
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) | OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) | OutboundHTLCState::RemoteRemoved(_) =>
return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) that they'd already fulfilled/failed", htlc_id))),
return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish".to_owned()));
}
- let payment_hash = PaymentHash(Sha256::hash(&msg.payment_preimage.0[..]).into_inner());
- self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None).map(|htlc| (htlc.source.clone(), htlc.amount_msat))
+ self.mark_outbound_htlc_removed(msg.htlc_id, Some(msg.payment_preimage), None).map(|htlc| (htlc.source.clone(), htlc.amount_msat))
}
pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> {
);
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, commitment_stats.preimages)
.map_err(|_| (None, ChannelError::Close("Failed to validate our commitment".to_owned())))?;
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1);
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if let Some(fail_reason) = if let &mut OutboundHTLCState::RemoteRemoved(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::RemoteRemoved(ref mut outcome) = &mut htlc.state {
log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToRemove due to commitment_signed in channel {}.",
log_bytes!(htlc.payment_hash.0), log_bytes!(self.channel_id));
- htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(reason);
need_commitment = true;
}
}
} else { true }
});
pending_outbound_htlcs.retain(|htlc| {
- if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) = &htlc.state {
+ if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) = &htlc.state {
log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", log_bytes!(htlc.payment_hash.0));
- if let Some(reason) = fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
+ if let OutboundHTLCOutcome::Failure(reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
finalized_claimed_htlcs.push(htlc.source.clone());
log_trace!(logger, " ...promoting outbound LocalAnnounced {} to Committed", log_bytes!(htlc.payment_hash.0));
htlc.state = OutboundHTLCState::Committed;
}
- if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
- htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
require_commitment = true;
}
}
fn get_outbound_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> where L::Target: Logger {
let counterparty_keys = self.build_remote_transaction_keys()?;
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
- Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx)
+ Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
}
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
- htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
}
}
if let Some((feerate, update_state)) = self.pending_update_fee {
htlcs.push(htlc);
}
- let res = self.holder_signer.sign_counterparty_commitment(&commitment_stats.tx, &self.secp_ctx)
+ let res = self.holder_signer.sign_counterparty_commitment(&commitment_stats.tx, commitment_stats.preimages, &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?;
signature = res.0;
htlc_signatures = res.1;
}
}
+ let mut preimages: Vec<&Option<PaymentPreimage>> = vec![];
+
(self.pending_outbound_htlcs.len() as u64).write(writer)?;
for htlc in self.pending_outbound_htlcs.iter() {
htlc.htlc_id.write(writer)?;
// resend the claim/fail on reconnect as we all (hopefully) the missing CS.
1u8.write(writer)?;
},
- &OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref fail_reason) => {
+ &OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref outcome) => {
3u8.write(writer)?;
- fail_reason.write(writer)?;
- },
- &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) => {
+ if let OutboundHTLCOutcome::Success(preimage) = outcome {
+ preimages.push(preimage);
+ }
+ let reason: Option<&HTLCFailReason> = outcome.into();
+ reason.write(writer)?;
+ }
+ &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) => {
4u8.write(writer)?;
- fail_reason.write(writer)?;
- },
+ if let OutboundHTLCOutcome::Success(preimage) = outcome {
+ preimages.push(preimage);
+ }
+ let reason: Option<&HTLCFailReason> = outcome.into();
+ reason.write(writer)?;
+ }
}
}
(9, self.target_closing_feerate_sats_per_kw, option),
(11, self.monitor_pending_finalized_fulfills, vec_type),
(13, self.channel_creation_height, required),
+ (15, preimages, vec_type),
});
Ok(())
state: match <u8 as Readable>::read(reader)? {
0 => OutboundHTLCState::LocalAnnounced(Box::new(Readable::read(reader)?)),
1 => OutboundHTLCState::Committed,
- 2 => OutboundHTLCState::RemoteRemoved(Readable::read(reader)?),
- 3 => OutboundHTLCState::AwaitingRemoteRevokeToRemove(Readable::read(reader)?),
- 4 => OutboundHTLCState::AwaitingRemovedRemoteRevoke(Readable::read(reader)?),
+ 2 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::RemoteRemoved(option.into())
+ },
+ 3 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(option.into())
+ },
+ 4 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(option.into())
+ },
_ => return Err(DecodeError::InvalidValue),
},
});
// only, so we default to that if none was written.
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
let mut channel_creation_height = Some(serialized_height);
+ let mut preimages_opt: Option<Vec<Option<PaymentPreimage>>> = None;
+
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
(9, target_closing_feerate_sats_per_kw, option),
(11, monitor_pending_finalized_fulfills, vec_type),
(13, channel_creation_height, option),
+ (15, preimages_opt, vec_type),
});
+ if let Some(preimages) = preimages_opt {
+ let mut iter = preimages.into_iter();
+ for htlc in pending_outbound_htlcs.iter_mut() {
+ match &htlc.state {
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(None)) => {
+ htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
+ }
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(None)) => {
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
+ }
+ _ => {}
+ }
+ }
+ // We expect all preimages to be consumed above
+ if iter.next().is_some() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+
let chan_features = channel_type.as_ref().unwrap();
if chan_features.supports_unknown_bits() || chan_features.requires_unknown_bits() {
// If the channel was written by a new version and negotiated with features we don't