Skip to content

Commit 17cd6e3

Browse files
committed
Introduce DynSigner, a dynamically dispatched signer
DynSigner provides an abstraction for specifying an external signer for functional tests.
1 parent 60222d0 commit 17cd6e3

File tree

6 files changed

+425
-31
lines changed

6 files changed

+425
-31
lines changed

lightning/src/sign/ecdsa.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub trait EcdsaChannelSigner: ChannelSigner {
8484
/// only ever get called once.
8585
///
8686
/// This method is *not* async as it is intended only for testing purposes.
87-
#[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
87+
#[cfg(any(test, feature = "_test_utils", feature = "unsafe_revoked_tx_signing"))]
8888
fn unsafe_sign_holder_commitment(
8989
&self, channel_parameters: &ChannelTransactionParameters,
9090
commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,

lightning/src/sign/mod.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ use bitcoin::hashes::{Hash, HashEngine};
3131
use bitcoin::secp256k1::ecdh::SharedSecret;
3232
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
3333
use bitcoin::secp256k1::schnorr;
34-
#[cfg(taproot)]
3534
use bitcoin::secp256k1::All;
3635
use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
3736
use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness};
@@ -898,10 +897,10 @@ pub trait OutputSpender {
898897
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
899898
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
900899
/// does not match the one we can spend.
901-
fn spend_spendable_outputs<C: Signing>(
900+
fn spend_spendable_outputs(
902901
&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
903902
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
904-
locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
903+
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>,
905904
) -> Result<Transaction, ()>;
906905
}
907906

@@ -1351,7 +1350,7 @@ impl EcdsaChannelSigner for InMemorySigner {
13511350
))
13521351
}
13531352

