Add anchor outputs pair in CommitmentTransaction
authorDevrandom <c1.devrandom@niftybox.net>
Sun, 22 Aug 2021 09:08:28 +0000 (11:08 +0200)
committerDevrandom <c1.devrandom@niftybox.net>
Thu, 2 Sep 2021 07:13:46 +0000 (09:13 +0200)
The anchor ouputs pair is added if there are pending HTLCs. Or a
a per-party anchor is added if the party has a pending balance.

lightning/src/ln/chan_utils.rs
lightning/src/ln/channel.rs
lightning/src/ln/functional_tests.rs
lightning/src/util/ser.rs

index 982257b633673f05521d7788167059ebf60af0da..6bdef4307350d17c158ef2fe88dac121532db532 100644 (file)
@@ -36,7 +36,7 @@ use prelude::*;
 use core::cmp;
 use ln::chan_utils;
 use util::transaction_utils::sort_outputs;
-use ln::channel::INITIAL_COMMITMENT_NUMBER;
+use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
 use core::ops::Deref;
 use chain;
 
@@ -569,6 +569,7 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte
 /// <BIP 143 funding_signature>
 /// After 16 blocks of confirmation, an alternative satisfying witness could be:
 /// <>
+/// (empty vector required to satisfy compliance with MINIMALIF-standard rule)
 #[inline]
 pub(crate) fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> Script {
        Builder::new().push_slice(&funding_pubkey.serialize()[..])
@@ -771,7 +772,7 @@ impl HolderCommitmentTransaction {
                        funding_outpoint: Some(chain::transaction::OutPoint { txid: Default::default(), index: 0 })
                };
                let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
-               let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, keys, 0, &mut htlcs_with_aux, &channel_parameters.as_counterparty_broadcastable());
+               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());
                HolderCommitmentTransaction {
                        inner,
                        counterparty_sig: dummy_sig,
@@ -858,6 +859,8 @@ pub struct CommitmentTransaction {
        to_countersignatory_value_sat: u64,
        feerate_per_kw: u32,
        htlcs: Vec<HTLCOutputInCommitment>,
+       // A boolean that is serialization backwards-compatible
+       opt_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()
@@ -871,6 +874,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.keys == o.keys;
                if eq {
                        debug_assert_eq!(self.built.transaction, o.built.transaction);
@@ -888,6 +892,7 @@ impl_writeable_tlv_based!(CommitmentTransaction, {
        (8, keys, required),
        (10, built, required),
        (12, htlcs, vec_type),
+       (14, opt_anchors, option),
 });
 
 impl CommitmentTransaction {
@@ -901,9 +906,9 @@ 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<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
+       pub fn new_with_auxiliary_htlc_data<T>(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 {
                // 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).unwrap();
+               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 (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters);
                let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
@@ -914,6 +919,7 @@ impl CommitmentTransaction {
                        to_countersignatory_value_sat,
                        feerate_per_kw,
                        htlcs,
+                       opt_anchors: if opt_anchors { Some(()) } else { None },
                        keys,
                        built: BuiltCommitmentTransaction {
                                transaction,
@@ -922,11 +928,11 @@ impl CommitmentTransaction {
                }
        }
 
-       fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters) -> Result<BuiltCommitmentTransaction, ()> {
+       fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<BuiltCommitmentTransaction, ()> {
                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)?;
+               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 transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
                let txid = transaction.txid();
@@ -950,7 +956,7 @@ 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<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
+       fn internal_build_outputs<T>(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<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
                let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
                let contest_delay = channel_parameters.contest_delay();
 
@@ -982,6 +988,30 @@ impl CommitmentTransaction {
                        ));
                }
 
+               if opt_anchors {
+                       if to_broadcaster_value_sat > 0 || !htlcs_with_aux.is_empty() {
+                               let anchor_script = get_anchor_redeemscript(broadcaster_funding_key);
+                               txouts.push((
+                                       TxOut {
+                                               script_pubkey: anchor_script.to_v0_p2wsh(),
+                                               value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+                                       },
+                                       None,
+                               ));
+                       }
+
+                       if to_countersignatory_value_sat > 0 || !htlcs_with_aux.is_empty() {
+                               let anchor_script = get_anchor_redeemscript(countersignatory_funding_key);
+                               txouts.push((
+                                       TxOut {
+                                               script_pubkey: anchor_script.to_v0_p2wsh(),
+                                               value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+                                       },
+                                       None,
+                               ));
+                       }
+               }
+
                let mut htlcs = Vec::with_capacity(htlcs_with_aux.len());
                for (htlc, _) in htlcs_with_aux {
                        let script = chan_utils::get_htlc_redeemscript(&htlc, &keys);
@@ -1098,7 +1128,7 @@ impl CommitmentTransaction {
                if keys != self.keys {
                        return Err(());
                }
-               let tx = self.internal_rebuild_transaction(&keys, channel_parameters)?;
+               let tx = self.internal_rebuild_transaction(&keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?;
                if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
                        return Err(());
                }
@@ -1236,8 +1266,105 @@ fn script_for_p2wpkh(key: &PublicKey) -> Script {
 #[cfg(test)]
 mod tests {
        use super::CounterpartyCommitmentSecrets;
-       use hex;
+       use ::{hex, chain};
        use prelude::*;
+       use ln::chan_utils::{CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
+       use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
+       use util::test_utils;
+       use chain::keysinterface::{KeysInterface, BaseSign};
+       use bitcoin::Network;
+       use ln::PaymentHash;
+
+       #[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.get_channel_signer(false, 3000);
+               let counterparty_signer = keys_provider.get_channel_signer(false, 3000);
+               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).unwrap();
+               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: Default::default(), index: 0 })
+               };
+
+               let mut htlcs_with_aux: Vec<(_, ())> = Vec::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()
+               );
+               assert_eq!(tx.built.transaction.output.len(), 2);
+
+               // 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()
+               );
+               assert_eq!(tx.built.transaction.output.len(), 4);
+
+               // 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()
+               );
+               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()
+               );
+               assert_eq!(tx.built.transaction.output.len(), 2);
+
+               // Generate broadcaster output, an HTLC output and two anchors
+               let payment_hash = PaymentHash([42; 32]);
+               let htlc_info = HTLCOutputInCommitment {
+                       offered: false,
+                       amount_msat: 1000000,
+                       cltv_expiry: 100,
+                       payment_hash,
+                       transaction_output_index: None,
+               };
+
+               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![(htlc_info, ())], &channel_parameters.as_holder_broadcastable()
+               );
+               assert_eq!(tx.built.transaction.output.len(), 4);
+       }
 
        #[test]
        fn test_per_commitment_storage() {
index 03968118a16a59aa481cd148a81309f30cfdcc95..b44a642b7f265226f9a83fd8ca7f0f7e21cd0029 100644 (file)
@@ -567,7 +567,9 @@ const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
 #[cfg(test)]
 pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
 
-/// Maximmum `funding_satoshis` value, according to the BOLT #2 specification
+pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
+
+/// Maximum `funding_satoshis` value, according to the BOLT #2 specification
 /// it's 2^24.
 pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
 
@@ -1206,6 +1208,11 @@ impl<Signer: Sign> Channel<Signer> {
 
                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.get_holder_pubkeys().funding_pubkey, self.get_counterparty_pubkeys().funding_pubkey)
+               } else {
+                       (self.get_counterparty_pubkeys().funding_pubkey, self.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);
@@ -1227,6 +1234,9 @@ impl<Signer: Sign> Channel<Signer> {
                let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
                                                                             value_to_a as u64,
                                                                             value_to_b as u64,
+                                                                            false,
+                                                                            funding_pubkey_a,
+                                                                            funding_pubkey_b,
                                                                             keys.clone(),
                                                                             feerate_per_kw,
                                                                             &mut included_non_dust_htlcs,
index 1328939570eb9204b31153597841fbda151f2ef3..b7eb6c006f4baddbdc0b71c18d3f19704cc0c457 100644 (file)
@@ -1297,22 +1297,24 @@ fn test_fee_spike_violation_fails_htlc() {
 
        // Get the EnforcingSigner for each channel, which will be used to (1) get the keys
        // needed to sign the new commitment tx and (2) sign the new commitment tx.
-       let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point) = {
+       let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point, local_funding) = {
                let chan_lock = nodes[0].node.channel_state.lock().unwrap();
                let local_chan = chan_lock.by_id.get(&chan.2).unwrap();
                let chan_signer = local_chan.get_signer();
                let pubkeys = chan_signer.pubkeys();
                (pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
                 chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
-                chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx))
+                chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx),
+                chan_signer.pubkeys().funding_pubkey)
        };
