Skip to content

Commit 6c1edd1

Browse files
committed
Implement Script for Witness and Add Tweak in PSBT.
Adding Witness Script and key tweaks makes a Partially Signed Bitcoin Transaction the single data source needed for a Signer to produce valid signatures. A Signer is not required to be able to generate L2 keys, e.g delayed payment basepoint.
1 parent 6264a44 commit 6c1edd1

File tree

3 files changed

+138
-11
lines changed

3 files changed

+138
-11
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4330,6 +4330,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
43304330
revocation_pubkey: broadcasted_holder_revokable_script.2,
43314331
channel_keys_id: self.channel_keys_id,
43324332
channel_value_satoshis: self.channel_value_satoshis,
4333+
channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
43334334
}));
43344335
}
43354336
}

lightning/src/ln/channel_keys.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,30 @@ macro_rules! basepoint_impl {
3737
pub fn to_public_key(&self) -> PublicKey {
3838
self.0
3939
}
40+
41+
/// Derives a per-commitment-transaction (eg an htlc key or delayed_payment key) private key addition tweak
42+
/// from a basepoint and a per_commitment_point:
43+
/// `privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`
44+
/// This calculates the hash part in the tweak derivation process, which is used to ensure
45+
/// that each key is unique and cannot be guessed by an external party. It is equivalent
46+
/// to the `from_basepoint` method, but without the addition operation, providing just the
47+
/// tweak from the hash of the per_commitment_point and the basepoint.
48+
pub fn derive_add_tweak(
49+
&self,
50+
per_commitment_point: &PublicKey,
51+
) -> [u8; 32] {
52+
let mut sha = Sha256::engine();
53+
sha.input(&per_commitment_point.serialize());
54+
sha.input(&self.to_public_key().serialize());
55+
Sha256::from_engine(sha).to_byte_array()
56+
}
4057
}
4158