1354-
#[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
1353+
#[cfg(any(test, feature = "_test_utils", feature = "unsafe_revoked_tx_signing"))]
13551354
fn unsafe_sign_holder_commitment(
13561355
&self, channel_parameters: &ChannelTransactionParameters,
13571356
commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
@@ -2044,10 +2043,10 @@ impl OutputSpender for KeysManager {
20442043
///
20452044
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
20462045
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
2047-
fn spend_spendable_outputs<C: Signing>(
2046+
fn spend_spendable_outputs(
20482047
&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
20492048
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
2050-
locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
2049+
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>,
20512050
) -> Result<Transaction, ()> {
20522051
let (mut psbt, expected_max_weight) =
20532052
SpendableOutputDescriptor::create_spendable_outputs_psbt(
@@ -2194,10 +2193,10 @@ impl NodeSigner for PhantomKeysManager {
21942193
impl OutputSpender for PhantomKeysManager {
21952194
/// See [`OutputSpender::spend_spendable_outputs`] and [`KeysManager::spend_spendable_outputs`]
21962195
/// for documentation on this method.
2197-
fn spend_spendable_outputs<C: Signing>(
2196+
fn spend_spendable_outputs(
21982197
&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
21992198
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
2200-
locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
2199+
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>,
22012200
) -> Result<Transaction, ()> {
22022201
self.inner.spend_spendable_outputs(
22032202
descriptors,

lightning/src/util/dyn_signer.rs

+330
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
//! A dynamically dispatched signer
2+
3+
use crate::prelude::*;
4+
5+
use core::any::Any;
6+
7+
use crate::ln::chan_utils::{
8+
ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
9+
HTLCOutputInCommitment, HolderCommitmentTransaction,
10+
};
11+
use crate::ln::inbound_payment::ExpandedKey;
12+
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
13+
use crate::ln::script::ShutdownScript;
14+
use crate::sign::ecdsa::EcdsaChannelSigner;
15+
#[cfg(taproot)]
16+
use crate::sign::taproot::TaprootChannelSigner;
17+
use crate::sign::ChannelSigner;
18+
use crate::sign::InMemorySigner;
19+
use crate::sign::{EntropySource, HTLCDescriptor, OutputSpender, PhantomKeysManager};
20+
use crate::sign::{NodeSigner, Recipient, SignerProvider, SpendableOutputDescriptor};
21+
use bitcoin;
22+
use bitcoin::absolute::LockTime;
23+
use bitcoin::secp256k1::All;
24+
use bitcoin::{secp256k1, ScriptBuf, Transaction, TxOut};
25+
use lightning_invoice::RawBolt11Invoice;
26+
#[cfg(taproot)]
27+
use musig2::types::{PartialSignature, PublicNonce};
28+
use secp256k1::ecdsa::RecoverableSignature;
29+
use secp256k1::{ecdh::SharedSecret, ecdsa::Signature, PublicKey, Scalar, Secp256k1, SecretKey};
30+
use types::payment::PaymentPreimage;
31+
32+
#[cfg(not(taproot))]
33+
/// A super-trait for all the traits that a dyn signer backing implements
34+
pub trait DynSignerTrait: EcdsaChannelSigner + Send + Sync {}
35+
36+
#[cfg(taproot)]
37+
/// A super-trait for all the traits that a dyn signer backing implements
38+
pub trait DynSignerTrait: EcdsaChannelSigner + TaprootChannelSigner + Send + Sync {}
39+
40+
/// Helper to allow DynSigner to clone itself
41+
pub trait InnerSign: DynSignerTrait {
42+
/// Clone into a Box
43+
fn box_clone(&self) -> Box<dyn InnerSign>;
44+
/// Cast to Any for runtime type checking
45+
fn as_any(&self) -> &dyn Any;
46+
}
47+
48+
/// A ChannelSigner derived struct allowing run-time selection of a signer
49+
pub struct DynSigner {
50+
/// The inner signer
51+
pub inner: Box<dyn InnerSign>,
52+
}
53+
54+
impl DynSigner {
55+
/// Create a new DynSigner
56+
pub fn new<S: InnerSign + 'static>(inner: S) -> Self {
57+
DynSigner { inner: Box::new(inner) }
58+
}
59+
}
60+
61+
#[cfg(taproot)]
62+
#[allow(unused_variables)]
63+
impl TaprootChannelSigner for DynSigner {
64+
fn generate_local_nonce_pair(
65+
&self, commitment_number: u64, secp_ctx: &Secp256k1<All>,
66+
) -> PublicNonce {
67+
todo!()
68+
}
69+
70+
fn partially_sign_counterparty_commitment(
71+
&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction,
72+
inbound_htlc_preimages: Vec<PaymentPreimage>,
73+
outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>,
74+
) -> Result<(crate::ln::msgs::PartialSignatureWithNonce, Vec<secp256k1::schnorr::Signature>), ()>
75+
{
76+
todo!();
77+
}
78+
79+
fn finalize_holder_commitment(
80+
&self, commitment_tx: &HolderCommitmentTransaction,
81+
counterparty_partial_signature: crate::ln::msgs::PartialSignatureWithNonce,
82+
secp_ctx: &Secp256k1<All>,
83+
) -> Result<PartialSignature, ()> {
84+
todo!();
85+
}
86+
87+
fn sign_justice_revoked_output(
88+
&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
89+
secp_ctx: &Secp256k1<All>,
90+
) -> Result<secp256k1::schnorr::Signature, ()> {
91+
todo!();
92+
}
93+
94+
fn sign_justice_revoked_htlc(
95+
&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
96+
htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>,
97+
) -> Result<secp256k1::schnorr::Signature, ()> {
98+
todo!();
99+
}
100+
101+
fn sign_holder_htlc_transaction(
102+
&self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
103+
secp_ctx: &Secp256k1<All>,
104+
) -> Result<secp256k1::schnorr::Signature, ()> {
105+
todo!();
106+
}
107+
108+
fn sign_counterparty_htlc_transaction(
109+
&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey,
110+
htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>,
111+
) -> Result<secp256k1::schnorr::Signature, ()> {
112+
todo!();
113+
}
114+
115+
fn partially_sign_closing_transaction(
116+
&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<All>,
117+
) -> Result<PartialSignature, ()> {
118+
todo!();
119+
}
120+
121+
fn sign_holder_anchor_input(
122+
&self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<All>,
123+
) -> Result<secp256k1::schnorr::Signature, ()> {
124+
todo!();
125+
}
126+
}
127+
128+
impl Clone for DynSigner {
129+
fn clone(&self) -> Self {
130+
DynSigner { inner: self.inner.box_clone() }
131+
}
132+
}
133+
134+
delegate!(DynSigner, EcdsaChannelSigner, inner,
135+
fn sign_holder_commitment(, channel_parameters: &ChannelTransactionParameters,
136+
commitment_tx: &HolderCommitmentTransaction,
137+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
138+
#[cfg(any(test, feature = "_test_utils", feature = "unsafe_revoked_tx_signing"))]
139+
fn unsafe_sign_holder_commitment(, channel_parameters: &ChannelTransactionParameters,
140+
commitment_tx: &HolderCommitmentTransaction,
141+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
142+
fn sign_counterparty_commitment(, channel_parameters: &ChannelTransactionParameters,
143+
commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>,
144+
outbound_htlc_preimages: Vec<PaymentPreimage>,
145+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>,
146+
fn sign_justice_revoked_output(, channel_parameters: &ChannelTransactionParameters,
147+
justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
148+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
149+
fn sign_justice_revoked_htlc(, channel_parameters: &ChannelTransactionParameters,
150+
justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
151+
htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
152+
fn sign_counterparty_htlc_transaction(, channel_parameters: &ChannelTransactionParameters,
153+
htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey,
154+
htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
155+
fn sign_closing_transaction(, channel_parameters: &ChannelTransactionParameters,
156+
closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
157+
fn sign_channel_announcement_with_funding_key(, msg: &UnsignedChannelAnnouncement,
158+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
159+
fn sign_holder_anchor_input(, channel_parameters: &ChannelTransactionParameters,
160+
anchor_tx: &Transaction, input: usize,
161+
secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>,
162+
fn sign_holder_htlc_transaction(, htlc_tx: &Transaction, input: usize,
163+
htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<All>) -> Result<Signature, ()>,
164+
fn sign_splicing_funding_input(, channel_parameters: &ChannelTransactionParameters,
165+
tx: &Transaction, input_index: usize, input_value: u64,
166+
secp_ctx: &Secp256k1<All>) -> Result<Signature, ()>
167+
);
168+
169+
delegate!(DynSigner, ChannelSigner,
170+
inner,
171+
fn get_per_commitment_point(,
172+
idx: u64,
173+
secp_ctx: &Secp256k1<secp256k1::All>
174+
) -> Result<PublicKey, ()>,
175+
fn release_commitment_secret(, idx: u64) -> Result<[u8; 32], ()>,
176+
fn validate_holder_commitment(,
177+
holder_tx: &HolderCommitmentTransaction,
178+
preimages: Vec<PaymentPreimage>
179+
) -> Result<(), ()>,
180+
fn pubkeys(,) -> &ChannelPublicKeys,
181+
fn channel_keys_id(,) -> [u8; 32],
182+
fn validate_counterparty_revocation(, idx: u64, secret: &SecretKey) -> Result<(), ()>
183+
);
184+
185+
impl DynSignerTrait for InMemorySigner {}
186+
187+
impl InnerSign for InMemorySigner {
188+
fn box_clone(&self) -> Box<dyn InnerSign> {
189+
Box::new(self.clone())
190+
}
191+
192+
fn as_any(&self) -> &dyn Any {
193+
self
194+
}
195+
}
196+
197+
/// A convenience wrapper for DynKeysInterfaceTrait
198+
pub struct DynKeysInterface {
199+
/// The inner dyn keys interface
200+
pub inner: Box<dyn DynKeysInterfaceTrait>,
201+
}
202+
203+
impl DynKeysInterface {
204+
/// Create a new DynKeysInterface
205+
pub fn new(inner: Box<dyn DynKeysInterfaceTrait>) -> Self {
206+
DynKeysInterface { inner }
207+
}
208+
}
209+
210+
delegate!(DynKeysInterface, NodeSigner,
211+
inner,
212+
fn get_node_id(, recipient: Recipient) -> Result<PublicKey, ()>,
213+
fn sign_gossip_message(, msg: UnsignedGossipMessage) -> Result<Signature, ()>,
214+
fn ecdh(, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>,
215+
fn sign_invoice(, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result<RecoverableSignature, ()>,
216+
fn sign_bolt12_invoice(,
217+
invoice: &crate::offers::invoice::UnsignedBolt12Invoice
218+
) -> Result<secp256k1::schnorr::Signature, ()>,
219+
fn get_inbound_payment_key(,) -> ExpandedKey
220+
);
221+
222+
delegate!(DynKeysInterface, SignerProvider,
223+
inner,
224+
fn get_destination_script(, channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()>,
225+
fn get_shutdown_scriptpubkey(,) -> Result<ShutdownScript, ()>,
226+
fn generate_channel_keys_id(, _inbound: bool, _user_channel_id: u128) -> [u8; 32],
227+
fn derive_channel_signer(, _channel_keys_id: [u8; 32]) -> Self::EcdsaSigner;
228+
type EcdsaSigner = DynSigner,
229+
#[cfg(taproot)]
230+
type TaprootSigner = DynSigner
231+
);
232+
233+
delegate!(DynKeysInterface, EntropySource, inner,
234+
fn get_secure_random_bytes(,) -> [u8; 32]
235+
);
236+
237+
delegate!(DynKeysInterface, OutputSpender, inner,
238+
fn spend_spendable_outputs(,
239+
descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
240+
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
241+
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>
242+
) -> Result<Transaction, ()>
243+
);
244+
#[cfg(not(taproot))]
245+
/// A supertrait for all the traits that a keys interface implements
246+
pub trait DynKeysInterfaceTrait:
247+
NodeSigner + OutputSpender + SignerProvider<EcdsaSigner = DynSigner> + EntropySource + Send + Sync
248+
{
249+
#[cfg(test)]
250+
fn set_counter(&self, _count: u64) {}
251+
}
252+
253+
#[cfg(taproot)]
254+
/// A supertrait for all the traits that a keys interface implements
255+
pub trait DynKeysInterfaceTrait:
256+
NodeSigner
257+
+ OutputSpender
258+
+ SignerProvider<EcdsaSigner = DynSigner, TaprootSigner = DynSigner>
259+
+ EntropySource
260+
+ Send
261+
+ Sync
262+
{
263+
#[cfg(test)]
264+
fn set_counter(&self, _count: u64) {}
265+
}
266+
267+
/// A dyn wrapper for PhantomKeysManager
268+
pub struct DynPhantomKeysInterface {
269+
inner: Box<PhantomKeysManager>,
270+
}
271+
272+
impl DynPhantomKeysInterface {
273+
/// Create a new DynPhantomKeysInterface
274+
pub fn new(inner: PhantomKeysManager) -> Self {
275+
DynPhantomKeysInterface { inner: Box::new(inner) }
276+
}
277+
}
278+
279+
delegate!(DynPhantomKeysInterface, NodeSigner,
280+
inner,
281+
fn get_node_id(, recipient: Recipient) -> Result<PublicKey, ()>,
282+
fn sign_gossip_message(, msg: UnsignedGossipMessage) -> Result<Signature, ()>,
283+
fn ecdh(, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>,
284+
fn sign_invoice(, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result<RecoverableSignature, ()>,
285+
fn sign_bolt12_invoice(, invoice: &crate::offers::invoice::UnsignedBolt12Invoice
286+
) -> Result<secp256k1::schnorr::Signature, ()>,
287+
fn get_inbound_payment_key(,) -> ExpandedKey
288+
);
289+
290+
impl SignerProvider for DynPhantomKeysInterface {
291+
type EcdsaSigner = DynSigner;
292+
#[cfg(taproot)]
293+
type TaprootSigner = DynSigner;
294+
295+
fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
296+
self.inner.get_destination_script(channel_keys_id)
297+
}
298+
299+
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
300+
self.inner.get_shutdown_scriptpubkey()
301+
}
302+
303+
fn generate_channel_keys_id(&self, _inbound: bool, _user_channel_id: u128) -> [u8; 32] {
304+
self.inner.generate_channel_keys_id(_inbound, _user_channel_id)
305+
}
306+
307+
fn derive_channel_signer(&self, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner {
308+
let inner = self.inner.derive_channel_signer(channel_keys_id);
309+
DynSigner::new(inner)
310+
}
311+
}
312+
313+
delegate!(DynPhantomKeysInterface, EntropySource, inner,
314+
fn get_secure_random_bytes(,) -> [u8; 32]
315+
);
316+
317+
delegate!(DynPhantomKeysInterface, OutputSpender, inner,
318+
fn spend_spendable_outputs(,
319+
descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
320+
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
321+
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>
322+
) -> Result<Transaction, ()>
323+
);
324+
325+
impl DynKeysInterfaceTrait for DynPhantomKeysInterface {
326+
#[cfg(test)]
327+
fn set_counter(&self, count: u64) {
328+
self.inner.inner.entropy_source.set_counter(count);
329+
}
330+
}

0 commit comments

Comments
 (0)