X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fsign%2Fmod.rs;h=2a5d19efd1c59f119fc1554cc60190c5cd239060;hb=9f1ffab24c4870d0f9846d75a693f2a784648a60;hp=9de3ed8dc534ff9d08721d5640ddbb7fa717541a;hpb=88ce7d6575dd376e98e680d0c0813c7b79151773;p=rust-lightning diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 9de3ed8d..2a5d19ef 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -38,7 +38,7 @@ use bitcoin::secp256k1::schnorr; use bitcoin::{secp256k1, Sequence, Witness, Txid}; use crate::util::transaction_utils; -use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; +use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs}; use crate::chain::transaction::OutPoint; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; @@ -53,11 +53,10 @@ use crate::offers::invoice::UnsignedBolt12Invoice; use crate::offers::invoice_request::UnsignedInvoiceRequest; use crate::prelude::*; -use core::convert::TryInto; use core::ops::Deref; use core::sync::atomic::{AtomicUsize, Ordering}; #[cfg(taproot)] -use musig2::types::{PartialSignature, PublicNonce, SecretNonce}; +use musig2::types::{PartialSignature, PublicNonce}; use crate::io::{self, Error}; use crate::ln::features::ChannelTypeFeatures; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; @@ -65,7 +64,7 @@ use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner}; #[cfg(taproot)] use crate::sign::taproot::TaprootChannelSigner; use crate::util::atomic_counter::AtomicCounter; -use crate::util::chacha20::ChaCha20; +use crate::crypto::chacha20::ChaCha20; use crate::util::invoice::construct_invoice_preimage; pub(crate) mod type_resolver; @@ -215,6 +214,15 @@ pub enum SpendableOutputDescriptor { outpoint: OutPoint, /// The output which is referenced by the given outpoint. output: TxOut, + /// The `channel_keys_id` for the channel which this output came from. + /// + /// For channels which were generated on LDK 0.0.119 or later, this is the value which was + /// passed to the [`SignerProvider::get_destination_script`] call which provided this + /// output script. + /// + /// For channels which were generated prior to LDK 0.0.119, no such argument existed, + /// however this field may still be filled in if such data is available. + channel_keys_id: Option<[u8; 32]> }, /// An output to a P2WSH script which can be spent with a single signature after an `OP_CSV` /// delay. @@ -278,6 +286,7 @@ pub enum SpendableOutputDescriptor { impl_writeable_tlv_based_enum!(SpendableOutputDescriptor, (0, StaticOutput) => { (0, outpoint, required), + (1, channel_keys_id, option), (2, output, required), }, ; @@ -340,7 +349,7 @@ impl SpendableOutputDescriptor { let mut input = Vec::with_capacity(descriptors.len()); let mut input_value = 0; let mut witness_weight = 0; - let mut output_set = HashSet::with_capacity(descriptors.len()); + let mut output_set = hash_set_with_capacity(descriptors.len()); for outp in descriptors { match outp { SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => { @@ -378,7 +387,7 @@ impl SpendableOutputDescriptor { { witness_weight -= 1; } // Guarantees a low R signature input_value += descriptor.output.value; }, - SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => { + SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output, .. } => { if !output_set.insert(*outpoint) { return Err(()); } input.push(TxIn { previous_output: outpoint.into_bitcoin_outpoint(), @@ -584,14 +593,20 @@ pub trait ChannelSigner { /// Policy checks should be implemented in this function, including checking the amount /// sent to us and checking the HTLCs. /// - /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided. + /// The preimages of outbound HTLCs that were fulfilled since the last commitment are provided. /// A validating signer should ensure that an HTLC output is removed only when the matching /// preimage is provided, or when the value to holder is restored. /// /// Note that all the relevant preimages will be provided, but there may also be additional /// irrelevant or duplicate preimages. fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, - preimages: Vec) -> Result<(), ()>; + outbound_htlc_preimages: Vec) -> Result<(), ()>; + + /// Validate the counterparty's revocation. + /// + /// This is required in order for the signer to make sure that the state has moved + /// forward and it is safe to sign the next counterparty commitment. + fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>; /// Returns the holder's channel public keys and basepoints. fn pubkeys(&self) -> &ChannelPublicKeys; @@ -720,6 +735,19 @@ pub trait NodeSigner { fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result; } +// Primarily needed in doctests because of https://github.com/rust-lang/rust/issues/67295 +/// A dynamic [`SignerProvider`] temporarily needed for doc tests. +#[cfg(taproot)] +#[doc(hidden)] +#[deprecated(note = "Remove once taproot cfg is removed")] +pub type DynSignerProvider = dyn SignerProvider; + +/// A dynamic [`SignerProvider`] temporarily needed for doc tests. +#[cfg(not(taproot))] +#[doc(hidden)] +#[deprecated(note = "Remove once taproot cfg is removed")] +pub type DynSignerProvider = dyn SignerProvider; + /// A trait that can return signer instances for individual channels. pub trait SignerProvider { /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`]. @@ -806,11 +834,8 @@ pub struct InMemorySigner { channel_value_satoshis: u64, /// Key derivation parameters. channel_keys_id: [u8; 32], - /// Seed from which all randomness produced is derived from. - rand_bytes_unique_start: [u8; 32], - /// Tracks the number of times we've produced randomness to ensure we don't return the same - /// bytes twice. - rand_bytes_index: AtomicCounter, + /// A source of random bytes. + entropy_source: RandomBytes, } impl PartialEq for InMemorySigner { @@ -841,8 +866,7 @@ impl Clone for InMemorySigner { channel_parameters: self.channel_parameters.clone(), channel_value_satoshis: self.channel_value_satoshis, channel_keys_id: self.channel_keys_id, - rand_bytes_unique_start: self.get_secure_random_bytes(), - rand_bytes_index: AtomicCounter::new(), + entropy_source: RandomBytes::new(self.get_secure_random_bytes()), } } } @@ -876,8 +900,7 @@ impl InMemorySigner { holder_channel_pubkeys, channel_parameters: None, channel_keys_id, - rand_bytes_unique_start, - rand_bytes_index: AtomicCounter::new(), + entropy_source: RandomBytes::new(rand_bytes_unique_start), } } @@ -1053,10 +1076,7 @@ impl InMemorySigner { impl EntropySource for InMemorySigner { fn get_secure_random_bytes(&self) -> [u8; 32] { - let index = self.rand_bytes_index.get_increment(); - let mut nonce = [0u8; 16]; - nonce[..8].copy_from_slice(&index.to_be_bytes()); - ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce) + self.entropy_source.get_secure_random_bytes() } } @@ -1070,7 +1090,11 @@ impl ChannelSigner for InMemorySigner { chan_utils::build_commitment_secret(&self.commitment_seed, idx) } - fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec) -> Result<(), ()> { + fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _outbound_htlc_preimages: Vec) -> Result<(), ()> { + Ok(()) + } + + fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> { Ok(()) } @@ -1092,7 +1116,7 @@ impl ChannelSigner for InMemorySigner { const MISSING_PARAMS_ERR: &'static str = "ChannelSigner::provide_channel_parameters must be called before signing operations"; impl EcdsaChannelSigner for InMemorySigner { - fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { + fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _inbound_htlc_preimages: Vec, _outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { let trusted_tx = commitment_tx.trust(); let keys = trusted_tx.keys(); @@ -1121,10 +1145,6 @@ impl EcdsaChannelSigner for InMemorySigner { Ok((commitment_sig, htlc_sigs)) } - fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> { - Ok(()) - } - fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); @@ -1244,11 +1264,11 @@ impl TaprootChannelSigner for InMemorySigner { todo!() } - fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, preimages: Vec, secp_ctx: &Secp256k1) -> Result<(PartialSignatureWithNonce, Vec), ()> { + fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec, outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1) -> Result<(PartialSignatureWithNonce, Vec), ()> { todo!() } - fn finalize_holder_commitment(&self, commitment_number: u64, commitment_tx: &HolderCommitmentTransaction, counterparty_partial_signature: PartialSignatureWithNonce, secp_ctx: &Secp256k1) -> Result { + fn finalize_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, counterparty_partial_signature: PartialSignatureWithNonce, secp_ctx: &Secp256k1) -> Result { todo!() } @@ -1334,8 +1354,7 @@ impl ReadableArgs for InMemorySigner where ES::Target: EntropySou holder_channel_pubkeys, channel_parameters: counterparty_channel_data, channel_keys_id: keys_id, - rand_bytes_unique_start: entropy_source.get_secure_random_bytes(), - rand_bytes_index: AtomicCounter::new(), + entropy_source: RandomBytes::new(entropy_source.get_secure_random_bytes()), }) } } @@ -1363,8 +1382,7 @@ pub struct KeysManager { channel_master_key: ExtendedPrivKey, channel_child_index: AtomicUsize, - rand_bytes_unique_start: [u8; 32], - rand_bytes_index: AtomicCounter, + entropy_source: RandomBytes, seed: [u8; 32], starting_time_secs: u64, @@ -1433,8 +1451,7 @@ impl KeysManager { channel_master_key, channel_child_index: AtomicUsize::new(0), - rand_bytes_unique_start, - rand_bytes_index: AtomicCounter::new(), + entropy_source: RandomBytes::new(rand_bytes_unique_start), seed: *seed, starting_time_secs, @@ -1540,7 +1557,7 @@ impl KeysManager { let witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?; psbt.inputs[input_idx].final_script_witness = Some(witness); }, - SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => { + SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output, .. } => { let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == outpoint.into_bitcoin_outpoint()).ok_or(())?; let derivation_idx = if output.script_pubkey == self.destination_script { 1 @@ -1615,10 +1632,7 @@ impl KeysManager { impl EntropySource for KeysManager { fn get_secure_random_bytes(&self) -> [u8; 32] { - let index = self.rand_bytes_index.get_increment(); - let mut nonce = [0u8; 16]; - nonce[..8].copy_from_slice(&index.to_be_bytes()); - ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce) + self.entropy_source.get_secure_random_bytes() } } @@ -1872,6 +1886,35 @@ impl PhantomKeysManager { } } +/// An implementation of [`EntropySource`] using ChaCha20. +#[derive(Debug)] +pub struct RandomBytes { + /// Seed from which all randomness produced is derived from. + seed: [u8; 32], + /// Tracks the number of times we've produced randomness to ensure we don't return the same + /// bytes twice. + index: AtomicCounter, +} + +impl RandomBytes { + /// Creates a new instance using the given seed. + pub fn new(seed: [u8; 32]) -> Self { + Self { + seed, + index: AtomicCounter::new(), + } + } +} + +impl EntropySource for RandomBytes { + fn get_secure_random_bytes(&self) -> [u8; 32] { + let index = self.index.get_increment(); + let mut nonce = [0u8; 16]; + nonce[..8].copy_from_slice(&index.to_be_bytes()); + ChaCha20::get_single_block(&self.seed, &nonce) + } +} + // Ensure that EcdsaChannelSigner can have a vtable #[test] pub fn dyn_sign() {