-       let (remote_delayed_payment_basepoint, remote_htlc_basepoint,remote_point) = {
+       let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
                let chan_lock = nodes[1].node.channel_state.lock().unwrap();
                let remote_chan = chan_lock.by_id.get(&chan.2).unwrap();
                let chan_signer = remote_chan.get_signer();
                let pubkeys = chan_signer.pubkeys();
                (pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
-                chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx))
+                chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
+                chan_signer.pubkeys().funding_pubkey)
        };
 
        // Assemble the set of keys we can use for signatures for our commitment_signed message.
@@ -1341,6 +1343,7 @@ fn test_fee_spike_violation_fails_htlc() {
                        commitment_number,
                        95000,
                        local_chan_balance,
+                       false, local_funding, remote_funding,
                        commit_tx_keys.clone(),
                        feerate_per_kw,
                        &mut vec![(accepted_htlc_info, ())],
index a7f69fcfcb583b13c47179e8cd9f8ad12f9d2573..0b5036bd749424dd4529cb23481d5e01236d6974 100644 (file)
@@ -861,3 +861,9 @@ impl<A: Writeable, B: Writeable, C: Writeable> Writeable for (A, B, C) {
                self.2.write(w)
        }
 }
+
+impl Readable for () {
+       fn read<R: Read>(_r: &mut R) -> Result<Self, DecodeError> {
+               Ok(())
+       }
+}