X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchan_utils.rs;h=3a6e81e6399c804bed7fac94a8526ddef809e7df;hb=af7c2920e1e5ae4ba3f522730e5cce99bedacc8c;hp=d53863289bc5807464739f0707fbb390c25f9869;hpb=7e05623befc111422d1d4230f4e51629566a2ae6;p=rust-lightning diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index d5386328..3a6e81e6 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -20,10 +20,10 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::ripemd160::Hash as Ripemd160; use bitcoin::hash_types::{Txid, PubkeyHash}; -use ln::{PaymentHash, PaymentPreimage}; -use ln::msgs::DecodeError; -use util::ser::{Readable, Writeable, Writer}; -use util::{byte_utils, transaction_utils}; +use crate::ln::{PaymentHash, PaymentPreimage}; +use crate::ln::msgs::DecodeError; +use crate::util::ser::{Readable, Writeable, Writer}; +use crate::util::{byte_utils, transaction_utils}; use bitcoin::hash_types::WPubkeyHash; use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar}; @@ -31,17 +31,23 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message}; use bitcoin::secp256k1::Error as SecpError; use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness}; -use io; -use prelude::*; +use crate::io; +use crate::prelude::*; use core::cmp; -use ln::chan_utils; -use util::transaction_utils::sort_outputs; -use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI}; +use crate::ln::chan_utils; +use crate::util::transaction_utils::sort_outputs; +use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI}; use core::ops::Deref; -use chain; -use util::crypto::sign; +use crate::chain; +use crate::util::crypto::sign; pub(crate) const MAX_HTLCS: u16 = 483; +pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133; +pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 136; +// The weight of `accepted_htlc_script` can vary in function of its CLTV argument value. We define a +// range that encompasses both its non-anchors and anchors variants. +pub(crate) const MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 136; +pub(crate) const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143; /// Gets the weight for an HTLC-Success transaction. #[inline] @@ -59,19 +65,73 @@ pub fn htlc_timeout_tx_weight(opt_anchors: bool) -> u64 { if opt_anchors { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT } } -#[derive(PartialEq)] -pub(crate) enum HTLCType { - AcceptedHTLC, - OfferedHTLC +#[derive(PartialEq, Eq)] +pub(crate) enum HTLCClaim { + OfferedTimeout, + OfferedPreimage, + AcceptedTimeout, + AcceptedPreimage, + Revocation, } -impl HTLCType { - /// Check if a given tx witnessScript len matchs one of a pre-signed HTLC - pub(crate) fn scriptlen_to_htlctype(witness_script_len: usize) -> Option { - if witness_script_len == 133 { - Some(HTLCType::OfferedHTLC) - } else if witness_script_len >= 136 && witness_script_len <= 139 { - Some(HTLCType::AcceptedHTLC) +impl HTLCClaim { + /// Check if a given input witness attempts to claim a HTLC. + pub(crate) fn from_witness(witness: &Witness) -> Option { + debug_assert_eq!(OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS, MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT); + if witness.len() < 2 { + return None; + } + let witness_script = witness.last().unwrap(); + let second_to_last = witness.second_to_last().unwrap(); + if witness_script.len() == OFFERED_HTLC_SCRIPT_WEIGHT { + if witness.len() == 3 && second_to_last.len() == 33 { + // + Some(Self::Revocation) + } else if witness.len() == 3 && second_to_last.len() == 32 { + // + Some(Self::OfferedPreimage) + } else if witness.len() == 5 && second_to_last.len() == 0 { + // 0 <> + Some(Self::OfferedTimeout) + } else { + None + } + } else if witness_script.len() == OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS { + // It's possible for the weight of `offered_htlc_script` and `accepted_htlc_script` to + // match so we check for both here. + if witness.len() == 3 && second_to_last.len() == 33 { + // + Some(Self::Revocation) + } else if witness.len() == 3 && second_to_last.len() == 32 { + // + Some(Self::OfferedPreimage) + } else if witness.len() == 5 && second_to_last.len() == 0 { + // 0 <> + Some(Self::OfferedTimeout) + } else if witness.len() == 3 && second_to_last.len() == 0 { + // <> + Some(Self::AcceptedTimeout) + } else if witness.len() == 5 && second_to_last.len() == 32 { + // 0 + Some(Self::AcceptedPreimage) + } else { + None + } + } else if witness_script.len() > MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT && + witness_script.len() <= MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT { + // Handle remaining range of ACCEPTED_HTLC_SCRIPT_WEIGHT. + if witness.len() == 3 && second_to_last.len() == 33 { + // + Some(Self::Revocation) + } else if witness.len() == 3 && second_to_last.len() == 0 { + // <> + Some(Self::AcceptedTimeout) + } else if witness.len() == 5 && second_to_last.len() == 32 { + // 0 + Some(Self::AcceptedPreimage) + } else { + None + } } else { None } @@ -148,6 +208,7 @@ pub struct CounterpartyCommitmentSecrets { old_secrets: [([u8; 32], u64); 49], } +impl Eq for CounterpartyCommitmentSecrets {} impl PartialEq for CounterpartyCommitmentSecrets { fn eq(&self, other: &Self) -> bool { for (&(ref secret, ref idx), &(ref o_secret, ref o_idx)) in self.old_secrets.iter().zip(other.old_secrets.iter()) { @@ -285,7 +346,7 @@ pub fn derive_public_key(secp_ctx: &Secp256k1, per_com /// Derives a per-commitment-transaction revocation key from its constituent parts. /// -/// Only the cheating participant owns a valid witness to propagate a revoked +/// Only the cheating participant owns a valid witness to propagate a revoked /// commitment transaction, thus per_commitment_secret always come from cheater /// and revocation_base_secret always come from punisher, which is the broadcaster /// of the transaction spending with this key knowledge. @@ -320,7 +381,7 @@ pub fn derive_private_revocation_key(secp_ctx: &Secp256k1 /// the public equivalend of derive_private_revocation_key - using only public keys to derive a /// public key instead of private keys. /// -/// Only the cheating participant owns a valid witness to propagate a revoked +/// Only the cheating participant owns a valid witness to propagate a revoked /// commitment transaction, thus per_commitment_point always come from cheater /// and revocation_base_point always come from punisher, which is the broadcaster /// of the transaction spending with this key knowledge. @@ -359,7 +420,7 @@ pub fn derive_public_revocation_key(secp_ctx: &Secp2 /// channel basepoints via the new function, or they were obtained via /// CommitmentTransaction.trust().keys() because we trusted the source of the /// pre-calculated keys. -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Eq, Clone)] pub struct TxCreationKeys { /// The broadcaster's per-commitment public key which was used to derive the other keys. pub per_commitment_point: PublicKey, @@ -384,7 +445,7 @@ impl_writeable_tlv_based!(TxCreationKeys, { }); /// One counterparty's public keys which do not change over the life of a channel. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] pub struct ChannelPublicKeys { /// The public key which is used to sign all commitment transactions, as it appears in the /// on-chain channel lock-in 2-of-2 multisig output. @@ -465,8 +526,8 @@ pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u1 res } -#[derive(Clone, PartialEq)] /// Information about an HTLC as it appears in a commitment transaction +#[derive(Clone, Debug, PartialEq, Eq)] pub struct HTLCOutputInCommitment { /// Whether the HTLC was "offered" (ie outbound in relation to this commitment transaction). /// Note that this is not the same as whether it is ountbound *from us*. To determine that you @@ -599,7 +660,7 @@ pub fn make_funding_redeemscript(broadcaster: &PublicKey, countersignatory: &Pub /// /// Panics if htlc.transaction_output_index.is_none() (as such HTLCs do not appear in the /// commitment transaction). -pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, opt_anchors: bool, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction { +pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, opt_anchors: bool, use_non_zero_fee_anchors: bool, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction { let mut txins: Vec = Vec::new(); txins.push(TxIn { previous_output: OutPoint { @@ -616,12 +677,17 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte } else { htlc_success_tx_weight(opt_anchors) }; - let total_fee = feerate_per_kw as u64 * weight / 1000; + let output_value = if opt_anchors && !use_non_zero_fee_anchors { + htlc.amount_msat / 1000 + } else { + let total_fee = feerate_per_kw as u64 * weight / 1000; + htlc.amount_msat / 1000 - total_fee + }; let mut txouts: Vec = Vec::new(); txouts.push(TxOut { script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_v0_p2wsh(), - value: htlc.amount_msat / 1000 - total_fee //TODO: BOLT 3 does not specify if we should add amount_msat before dividing or if we should divide by 1000 before subtracting (as we do here) + value: output_value, }); Transaction { @@ -661,6 +727,23 @@ pub fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> Script { .into_script() } +#[cfg(anchors)] +/// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`. +pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> { + let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_v0_p2wsh(); + commitment_tx.output.iter().enumerate() + .find(|(_, txout)| txout.script_pubkey == anchor_script) + .map(|(idx, txout)| (idx as u32, txout)) +} + +/// Returns the witness required to satisfy and spend an anchor input. +pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness { + let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key); + let mut funding_sig = funding_sig.serialize_der().to_vec(); + funding_sig.push(EcdsaSighashType::All as u8); + Witness::from_vec(vec![funding_sig, anchor_redeem_script.to_bytes()]) +} + /// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction). /// The fields are organized by holder/counterparty. /// @@ -680,8 +763,13 @@ pub struct ChannelTransactionParameters { pub counterparty_parameters: Option, /// The late-bound funding outpoint pub funding_outpoint: Option, - /// Are anchors used for this channel. Boolean is serialization backwards-compatible - pub opt_anchors: Option<()> + /// Are anchors (zero fee HTLC transaction variant) used for this channel. Boolean is + /// serialization backwards-compatible. + pub opt_anchors: Option<()>, + /// Are non-zero-fee anchors are enabled (used in conjuction with opt_anchors) + /// It is intended merely for backwards compatibility with signers that need it. + /// There is no support for this feature in LDK channel negotiation. + pub opt_non_zero_fee_anchors: Option<()>, } /// Late-bound per-channel counterparty data used to build transactions. @@ -736,6 +824,7 @@ impl_writeable_tlv_based!(ChannelTransactionParameters, { (6, counterparty_parameters, option), (8, funding_outpoint, option), (10, opt_anchors, option), + (12, opt_non_zero_fee_anchors, option), }); /// Static channel fields used to build transactions given per-commitment fields, organized by @@ -816,6 +905,7 @@ impl Deref for HolderCommitmentTransaction { fn deref(&self) -> &Self::Target { &self.inner } } +impl Eq for HolderCommitmentTransaction {} impl PartialEq for HolderCommitmentTransaction { // We dont care whether we are signed in equality comparison fn eq(&self, o: &Self) -> bool { @@ -857,7 +947,8 @@ impl HolderCommitmentTransaction { is_outbound_from_holder: false, counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: channel_pubkeys.clone(), selected_contest_delay: 0 }), funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }), - opt_anchors: None + opt_anchors: None, + opt_non_zero_fee_anchors: None, }; let mut htlcs_with_aux: Vec<(_, ())> = Vec::new(); let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, false, dummy_key.clone(), dummy_key.clone(), keys, 0, &mut htlcs_with_aux, &channel_parameters.as_counterparty_broadcastable()); @@ -941,7 +1032,7 @@ impl BuiltCommitmentTransaction { /// /// This class can be used inside a signer implementation to generate a signature given the relevant /// secret key. -#[derive(Clone, Hash, PartialEq)] +#[derive(Clone, Hash, PartialEq, Eq)] pub struct ClosingTransaction { to_holder_value_sat: u64, to_counterparty_value_sat: u64, @@ -1075,12 +1166,15 @@ pub struct CommitmentTransaction { htlcs: Vec, // A boolean that is serialization backwards-compatible opt_anchors: Option<()>, + // Whether non-zero-fee anchors should be used + opt_non_zero_fee_anchors: Option<()>, // A cache of the parties' pubkeys required to construct the transaction, see doc for trust() keys: TxCreationKeys, // For access to the pre-built transaction, see doc for trust() built: BuiltCommitmentTransaction, } +impl Eq for CommitmentTransaction {} impl PartialEq for CommitmentTransaction { fn eq(&self, o: &Self) -> bool { let eq = self.commitment_number == o.commitment_number && @@ -1107,6 +1201,7 @@ impl_writeable_tlv_based!(CommitmentTransaction, { (10, built, required), (12, htlcs, vec_type), (14, opt_anchors, option), + (16, opt_non_zero_fee_anchors, option), }); impl CommitmentTransaction { @@ -1139,9 +1234,18 @@ impl CommitmentTransaction { transaction, txid }, + opt_non_zero_fee_anchors: None, } } + /// Use non-zero fee anchors + /// + /// (C-not exported) due to move, and also not likely to be useful for binding users + pub fn with_non_zero_fee_anchors(mut self) -> Self { + self.opt_non_zero_fee_anchors = Some(()); + self + } + fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result { let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters); @@ -1406,7 +1510,7 @@ impl<'a> TrustedCommitmentTransaction<'a> { for this_htlc in inner.htlcs.iter() { assert!(this_htlc.transaction_output_index.is_some()); - let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); + let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, self.opt_anchors(), self.opt_non_zero_fee_anchors.is_some(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key); @@ -1428,7 +1532,7 @@ impl<'a> TrustedCommitmentTransaction<'a> { // Further, we should never be provided the preimage for an HTLC-Timeout transaction. if this_htlc.offered && preimage.is_some() { unreachable!(); } - let mut htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); + let mut htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, self.opt_anchors(), self.opt_non_zero_fee_anchors.is_some(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key); @@ -1495,15 +1599,15 @@ fn get_p2wpkh_redeemscript(key: &PublicKey) -> Script { #[cfg(test)] mod tests { use super::CounterpartyCommitmentSecrets; - use ::{hex, chain}; - use prelude::*; - use ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, get_p2wpkh_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment}; + use crate::{hex, chain}; + use crate::prelude::*; + use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, get_p2wpkh_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment}; use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1}; - use util::test_utils; - use chain::keysinterface::{KeysInterface, BaseSign}; + use crate::util::test_utils; + use crate::chain::keysinterface::{KeysInterface, BaseSign}; use bitcoin::{Network, Txid}; use bitcoin::hashes::Hash; - use ln::PaymentHash; + use crate::ln::PaymentHash; use bitcoin::hashes::hex::ToHex; #[test] @@ -1528,7 +1632,8 @@ mod tests { is_outbound_from_holder: false, counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }), funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }), - opt_anchors: None + opt_anchors: None, + opt_non_zero_fee_anchors: None, }; let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();