- /// Transaction nomenclature is somewhat confusing here as there are many different cases - a
- /// transaction is referred to as "a's transaction" implying that a will be able to broadcast
- /// the transaction. Thus, b will generally be sending a signature over such a transaction to
- /// a, and a can revoke the transaction by providing b the relevant per_commitment_secret. As
- /// such, a transaction is generally the result of b increasing the amount paid to a (or adding
- /// an HTLC to a).
- /// @local is used only to convert relevant internal structures which refer to remote vs local
- /// to decide value of outputs and direction of HTLCs.
- /// @generated_by_local is used to determine *which* HTLCs to include - noting that the HTLC
- /// state may indicate that one peer has informed the other that they'd like to add an HTLC but
- /// have not yet committed it. Such HTLCs will only be included in transactions which are being
- /// generated by the peer which proposed adding the HTLCs, and thus we need to understand both
- /// which peer generated this transaction and "to whom" this transaction flows.
- #[inline]
- fn build_commitment_transaction<L: Deref>(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, logger: &L) -> CommitmentStats
- where L::Target: Logger
- {
- let mut included_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::new();
- let num_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len();
- let mut included_non_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::with_capacity(num_htlcs);
-
- let broadcaster_dust_limit_satoshis = if local { self.context.holder_dust_limit_satoshis } else { self.context.counterparty_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;
-
- let mut feerate_per_kw = self.context.feerate_per_kw;
- if let Some((feerate, update_state)) = self.context.pending_update_fee {
- if match update_state {
- // Note that these match the inclusion criteria when scanning
- // pending_inbound_htlcs below.
- FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.context.is_outbound()); !generated_by_local },
- FeeUpdateState::AwaitingRemoteRevokeToAnnounce => { debug_assert!(!self.context.is_outbound()); !generated_by_local },
- FeeUpdateState::Outbound => { assert!(self.context.is_outbound()); generated_by_local },
- } {
- feerate_per_kw = feerate;
- }
- }
-
- log_trace!(logger, "Building commitment transaction number {} (really {} xor {}) for channel {} for {}, generated by {} with fee {}...",
- commitment_number, (INITIAL_COMMITMENT_NUMBER - commitment_number),
- get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound()),
- log_bytes!(self.context.channel_id), if local { "us" } else { "remote" }, if generated_by_local { "us" } else { "remote" }, feerate_per_kw);
-
- macro_rules! get_htlc_in_commitment {
- ($htlc: expr, $offered: expr) => {
- HTLCOutputInCommitment {
- offered: $offered,
- amount_msat: $htlc.amount_msat,
- cltv_expiry: $htlc.cltv_expiry,
- payment_hash: $htlc.payment_hash,
- transaction_output_index: None
- }
- }
- }
-
- macro_rules! add_htlc_output {
- ($htlc: expr, $outbound: expr, $source: expr, $state_name: expr) => {
- if $outbound == local { // "offered HTLC output"
- let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
- let htlc_tx_fee = if self.context.opt_anchors() {
- 0
- } else {
- feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000
- };
- if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
- log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
- included_non_dust_htlcs.push((htlc_in_tx, $source));
- } else {
- log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {} due to dust limit", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
- included_dust_htlcs.push((htlc_in_tx, $source));
- }
- } else {
- let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
- let htlc_tx_fee = if self.context.opt_anchors() {
- 0
- } else {
- feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000
- };
- if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
- log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
- included_non_dust_htlcs.push((htlc_in_tx, $source));
- } else {
- log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
- included_dust_htlcs.push((htlc_in_tx, $source));
- }
- }
- }
- }
-
- for ref htlc in self.context.pending_inbound_htlcs.iter() {
- let (include, state_name) = match htlc.state {
- InboundHTLCState::RemoteAnnounced(_) => (!generated_by_local, "RemoteAnnounced"),
- InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_) => (!generated_by_local, "AwaitingRemoteRevokeToAnnounce"),
- InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) => (true, "AwaitingAnnouncedRemoteRevoke"),
- InboundHTLCState::Committed => (true, "Committed"),
- InboundHTLCState::LocalRemoved(_) => (!generated_by_local, "LocalRemoved"),
- };
-
- if include {
- add_htlc_output!(htlc, false, None, state_name);
- remote_htlc_total_msat += htlc.amount_msat;
- } else {
- log_trace!(logger, " ...not including inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name);
- match &htlc.state {
- &InboundHTLCState::LocalRemoved(ref reason) => {
- if generated_by_local {
- if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
- value_to_self_msat_offset += htlc.amount_msat as i64;
- }
- }
- },
- _ => {},
- }
- }
- }
-
- let mut preimages: Vec<PaymentPreimage> = Vec::new();
-
- for ref htlc in self.context.pending_outbound_htlcs.iter() {
- let (include, state_name) = match htlc.state {
- OutboundHTLCState::LocalAnnounced(_) => (generated_by_local, "LocalAnnounced"),
- OutboundHTLCState::Committed => (true, "Committed"),
- OutboundHTLCState::RemoteRemoved(_) => (generated_by_local, "RemoteRemoved"),
- OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) => (generated_by_local, "AwaitingRemoteRevokeToRemove"),
- 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(OutboundHTLCOutcome::Success(_))|OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => {
- value_to_self_msat_offset -= htlc.amount_msat as i64;
- },
- OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => {
- if !generated_by_local {
- value_to_self_msat_offset -= htlc.amount_msat as i64;
- }
- },
- _ => {},
- }
- }
- }
-
- let mut value_to_self_msat: i64 = (self.context.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset;
- assert!(value_to_self_msat >= 0);
- // Note that in case they have several just-awaiting-last-RAA fulfills in-progress (ie
- // AwaitingRemoteRevokeToRemove or AwaitingRemovedRemoteRevoke) we may have allowed them to
- // "violate" their reserve value by couting those against it. Thus, we have to convert
- // everything to i64 before subtracting as otherwise we can overflow.
- let mut value_to_remote_msat: i64 = (self.context.channel_value_satoshis * 1000) as i64 - (self.context.value_to_self_msat as i64) - (remote_htlc_total_msat as i64) - value_to_self_msat_offset;
- assert!(value_to_remote_msat >= 0);
-
- #[cfg(debug_assertions)]
- {
- // Make sure that the to_self/to_remote is always either past the appropriate
- // channel_reserve *or* it is making progress towards it.
- let mut broadcaster_max_commitment_tx_output = if generated_by_local {
- self.context.holder_max_commitment_tx_output.lock().unwrap()
- } else {
- self.context.counterparty_max_commitment_tx_output.lock().unwrap()
- };
- debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= self.context.counterparty_selected_channel_reserve_satoshis.unwrap() as i64);
- broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat as u64);
- debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= self.context.holder_selected_channel_reserve_satoshis as i64);
- broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64);
- }
-
- let total_fee_sat = Channel::<Signer>::commit_tx_fee_sat(feerate_per_kw, included_non_dust_htlcs.len(), self.context.channel_transaction_parameters.opt_anchors.is_some());
- let anchors_val = if self.context.channel_transaction_parameters.opt_anchors.is_some() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } as i64;
- let (value_to_self, value_to_remote) = if self.context.is_outbound() {
- (value_to_self_msat / 1000 - anchors_val - total_fee_sat as i64, value_to_remote_msat / 1000)
- } else {
- (value_to_self_msat / 1000, value_to_remote_msat / 1000 - anchors_val - total_fee_sat as i64)
- };
-
- let mut value_to_a = if local { value_to_self } else { value_to_remote };
- let mut value_to_b = if local { value_to_remote } else { value_to_self };
- let (funding_pubkey_a, funding_pubkey_b) = if local {
- (self.context.get_holder_pubkeys().funding_pubkey, self.context.get_counterparty_pubkeys().funding_pubkey)
- } else {
- (self.context.get_counterparty_pubkeys().funding_pubkey, self.context.get_holder_pubkeys().funding_pubkey)
- };
-
- if value_to_a >= (broadcaster_dust_limit_satoshis as i64) {
- log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
- } else {
- value_to_a = 0;
- }
-
- if value_to_b >= (broadcaster_dust_limit_satoshis as i64) {
- log_trace!(logger, " ...including {} output with value {}", if local { "to_remote" } else { "to_local" }, value_to_b);
- } else {
- value_to_b = 0;
- }
-
- let num_nondust_htlcs = included_non_dust_htlcs.len();
-
- let channel_parameters =
- if local { self.context.channel_transaction_parameters.as_holder_broadcastable() }
- else { self.context.channel_transaction_parameters.as_counterparty_broadcastable() };
- let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
- value_to_a as u64,
- value_to_b as u64,
- self.context.channel_transaction_parameters.opt_anchors.is_some(),
- funding_pubkey_a,
- funding_pubkey_b,
- keys.clone(),
- feerate_per_kw,
- &mut included_non_dust_htlcs,
- &channel_parameters
- );
- let mut htlcs_included = included_non_dust_htlcs;
- // The unwrap is safe, because all non-dust HTLCs have been assigned an output index
- htlcs_included.sort_unstable_by_key(|h| h.0.transaction_output_index.unwrap());
- htlcs_included.append(&mut included_dust_htlcs);
-
- // For the stats, trimmed-to-0 the value in msats accordingly
- value_to_self_msat = if (value_to_self_msat * 1000) < broadcaster_dust_limit_satoshis as i64 { 0 } else { value_to_self_msat };
- value_to_remote_msat = if (value_to_remote_msat * 1000) < broadcaster_dust_limit_satoshis as i64 { 0 } else { value_to_remote_msat };
-
- CommitmentStats {
- tx,
- feerate_per_kw,
- total_fee_sat,
- num_nondust_htlcs,
- htlcs_included,
- local_balance_msat: value_to_self_msat as u64,
- remote_balance_msat: value_to_remote_msat as u64,
- preimages
- }
- }
-