X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchan_utils.rs;h=18048d8efd87a50fea85cd037f375148b0f916c3;hb=d9eb201bd8da97e9f249c793abeb9cbdb00f4744;hp=acf9539d7c6668c95c09cb0fca6d1f70fd58a1f7;hpb=963d6c4a51bdbbd1598d85d4657fe9f8d81f3cb5;p=rust-lightning diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index acf9539d..18048d8e 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -8,7 +8,7 @@ // licenses. //! Various utilities for building scripts and deriving keys related to channels. These are -//! largely of interest for those implementing the traits on [`chain::keysinterface`] by hand. +//! largely of interest for those implementing the traits on [`crate::sign`] by hand. use bitcoin::blockdata::script::{Script,Builder}; use bitcoin::blockdata::opcodes; @@ -21,9 +21,12 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::ripemd160::Hash as Ripemd160; use bitcoin::hash_types::{Txid, PubkeyHash}; +use crate::chain::chaininterface::fee_for_weight; +use crate::chain::package::WEIGHT_REVOKED_OUTPUT; +use crate::sign::EntropySource; use crate::ln::{PaymentHash, PaymentPreimage}; use crate::ln::msgs::DecodeError; -use crate::util::ser::{Readable, Writeable, Writer}; +use crate::util::ser::{Readable, RequiredWrapper, Writeable, Writer}; use crate::util::transaction_utils; use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar}; @@ -39,7 +42,8 @@ use crate::util::transaction_utils::sort_outputs; use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI}; use core::ops::Deref; use crate::chain; -use crate::util::crypto::sign; +use crate::ln::features::ChannelTypeFeatures; +use crate::util::crypto::{sign, sign_with_aux_rand}; /// Maximum number of one-way in-flight HTLC (protocol-level value). pub const MAX_HTLCS: u16 = 483; @@ -56,20 +60,29 @@ pub(crate) const MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 136; /// This is the maximum post-anchor value. pub const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143; +/// The upper bound weight of an anchor input. +pub const ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 116; +/// The upper bound weight of an HTLC timeout input from a commitment transaction with anchor +/// outputs. +pub const HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT: u64 = 288; +/// The upper bound weight of an HTLC success input from a commitment transaction with anchor +/// outputs. +pub const HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT: u64 = 327; + /// Gets the weight for an HTLC-Success transaction. #[inline] -pub fn htlc_success_tx_weight(opt_anchors: bool) -> u64 { +pub fn htlc_success_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u64 { const HTLC_SUCCESS_TX_WEIGHT: u64 = 703; const HTLC_SUCCESS_ANCHOR_TX_WEIGHT: u64 = 706; - if opt_anchors { HTLC_SUCCESS_ANCHOR_TX_WEIGHT } else { HTLC_SUCCESS_TX_WEIGHT } + if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_SUCCESS_ANCHOR_TX_WEIGHT } else { HTLC_SUCCESS_TX_WEIGHT } } /// Gets the weight for an HTLC-Timeout transaction. #[inline] -pub fn htlc_timeout_tx_weight(opt_anchors: bool) -> u64 { +pub fn htlc_timeout_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u64 { const HTLC_TIMEOUT_TX_WEIGHT: u64 = 663; const HTLC_TIMEOUT_ANCHOR_TX_WEIGHT: u64 = 666; - if opt_anchors { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT } + if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT } } /// Describes the type of HTLC claim as determined by analyzing the witness. @@ -573,7 +586,7 @@ impl_writeable_tlv_based!(HTLCOutputInCommitment, { }); #[inline] -pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, opt_anchors: bool, broadcaster_htlc_key: &PublicKey, countersignatory_htlc_key: &PublicKey, revocation_key: &PublicKey) -> Script { +pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_htlc_key: &PublicKey, countersignatory_htlc_key: &PublicKey, revocation_key: &PublicKey) -> Script { let payment_hash160 = Ripemd160::hash(&htlc.payment_hash.0[..]).into_inner(); if htlc.offered { let mut bldr = Builder::new().push_opcode(opcodes::all::OP_DUP) @@ -601,7 +614,7 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ENDIF); - if opt_anchors { + if channel_type_features.supports_anchors_zero_fee_htlc_tx() { bldr = bldr.push_opcode(opcodes::all::OP_PUSHNUM_1) .push_opcode(opcodes::all::OP_CSV) .push_opcode(opcodes::all::OP_DROP); @@ -637,7 +650,7 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit .push_opcode(opcodes::all::OP_DROP) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ENDIF); - if opt_anchors { + if channel_type_features.supports_anchors_zero_fee_htlc_tx() { bldr = bldr.push_opcode(opcodes::all::OP_PUSHNUM_1) .push_opcode(opcodes::all::OP_CSV) .push_opcode(opcodes::all::OP_DROP); @@ -650,8 +663,8 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit /// Gets the witness redeemscript for an HTLC output in a commitment transaction. Note that htlc /// does not need to have its previous_output_index filled. #[inline] -pub fn get_htlc_redeemscript(htlc: &HTLCOutputInCommitment, opt_anchors: bool, keys: &TxCreationKeys) -> Script { - get_htlc_redeemscript_with_explicit_keys(htlc, opt_anchors, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key) +pub fn get_htlc_redeemscript(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, keys: &TxCreationKeys) -> Script { + get_htlc_redeemscript_with_explicit_keys(htlc, channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key) } /// Gets the redeemscript for a funding output from the two funding public keys. @@ -681,13 +694,13 @@ pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u /// /// 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, use_non_zero_fee_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, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction { let mut txins: Vec = Vec::new(); - txins.push(build_htlc_input(commitment_txid, htlc, opt_anchors)); + txins.push(build_htlc_input(commitment_txid, htlc, channel_type_features)); let mut txouts: Vec = Vec::new(); txouts.push(build_htlc_output( - feerate_per_kw, contest_delay, htlc, opt_anchors, use_non_zero_fee_anchors, + feerate_per_kw, contest_delay, htlc, channel_type_features, broadcaster_delayed_payment_key, revocation_key )); @@ -699,28 +712,27 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte } } -pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommitment, opt_anchors: bool) -> TxIn { +pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures) -> TxIn { TxIn { previous_output: OutPoint { txid: commitment_txid.clone(), vout: htlc.transaction_output_index.expect("Can't build an HTLC transaction for a dust output"), }, script_sig: Script::new(), - sequence: Sequence(if opt_anchors { 1 } else { 0 }), + sequence: Sequence(if channel_type_features.supports_anchors_zero_fee_htlc_tx() { 1 } else { 0 }), witness: Witness::new(), } } pub(crate) fn build_htlc_output( - 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 + feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey ) -> TxOut { let weight = if htlc.offered { - htlc_timeout_tx_weight(opt_anchors) + htlc_timeout_tx_weight(channel_type_features) } else { - htlc_success_tx_weight(opt_anchors) + htlc_success_tx_weight(channel_type_features) }; - let output_value = if opt_anchors && !use_non_zero_fee_anchors { + let output_value = if channel_type_features.supports_anchors_zero_fee_htlc_tx() && !channel_type_features.supports_anchors_nonzero_fee_htlc_tx() { htlc.amount_msat / 1000 } else { let total_fee = feerate_per_kw as u64 * weight / 1000; @@ -736,9 +748,9 @@ pub(crate) fn build_htlc_output( /// Returns the witness required to satisfy and spend a HTLC input. pub fn build_htlc_input_witness( local_sig: &Signature, remote_sig: &Signature, preimage: &Option, - redeem_script: &Script, opt_anchors: bool, + redeem_script: &Script, channel_type_features: &ChannelTypeFeatures, ) -> Witness { - let remote_sighash_type = if opt_anchors { + let remote_sighash_type = if channel_type_features.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All @@ -759,6 +771,37 @@ pub fn build_htlc_input_witness( witness } +/// Pre-anchors channel type features did not use to get serialized in the following six structs: +/// — [`ChannelTransactionParameters`] +/// — [`CommitmentTransaction`] +/// — [`CounterpartyOfferedHTLCOutput`] +/// — [`CounterpartyReceivedHTLCOutput`] +/// — [`HolderHTLCOutput`] +/// — [`HolderFundingOutput`] +/// +/// To ensure a forwards-compatible serialization, we use odd TLV fields. However, if new features +/// are used that could break security, where old signers should be prevented from handling the +/// serialized data, an optional even-field TLV will be used as a stand-in to break compatibility. +/// +/// This method determines whether or not that option needs to be set based on the chanenl type +/// features, and returns it. +/// +/// [`CounterpartyOfferedHTLCOutput`]: crate::chain::package::CounterpartyOfferedHTLCOutput +/// [`CounterpartyReceivedHTLCOutput`]: crate::chain::package::CounterpartyReceivedHTLCOutput +/// [`HolderHTLCOutput`]: crate::chain::package::HolderHTLCOutput +/// [`HolderFundingOutput`]: crate::chain::package::HolderFundingOutput +pub(crate) fn legacy_deserialization_prevention_marker_for_channel_type_features(features: &ChannelTypeFeatures) -> Option<()> { + let mut legacy_version_bit_set = ChannelTypeFeatures::only_static_remote_key(); + legacy_version_bit_set.set_scid_privacy_required(); + legacy_version_bit_set.set_zero_conf_required(); + + if features.is_subset(&legacy_version_bit_set) { + None + } else { + Some(()) + } +} + /// Gets the witnessScript for the to_remote output when anchors are enabled. #[inline] pub fn get_to_countersignatory_with_anchors_redeemscript(payment_point: &PublicKey) -> Script { @@ -788,7 +831,6 @@ 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(); @@ -811,7 +853,7 @@ pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signatu /// /// Normally, this is converted to the broadcaster/countersignatory-organized DirectedChannelTransactionParameters /// before use, via the as_holder_broadcastable and as_counterparty_broadcastable functions. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ChannelTransactionParameters { /// Holder public keys pub holder_pubkeys: ChannelPublicKeys, @@ -825,17 +867,13 @@ pub struct ChannelTransactionParameters { pub counterparty_parameters: Option, /// The late-bound funding outpoint pub funding_outpoint: 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<()>, + /// This channel's type, as negotiated during channel open. For old objects where this field + /// wasn't serialized, it will default to static_remote_key at deserialization. + pub channel_type_features: ChannelTypeFeatures } /// Late-bound per-channel counterparty data used to build transactions. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct CounterpartyChannelTransactionParameters { /// Counter-party public keys pub pubkeys: ChannelPublicKeys, @@ -879,15 +917,56 @@ impl_writeable_tlv_based!(CounterpartyChannelTransactionParameters, { (2, selected_contest_delay, required), }); -impl_writeable_tlv_based!(ChannelTransactionParameters, { - (0, holder_pubkeys, required), - (2, holder_selected_contest_delay, required), - (4, is_outbound_from_holder, required), - (6, counterparty_parameters, option), - (8, funding_outpoint, option), - (10, opt_anchors, option), - (12, opt_non_zero_fee_anchors, option), -}); +impl Writeable for ChannelTransactionParameters { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features); + write_tlv_fields!(writer, { + (0, self.holder_pubkeys, required), + (2, self.holder_selected_contest_delay, required), + (4, self.is_outbound_from_holder, required), + (6, self.counterparty_parameters, option), + (8, self.funding_outpoint, option), + (10, legacy_deserialization_prevention_marker, option), + (11, self.channel_type_features, required), + }); + Ok(()) + } +} + +impl Readable for ChannelTransactionParameters { + fn read(reader: &mut R) -> Result { + let mut holder_pubkeys = RequiredWrapper(None); + let mut holder_selected_contest_delay = RequiredWrapper(None); + let mut is_outbound_from_holder = RequiredWrapper(None); + let mut counterparty_parameters = None; + let mut funding_outpoint = None; + let mut _legacy_deserialization_prevention_marker: Option<()> = None; + let mut channel_type_features = None; + + read_tlv_fields!(reader, { + (0, holder_pubkeys, required), + (2, holder_selected_contest_delay, required), + (4, is_outbound_from_holder, required), + (6, counterparty_parameters, option), + (8, funding_outpoint, option), + (10, _legacy_deserialization_prevention_marker, option), + (11, channel_type_features, option), + }); + + let mut additional_features = ChannelTypeFeatures::empty(); + additional_features.set_anchors_nonzero_fee_htlc_tx_required(); + chain::package::verify_channel_type_features(&channel_type_features, Some(&additional_features))?; + + Ok(Self { + holder_pubkeys: holder_pubkeys.0.unwrap(), + holder_selected_contest_delay: holder_selected_contest_delay.0.unwrap(), + is_outbound_from_holder: is_outbound_from_holder.0.unwrap(), + counterparty_parameters, + funding_outpoint, + channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()) + }) + } +} /// Static channel fields used to build transactions given per-commitment fields, organized by /// broadcaster/countersignatory. @@ -941,8 +1020,8 @@ impl<'a> DirectedChannelTransactionParameters<'a> { } /// Whether to use anchors for this channel - pub fn opt_anchors(&self) -> bool { - self.inner.opt_anchors.is_some() + pub fn channel_type_features(&self) -> &ChannelTypeFeatures { + &self.inner.channel_type_features } } @@ -979,12 +1058,12 @@ impl_writeable_tlv_based!(HolderCommitmentTransaction, { (0, inner, required), (2, counterparty_sig, required), (4, holder_sig_first, required), - (6, counterparty_htlc_sigs, vec_type), + (6, counterparty_htlc_sigs, required_vec), }); impl HolderCommitmentTransaction { #[cfg(test)] - pub fn dummy() -> Self { + pub fn dummy(htlcs: &mut Vec<(HTLCOutputInCommitment, ())>) -> Self { let secp_ctx = Secp256k1::new(); let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap()); @@ -1009,15 +1088,18 @@ 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_non_zero_fee_anchors: None, + channel_type_features: ChannelTypeFeatures::only_static_remote_key(), }; - 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()); + let mut counterparty_htlc_sigs = Vec::new(); + for _ in 0..htlcs.len() { + counterparty_htlc_sigs.push(dummy_sig); + } + let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, dummy_key.clone(), dummy_key.clone(), keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable()); + htlcs.sort_by_key(|htlc| htlc.0.transaction_output_index); HolderCommitmentTransaction { inner, counterparty_sig: dummy_sig, - counterparty_htlc_sigs: Vec::new(), + counterparty_htlc_sigs, holder_sig_first: false } } @@ -1077,12 +1159,20 @@ impl BuiltCommitmentTransaction { hash_to_message!(sighash) } - /// Sign a transaction, either because we are counter-signing the counterparty's transaction or - /// because we are about to broadcast a holder transaction. - pub fn sign(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1) -> Signature { + /// Signs the counterparty's commitment transaction. + pub fn sign_counterparty_commitment(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1) -> Signature { let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis); sign(secp_ctx, &sighash, funding_key) } + + /// Signs the holder commitment transaction because we are about to broadcast it. + pub fn sign_holder_commitment( + &self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, + entropy_source: &ES, secp_ctx: &Secp256k1 + ) -> Signature where ES::Target: EntropySource { + let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis); + sign_with_aux_rand(secp_ctx, &sighash, funding_key, entropy_source) + } } /// This class tracks the per-transaction information needed to build a closing transaction and will @@ -1220,12 +1310,11 @@ pub struct CommitmentTransaction { commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, + to_broadcaster_delay: Option, // Added in 0.0.117 feerate_per_kw: u32, 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<()>, + // Note that on upgrades, some features of existing outputs may be missed. + channel_type_features: ChannelTypeFeatures, // 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() @@ -1240,7 +1329,7 @@ impl PartialEq for CommitmentTransaction { self.to_countersignatory_value_sat == o.to_countersignatory_value_sat && self.feerate_per_kw == o.feerate_per_kw && self.htlcs == o.htlcs && - self.opt_anchors == o.opt_anchors && + self.channel_type_features == o.channel_type_features && self.keys == o.keys; if eq { debug_assert_eq!(self.built.transaction, o.built.transaction); @@ -1250,17 +1339,57 @@ impl PartialEq for CommitmentTransaction { } } -impl_writeable_tlv_based!(CommitmentTransaction, { - (0, commitment_number, required), - (2, to_broadcaster_value_sat, required), - (4, to_countersignatory_value_sat, required), - (6, feerate_per_kw, required), - (8, keys, required), - (10, built, required), - (12, htlcs, vec_type), - (14, opt_anchors, option), - (16, opt_non_zero_fee_anchors, option), -}); +impl Writeable for CommitmentTransaction { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features); + write_tlv_fields!(writer, { + (0, self.commitment_number, required), + (1, self.to_broadcaster_delay, option), + (2, self.to_broadcaster_value_sat, required), + (4, self.to_countersignatory_value_sat, required), + (6, self.feerate_per_kw, required), + (8, self.keys, required), + (10, self.built, required), + (12, self.htlcs, required_vec), + (14, legacy_deserialization_prevention_marker, option), + (15, self.channel_type_features, required), + }); + Ok(()) + } +} + +impl Readable for CommitmentTransaction { + fn read(reader: &mut R) -> Result { + _init_and_read_len_prefixed_tlv_fields!(reader, { + (0, commitment_number, required), + (1, to_broadcaster_delay, option), + (2, to_broadcaster_value_sat, required), + (4, to_countersignatory_value_sat, required), + (6, feerate_per_kw, required), + (8, keys, required), + (10, built, required), + (12, htlcs, required_vec), + (14, _legacy_deserialization_prevention_marker, option), + (15, channel_type_features, option), + }); + + let mut additional_features = ChannelTypeFeatures::empty(); + additional_features.set_anchors_nonzero_fee_htlc_tx_required(); + chain::package::verify_channel_type_features(&channel_type_features, Some(&additional_features))?; + + Ok(Self { + commitment_number: commitment_number.0.unwrap(), + to_broadcaster_value_sat: to_broadcaster_value_sat.0.unwrap(), + to_countersignatory_value_sat: to_countersignatory_value_sat.0.unwrap(), + to_broadcaster_delay, + feerate_per_kw: feerate_per_kw.0.unwrap(), + keys: keys.0.unwrap(), + built: built.0.unwrap(), + htlcs, + channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()) + }) + } +} impl CommitmentTransaction { /// Construct an object of the class while assigning transaction output indices to HTLCs. @@ -1272,10 +1401,10 @@ impl CommitmentTransaction { /// /// Only include HTLCs that are above the dust limit for the channel. /// - /// (C-not exported) due to the generic though we likely should expose a version without - pub fn new_with_auxiliary_htlc_data(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, opt_anchors: bool, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction { + /// This is not exported to bindings users due to the generic though we likely should expose a version without + pub fn new_with_auxiliary_htlc_data(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction { // Sort outputs and populate output indices while keeping track of the auxiliary data - let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, opt_anchors, &broadcaster_funding_key, &countersignatory_funding_key).unwrap(); + let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap(); let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters); let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs); @@ -1284,23 +1413,23 @@ impl CommitmentTransaction { commitment_number, to_broadcaster_value_sat, to_countersignatory_value_sat, + to_broadcaster_delay: Some(channel_parameters.contest_delay()), feerate_per_kw, htlcs, - opt_anchors: if opt_anchors { Some(()) } else { None }, + channel_type_features: channel_parameters.channel_type_features().clone(), keys, built: BuiltCommitmentTransaction { 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 + /// This is not exported to bindings users 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.channel_type_features.set_anchors_nonzero_fee_htlc_tx_required(); self } @@ -1308,7 +1437,7 @@ impl CommitmentTransaction { let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters); let mut htlcs_with_aux = self.htlcs.iter().map(|h| (h.clone(), ())).collect(); - let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, self.opt_anchors.is_some(), broadcaster_funding_key, countersignatory_funding_key)?; + let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key)?; let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs); let txid = transaction.txid(); @@ -1332,14 +1461,14 @@ impl CommitmentTransaction { // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the // caller needs to have sorted together with the HTLCs so it can keep track of the output index // - building of a bitcoin transaction during a verify() call, in which case T is just () - fn internal_build_outputs(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, opt_anchors: bool, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec, Vec), ()> { + fn internal_build_outputs(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec, Vec), ()> { let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys(); let contest_delay = channel_parameters.contest_delay(); let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new(); if to_countersignatory_value_sat > 0 { - let script = if opt_anchors { + let script = if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_v0_p2wsh() } else { Payload::p2wpkh(&BitcoinPublicKey::new(countersignatory_pubkeys.payment_point)).unwrap().script_pubkey() @@ -1368,7 +1497,7 @@ impl CommitmentTransaction { )); } - if opt_anchors { + if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { if to_broadcaster_value_sat > 0 || !htlcs_with_aux.is_empty() { let anchor_script = get_anchor_redeemscript(broadcaster_funding_key); txouts.push(( @@ -1394,7 +1523,7 @@ impl CommitmentTransaction { let mut htlcs = Vec::with_capacity(htlcs_with_aux.len()); for (htlc, _) in htlcs_with_aux { - let script = chan_utils::get_htlc_redeemscript(&htlc, opt_anchors, &keys); + let script = chan_utils::get_htlc_redeemscript(&htlc, &channel_parameters.channel_type_features(), &keys); let txout = TxOut { script_pubkey: script.to_v0_p2wsh(), value: htlc.amount_msat / 1000, @@ -1479,7 +1608,7 @@ impl CommitmentTransaction { /// which were included in this commitment transaction in output order. /// The transaction index is always populated. /// - /// (C-not exported) as we cannot currently convert Vec references to/from C, though we should + /// This is not exported to bindings users as we cannot currently convert Vec references to/from C, though we should /// expose a less effecient version which creates a Vec of references in the future. pub fn htlcs(&self) -> &Vec { &self.htlcs @@ -1549,8 +1678,8 @@ impl<'a> TrustedCommitmentTransaction<'a> { } /// Should anchors be used. - pub fn opt_anchors(&self) -> bool { - self.opt_anchors.is_some() + pub fn channel_type_features(&self) -> &ChannelTypeFeatures { + &self.inner.channel_type_features } /// Get a signature for each HTLC which was included in the commitment transaction (ie for @@ -1559,7 +1688,10 @@ impl<'a> TrustedCommitmentTransaction<'a> { /// The returned Vec has one entry for each HTLC, and in the same order. /// /// This function is only valid in the holder commitment context, it always uses EcdsaSighashType::All. - pub fn get_htlc_sigs(&self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1) -> Result, ()> { + pub fn get_htlc_sigs( + &self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters, + entropy_source: &ES, secp_ctx: &Secp256k1, + ) -> Result, ()> where ES::Target: EntropySource { let inner = self.inner; let keys = &inner.keys; let txid = inner.built.txid; @@ -1568,12 +1700,12 @@ 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(), self.opt_non_zero_fee_anchors.is_some(), &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.channel_type_features, &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); + let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key); let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, EcdsaSighashType::All).unwrap()[..]); - ret.push(sign(secp_ctx, &sighash, &holder_htlc_key)); + ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source)); } Ok(ret) } @@ -1590,15 +1722,78 @@ 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(), self.opt_non_zero_fee_anchors.is_some(), &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.channel_type_features, &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); + let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key); htlc_tx.input[0].witness = chan_utils::build_htlc_input_witness( - signature, counterparty_signature, preimage, &htlc_redeemscript, self.opt_anchors(), + signature, counterparty_signature, preimage, &htlc_redeemscript, &self.channel_type_features, ); htlc_tx } + + /// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to + /// the broadcaster, in the built transaction, if any exists. + /// + /// There are two cases where this may return `None`: + /// - The balance of the revokeable output is below the dust limit (only found on commitments + /// early in the channel's lifetime, i.e. before the channel reserve is met). + /// - This commitment was created before LDK 0.0.117. In this case, the + /// commitment transaction previously didn't contain enough information to locate the + /// revokeable output. + pub fn revokeable_output_index(&self) -> Option { + let revokeable_redeemscript = get_revokeable_redeemscript( + &self.keys.revocation_key, + self.to_broadcaster_delay?, + &self.keys.broadcaster_delayed_payment_key, + ); + let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); + let outputs = &self.inner.built.transaction.output; + outputs.iter().enumerate() + .find(|(_, out)| out.script_pubkey == revokeable_p2wsh) + .map(|(idx, _)| idx) + } + + /// Helper method to build an unsigned justice transaction spending the revokeable + /// `to_local` output to a destination script. Fee estimation accounts for the expected + /// revocation witness data that will be added when signed. + /// + /// This method will error if the given fee rate results in a fee greater than the value + /// of the output being spent, or if there exists no revokeable `to_local` output on this + /// commitment transaction. See [`Self::revokeable_output_index`] for more details. + /// + /// The built transaction will allow fee bumping with RBF, and this method takes + /// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different + /// fee rates may be built. + pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script) + -> Result { + let output_idx = self.revokeable_output_index().ok_or(())?; + let input = vec![TxIn { + previous_output: OutPoint { + txid: self.trust().txid(), + vout: output_idx as u32, + }, + script_sig: Script::new(), + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + witness: Witness::new(), + }]; + let value = self.inner.built.transaction.output[output_idx].value; + let output = vec![TxOut { + script_pubkey: destination_script, + value, + }]; + let mut justice_tx = Transaction { + version: 2, + lock_time: PackedLockTime::ZERO, + input, + output, + }; + let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT; + let fee = fee_for_weight(feerate_per_kw as u32, weight); + justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?; + Ok(justice_tx) + } + } /// Commitment transaction numbers which appear in the transactions themselves are XOR'd with a @@ -1633,92 +1828,101 @@ pub fn get_commitment_transaction_number_obscure_factor( #[cfg(test)] mod tests { - use super::CounterpartyCommitmentSecrets; + use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys}; use crate::{hex, chain}; use crate::prelude::*; use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment}; use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1}; use crate::util::test_utils; - use crate::chain::keysinterface::{ChannelSigner, SignerProvider}; - use bitcoin::{Network, Txid}; + use crate::sign::{ChannelSigner, SignerProvider}; + use bitcoin::{Network, Txid, Script}; use bitcoin::hashes::Hash; use crate::ln::PaymentHash; use bitcoin::hashes::hex::ToHex; use bitcoin::util::address::Payload; use bitcoin::PublicKey as BitcoinPublicKey; + use crate::ln::features::ChannelTypeFeatures; + + struct TestCommitmentTxBuilder { + commitment_number: u64, + holder_funding_pubkey: PublicKey, + counterparty_funding_pubkey: PublicKey, + keys: TxCreationKeys, + feerate_per_kw: u32, + htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>, + channel_parameters: ChannelTransactionParameters, + counterparty_pubkeys: ChannelPublicKeys, + } + + impl TestCommitmentTxBuilder { + fn new() -> Self { + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0)); + let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1)); + let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint; + let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); + let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); + let htlc_basepoint = &signer.pubkeys().htlc_basepoint; + let holder_pubkeys = signer.pubkeys(); + let counterparty_pubkeys = counterparty_signer.pubkeys().clone(); + let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint); + let channel_parameters = ChannelTransactionParameters { + holder_pubkeys: holder_pubkeys.clone(), + holder_selected_contest_delay: 0, + 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 }), + channel_type_features: ChannelTypeFeatures::only_static_remote_key(), + }; + let htlcs_with_aux = Vec::new(); + + Self { + commitment_number: 0, + holder_funding_pubkey: holder_pubkeys.funding_pubkey, + counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey, + keys, + feerate_per_kw: 1, + htlcs_with_aux, + channel_parameters, + counterparty_pubkeys, + } + } + + fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction { + CommitmentTransaction::new_with_auxiliary_htlc_data( + self.commitment_number, to_broadcaster_sats, to_countersignatory_sats, + self.holder_funding_pubkey.clone(), + self.counterparty_funding_pubkey.clone(), + self.keys.clone(), self.feerate_per_kw, + &mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable() + ) + } + } #[test] fn test_anchors() { - let secp_ctx = Secp256k1::new(); - - let seed = [42; 32]; - let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0)); - let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1)); - let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint; - let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); - let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); - let htlc_basepoint = &signer.pubkeys().htlc_basepoint; - let holder_pubkeys = signer.pubkeys(); - let counterparty_pubkeys = counterparty_signer.pubkeys(); - let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint); - let mut channel_parameters = ChannelTransactionParameters { - holder_pubkeys: holder_pubkeys.clone(), - holder_selected_contest_delay: 0, - 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_non_zero_fee_anchors: None, - }; - - let mut htlcs_with_aux: Vec<(_, ())> = Vec::new(); + let mut builder = TestCommitmentTxBuilder::new(); // Generate broadcaster and counterparty outputs - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 1000, 2000, - false, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable() - ); + let tx = builder.build(1000, 2000); assert_eq!(tx.built.transaction.output.len(), 2); - assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(counterparty_pubkeys.payment_point)).unwrap().script_pubkey()); + assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey()); // Generate broadcaster and counterparty outputs as well as two anchors - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 1000, 2000, - true, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable() - ); + builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + let tx = builder.build(1000, 2000); assert_eq!(tx.built.transaction.output.len(), 4); - assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&counterparty_pubkeys.payment_point).to_v0_p2wsh()); + assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh()); // Generate broadcaster output and anchor - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 3000, 0, - true, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable() - ); + let tx = builder.build(3000, 0); assert_eq!(tx.built.transaction.output.len(), 2); // Generate counterparty output and anchor - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 0, 3000, - true, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable() - ); + let tx = builder.build(0, 3000); assert_eq!(tx.built.transaction.output.len(), 2); let received_htlc = HTLCOutputInCommitment { @@ -1738,43 +1942,86 @@ mod tests { }; // Generate broadcaster output and received and offered HTLC outputs, w/o anchors - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 3000, 0, - false, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())], - &channel_parameters.as_holder_broadcastable() - ); + builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key(); + builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())]; + let tx = builder.build(3000, 0); + let keys = &builder.keys.clone(); assert_eq!(tx.built.transaction.output.len(), 3); - assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, false, &keys).to_v0_p2wsh()); - assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, false, &keys).to_v0_p2wsh()); - assert_eq!(get_htlc_redeemscript(&received_htlc, false, &keys).to_v0_p2wsh().to_hex(), + assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh()); + assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh()); + assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex(), "0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb"); - assert_eq!(get_htlc_redeemscript(&offered_htlc, false, &keys).to_v0_p2wsh().to_hex(), + assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex(), "0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d"); // Generate broadcaster output and received and offered HTLC outputs, with anchors - channel_parameters.opt_anchors = Some(()); - let tx = CommitmentTransaction::new_with_auxiliary_htlc_data( - 0, 3000, 0, - true, - holder_pubkeys.funding_pubkey, - counterparty_pubkeys.funding_pubkey, - keys.clone(), 1, - &mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())], - &channel_parameters.as_holder_broadcastable() - ); + builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())]; + let tx = builder.build(3000, 0); assert_eq!(tx.built.transaction.output.len(), 5); - assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, true, &keys).to_v0_p2wsh()); - assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, true, &keys).to_v0_p2wsh()); - assert_eq!(get_htlc_redeemscript(&received_htlc, true, &keys).to_v0_p2wsh().to_hex(), + assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh()); + assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh()); + assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex(), "0020b70d0649c72b38756885c7a30908d912a7898dd5d79457a7280b8e9a20f3f2bc"); - assert_eq!(get_htlc_redeemscript(&offered_htlc, true, &keys).to_v0_p2wsh().to_hex(), + assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex(), "002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7"); } + #[test] + fn test_finding_revokeable_output_index() { + let mut builder = TestCommitmentTxBuilder::new(); + + // Revokeable output present + let tx = builder.build(1000, 2000); + assert_eq!(tx.built.transaction.output.len(), 2); + assert_eq!(tx.trust().revokeable_output_index(), Some(0)); + + // Revokeable output present (but to_broadcaster_delay missing) + let tx = CommitmentTransaction { to_broadcaster_delay: None, ..tx }; + assert_eq!(tx.built.transaction.output.len(), 2); + assert_eq!(tx.trust().revokeable_output_index(), None); + + // Revokeable output not present (our balance is dust) + let tx = builder.build(0, 2000); + assert_eq!(tx.built.transaction.output.len(), 1); + assert_eq!(tx.trust().revokeable_output_index(), None); + } + + #[test] + fn test_building_to_local_justice_tx() { + let mut builder = TestCommitmentTxBuilder::new(); + + // Revokeable output not present (our balance is dust) + let tx = builder.build(0, 2000); + assert_eq!(tx.built.transaction.output.len(), 1); + assert!(tx.trust().build_to_local_justice_tx(253, Script::new()).is_err()); + + // Revokeable output present + let tx = builder.build(1000, 2000); + assert_eq!(tx.built.transaction.output.len(), 2); + + // Too high feerate + assert!(tx.trust().build_to_local_justice_tx(100_000, Script::new()).is_err()); + + // Generate a random public key for destination script + let secret_key = SecretKey::from_slice( + &hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100") + .unwrap()[..]).unwrap(); + let pubkey_hash = BitcoinPublicKey::new( + PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap(); + let destination_script = Script::new_v0_p2wpkh(&pubkey_hash); + + let justice_tx = tx.trust().build_to_local_justice_tx(253, destination_script.clone()).unwrap(); + assert_eq!(justice_tx.input.len(), 1); + assert_eq!(justice_tx.input[0].previous_output.txid, tx.built.transaction.txid()); + assert_eq!(justice_tx.input[0].previous_output.vout, tx.trust().revokeable_output_index().unwrap() as u32); + assert!(justice_tx.input[0].sequence.is_rbf()); + + assert_eq!(justice_tx.output.len(), 1); + assert!(justice_tx.output[0].value < 1000); + assert_eq!(justice_tx.output[0].script_pubkey, destination_script); + } + #[test] fn test_per_commitment_storage() { // Test vectors from BOLT 3: