@@ -35,7 +35,7 @@ use crate::util::transaction_utils;
35
35
36
36
use bitcoin:: locktime:: absolute:: LockTime ;
37
37
use bitcoin:: ecdsa:: Signature as BitcoinSignature ;
38
- use bitcoin:: secp256k1:: { SecretKey , PublicKey , Scalar } ;
38
+ use bitcoin:: secp256k1:: { SecretKey , PublicKey , Scalar , Verification } ;
39
39
use bitcoin:: secp256k1:: { Secp256k1 , ecdsa:: Signature , Message } ;
40
40
use bitcoin:: { secp256k1, Sequence , Witness } ;
41
41
@@ -430,6 +430,26 @@ pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1
430
430
. expect ( "Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key." )
431
431
}
432
432
433
+ /// Computes the tweak to apply to the base funding key of a channel.
434
+ ///
435
+ /// The tweak is computed similar to existing tweaks used in
436
+ /// [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation), but
437
+ /// rather than using the `per_commitment_point`, we use the txid of the funding transaction the
438
+ /// splice transaction is spending to guarantee uniqueness, and the `revocation_basepoint` to
439
+ /// guarantee only the channel participants can re-derive the new funding key.
440
+ ///
441
+ /// tweak = SHA256(splice_parent_funding_txid || revocation_basepoint || base_funding_pubkey)
442
+ /// tweaked_funding_key = base_funding_key + tweak
443
+ //
444
+ // TODO: Expose a helper on `FundingScope` that calls this.
445
+ pub fn compute_funding_key_tweak ( base_funding_pubkey : & PublicKey , revocation_basepoint : & PublicKey , splice_parent_funding_txid : & Txid ) -> Scalar {
446
+ let mut sha = Sha256 :: engine ( ) ;
447
+ sha. input ( splice_parent_funding_txid. as_byte_array ( ) ) ;
448
+ sha. input ( & revocation_basepoint. serialize ( ) ) ;
449
+ sha. input ( & base_funding_pubkey. serialize ( ) ) ;
450
+ Scalar :: from_be_bytes ( Sha256 :: from_engine ( sha) . to_byte_array ( ) ) . unwrap ( )
451
+ }
452
+
433
453
/// The set of public keys which are used in the creation of one commitment transaction.
434
454
/// These are derived from the channel base keys and per-commitment data.
435
455
///
@@ -470,6 +490,9 @@ impl_writeable_tlv_based!(TxCreationKeys, {
470
490
pub struct ChannelPublicKeys {
471
491
/// The public key which is used to sign all commitment transactions, as it appears in the
472
492
/// on-chain channel lock-in 2-of-2 multisig output.
493
+ ///
494
+ /// NOTE: This key will already have the [`HolderChannelPublicKeys::funding_key_tweak`] applied
495
+ /// if one existed.
473
496
pub funding_pubkey : PublicKey ,
474
497
/// The base point which is used (with [`RevocationKey::from_basepoint`]) to derive per-commitment
475
498
/// revocation keys. This is combined with the per-commitment-secret generated by the
@@ -497,6 +520,113 @@ impl_writeable_tlv_based!(ChannelPublicKeys, {
497
520
( 8 , htlc_basepoint, required) ,
498
521
} ) ;
499
522
523
+ /// The holder's public keys which do not change over the life of a channel, except for the
524
+ /// `funding_pubkey`, which may rotate after each successful splice attempt via the
525
+ /// `funding_key_tweak`.
526
+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
527
+ pub struct HolderChannelPublicKeys {
528
+ keys : ChannelPublicKeys ,
529
+ /// A optional scalar tweak applied to the base funding key to obtain the channel's funding key
530
+ /// used in the 2-of-2 multisig. This is used to derive additional keys from the same secret
531
+ /// backing the base `funding_pubkey`, as we have to rotate keys for each successful splice
532
+ /// attempt. The tweak is computed as described in [`compute_funding_key_tweak`].
533
+ //
534
+ // TODO: Expose `splice_parent_funding_txid` instead so the signer can re-derive the tweak?
535
+ // There's no harm in the signer trusting the tweak as long as its funding secret has not
536
+ // been leaked.
537
+ pub funding_key_tweak : Option < Scalar > ,
538
+ }
539
+
540
+ // `HolderChannelPublicKeys` may have been previously written as `ChannelPublicKeys` so we have to
541
+ // mimic its serialization.
542
+ impl Writeable for HolderChannelPublicKeys {
543
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
544
+ write_tlv_fields ! ( writer, {
545
+ ( 0 , self . keys. funding_pubkey, required) ,
546
+ ( 2 , self . keys. revocation_basepoint, required) ,
547
+ ( 4 , self . keys. payment_point, required) ,
548
+ ( 6 , self . keys. delayed_payment_basepoint, required) ,
549
+ ( 8 , self . keys. htlc_basepoint, required) ,
550
+ ( 10 , self . funding_key_tweak, option) ,
551
+ } ) ;
552
+ Ok ( ( ) )
553
+ }
554
+ }
555
+
556
+ impl Readable for HolderChannelPublicKeys {
557
+ fn read < R : io:: Read > ( reader : & mut R ) -> Result < Self , DecodeError > {
558
+ let mut funding_pubkey = RequiredWrapper ( None ) ;
559
+ let mut revocation_basepoint = RequiredWrapper ( None ) ;
560
+ let mut payment_point = RequiredWrapper ( None ) ;
561
+ let mut delayed_payment_basepoint = RequiredWrapper ( None ) ;
562
+ let mut htlc_basepoint = RequiredWrapper ( None ) ;
563
+ let mut funding_key_tweak: Option < Scalar > = None ;
564
+
565
+ read_tlv_fields ! ( reader, {
566
+ ( 0 , funding_pubkey, required) ,
567
+ ( 2 , revocation_basepoint, required) ,
568
+ ( 4 , payment_point, required) ,
569
+ ( 6 , delayed_payment_basepoint, required) ,
570
+ ( 8 , htlc_basepoint, required) ,
571
+ ( 10 , funding_key_tweak, option) ,
572
+ } ) ;
573
+
574
+ Ok ( Self {
575
+ keys : ChannelPublicKeys {
576
+ funding_pubkey : funding_pubkey. 0 . unwrap ( ) ,
577
+ revocation_basepoint : revocation_basepoint. 0 . unwrap ( ) ,
578
+ payment_point : payment_point. 0 . unwrap ( ) ,
579
+ delayed_payment_basepoint : delayed_payment_basepoint. 0 . unwrap ( ) ,
580
+ htlc_basepoint : htlc_basepoint. 0 . unwrap ( ) ,
581
+ } ,
582
+ funding_key_tweak,
583
+ } )
584
+ }
585
+ }
586
+
587
+ impl AsRef < ChannelPublicKeys > for HolderChannelPublicKeys {
588
+ fn as_ref ( & self ) -> & ChannelPublicKeys {
589
+ & self . keys
590
+ }
591
+ }
592
+
593
+ impl From < ChannelPublicKeys > for HolderChannelPublicKeys {
594
+ fn from ( value : ChannelPublicKeys ) -> Self {
595
+ Self {
596
+ keys : value,
597
+ funding_key_tweak : None ,
598
+ }
599
+ }
600
+ }
601
+
602
+ impl HolderChannelPublicKeys {
603
+ /// Constructs a new instance of [`HolderChannelPublicKeys`].
604
+ pub fn new < C : Verification > (
605
+ funding_pubkey : PublicKey , revocation_basepoint : RevocationBasepoint ,
606
+ payment_point : PublicKey , delayed_payment_basepoint : DelayedPaymentBasepoint ,
607
+ htlc_basepoint : HtlcBasepoint , funding_key_tweak : Option < Scalar > , secp : & Secp256k1 < C > ,
608
+ ) -> Self {
609
+ let funding_pubkey = funding_key_tweak
610
+ . map ( |tweak| {
611
+ funding_pubkey
612
+ . add_exp_tweak ( secp, & tweak)
613
+ . expect ( "Addition only fails if the tweak is the inverse of the key" )
614
+ } )
615
+ . unwrap_or ( funding_pubkey) ;
616
+
617
+ Self {
618
+ keys : ChannelPublicKeys {
619
+ funding_pubkey,
620
+ revocation_basepoint,
621
+ payment_point,
622
+ delayed_payment_basepoint,
623
+ htlc_basepoint,
624
+ } ,
625
+ funding_key_tweak,
626
+ }
627
+ }
628
+ }
629
+
500
630
impl TxCreationKeys {
501
631
/// Create per-state keys from channel base points and the per-commitment point.
502
632
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
@@ -869,7 +999,7 @@ pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signatu
869
999
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
870
1000
pub struct ChannelTransactionParameters {
871
1001
/// Holder public keys
872
- pub holder_pubkeys : ChannelPublicKeys ,
1002
+ pub holder_pubkeys : HolderChannelPublicKeys ,
873
1003
/// The contest delay selected by the holder, which applies to counterparty-broadcast transactions
874
1004
pub holder_selected_contest_delay : u16 ,
875
1005
/// Whether the holder is the initiator of this channel.
@@ -933,7 +1063,7 @@ impl ChannelTransactionParameters {
933
1063
934
1064
pub ( crate ) fn make_funding_redeemscript ( & self ) -> ScriptBuf {
935
1065
make_funding_redeemscript (
936
- & self . holder_pubkeys . funding_pubkey ,
1066
+ & self . holder_pubkeys . as_ref ( ) . funding_pubkey ,
937
1067
& self . counterparty_parameters . as_ref ( ) . unwrap ( ) . pubkeys . funding_pubkey
938
1068
)
939
1069
}
@@ -953,7 +1083,10 @@ impl ChannelTransactionParameters {
953
1083
htlc_basepoint : PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) . into ( ) ,
954
1084
} ;
955
1085
Self {
956
- holder_pubkeys : dummy_keys. clone ( ) ,
1086
+ holder_pubkeys : HolderChannelPublicKeys {
1087
+ keys : dummy_keys. clone ( ) ,
1088
+ funding_key_tweak : None ,
1089
+ } ,
957
1090
holder_selected_contest_delay : 42 ,
958
1091
is_outbound_from_holder : true ,
959
1092
counterparty_parameters : Some ( CounterpartyChannelTransactionParameters {
@@ -1050,7 +1183,7 @@ impl<'a> DirectedChannelTransactionParameters<'a> {
1050
1183
/// Get the channel pubkeys for the broadcaster
1051
1184
pub fn broadcaster_pubkeys ( & self ) -> & ' a ChannelPublicKeys {
1052
1185
if self . holder_is_broadcaster {
1053
- & self . inner . holder_pubkeys
1186
+ self . inner . holder_pubkeys . as_ref ( )
1054
1187
} else {
1055
1188
& self . inner . counterparty_parameters . as_ref ( ) . unwrap ( ) . pubkeys
1056
1189
}
@@ -1061,7 +1194,7 @@ impl<'a> DirectedChannelTransactionParameters<'a> {
1061
1194
if self . holder_is_broadcaster {
1062
1195
& self . inner . counterparty_parameters . as_ref ( ) . unwrap ( ) . pubkeys
1063
1196
} else {
1064
- & self . inner . holder_pubkeys
1197
+ self . inner . holder_pubkeys . as_ref ( )
1065
1198
}
1066
1199
}
1067
1200
@@ -1149,7 +1282,10 @@ impl HolderCommitmentTransaction {
1149
1282
htlc_basepoint : HtlcBasepoint :: from ( dummy_key. clone ( ) )
1150
1283
} ;
1151
1284
let channel_parameters = ChannelTransactionParameters {
1152
- holder_pubkeys : channel_pubkeys. clone ( ) ,
1285
+ holder_pubkeys : HolderChannelPublicKeys {
1286
+ keys : channel_pubkeys. clone ( ) ,
1287
+ funding_key_tweak : None ,
1288
+ } ,
1153
1289
holder_selected_contest_delay : 0 ,
1154
1290
is_outbound_from_holder : false ,
1155
1291
counterparty_parameters : Some ( CounterpartyChannelTransactionParameters { pubkeys : channel_pubkeys. clone ( ) , selected_contest_delay : 0 } ) ,
@@ -1918,7 +2054,7 @@ pub fn get_commitment_transaction_number_obscure_factor(
1918
2054
1919
2055
#[ cfg( test) ]
1920
2056
mod tests {
1921
- use super :: { CounterpartyCommitmentSecrets , ChannelPublicKeys } ;
2057
+ use super :: { CounterpartyCommitmentSecrets , ChannelPublicKeys , HolderChannelPublicKeys } ;
1922
2058
use crate :: chain;
1923
2059
use crate :: ln:: chan_utils:: { get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction , TxCreationKeys , ChannelTransactionParameters , CounterpartyChannelTransactionParameters , HTLCOutputInCommitment } ;
1924
2060
use bitcoin:: secp256k1:: { PublicKey , SecretKey , Secp256k1 } ;
@@ -1961,7 +2097,7 @@ mod tests {
1961
2097
let counterparty_pubkeys = counterparty_signer. pubkeys ( ) . clone ( ) ;
1962
2098
let keys = TxCreationKeys :: derive_new ( & secp_ctx, & per_commitment_point, delayed_payment_base, htlc_basepoint, & counterparty_pubkeys. revocation_basepoint , & counterparty_pubkeys. htlc_basepoint ) ;
1963
2099
let channel_parameters = ChannelTransactionParameters {
1964
- holder_pubkeys : holder_pubkeys. clone ( ) ,
2100
+ holder_pubkeys : HolderChannelPublicKeys :: from ( holder_pubkeys. clone ( ) ) ,
1965
2101
holder_selected_contest_delay : 0 ,
1966
2102
is_outbound_from_holder : false ,
1967
2103
counterparty_parameters : Some ( CounterpartyChannelTransactionParameters { pubkeys : counterparty_pubkeys. clone ( ) , selected_contest_delay : 0 } ) ,
0 commit comments