Skip to content

Commit 5a67fef

Browse files
committed
Introduce DynSigner, a dynamically dispatched signer
DynSigner provides an abstraction for specifying an external signer for functional tests.
1 parent b16366b commit 5a67fef

File tree

6 files changed

+414
-29
lines changed

6 files changed

+414
-29
lines changed

lightning/src/sign/ecdsa.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 7 additions & 8 deletions
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

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

0 commit comments

Comments
 (0)