4259
impl From<PublicKey> for $BasepointT {
4360
fn from(value: PublicKey) -> Self {
4461
Self(value)
4562
}
4663
}
47-
4864
}
4965
}
5066
macro_rules! key_impl {

lightning/src/sign/mod.rs

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ use bitcoin::{secp256k1, Sequence, Txid, Witness};
4040
use crate::chain::transaction::OutPoint;
4141
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
4242
use crate::ln::chan_utils::{
43-
make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction,
44-
CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
43+
get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
44+
ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
45+
HTLCOutputInCommitment, HolderCommitmentTransaction,
4546
};
4647
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
4748
use crate::ln::channel_keys::{
@@ -68,6 +69,7 @@ use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
6869
use crate::sign::taproot::TaprootChannelSigner;
6970
use crate::util::atomic_counter::AtomicCounter;
7071
use crate::util::invoice::construct_invoice_preimage;
72+
use core::convert::TryInto;
7173
use core::ops::Deref;
7274
use core::sync::atomic::{AtomicUsize, Ordering};
7375
#[cfg(taproot)]
@@ -108,7 +110,13 @@ pub struct DelayedPaymentOutputDescriptor {
108110
pub channel_keys_id: [u8; 32],
109111
/// The value of the channel which this output originated from, possibly indirectly.
110112
pub channel_value_satoshis: u64,
113+
/// The channel public keys and other parameters needed to generate a spending transaction or to provide to a re-derived signer through
114+
/// [`ChannelSigner::provide_channel_parameters`].
115+
///
116+
/// Added as optional, but always `Some` if the descriptor was produced in v0.0.123 or later.
117+
pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
111118
}
119+
112120
impl DelayedPaymentOutputDescriptor {
113121
/// The maximum length a well-formed witness spending one of these should have.
114122
/// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
@@ -127,6 +135,7 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
127135
(8, revocation_pubkey, required),
128136
(10, channel_keys_id, required),
129137
(12, channel_value_satoshis, required),
138+
(13, channel_transaction_parameters, option),
130139
});
131140

132141
pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
@@ -155,6 +164,7 @@ pub struct StaticPaymentOutputDescriptor {
155164
/// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later.
156165
pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
157166
}
167+
158168
impl StaticPaymentOutputDescriptor {
159169
/// Returns the `witness_script` of the spendable output.
160170
///
@@ -306,23 +316,121 @@ impl SpendableOutputDescriptor {
306316
///
307317
/// This is not exported to bindings users as there is no standard serialization for an input.
308318
/// See [`Self::create_spendable_outputs_psbt`] instead.
309-
pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
319+
///
320+
/// The proprietary field is used to store add tweak for the signing key of this transaction.
321+
/// See the [DelayedPaymentBasepoint::derive_add_tweak] docs for more info on add tweak and how to use it.
322+
///
323+
/// To get the proprietary field use:
324+
/// ```
325+
/// use bitcoin::psbt::{PartiallySignedTransaction};
326+
/// use bitcoin::hashes::hex::FromHex;
327+
///
328+
/// # let s = "70736274ff0100520200000001dee978529ab3e61a2987bea5183713d0e6d5ceb5ac81100fdb54a1a2\
329+
/// # 69cef505000000000090000000011f26000000000000160014abb3ab63280d4ccc5c11d6b50fd427a8\
330+
/// # e19d6470000000000001012b10270000000000002200200afe4736760d814a2651bae63b572d935d9a\
331+
/// # b74a1a16c01774e341a32afa763601054d63210394a27a700617f5b7aee72bd4f8076b5770a582b7fb\
332+
/// # d1d4ee2ea3802cd3cfbe2067029000b27521034629b1c8fdebfaeb58a74cd181f485e2c462e594cb30\
333+
/// # 34dee655875f69f6c7c968ac20fc144c444b5f7370656e6461626c655f6f7574707574006164645f74\
334+
/// # 7765616b20a86534f38ad61dc580ef41c3886204adf0911b81619c1ad7a2f5b5de39a2ba600000";
335+
/// # let psbt = PartiallySignedTransaction::deserialize(<Vec<u8> as FromHex>::from_hex(s).unwrap().as_slice()).unwrap();
336+
/// let key = bitcoin::psbt::raw::ProprietaryKey {
337+
/// prefix: "LDK_spendable_output".as_bytes().to_vec(),
338+
/// subtype: 0,
339+
/// key: "add_tweak".as_bytes().to_vec(),
340+
/// };
341+
/// let value = psbt
342+
/// .inputs
343+
/// .first()
344+
/// .expect("Unable to get add tweak as there are no inputs")
345+
/// .proprietary
346+
/// .get(&key)
347+
/// .map(|x| x.to_owned());
348+
/// ```
349+
pub fn to_psbt_input<T: secp256k1::Signing>(
350+
&self, secp_ctx: &Secp256k1<T>,
351+
) -> bitcoin::psbt::Input {
310352
match self {
311353
SpendableOutputDescriptor::StaticOutput { output, .. } => {
312354
// Is a standard P2WPKH, no need for witness script
313355
bitcoin::psbt::Input { witness_utxo: Some(output.clone()), ..Default::default() }
314356
},
315-
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
316-
// TODO we could add the witness script as well
357+
SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
358+
channel_transaction_parameters,
359+
per_commitment_point,
360+
revocation_pubkey,
361+
to_self_delay,
362+
output,
363+
..
364+
}) => {
365+
let delayed_payment_basepoint = channel_transaction_parameters
366+
.as_ref()
367+
.map(|params| params.holder_pubkeys.delayed_payment_basepoint);
368+
369+
let (witness_script, add_tweak) =
370+
if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
371+
let payment_key = DelayedPaymentKey::from_basepoint(
372+
secp_ctx,
373+
basepoint,
374+
&per_commitment_point,
375+
);
376+
// Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
377+
let add_tweak = basepoint.derive_add_tweak(&per_commitment_point);
378+
(
379+
Some(get_revokeable_redeemscript(
380+
&revocation_pubkey,
381+
*to_self_delay,
382+
&payment_key,
383+
)),
384+
Some(add_tweak),
385+
)
386+
} else {
387+
(None, None)
388+
};
389+
317390
bitcoin::psbt::Input {
318-
witness_utxo: Some(descriptor.output.clone()),
391+
witness_utxo: Some(output.clone()),
392+
witness_script,
393+
proprietary: add_tweak
394+
.map(|add_tweak| {
395+
[(
396+
bitcoin::psbt::raw::ProprietaryKey {
397+
// A non standard namespace for spendable outputs, used to store the tweak needed
398+
// to derive the private key
399+
prefix: "LDK_spendable_output".as_bytes().to_vec(),
400+
subtype: 0,
401+
key: "add_tweak".as_bytes().to_vec(),
402+
},
403+
add_tweak.to_vec(),
404+
)]
405+
.into_iter()
406+
.collect()
407+
})
408+
.unwrap_or_default(),
319409
..Default::default()
320410
}
321411
},
322412
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
323-
// TODO we could add the witness script as well
413+
let witness_script = if let Some(true) = descriptor
414+
.channel_transaction_parameters
415+
.as_ref()
416+
.and_then(|channel_params| Some(channel_params.supports_anchors()))
417+
{
418+
descriptor.witness_script()
419+
} else {
420+
descriptor.channel_transaction_parameters.as_ref().and_then(|channel_params| {
421+
// Use simplified derivation, assuming `option_static_remotekey` is negotiated.
422+
// `remote_payment_basepoint` is used as Payment Key.
423+
let payment_point = channel_params.holder_pubkeys.payment_point;
424+
Some(ScriptBuf::new_p2pkh(
425+
&bitcoin::PublicKey::new(payment_point).pubkey_hash(),
426+
))
427+
})
428+
};
429+
// With simplified derivation, the private payment key is equal to private payment basepoint,
430+
// so add tweak is not needed.
324431
bitcoin::psbt::Input {
325432
witness_utxo: Some(descriptor.output.clone()),
433+
witness_script,
326434
..Default::default()
327435
}
328436
},
@@ -345,8 +453,8 @@ impl SpendableOutputDescriptor {
345453
/// does not match the one we can spend.
346454
///
347455
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
348-
pub fn create_spendable_outputs_psbt(
349-
descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
456+
pub fn create_spendable_outputs_psbt<T: secp256k1::Signing>(
457+
secp_ctx: &Secp256k1<T>, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
350458
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
351459
locktime: Option<LockTime>,
352460
) -> Result<(PartiallySignedTransaction, u64), ()> {
@@ -438,7 +546,8 @@ impl SpendableOutputDescriptor {
438546
change_destination_script,
439547
)?;
440548

441-
let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
549+
let psbt_inputs =
550+
descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx)).collect::<Vec<_>>();
442551
let psbt = PartiallySignedTransaction {
443552
inputs: psbt_inputs,
444553
outputs: vec![Default::default(); tx.output.len()],
@@ -2112,6 +2221,7 @@ impl OutputSpender for KeysManager {
21122221
) -> Result<Transaction, ()> {
21132222
let (mut psbt, expected_max_weight) =
21142223
SpendableOutputDescriptor::create_spendable_outputs_psbt(
2224+
secp_ctx,
21152225
descriptors,
21162226
outputs,
21172227
change_destination_script,

0 commit comments

Comments
 (0)