1#![allow(missing_docs)]
2
3use bitcoin::bip32::DerivationPath;
4use bitcoin::hashes::Hash;
5use bitcoin::io::Error as IOError;
6use bitcoin::secp256k1::ecdh::SharedSecret;
7use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
8use bitcoin::secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey};
9use bitcoin::{ScriptBuf, Transaction, TxOut, WPubkeyHash};
10use lightning::ln::chan_utils;
11use lightning::ln::chan_utils::{
12 ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
13 HTLCOutputInCommitment, HolderCommitmentTransaction,
14};
15use lightning::ln::channel_keys::{DelayedPaymentKey, RevocationKey};
16use lightning::ln::msgs::{DecodeError, UnsignedChannelAnnouncement, UnsignedGossipMessage};
17use lightning::ln::script::ShutdownScript;
18use lightning::types::features::ChannelTypeFeatures;
19use lightning::types::payment::PaymentPreimage;
20
21use super::crypto_utils;
22use crate::channel::{ChannelBase, ChannelId, ChannelSetup, CommitmentType};
23use crate::invoice::Invoice;
24use crate::node::Node;
25use crate::prelude::*;
26use crate::signer::multi_signer::MultiSigner;
27use crate::tx::tx::HTLCInfo2;
28use crate::util::crypto_utils::derive_public_key;
29use crate::util::status::Status;
30use crate::util::INITIAL_COMMITMENT_NUMBER;
31use crate::Arc;
32use lightning::ln::inbound_payment::ExpandedKey;
33use lightning::sign::ecdsa::EcdsaChannelSigner;
34use lightning::sign::HTLCDescriptor;
35use lightning::sign::{
36 ChannelSigner, EntropySource, NodeSigner, Recipient, SignerProvider, SpendableOutputDescriptor,
37};
38use lightning::util::ser::{Readable, Writeable, Writer};
39use lightning_invoice::RawBolt11Invoice;
40use log::{debug, error, info};
41use vls_common::to_derivation_path;
42
43pub struct LoopbackSignerKeysInterface {
45 pub node_id: PublicKey,
46 pub signer: Arc<MultiSigner>,
47}
48
49impl LoopbackSignerKeysInterface {
50 pub fn get_node(&self) -> Arc<Node> {
51 self.signer.get_node(&self.node_id).expect("our node is missing")
52 }
53
54 pub fn add_invoice(&self, invoice: Invoice) {
55 self.get_node().add_invoice(invoice).expect("could not add invoice");
56 }
57
58 pub fn spend_spendable_outputs(
59 &self,
60 descriptors: &[&SpendableOutputDescriptor],
61 outputs: Vec<TxOut>,
62 change_destination_script: ScriptBuf,
63 feerate_sat_per_1000_weight: u32,
64 ) -> Result<Transaction, ()> {
65 self.get_node().spend_spendable_outputs(
66 descriptors,
67 outputs,
68 change_destination_script,
69 feerate_sat_per_1000_weight,
70 )
71 }
72
73 fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
74 match recipient {
75 Recipient::Node => Ok(self.get_node().get_node_secret()),
76 Recipient::PhantomNode => Err(()),
77 }
78 }
79}
80
81#[derive(Clone)]
82pub struct LoopbackChannelSigner {
83 pub node_id: PublicKey,
84 pub channel_id: ChannelId,
85 pub signer: Arc<MultiSigner>,
86 pub pubkeys: ChannelPublicKeys,
87 pub channel_value_sat: u64,
88}
89
90impl LoopbackChannelSigner {
91 fn new(
92 node_id: &PublicKey,
93 channel_id: &ChannelId,
94 signer: Arc<MultiSigner>,
95 channel_value_sat: u64,
96 ) -> LoopbackChannelSigner {
97 info!("new channel {:?} {:?}", node_id, channel_id);
98 let pubkeys = signer
99 .with_channel_base(&node_id, &channel_id, |base| Ok(base.get_channel_basepoints()))
100 .map_err(|s| {
101 error!("bad status {:?} on channel {}", s, channel_id);
102 ()
103 })
104 .expect("must be able to get basepoints");
105 LoopbackChannelSigner {
106 node_id: *node_id,
107 channel_id: channel_id.clone(),
108 signer: signer.clone(),
109 pubkeys,
110 channel_value_sat,
111 }
112 }
113
114 fn get_channel_setup(&self) -> Result<ChannelSetup, ()> {
115 self.signer
116 .with_channel(&self.node_id, &self.channel_id, |chan| Ok(chan.setup.clone()))
117 .map_err(|s| self.bad_status(s))
118 }
119
120 fn bad_status(&self, s: Status) {
121 error!("bad status {:?} on channel {}", s, self.channel_id);
122 }
123
124 fn convert_to_htlc_info2(htlcs: &[HTLCOutputInCommitment]) -> (Vec<HTLCInfo2>, Vec<HTLCInfo2>) {
125 let mut offered_htlcs = Vec::new();
126 let mut received_htlcs = Vec::new();
127 for htlc in htlcs {
128 let htlc_info = HTLCInfo2 {
129 value_sat: htlc.amount_msat / 1000,
130 payment_hash: htlc.payment_hash,
131 cltv_expiry: htlc.cltv_expiry,
132 };
133 if htlc.offered {
134 offered_htlcs.push(htlc_info);
135 } else {
136 received_htlcs.push(htlc_info);
137 }
138 }
139 (offered_htlcs, received_htlcs)
140 }
141
142 fn dest_wallet_path() -> DerivationPath {
143 to_derivation_path(&[1u32])
144 }
145
146 fn features(&self) -> ChannelTypeFeatures {
147 let setup = self.get_channel_setup().expect("not ready");
148 setup.features()
149 }
150}
151
152impl Writeable for LoopbackChannelSigner {
153 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), IOError> {
154 self.channel_id.inner().write(writer)?;
155 self.channel_value_sat.write(writer)?;
156 Ok(())
157 }
158}
159
160impl ChannelSigner for LoopbackChannelSigner {
161 fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()> {
162 let forward_idx = INITIAL_COMMITMENT_NUMBER - idx;
163 self.signer
164 .with_channel(&self.node_id, &self.channel_id, |chan| {
165 chan.validate_counterparty_revocation(forward_idx, secret)
166 })
167 .map_err(|s| self.bad_status(s))?;
168
169 Ok(())
170 }
171
172 fn get_per_commitment_point(
173 &self,
174 idx: u64,
175 _secp_ctx: &Secp256k1<All>,
176 ) -> Result<PublicKey, ()> {
177 self.signer
180 .with_channel_base(&self.node_id, &self.channel_id, |base| {
181 Ok(base.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - idx).unwrap())
182 })
183 .map_err(|s| self.bad_status(s))
184 }
185
186 fn release_commitment_secret(&self, commitment_number: u64) -> Result<[u8; 32], ()> {
187 let secret = self.signer.with_channel(&self.node_id, &self.channel_id, |chan| {
190 let secret = chan
191 .get_per_commitment_secret(INITIAL_COMMITMENT_NUMBER - commitment_number)
192 .unwrap();
193 Ok(*secret.as_ref())
194 });
195 Ok(secret.expect("missing channel"))
196 }
197
198 fn validate_holder_commitment(
199 &self,
200 holder_tx: &HolderCommitmentTransaction,
201 preimages: Vec<PaymentPreimage>,
202 ) -> Result<(), ()> {
203 let commitment_number = INITIAL_COMMITMENT_NUMBER - holder_tx.commitment_number();
204
205 self.signer
206 .with_channel(&self.node_id, &self.channel_id, |chan| {
207 chan.htlcs_fulfilled(preimages);
208 let (offered_htlcs, received_htlcs) =
209 LoopbackChannelSigner::convert_to_htlc_info2(holder_tx.htlcs());
210 chan.validate_holder_commitment_tx_phase2(
211 commitment_number,
212 holder_tx.feerate_per_kw(),
213 holder_tx.to_broadcaster_value_sat(),
214 holder_tx.to_countersignatory_value_sat(),
215 offered_htlcs,
216 received_htlcs,
217 &holder_tx.counterparty_sig,
218 &holder_tx.counterparty_htlc_sigs,
219 )?;
220 chan.revoke_previous_holder_commitment(commitment_number)?;
221 Ok(())
222 })
223 .map_err(|s| self.bad_status(s))?;
224
225 Ok(())
226 }
227
228 fn pubkeys(&self) -> &ChannelPublicKeys {
229 &self.pubkeys
230 }
231
232 fn channel_keys_id(&self) -> [u8; 32] {
233 self.signer
234 .with_channel(&self.node_id, &self.channel_id, |chan| Ok(chan.keys.channel_keys_id()))
235 .expect("missing channel")
236 }
237
238 fn provide_channel_parameters(&mut self, parameters: &ChannelTransactionParameters) {
239 info!("set_remote_channel_pubkeys {:?} {:?}", self.node_id, self.channel_id);
240
241 let funding_outpoint = parameters.funding_outpoint.unwrap().into_bitcoin_outpoint();
243 let counterparty_parameters = parameters.counterparty_parameters.as_ref().unwrap();
244 let setup = ChannelSetup {
245 is_outbound: parameters.is_outbound_from_holder,
246 channel_value_sat: self.channel_value_sat,
247 push_value_msat: 0, funding_outpoint,
249 holder_selected_contest_delay: parameters.holder_selected_contest_delay,
250 holder_shutdown_script: None, counterparty_points: counterparty_parameters.pubkeys.clone(),
252 counterparty_selected_contest_delay: counterparty_parameters.selected_contest_delay,
253 counterparty_shutdown_script: None, commitment_type: CommitmentType::StaticRemoteKey, };
256 let node = self.signer.get_node(&self.node_id).expect("no such node");
257
258 node.setup_channel(self.channel_id.clone(), None, setup, &DerivationPath::master())
259 .expect("channel already ready or does not exist");
260 }
261}
262
263impl EcdsaChannelSigner for LoopbackChannelSigner {
264 fn sign_counterparty_commitment(
266 &self,
267 commitment_tx: &CommitmentTransaction,
268 inbound_htlc_preimages: Vec<PaymentPreimage>,
269 outbound_htlc_preimages: Vec<PaymentPreimage>,
270 _secp_ctx: &Secp256k1<All>,
271 ) -> Result<(Signature, Vec<Signature>), ()> {
272 let trusted_tx = commitment_tx.trust();
273 info!(
274 "sign_counterparty_commitment {:?} {:?} txid {}",
275 self.node_id,
276 self.channel_id,
277 trusted_tx.built_transaction().txid,
278 );
279
280 let (offered_htlcs, received_htlcs) =
281 LoopbackChannelSigner::convert_to_htlc_info2(commitment_tx.htlcs());
282
283 let per_commitment_point = trusted_tx.keys().per_commitment_point;
285
286 let commitment_number = INITIAL_COMMITMENT_NUMBER - commitment_tx.commitment_number();
287 let to_holder_value_sat = commitment_tx.to_countersignatory_value_sat();
288 let to_counterparty_value_sat = commitment_tx.to_broadcaster_value_sat();
289 let feerate_per_kw = commitment_tx.feerate_per_kw();
290
291 let (commitment_sig, htlc_sigs) = self
292 .signer
293 .with_channel(&self.node_id, &self.channel_id, |chan| {
294 chan.htlcs_fulfilled(inbound_htlc_preimages);
295 chan.htlcs_fulfilled(outbound_htlc_preimages);
296 chan.sign_counterparty_commitment_tx_phase2(
297 &per_commitment_point,
298 commitment_number,
299 feerate_per_kw,
300 to_holder_value_sat,
301 to_counterparty_value_sat,
302 offered_htlcs,
303 received_htlcs,
304 )
305 })
306 .map_err(|s| self.bad_status(s))?;
307 Ok((commitment_sig, htlc_sigs))
308 }
309
310 fn sign_holder_commitment(
311 &self,
312 hct: &HolderCommitmentTransaction,
313 _secp_ctx: &Secp256k1<All>,
314 ) -> Result<Signature, ()> {
315 let commitment_tx = hct.trust();
316
317 debug!("loopback: sign local txid {}", commitment_tx.built_transaction().txid);
318
319 let commitment_number = INITIAL_COMMITMENT_NUMBER - hct.commitment_number();
320 let to_holder_value_sat = hct.to_broadcaster_value_sat();
321 let to_counterparty_value_sat = hct.to_countersignatory_value_sat();
322 let feerate_per_kw = hct.feerate_per_kw();
323 let (offered_htlcs, received_htlcs) =
324 LoopbackChannelSigner::convert_to_htlc_info2(hct.htlcs());
325
326 let sig = self
327 .signer
328 .with_channel(&self.node_id, &self.channel_id, |chan| {
329 let result = chan.sign_holder_commitment_tx_phase2_redundant(
330 commitment_number,
331 feerate_per_kw,
332 to_holder_value_sat,
333 to_counterparty_value_sat,
334 offered_htlcs.clone(),
335 received_htlcs.clone(),
336 )?;
337 Ok(result)
338 })
339 .map_err(|s| self.bad_status(s))?;
340
341 Ok(sig)
342 }
343
344 fn unsafe_sign_holder_commitment(
345 &self,
346 hct: &HolderCommitmentTransaction,
347 secp_ctx: &Secp256k1<All>,
348 ) -> Result<Signature, ()> {
349 let signature = self
350 .signer
351 .with_channel(&self.node_id, &self.channel_id, |chan| {
352 chan.keys
353 .unsafe_sign_holder_commitment(hct, secp_ctx)
354 .map_err(|_| Status::internal("could not unsafe-sign"))
355 })
356 .map_err(|_s| ())?;
357 Ok(signature)
358 }
359
360 fn sign_justice_revoked_output(
361 &self,
362 justice_tx: &Transaction,
363 input: usize,
364 amount: u64,
365 per_commitment_key: &SecretKey,
366 secp_ctx: &Secp256k1<All>,
367 ) -> Result<Signature, ()> {
368 let per_commitment_point = PublicKey::from_secret_key(secp_ctx, per_commitment_key);
369 let setup = self.get_channel_setup()?;
370 let counterparty_pubkeys = setup.counterparty_points;
371
372 let (revocation_key, delayed_payment_key) = get_delayed_payment_keys(
373 secp_ctx,
374 &per_commitment_point,
375 &counterparty_pubkeys,
376 &self.pubkeys,
377 )?;
378 let redeem_script = chan_utils::get_revokeable_redeemscript(
379 &RevocationKey(revocation_key),
380 setup.holder_selected_contest_delay,
381 &DelayedPaymentKey(delayed_payment_key),
382 );
383
384 let wallet_path = LoopbackChannelSigner::dest_wallet_path();
385
386 let sig = self
388 .signer
389 .with_channel(&self.node_id, &self.channel_id, |chan| {
390 chan.sign_justice_sweep(
391 justice_tx,
392 input,
393 per_commitment_key,
394 &redeem_script,
395 amount,
396 &wallet_path,
397 )
398 })
399 .map_err(|s| self.bad_status(s))?;
400
401 Ok(sig)
402 }
403
404 fn sign_justice_revoked_htlc(
405 &self,
406 justice_tx: &Transaction,
407 input: usize,
408 amount: u64,
409 per_commitment_key: &SecretKey,
410 htlc: &HTLCOutputInCommitment,
411 secp_ctx: &Secp256k1<All>,
412 ) -> Result<Signature, ()> {
413 let per_commitment_point = PublicKey::from_secret_key(secp_ctx, per_commitment_key);
414 let wallet_path = LoopbackChannelSigner::dest_wallet_path();
415
416 let sig = self
418 .signer
419 .with_channel(&self.node_id, &self.channel_id, |chan| {
420 let tx_keys = chan.make_counterparty_tx_keys(&per_commitment_point);
421 let redeem_script =
422 chan_utils::get_htlc_redeemscript(&htlc, &self.features(), &tx_keys);
423 chan.sign_justice_sweep(
424 justice_tx,
425 input,
426 per_commitment_key,
427 &redeem_script,
428 amount,
429 &wallet_path,
430 )
431 })
432 .map_err(|s| self.bad_status(s))?;
433
434 Ok(sig)
435 }
436
437 fn sign_holder_htlc_transaction(
438 &self,
439 htlc_tx: &Transaction,
440 _input: usize,
441 htlc_descriptor: &HTLCDescriptor,
442 secp_ctx: &Secp256k1<All>,
443 ) -> Result<Signature, ()> {
444 let signature = self
445 .signer
446 .with_channel(&self.node_id, &self.channel_id, |channel| {
447 let per_commitment_point = &htlc_descriptor.per_commitment_point;
448 let chan_keys = channel.make_holder_tx_keys(per_commitment_point);
449 let witness_script = htlc_descriptor.witness_script(secp_ctx);
450 let redeem_script = chan_utils::get_htlc_redeemscript(
451 &htlc_descriptor.htlc,
452 &self.features(),
453 &chan_keys,
454 );
455 channel.sign_htlc_tx(
457 htlc_tx,
458 per_commitment_point,
459 &redeem_script,
460 htlc_descriptor.htlc.amount_msat / 1000,
461 &witness_script,
462 false,
463 chan_keys.clone(),
464 )
465 })
466 .expect("sign_htlc_tx");
467 Ok(signature.sig)
468 }
469
470 fn sign_counterparty_htlc_transaction(
471 &self,
472 htlc_tx: &Transaction,
473 input: usize,
474 amount: u64,
475 per_commitment_point: &PublicKey,
476 htlc: &HTLCOutputInCommitment,
477 _secp_ctx: &Secp256k1<All>,
478 ) -> Result<Signature, ()> {
479 let wallet_path = LoopbackChannelSigner::dest_wallet_path();
480
481 let sig = self
483 .signer
484 .with_channel(&self.node_id, &self.channel_id, |chan| {
485 let chan_keys = chan.make_counterparty_tx_keys(per_commitment_point);
486 let redeem_script =
487 chan_utils::get_htlc_redeemscript(htlc, &self.features(), &chan_keys);
488 chan.sign_counterparty_htlc_sweep(
489 htlc_tx,
490 input,
491 per_commitment_point,
492 &redeem_script,
493 amount,
494 &wallet_path,
495 )
496 })
497 .map_err(|s| self.bad_status(s))?;
498
499 Ok(sig)
500 }
501
502 fn sign_closing_transaction(
504 &self,
505 closing_tx: &ClosingTransaction,
506 _secp_ctx: &Secp256k1<All>,
507 ) -> Result<Signature, ()> {
508 info!("sign_closing_transaction {:?} {:?}", self.node_id, self.channel_id);
509
510 self.signer
512 .with_channel(&self.node_id, &self.channel_id, |chan| {
513 let holder_wallet_path_hint = to_derivation_path(&[2u32]);
515
516 chan.sign_mutual_close_tx_phase2(
517 closing_tx.to_holder_value_sat(),
518 closing_tx.to_counterparty_value_sat(),
519 &Some(closing_tx.to_holder_script().into()),
520 &Some(closing_tx.to_counterparty_script().into()),
521 &holder_wallet_path_hint,
522 )
523 })
524 .map_err(|_| ())
525 }
526
527 fn sign_holder_anchor_input(
528 &self,
529 _anchor_tx: &Transaction,
530 _input: usize,
531 _secp_ctx: &Secp256k1<All>,
532 ) -> Result<Signature, ()> {
533 todo!()
534 }
535
536 fn sign_channel_announcement_with_funding_key(
537 &self,
538 msg: &UnsignedChannelAnnouncement,
539 _secp_ctx: &Secp256k1<All>,
540 ) -> Result<Signature, ()> {
541 info!("sign_counterparty_commitment {:?} {:?}", self.node_id, self.channel_id);
542
543 self.signer
544 .with_channel(&self.node_id, &self.channel_id, |chan| {
545 Ok(chan.sign_channel_announcement_with_funding_key(&msg.encode()))
546 })
547 .map_err(|s| self.bad_status(s))
548 }
549
550 fn sign_splicing_funding_input(
551 &self,
552 _tx: &Transaction,
553 _input_index: usize,
554 _input_value: u64,
555 _secp_ctx: &Secp256k1<All>,
556 ) -> Result<Signature, ()> {
557 todo!("sign_splicing_funding_input - #538")
558 }
559}
560
561impl SignerProvider for LoopbackSignerKeysInterface {
562 type EcdsaSigner = LoopbackChannelSigner;
563
564 fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
566 let wallet_path = LoopbackChannelSigner::dest_wallet_path();
567 let pubkey = self.get_node().get_wallet_pubkey(&wallet_path).expect("pubkey");
568 Ok(ScriptBuf::new_p2wpkh(&WPubkeyHash::hash(&pubkey.0.serialize())))
569 }
570
571 fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
572 Ok(self.get_node().get_ldk_shutdown_scriptpubkey())
574 }
575
576 fn generate_channel_keys_id(
577 &self,
578 _inbound: bool,
579 _channel_value_satoshis: u64,
580 _user_channel_id: u128,
581 ) -> [u8; 32] {
582 let node = self.signer.get_node(&self.node_id).unwrap();
583 let (channel_id, _) = node.new_channel_with_random_id(&node).unwrap();
584 channel_id.ldk_channel_keys_id()
585 }
586
587 fn derive_channel_signer(
588 &self,
589 channel_value_satoshis: u64,
590 channel_keys_id: [u8; 32],
591 ) -> Self::EcdsaSigner {
592 let channel_id = ChannelId::new(&channel_keys_id);
593 LoopbackChannelSigner::new(
594 &self.node_id,
595 &channel_id,
596 Arc::clone(&self.signer),
597 channel_value_satoshis,
598 )
599 }
600
601 fn read_chan_signer(&self, mut reader: &[u8]) -> Result<Self::EcdsaSigner, DecodeError> {
602 let channel_id = ChannelId::new(&Vec::read(&mut reader)?);
603 let channel_value_sat = Readable::read(&mut reader)?;
604 Ok(LoopbackChannelSigner::new(
605 &self.node_id,
606 &channel_id,
607 Arc::clone(&self.signer),
608 channel_value_sat,
609 ))
610 }
611}
612
613impl EntropySource for LoopbackSignerKeysInterface {
614 fn get_secure_random_bytes(&self) -> [u8; 32] {
615 self.get_node().get_secure_random_bytes()
616 }
617}
618
619impl NodeSigner for LoopbackSignerKeysInterface {
620 fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
621 let node_secret = self.get_node_secret(recipient)?;
622
623 Ok(PublicKey::from_secret_key(&Secp256k1::signing_only(), &node_secret))
624 }
625
626 fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
627 let node = self.get_node();
628 let sig = node.sign_gossip_message(&msg).expect("sign_gossip_message");
629 Ok(sig)
630 }
631
632 fn ecdh(
633 &self,
634 recipient: Recipient,
635 other_key: &PublicKey,
636 tweak: Option<&Scalar>,
637 ) -> Result<SharedSecret, ()> {
638 let mut node_secret = self.get_node_secret(recipient)?;
639 if let Some(tweak) = tweak {
640 node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
641 }
642 Ok(SharedSecret::new(other_key, &node_secret))
643 }
644
645 fn sign_invoice(
646 &self,
647 invoice: &RawBolt11Invoice,
648 recipient: Recipient,
649 ) -> Result<RecoverableSignature, ()> {
650 match recipient {
651 Recipient::Node => {}
652 Recipient::PhantomNode => return Err(()),
653 };
654 self.get_node().sign_bolt11_invoice(invoice.clone()).map_err(|_| ())
655 }
656
657 fn sign_bolt12_invoice(
658 &self,
659 _: &lightning::offers::invoice::UnsignedBolt12Invoice,
660 ) -> Result<bitcoin::secp256k1::schnorr::Signature, ()> {
661 todo!()
662 }
663
664 fn get_inbound_payment_key(&self) -> ExpandedKey {
665 self.get_node().get_inbound_payment_key_material()
666 }
667}
668
669fn get_delayed_payment_keys(
670 secp_ctx: &Secp256k1<All>,
671 per_commitment_point: &PublicKey,
672 a_pubkeys: &ChannelPublicKeys,
673 b_pubkeys: &ChannelPublicKeys,
674) -> Result<(PublicKey, PublicKey), ()> {
675 let revocation_key = crypto_utils::derive_public_revocation_key(
676 secp_ctx,
677 &per_commitment_point,
678 &b_pubkeys.revocation_basepoint,
679 )?;
680 let delayed_payment_key =
681 derive_public_key(secp_ctx, &per_commitment_point, &a_pubkeys.delayed_payment_basepoint.0)
682 .map_err(|_| ())?;
683 Ok((revocation_key.0, delayed_payment_key))
684}