Struct-ify SpendableOutputDescriptor entries relevant to channels
authorMatt Corallo <git@bluematt.me>
Wed, 3 Feb 2021 03:47:09 +0000 (22:47 -0500)
committerMatt Corallo <git@bluematt.me>
Tue, 16 Feb 2021 17:40:06 +0000 (12:40 -0500)
Both SpendableOutputDescriptor::DynamicOutputP2WSH and
SpendableOutputDescriptor::StaticOutputCounterpartyPayment are
relevant only in the context of a given channel, making them
candidates for being passed into helper functions in
`InMemoryChannelKeys`. This moves them into their own structs so
that they can later be used standalone.

lightning/src/chain/channelmonitor.rs
lightning/src/chain/keysinterface.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/util/macro_logger.rs

index 90f03520135053223ce996c076796f35370b470a..91c2a3c50c3531b813164eb4dc8c88287c4ce296 100644 (file)
@@ -43,7 +43,7 @@ use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
 use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
 use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use chain::transaction::{OutPoint, TransactionData};
-use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys, KeysInterface};
+use chain::keysinterface::{SpendableOutputDescriptor, StaticCounterpartyPaymentOutputDescriptor, DynamicP2WSHOutputDescriptor, ChannelKeys, KeysInterface};
 use util::logger::Logger;
 use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
 use util::byte_utils;
@@ -2201,7 +2201,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
                                break;
                        } else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
                                if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
-                                       spendable_output =  Some(SpendableOutputDescriptor::DynamicOutputP2WSH {
+                                       spendable_output =  Some(SpendableOutputDescriptor::DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor {
                                                outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
                                                per_commitment_point: broadcasted_holder_revokable_script.1,
                                                to_self_delay: self.on_holder_tx_csv,
@@ -2209,16 +2209,16 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
                                                revocation_pubkey: broadcasted_holder_revokable_script.2.clone(),
                                                channel_keys_id: self.channel_keys_id,
                                                channel_value_satoshis: self.channel_value_satoshis,
-                                       });
+                                       }));
                                        break;
                                }
                        } else if self.counterparty_payment_script == outp.script_pubkey {
-                               spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+                               spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor {
                                        outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
                                        output: outp.clone(),
                                        channel_keys_id: self.channel_keys_id,
                                        channel_value_satoshis: self.channel_value_satoshis,
-                               });
+                               }));
                                break;
                        } else if outp.script_pubkey == self.shutdown_script {
                                spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
index da5a99ddbb76a92bd7aaeef65ab0b574b136bbf1..8fd4463f4f68a15d8a308269d8cc7ca425c9d5b0 100644 (file)
@@ -40,6 +40,57 @@ use std::sync::atomic::{AtomicUsize, Ordering};
 use std::io::Error;
 use ln::msgs::DecodeError;
 
+/// Information about a spendable output to a P2WSH script. See
+/// SpendableOutputDescriptor::DynamicOutputP2WSH for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct DynamicP2WSHOutputDescriptor {
+       /// The outpoint which is spendable
+       pub outpoint: OutPoint,
+       /// Per commitment point to derive delayed_payment_key by key holder
+       pub per_commitment_point: PublicKey,
+       /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
+       /// the witness_script.
+       pub to_self_delay: u16,
+       /// The output which is referenced by the given outpoint
+       pub output: TxOut,
+       /// The revocation_pubkey used to derive witnessScript
+       pub revocation_pubkey: PublicKey,
+       /// Arbitrary identification information returned by a call to
+       /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+       /// the channel to spend the output.
+       pub channel_keys_id: [u8; 32],
+       /// The value of the channel which this output originated from, possibly indirectly.
+       pub channel_value_satoshis: u64,
+}
+impl DynamicP2WSHOutputDescriptor {
+       /// The maximum length a well-formed witness spending one of these should have.
+       // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+       // redeemscript push length.
+       pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
+}
+
+/// Information about a spendable output to our "payment key". See
+/// SpendableOutputDescriptor::StaticOutputCounterpartyPayment for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct StaticCounterpartyPaymentOutputDescriptor {
+       /// The outpoint which is spendable
+       pub outpoint: OutPoint,
+       /// The output which is reference by the given outpoint
+       pub output: TxOut,
+       /// Arbitrary identification information returned by a call to
+       /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+       /// the channel to spend the output.
+       pub channel_keys_id: [u8; 32],
+       /// The value of the channel which this transactions spends.
+       pub channel_value_satoshis: u64,
+}
+impl StaticCounterpartyPaymentOutputDescriptor {
+       /// The maximum length a well-formed witness spending one of these should have.
+       // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+       // redeemscript push length.
+       pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
+}
+
 /// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
 /// claim at any point in the future) an event is generated which you must track and be able to
 /// spend on-chain. The information needed to do this is provided in this enum, including the
