From 3a1986c5ec88e3acb2cc6de15fc4542a4758615a Mon Sep 17 00:00:00 2001 From: Devrandom Date: Wed, 24 Apr 2024 13:51:53 +0200 Subject: [PATCH] introduce DynSigner and TestSignerFactory --- lightning-invoice/src/payment.rs | 11 +- lightning-invoice/src/utils.rs | 67 ++-- lightning/src/chain/channelmonitor.rs | 2 +- lightning/src/sign/mod.rs | 98 +++--- lightning/src/util/dyn_signer.rs | 377 ++++++++++++++++++++++ lightning/src/util/mod.rs | 1 + lightning/src/util/test_channel_signer.rs | 34 +- lightning/src/util/test_utils.rs | 38 ++- 8 files changed, 538 insertions(+), 90 deletions(-) create mode 100644 lightning/src/util/dyn_signer.rs diff --git a/lightning-invoice/src/payment.rs b/lightning-invoice/src/payment.rs index 8196fa9eb89..8b17f17f419 100644 --- a/lightning-invoice/src/payment.rs +++ b/lightning-invoice/src/payment.rs @@ -91,6 +91,8 @@ mod tests { use core::time::Duration; #[cfg(feature = "std")] use std::time::SystemTime; + use bech32::ToBase32; + use lightning::sign::{NodeSigner, Recipient}; fn duration_since_epoch() -> Duration { #[cfg(feature = "std")] @@ -194,11 +196,10 @@ mod tests { .min_final_cltv_expiry_delta(144) .amount_milli_satoshis(50_000) .payment_metadata(payment_metadata.clone()) - .build_signed(|hash| { - Secp256k1::new().sign_ecdsa_recoverable(hash, - &nodes[1].keys_manager.backing.get_node_secret_key()) - }) - .unwrap(); + .build_raw().unwrap(); + let sig = nodes[1].keys_manager.backing.sign_invoice(invoice.hrp.to_string().as_bytes(), &invoice.data.to_base32(), Recipient::Node).unwrap(); + let invoice = invoice.sign::<_, ()>(|_| Ok(sig)).unwrap(); + let invoice = Bolt11Invoice::from_signed(invoice).unwrap(); let (hash, onion, params) = payment_parameters_from_invoice(&invoice).unwrap(); nodes[0].node.send_payment(hash, onion, PaymentId(hash.0), params, Retry::Attempts(0)).unwrap(); diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs index 1d6b9210afd..7c82a296614 100644 --- a/lightning-invoice/src/utils.rs +++ b/lightning-invoice/src/utils.rs @@ -835,6 +835,7 @@ mod test { use lightning::util::config::UserConfig; use crate::utils::{create_invoice_from_channelmanager_and_duration_since_epoch, rotate_through_iterators}; use std::collections::HashSet; + use lightning::util::dyn_signer::{DynKeysInterface, DynPhantomKeysInterface}; use lightning::util::string::UntrustedString; #[test] @@ -1295,6 +1296,13 @@ mod test { do_test_multi_node_receive(false); } + fn make_dyn_keys_interface(seed: &[u8; 32]) -> DynKeysInterface { + let cross_node_seed = [44u8; 32]; + let inner = PhantomKeysManager::new(&seed, 43, 44, &cross_node_seed); + let dyn_inner = DynPhantomKeysInterface::new(inner); + DynKeysInterface::new(Box::new(dyn_inner)) + } + #[cfg(feature = "std")] fn do_test_multi_node_receive(user_generated_pmt_hash: bool) { use lightning::events::{Event, EventsProvider}; @@ -1303,9 +1311,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(3); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); @@ -1407,9 +1414,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(3); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); @@ -1501,9 +1507,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(3); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); @@ -1530,9 +1535,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(4); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[3].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]); let nodes = create_network(4, &node_cfgs, &node_chanmgrs); @@ -1561,9 +1565,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(4); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[3].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]); let nodes = create_network(4, &node_cfgs, &node_chanmgrs); @@ -1619,9 +1622,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(3); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); @@ -1652,9 +1654,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(4); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]); let nodes = create_network(4, &node_cfgs, &node_chanmgrs); @@ -1686,9 +1687,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(3); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); @@ -1717,9 +1717,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(4); let seed_1 = [42u8; 32]; let seed_2 = [43u8; 32]; - let cross_node_seed = [44u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]); let nodes = create_network(4, &node_cfgs, &node_chanmgrs); @@ -1792,11 +1791,10 @@ mod test { let seed_2 = [43 as u8; 32]; let seed_3 = [44 as u8; 32]; let seed_4 = [45 as u8; 32]; - let cross_node_seed = [44 as u8; 32]; - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); - chanmon_cfgs[4].keys_manager.backing = PhantomKeysManager::new(&seed_3, 43, 44, &cross_node_seed); - chanmon_cfgs[5].keys_manager.backing = PhantomKeysManager::new(&seed_4, 43, 44, &cross_node_seed); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[3].keys_manager.backing = make_dyn_keys_interface(&seed_2); + chanmon_cfgs[4].keys_manager.backing = make_dyn_keys_interface(&seed_3); + chanmon_cfgs[4].keys_manager.backing = make_dyn_keys_interface(&seed_4); let node_cfgs = create_node_cfgs(6, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs, &[None, None, None, None, None, None]); let nodes = create_network(6, &node_cfgs, &node_chanmgrs); @@ -1849,9 +1847,8 @@ mod test { let mut chanmon_cfgs = create_chanmon_cfgs(5); let seed_1 = [42 as u8; 32]; let seed_2 = [43 as u8; 32]; - let cross_node_seed = [44 as u8; 32]; - chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed); - chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed); + chanmon_cfgs[1].keys_manager.backing = make_dyn_keys_interface(&seed_1); + chanmon_cfgs[2].keys_manager.backing = make_dyn_keys_interface(&seed_2); let node_cfgs = create_node_cfgs(5, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]); let nodes = create_network(5, &node_cfgs, &node_chanmgrs); diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 66d083377b0..c73e19d3406 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -4868,7 +4868,7 @@ mod tests { nodes[1].chain_monitor.chain_monitor.transactions_confirmed(&new_header, &[(0, broadcast_tx)], conf_height); - let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor)>::read( + let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor<_>)>::read( &mut io::Cursor::new(&get_monitor!(nodes[1], channel.2).encode()), (&nodes[1].keys_manager.backing, &nodes[1].keys_manager.backing)).unwrap(); diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 988acf7cc53..d99b5d987ba 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -32,9 +32,7 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; -#[cfg(taproot)] -use bitcoin::secp256k1::All; -use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing}; +use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing, All}; use bitcoin::{secp256k1, Sequence, Txid, Witness}; use crate::chain::transaction::OutPoint; @@ -727,6 +725,32 @@ impl HTLCDescriptor { /// A trait to handle Lightning channel key material without concretizing the channel type or /// the signature mechanism. pub trait ChannelSigner { + /// Returns the commitment seed for the channel. + fn commitment_seed(&self) -> [u8; 32]; + /// Returns the counterparty's pubkeys. + /// + /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. + /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. + fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys>; + /// Funding outpoint + /// + /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. + /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. + fn funding_outpoint(&self) -> Option<&OutPoint>; + /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or + /// building transactions. + /// + /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. + /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters>; + + /// Returns the channel type features of the channel parameters. Should be helpful for + /// determining a channel's category, i. e. legacy/anchors/taproot/etc. + /// + /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. + /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. + fn channel_type_features(&self) -> Option<&ChannelTypeFeatures>; + /// Gets the per-commitment point for a specific commitment number /// /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards. @@ -914,10 +938,10 @@ pub trait OutputSpender { /// Returns `Err(())` if the output value is greater than the input value minus required fee, /// if a descriptor was duplicated, or if an output descriptor `script_pubkey` /// does not match the one we can spend. - fn spend_spendable_outputs( + fn spend_spendable_outputs( &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, - locktime: Option, secp_ctx: &Secp256k1, + locktime: Option, secp_ctx: &Secp256k1, ) -> Result; } @@ -1120,16 +1144,6 @@ impl InMemorySigner { } } - /// Returns the counterparty's pubkeys. - /// - /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. - /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. - pub fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> { - self.get_channel_parameters().and_then(|params| { - params.counterparty_parameters.as_ref().map(|params| ¶ms.pubkeys) - }) - } - /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable /// transactions, i.e., the amount of time that we have to wait to recover our funds if we /// broadcast a transaction. @@ -1160,14 +1174,6 @@ impl InMemorySigner { self.get_channel_parameters().map(|params| params.is_outbound_from_holder) } - /// Funding outpoint - /// - /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. - /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. - pub fn funding_outpoint(&self) -> Option<&OutPoint> { - self.get_channel_parameters().map(|params| params.funding_outpoint.as_ref()).flatten() - } - /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or /// building transactions. /// @@ -1177,15 +1183,6 @@ impl InMemorySigner { self.channel_parameters.as_ref() } - /// Returns the channel type features of the channel parameters. Should be helpful for - /// determining a channel's category, i. e. legacy/anchors/taproot/etc. - /// - /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. - /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. - pub fn channel_type_features(&self) -> Option<&ChannelTypeFeatures> { - self.get_channel_parameters().map(|params| ¶ms.channel_type_features) - } - /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described /// by `descriptor`, returning the witness stack for the input. /// @@ -1338,6 +1335,35 @@ impl EntropySource for InMemorySigner { } impl ChannelSigner for InMemorySigner { + fn commitment_seed(&self) -> [u8; 32] { + self.commitment_seed + } + + fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> { + self.get_channel_parameters().and_then(|params| { + params.counterparty_parameters.as_ref().map(|params| ¶ms.pubkeys) + }) + } + + fn funding_outpoint(&self) -> Option<&OutPoint> { + self.get_channel_parameters().map(|params| params.funding_outpoint.as_ref()).flatten() + } + + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> { + self.channel_parameters.as_ref() + } + + + /// Returns the channel type features of the channel parameters. Should be helpful for + /// determining a channel's category, i. e. legacy/anchors/taproot/etc. + /// + /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. + /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. + fn channel_type_features(&self) -> Option<&ChannelTypeFeatures> { + self.get_channel_parameters().map(|params| ¶ms.channel_type_features) + } + + fn get_per_commitment_point( &self, idx: u64, secp_ctx: &Secp256k1, ) -> PublicKey { @@ -2199,10 +2225,10 @@ impl OutputSpender for KeysManager { /// /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`]. - fn spend_spendable_outputs( + fn spend_spendable_outputs( &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, - locktime: Option, secp_ctx: &Secp256k1, + locktime: Option, secp_ctx: &Secp256k1, ) -> Result { let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt( @@ -2363,10 +2389,10 @@ impl NodeSigner for PhantomKeysManager { impl OutputSpender for PhantomKeysManager { /// See [`OutputSpender::spend_spendable_outputs`] and [`KeysManager::spend_spendable_outputs`] /// for documentation on this method. - fn spend_spendable_outputs( + fn spend_spendable_outputs( &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, - locktime: Option, secp_ctx: &Secp256k1, + locktime: Option, secp_ctx: &Secp256k1, ) -> Result { self.inner.spend_spendable_outputs( descriptors, diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs new file mode 100644 index 00000000000..d71b37b20f1 --- /dev/null +++ b/lightning/src/util/dyn_signer.rs @@ -0,0 +1,377 @@ +//! A dynamically dispatched signer + +use core::any::Any; +use crate::io::{Read, Error}; +use crate::prelude::*; + +use delegate::delegate; + +use bitcoin::bech32::u5; +use bitcoin::{secp256k1, ScriptBuf, Transaction, TxOut}; +use crate::sign::{EntropySource, HTLCDescriptor, OutputSpender, PhantomKeysManager}; +use crate::ln::chan_utils::{ + ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction, + HTLCOutputInCommitment, HolderCommitmentTransaction, +}; +use crate::ln::msgs::{DecodeError, UnsignedChannelAnnouncement, UnsignedGossipMessage}; +use crate::ln::script::ShutdownScript; +use crate::ln::PaymentPreimage; +use crate::sign::ChannelSigner; +use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::InMemorySigner; +use crate::sign::{ + KeyMaterial, NodeSigner, Recipient, SignerProvider, SpendableOutputDescriptor, + ecdsa::WriteableEcdsaChannelSigner, +}; +use crate::util::ser::{Readable, ReadableArgs}; +use crate::util::ser::{Writeable, Writer}; +use bitcoin; +use bitcoin::absolute::LockTime; +use bitcoin::secp256k1::All; +use secp256k1::ecdsa::RecoverableSignature; +use secp256k1::{ecdh::SharedSecret, ecdsa::Signature, PublicKey, Scalar, Secp256k1, SecretKey}; + +use crate::chain::transaction::OutPoint; +use crate::ln::features::ChannelTypeFeatures; +#[cfg(any(test, feature = "_test_utils"))] +use crate::util::test_utils::OnlyReadsKeysInterface; + +/// Helper to allow DynSigner to clone itself +pub trait InnerSign: EcdsaChannelSigner + Send + Sync { + /// Clone into a Box + fn box_clone(&self) -> Box; + /// Cast to Any for runtime type checking + fn as_any(&self) -> &dyn Any; + /// Serialize the signer + fn vwrite(&self, writer: &mut Vec) -> Result<(), Error>; +} + +/// A ChannelSigner derived struct allowing run-time selection of a signer +pub struct DynSigner { + /// The inner signer + pub inner: Box, +} + +impl DynSigner { + /// Create a new DynSigner + pub fn new(inner: S) -> Self { + DynSigner { inner: Box::new(inner) } + } +} + +impl WriteableEcdsaChannelSigner for DynSigner {} + +impl Clone for DynSigner { + fn clone(&self) -> Self { + DynSigner { inner: self.inner.box_clone() } + } +} + +// This is taken care of by KeysInterface +impl Readable for DynSigner { + fn read(_reader: &mut R) -> Result { + unimplemented!() + } +} + +impl EcdsaChannelSigner for DynSigner { + delegate! { + to self.inner { + fn sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result; + + #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))] + fn unsafe_sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_counterparty_commitment( + &self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec, + outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1, + ) -> Result<(Signature, Vec), ()>; + + fn sign_justice_revoked_output( + &self, + justice_tx: &Transaction, + input: usize, + amount: u64, + per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_justice_revoked_htlc( + &self, + justice_tx: &Transaction, + input: usize, + amount: u64, + per_commitment_key: &SecretKey, + htlc: &HTLCOutputInCommitment, + secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_counterparty_htlc_transaction( + &self, + htlc_tx: &Transaction, + input: usize, + amount: u64, + per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, + secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_closing_transaction( + &self, + closing_tx: &ClosingTransaction, + secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_channel_announcement_with_funding_key( + &self, + msg: &UnsignedChannelAnnouncement, + secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_holder_anchor_input( + &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, + ) -> Result; + + fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1) -> Result; + } + } +} + +impl ChannelSigner for DynSigner { + delegate! { + to self.inner { + fn commitment_seed(&self) -> [u8; 32]; + fn channel_type_features(&self) -> Option<&ChannelTypeFeatures>; + fn get_per_commitment_point( + &self, + idx: u64, + secp_ctx: &Secp256k1, + ) -> PublicKey; + fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys>; + fn funding_outpoint(&self) -> Option<&OutPoint>; + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters>; + fn release_commitment_secret(&self, idx: u64) -> [u8; 32]; + + fn validate_holder_commitment( + &self, + holder_tx: &HolderCommitmentTransaction, + preimages: Vec, + ) -> Result<(), ()>; + + fn pubkeys(&self) -> &ChannelPublicKeys; + + fn channel_keys_id(&self) -> [u8; 32]; + + fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters); + + fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>; + } + } + +} + +impl Writeable for DynSigner { + fn write(&self, writer: &mut W) -> Result<(), Error> { + let inner = self.inner.as_ref(); + let mut buf = Vec::new(); + inner.vwrite(&mut buf)?; + writer.write_all(&buf) + } +} + +impl InnerSign for InMemorySigner { + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn vwrite(&self, writer: &mut Vec) -> Result<(), Error> { + self.write(writer) + } +} + +/// A convenience wrapper for DynKeysInterfaceTrait +pub struct DynKeysInterface { + /// The inner dyn keys interface + pub inner: Box>, +} + +impl DynKeysInterface { + /// Create a new DynKeysInterface + pub fn new(inner: Box>) -> Self { + DynKeysInterface { inner } + } +} + +impl NodeSigner for DynKeysInterface { + delegate! { + to self.inner { + fn get_node_id(&self, recipient: Recipient) -> Result; + fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result; + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result; + + fn sign_invoice( + &self, + hrp_bytes: &[u8], + invoice_data: &[u5], + recipient: Recipient, + ) -> Result; + + fn sign_bolt12_invoice( + &self, invoice: &crate::offers::invoice::UnsignedBolt12Invoice + ) -> Result; + + fn sign_bolt12_invoice_request( + &self, invoice_request: &crate::offers::invoice_request::UnsignedInvoiceRequest + ) -> Result; + + fn get_inbound_payment_key_material(&self) -> KeyMaterial; + } + } +} + +impl SignerProvider for DynKeysInterface { + type EcdsaSigner = DynSigner; + + delegate! { + to self.inner { + fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Result; + + fn get_shutdown_scriptpubkey(&self) -> Result; + + fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32]; + + fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::EcdsaSigner; + + fn read_chan_signer(&self, reader: &[u8]) -> Result; + } + } +} + +impl EntropySource for DynKeysInterface { + delegate! { + to self.inner { + fn get_secure_random_bytes(&self) -> [u8; 32]; + } + } +} + +impl OutputSpender for DynKeysInterface { + delegate! { + to self.inner { + fn spend_spendable_outputs( + &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, + change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, + locktime: Option, secp_ctx: &Secp256k1, + ) -> Result; + } + } +} + +/// A supertrait for all the traits that a keys interface implements +pub trait DynKeysInterfaceTrait: NodeSigner + OutputSpender + SignerProvider + EntropySource + Send + Sync { +} + +/// A dyn wrapper for PhantomKeysManager +pub struct DynPhantomKeysInterface { + inner: PhantomKeysManager, +} + +impl DynPhantomKeysInterface { + /// Create a new DynPhantomKeysInterface + pub fn new(inner: PhantomKeysManager) -> Self { + DynPhantomKeysInterface { inner } + } +} + +impl NodeSigner for DynPhantomKeysInterface { + delegate! { + to self.inner { + fn get_node_id(&self, recipient: Recipient) -> Result; + fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result; + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result; + + fn sign_invoice( + &self, + hrp_bytes: &[u8], + invoice_data: &[u5], + recipient: Recipient, + ) -> Result; + + fn sign_bolt12_invoice( + &self, invoice: &crate::offers::invoice::UnsignedBolt12Invoice + ) -> Result; + + fn sign_bolt12_invoice_request( + &self, invoice_request: &crate::offers::invoice_request::UnsignedInvoiceRequest + ) -> Result; + + fn get_inbound_payment_key_material(&self) -> KeyMaterial; + } + } +} + +impl SignerProvider for DynPhantomKeysInterface { + type EcdsaSigner = DynSigner; + + delegate! { + to self.inner { + fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Result; + + fn get_shutdown_scriptpubkey(&self) -> Result; + + fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32]; + } + } + + fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { + let inner = self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id); + DynSigner::new(inner) + } + + fn read_chan_signer(&self, _reader: &[u8]) -> Result { + todo!() + } +} + +impl EntropySource for DynPhantomKeysInterface { + delegate! { + to self.inner { + fn get_secure_random_bytes(&self) -> [u8; 32]; + } + } +} + +impl OutputSpender for DynPhantomKeysInterface { + delegate! { + to self.inner { + fn spend_spendable_outputs( + &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, + change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, + locktime: Option, secp_ctx: &Secp256k1, + ) -> Result; + } + } +} + +impl DynKeysInterfaceTrait for DynPhantomKeysInterface {} + +impl ReadableArgs<&DynKeysInterface> for DynSigner { + fn read(_reader: &mut R, _params: &DynKeysInterface) -> Result { + todo!() + } +} + +#[cfg(any(test, feature = "_test_utils"))] +impl ReadableArgs<&OnlyReadsKeysInterface> for DynSigner { + fn read(_reader: &mut R, _params: &OnlyReadsKeysInterface) -> Result { + todo!() + } +} diff --git a/lightning/src/util/mod.rs b/lightning/src/util/mod.rs index d76700ed910..4e65b6523e2 100644 --- a/lightning/src/util/mod.rs +++ b/lightning/src/util/mod.rs @@ -15,6 +15,7 @@ pub(crate) mod fuzz_wrappers; #[macro_use] pub mod ser_macros; +pub mod dyn_signer; pub mod mut_global; pub mod errors; diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 43ac9ff87e9..84ed08608f2 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -11,7 +11,7 @@ use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSH use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction}; use crate::ln::channel_keys::{HtlcKey}; use crate::ln::{msgs, PaymentPreimage}; -use crate::sign::{InMemorySigner, ChannelSigner}; +use crate::sign::ChannelSigner; use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner}; #[allow(unused_imports)] @@ -33,6 +33,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; #[cfg(taproot)] use musig2::types::{PartialSignature, PublicNonce, SecretNonce}; +use crate::chain::transaction::OutPoint; use crate::sign::HTLCDescriptor; use crate::util::ser::{Writeable, Writer}; use crate::io::Error; @@ -41,6 +42,7 @@ use crate::ln::features::ChannelTypeFeatures; use crate::ln::msgs::PartialSignatureWithNonce; #[cfg(taproot)] use crate::sign::taproot::TaprootChannelSigner; +use crate::util::dyn_signer::DynSigner; /// Initial value for revoked commitment downward counter pub const INITIAL_REVOKED_COMMITMENT_NUMBER: u64 = 1 << 48; @@ -66,7 +68,7 @@ pub const INITIAL_REVOKED_COMMITMENT_NUMBER: u64 = 1 << 48; /// forwards-compatibility prefix/suffixes! #[derive(Clone)] pub struct TestChannelSigner { - pub inner: InMemorySigner, + pub inner: DynSigner, /// Channel state used for policy enforcement pub state: Arc>, pub disable_revocation_policy_check: bool, @@ -83,7 +85,7 @@ impl PartialEq for TestChannelSigner { impl TestChannelSigner { /// Construct an TestChannelSigner - pub fn new(inner: InMemorySigner) -> Self { + pub fn new(inner: DynSigner) -> Self { let state = Arc::new(Mutex::new(EnforcementState::new())); Self { inner, @@ -98,7 +100,7 @@ impl TestChannelSigner { /// Since there are multiple copies of this struct for each channel, some coordination is needed /// so that all copies are aware of enforcement state. A pointer to this state is provided /// here, usually by an implementation of KeysInterface. - pub fn new_with_revoked(inner: InMemorySigner, state: Arc>, disable_revocation_policy_check: bool) -> Self { + pub fn new_with_revoked(inner: DynSigner, state: Arc>, disable_revocation_policy_check: bool) -> Self { Self { inner, state, @@ -125,6 +127,26 @@ impl TestChannelSigner { } impl ChannelSigner for TestChannelSigner { + fn commitment_seed(&self) -> [u8; 32] { + todo!() + } + + fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> { + todo!() + } + + fn funding_outpoint(&self) -> Option<&OutPoint> { + todo!() + } + + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> { + todo!() + } + + fn channel_type_features(&self) -> Option<&ChannelTypeFeatures> { + todo!() + } + fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1) -> PublicKey { self.inner.get_per_commitment_point(idx, secp_ctx) } @@ -199,7 +221,7 @@ impl EcdsaChannelSigner for TestChannelSigner { if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number { if !self.disable_revocation_policy_check { panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", - state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0]) + state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed()[0]) } } Ok(self.inner.sign_holder_commitment(commitment_tx, secp_ctx).unwrap()) @@ -237,7 +259,7 @@ impl EcdsaChannelSigner for TestChannelSigner { { if !self.disable_revocation_policy_check { panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", - state.last_holder_revoked_commitment, htlc_descriptor.per_commitment_number, self.inner.commitment_seed[0]) + state.last_holder_revoked_commitment, htlc_descriptor.per_commitment_number, self.inner.commitment_seed()[0]) } } assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input()); diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 95bc2a7c661..4b17a564681 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -40,11 +40,14 @@ use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult}; use crate::routing::router::{DefaultRouter, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs}; use crate::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp}; use crate::sync::RwLock; +use crate::sign::ChannelSigner; use crate::util::config::UserConfig; use crate::util::test_channel_signer::{TestChannelSigner, EnforcementState}; use crate::util::logger::{Logger, Level, Record}; +use crate::util::mut_global::MutGlobal; use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable}; use crate::util::persist::KVStore; +use crate::util::dyn_signer::{DynKeysInterface, DynPhantomKeysInterface, DynSigner, DynKeysInterfaceTrait}; use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::constants::genesis_block; @@ -69,7 +72,7 @@ use crate::sync::{Mutex, Arc}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::mem; use bitcoin::bech32::u5; -use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; +use crate::sign::{RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; @@ -294,7 +297,7 @@ impl SignerProvider for OnlyReadsKeysInterface { fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { unreachable!(); } fn read_chan_signer(&self, mut reader: &[u8]) -> Result { - let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; + let inner: DynSigner = ReadableArgs::read(&mut reader, self)?; let state = Arc::new(Mutex::new(EnforcementState::new())); Ok(TestChannelSigner::new_with_revoked( @@ -1194,7 +1197,7 @@ impl NodeSigner for TestNodeSigner { } pub struct TestKeysInterface { - pub backing: sign::PhantomKeysManager, + pub backing: DynKeysInterface, pub override_random_bytes: Mutex>, pub disable_revocation_policy_check: bool, enforcement_states: Mutex>>>, @@ -1257,7 +1260,7 @@ impl SignerProvider for TestKeysInterface { fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> TestChannelSigner { let keys = self.backing.derive_channel_signer(channel_value_satoshis, channel_keys_id); - let state = self.make_enforcement_state_cell(keys.commitment_seed); + let state = self.make_enforcement_state_cell(keys.commitment_seed()); let signer = TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check); if self.unavailable_signers.lock().unwrap().contains(&channel_keys_id) { signer.set_available(false); @@ -1268,8 +1271,8 @@ impl SignerProvider for TestKeysInterface { fn read_chan_signer(&self, buffer: &[u8]) -> Result { let mut reader = io::Cursor::new(buffer); - let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; - let state = self.make_enforcement_state_cell(inner.commitment_seed); + let inner: DynSigner = ReadableArgs::read(&mut reader, &self.backing)?; + let state = self.make_enforcement_state_cell(inner.commitment_seed()); Ok(TestChannelSigner::new_with_revoked( inner, @@ -1291,11 +1294,32 @@ impl SignerProvider for TestKeysInterface { } } +pub static SIGNER_FACTORY: MutGlobal> = MutGlobal::new(); + +pub trait TestSignerFactory: Send + Sync { + /// Make a dynamic signer + fn make_signer(&self, seed: &[u8; 32], now: Duration) -> Box>; +} + +#[derive(Clone)] +struct DefaultSignerFactory(); + +impl TestSignerFactory for DefaultSignerFactory { + fn make_signer(&self, seed: &[u8; 32], now: Duration) -> Box> { + let phantom = sign::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed); + let dphantom = DynPhantomKeysInterface::new(phantom); + let backing = Box::new(dphantom) as Box>; + backing + } +} + impl TestKeysInterface { pub fn new(seed: &[u8; 32], network: Network) -> Self { + SIGNER_FACTORY.set(Arc::new(DefaultSignerFactory())); let now = Duration::from_secs(genesis_block(network).header.time as u64); + let backing = SIGNER_FACTORY.get().make_signer(seed, now); Self { - backing: sign::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed), + backing: DynKeysInterface::new(backing), override_random_bytes: Mutex::new(None), disable_revocation_policy_check: false, enforcement_states: Mutex::new(new_hash_map()),