@@ -88,25 +139,7 @@ pub enum SpendableOutputDescriptor {
        /// chan_utils::get_revokeable_redeemscript.
        //
        // TODO: we need to expose utility methods in KeyManager to do all the relevant derivation.
-       DynamicOutputP2WSH {
-               /// The outpoint which is spendable
-               outpoint: OutPoint,
-               /// Per commitment point to derive delayed_payment_key by key holder
-               per_commitment_point: PublicKey,
-               /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
-               /// the witness_script.
-               to_self_delay: u16,
-               /// The output which is referenced by the given outpoint
-               output: TxOut,
-               /// The revocation_pubkey used to derive witnessScript
-               revocation_pubkey: PublicKey,
-               /// Arbitrary identification information returned by a call to
-               /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
-               /// the channel to spend the output.
-               channel_keys_id: [u8; 32],
-               /// The value of the channel which this output originated from, possibly indirectly.
-               channel_value_satoshis: u64,
-       },
+       DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor),
        /// An output to a P2WPKH, spendable exclusively by our payment key (ie the private key which
        /// corresponds to the public key in ChannelKeys::pubkeys().payment_point).
        /// The witness in the spending input, is, thus, simply:
@@ -114,18 +147,7 @@ pub enum SpendableOutputDescriptor {
        ///
        /// These are generally the result of our counterparty having broadcast the current state,
        /// allowing us to claim the non-HTLC-encumbered outputs immediately.
-       StaticOutputCounterpartyPayment {
-               /// The outpoint which is spendable
-               outpoint: OutPoint,
-               /// The output which is reference by the given outpoint
-               output: TxOut,
-               /// Arbitrary identification information returned by a call to
-               /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
-               /// the channel to spend the output.
-               channel_keys_id: [u8; 32],
-               /// The value of the channel which this transactions spends.
-               channel_value_satoshis: u64,
-       }
+       StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor),
 }
 
 impl Writeable for SpendableOutputDescriptor {
@@ -136,22 +158,22 @@ impl Writeable for SpendableOutputDescriptor {
                                outpoint.write(writer)?;
                                output.write(writer)?;
                        },
-                       &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis } => {
+                       &SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
                                1u8.write(writer)?;
-                               outpoint.write(writer)?;
-                               per_commitment_point.write(writer)?;
-                               to_self_delay.write(writer)?;
-                               output.write(writer)?;
-                               revocation_pubkey.write(writer)?;
-                               channel_keys_id.write(writer)?;
-                               channel_value_satoshis.write(writer)?;
+                               descriptor.outpoint.write(writer)?;
+                               descriptor.per_commitment_point.write(writer)?;
+                               descriptor.to_self_delay.write(writer)?;
+                               descriptor.output.write(writer)?;
+                               descriptor.revocation_pubkey.write(writer)?;
+                               descriptor.channel_keys_id.write(writer)?;
+                               descriptor.channel_value_satoshis.write(writer)?;
                        },
-                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
+                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
                                2u8.write(writer)?;
-                               outpoint.write(writer)?;
-                               output.write(writer)?;
-                               channel_keys_id.write(writer)?;
-                               channel_value_satoshis.write(writer)?;
+                               descriptor.outpoint.write(writer)?;
+                               descriptor.output.write(writer)?;
+                               descriptor.channel_keys_id.write(writer)?;
+                               descriptor.channel_value_satoshis.write(writer)?;
                        },
                }
                Ok(())
@@ -165,7 +187,7 @@ impl Readable for SpendableOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                output: Readable::read(reader)?,
                        }),
-                       1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH {
+                       1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                per_commitment_point: Readable::read(reader)?,
                                to_self_delay: Readable::read(reader)?,
@@ -173,13 +195,13 @@ impl Readable for SpendableOutputDescriptor {
                                revocation_pubkey: Readable::read(reader)?,
                                channel_keys_id: Readable::read(reader)?,
                                channel_value_satoshis: Readable::read(reader)?,
-                       }),
-                       2u8 => Ok(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+                       })),
+                       2u8 => Ok(SpendableOutputDescriptor::StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                output: Readable::read(reader)?,
                                channel_keys_id: Readable::read(reader)?,
                                channel_value_satoshis: Readable::read(reader)?,
-                       }),
+                       })),
                        _ => Err(DecodeError::InvalidValue),
                }
        }
index b1171f543989c8d9deb7c6d68a18fb61e5e27af5..f86c53bc3bc7b01584ba9720014ec23b23330263 100644 (file)
@@ -384,11 +384,16 @@ impl TxCreationKeys {
        }
 }
 
+/// The maximum length of a script returned by get_revokeable_redeemscript.
+// Calculated as 6 bytes of opcodes, 1 byte push plus 2 bytes for contest_delay, and two public
+// keys of 33 bytes (+ 1 push).
+pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 3 + 34*2;
+
 /// A script either spendable by the revocation
 /// key or the broadcaster_delayed_payment_key and satisfying the relative-locktime OP_CSV constrain.
 /// Encumbering a `to_holder` output on a commitment transaction or 2nd-stage HTLC transactions.
 pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u16, broadcaster_delayed_payment_key: &PublicKey) -> Script {
-       Builder::new().push_opcode(opcodes::all::OP_IF)
+       let res = Builder::new().push_opcode(opcodes::all::OP_IF)
                      .push_slice(&revocation_key.serialize())
                      .push_opcode(opcodes::all::OP_ELSE)
                      .push_int(contest_delay as i64)
@@ -397,7 +402,9 @@ pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u1
                      .push_slice(&broadcaster_delayed_payment_key.serialize())
                      .push_opcode(opcodes::all::OP_ENDIF)
                      .push_opcode(opcodes::all::OP_CHECKSIG)
-                     .into_script()
+                     .into_script();
+       debug_assert!(res.len() <= REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH);
+       res
 }
 
 #[derive(Clone, PartialEq)]
index 11a202d5471c992bbe33457c502d510c5e95e5d0..b3f44044f632a752c64890380f1435e1538c5229 100644 (file)
@@ -4662,17 +4662,17 @@ macro_rules! check_spendable_outputs {
                                        Event::SpendableOutputs { ref outputs } => {
                                                for outp in outputs {
                                                        match *outp {
-                                                               SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
-                                                                       assert_eq!(channel_value_satoshis, $chan_value);
+                                                               SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
+                                                                       assert_eq!(descriptor.channel_value_satoshis, $chan_value);
                                                                        let input = TxIn {
-                                                                               previous_output: outpoint.into_bitcoin_outpoint(),
+                                                                               previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
                                                                                script_sig: Script::new(),
                                                                                sequence: 0,
                                                                                witness: Vec::new(),
                                                                        };
                                                                        let outp = TxOut {
                                                                                script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
-                                                                               value: output.value,
+                                                                               value: descriptor.output.value,
                                                                        };
                                                                        let mut spend_tx = Transaction {
                                                                                version: 2,
@@ -4682,27 +4682,27 @@ macro_rules! check_spendable_outputs {
                                                                        };
                                                                        spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
                                                                        let secp_ctx = Secp256k1::new();
-                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
+                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, &descriptor.channel_keys_id);
                                                                        let remotepubkey = keys.pubkeys().payment_point;
                                                                        let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
-                                                                       let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
+                                                                       let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, descriptor.output.value, SigHashType::All)[..]).unwrap();
                                                                        let remotesig = secp_ctx.sign(&sighash, &keys.inner.payment_key);
                                                                        spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec());
                                                                        spend_tx.input[0].witness[0].push(SigHashType::All as u8);
                                                                        spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec());
                                                                        txn.push(spend_tx);
                                                                },
-                                                               SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis  } => {
-                                                                       assert_eq!(channel_value_satoshis, $chan_value);
+                                                               SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
+                                                                       assert_eq!(descriptor.channel_value_satoshis, $chan_value);
                                                                        let input = TxIn {
-                                                                               previous_output: outpoint.into_bitcoin_outpoint(),
+                                                                               previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
                                                                                script_sig: Script::new(),
-                                                                               sequence: *to_self_delay as u32,
+                                                                               sequence: descriptor.to_self_delay as u32,
                                                                                witness: Vec::new(),
                                                                        };
                                                                        let outp = TxOut {
                                                                                script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
-                                                                               value: output.value,
+                                                                               value: descriptor.output.value,
                                                                        };
                                                                        let mut spend_tx = Transaction {
                                                                                version: 2,
@@ -4711,13 +4711,13 @@ macro_rules! check_spendable_outputs {
                                                                                output: vec![outp],
                                                                        };
                                                                        let secp_ctx = Secp256k1::new();
-                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
-                                                                       if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &keys.inner.delayed_payment_base_key) {
+                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, &descriptor.channel_keys_id);
+                                                                       if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &keys.inner.delayed_payment_base_key) {
 
                                                                                let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
-                                                                               let witness_script = chan_utils::get_revokeable_redeemscript(revocation_pubkey, *to_self_delay, &delayed_payment_pubkey);
+                                                                               let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
                                                                                spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 1 + witness_script.len() + 1 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
-                                                                               let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
+                                                                               let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, descriptor.output.value, SigHashType::All)[..]).unwrap();
                                                                                let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
                                                                                spend_tx.input[0].witness.push(local_delayedsig.serialize_der().to_vec());
                                                                                spend_tx.input[0].witness[0].push(SigHashType::All as u8);
index e667c4d726c5f2075907d9c9f4c2ea465fb8e137..222964b0a90480dfc9bf8761dfeca579f1e5c827 100644 (file)
@@ -138,11 +138,11 @@ impl<'a> std::fmt::Display for DebugSpendable<'a> {
                        &SpendableOutputDescriptor::StaticOutput { ref outpoint, .. } => {
                                write!(f, "StaticOutput {}:{} marked for spending", outpoint.txid, outpoint.index)?;
                        }
-                       &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, .. } => {
-                               write!(f, "DynamicOutputP2WSH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+                       &SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
+                               write!(f, "DynamicOutputP2WSH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
                        }
-                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, .. } => {
-                               write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
+                               write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
                        }
                }
                Ok(())