Skip to content

Commit 7c74131

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 cfb4391 commit 7c74131

File tree

3 files changed

+120
-23
lines changed

3 files changed

+120
-23
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4289,6 +4289,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
42894289
revocation_pubkey: broadcasted_holder_revokable_script.2,
42904290
channel_keys_id: self.channel_keys_id,
42914291
channel_value_satoshis: self.channel_value_satoshis,
4292+
channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
42924293
}));
42934294
}
42944295
}

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: 102 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
3737
use bitcoin::secp256k1::schnorr;
3838
use bitcoin::{secp256k1, Sequence, Witness, Txid};
3939

40+
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
4041
use crate::util::transaction_utils;
4142
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
4243
use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
4344
use crate::chain::transaction::OutPoint;
44-
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
4545
use crate::ln::{chan_utils, PaymentPreimage};
46-
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
46+
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction, get_revokeable_redeemscript};
4747
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint};
4848
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
4949
#[cfg(taproot)]
@@ -103,7 +103,13 @@ pub struct DelayedPaymentOutputDescriptor {
103103
pub channel_keys_id: [u8; 32],
104104
/// The value of the channel which this output originated from, possibly indirectly.
105105
pub channel_value_satoshis: u64,
106+
/// The channel public keys and other parameters needed to generate a spending transaction or to provide to a re-derived signer through
107+
/// [`ChannelSigner::provide_channel_parameters`].
108+
///
109+
/// Added as optional, but always `Some` if the descriptor was produced in v0.0.122 or later.
110+
pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
106111
}
112+
107113
impl DelayedPaymentOutputDescriptor {
108114
/// The maximum length a well-formed witness spending one of these should have.
109115
/// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
@@ -121,6 +127,7 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
121127
(8, revocation_pubkey, required),
122128
(10, channel_keys_id, required),
123129
(12, channel_value_satoshis, required),
130+
(13, channel_transaction_parameters, option),
124131
});
125132

126133
pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
@@ -149,21 +156,28 @@ pub struct StaticPaymentOutputDescriptor {
149156
/// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later.
150157
pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
151158
}
159+
152160
impl StaticPaymentOutputDescriptor {
153-
/// Returns the `witness_script` of the spendable output.
154-
///
155-
/// Note that this will only return `Some` for [`StaticPaymentOutputDescriptor`]s that
156-
/// originated from an anchor outputs channel, as they take the form of a P2WSH script.
161+
/// Returns the `witness_script` of the spendable output originated from a conventional channel.
157162
pub fn witness_script(&self) -> Option<ScriptBuf> {
158163
self.channel_transaction_parameters.as_ref()
159-
.and_then(|channel_params|
160-
if channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() {
161-
let payment_point = channel_params.holder_pubkeys.payment_point;
162-
Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point))
163-
} else {
164-
None
165-
}
166-
)
164+
.and_then(|channel_params| {
165+
// Use simplified derivation, assuming `option_static_remotekey` is negotiated.
166+
// `remote_payment_basepoint` is used as Payment Key.
167+
let payment_point = channel_params.holder_pubkeys.payment_point;
168+
Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point))
169+
})
170+
}
171+
172+
/// Returns the `scriptPubKey` of the spendable output originated from a anchor outputs channel.
173+
pub fn script_pubkey(&self) -> Option<ScriptBuf> {
174+
self.channel_transaction_parameters.as_ref()
175+
.and_then(|channel_params| {
176+
// Use simplified derivation, assuming `option_static_remotekey` is negotiated.
177+
// `remote_payment_basepoint` is used as Payment Key.
178+
let payment_point = channel_params.holder_pubkeys.payment_point;
179+
Some(ScriptBuf::new_p2pkh(&bitcoin::PublicKey::new(payment_point).pubkey_hash()))
180+
})
167181
}
168182

169183
/// The maximum length a well-formed witness spending one of these should have.
@@ -304,7 +318,34 @@ impl SpendableOutputDescriptor {
304318
///
305319
/// This is not exported to bindings users as there is no standard serialization for an input.
306320
/// See [`Self::create_spendable_outputs_psbt`] instead.
307-
pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
321+
/// To get the proprietary field use:
322+
/// ```
323+
/// use bitcoin::psbt::{PartiallySignedTransaction};
324+
/// use bitcoin::hashes::hex::FromHex;
325+
///
326+
/// let s = "70736274ff0100520200000001dee978529ab3e61a2987bea5183713d0e6d5ceb5ac81100fdb54a1a2\
327+
/// 69cef505000000000090000000011f26000000000000160014abb3ab63280d4ccc5c11d6b50fd427a8\
328+
/// e19d6470000000000001012b10270000000000002200200afe4736760d814a2651bae63b572d935d9a\
329+
/// b74a1a16c01774e341a32afa763601054d63210394a27a700617f5b7aee72bd4f8076b5770a582b7fb\
330+
/// d1d4ee2ea3802cd3cfbe2067029000b27521034629b1c8fdebfaeb58a74cd181f485e2c462e594cb30\
331+
/// 34dee655875f69f6c7c968ac20fc144c444b5f7370656e6461626c655f6f7574707574006164645f74\
332+
/// 7765616b20a86534f38ad61dc580ef41c3886204adf0911b81619c1ad7a2f5b5de39a2ba600000";
333+
/// let psbt = PartiallySignedTransaction::deserialize(<Vec<u8> as FromHex>::from_hex(s).unwrap().as_slice()).unwrap();
334+
/// let key = bitcoin::psbt::raw::ProprietaryKey {
335+
/// prefix: "LDK_spendable_output".as_bytes().to_vec(),
336+
/// subtype: 0,
337+
/// key: "add_tweak".as_bytes().to_vec(),
338+
/// };
339+
/// let value = psbt
340+
/// .inputs
341+
/// .first()
342+
/// .expect("Unable to get add tweak as there are no inputs")
343+
/// .proprietary
344+
/// .get(&key)
345+
/// .map(|x| x.to_owned());
346+
/// ```
347+
348+
pub fn to_psbt_input<T: secp256k1::Signing>(&self, secp_ctx: &Secp256k1<T>) -> bitcoin::psbt::Input {
308349
match self {
309350
SpendableOutputDescriptor::StaticOutput { output, .. } => {
310351
// Is a standard P2WPKH, no need for witness script
@@ -313,17 +354,56 @@ impl SpendableOutputDescriptor {
313354
..Default::default()
314355
}
315356
},
316-
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
317-
// TODO we could add the witness script as well
357+
SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor{
358+
channel_transaction_parameters, per_commitment_point, revocation_pubkey,
359+
to_self_delay, output, ..}) => {
360+
let delayed_payment_basepoint = channel_transaction_parameters.as_ref().map(|params| params.holder_pubkeys.delayed_payment_basepoint);
361+
362+
let (witness_script, add_tweak) = if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
363+
let payment_key = DelayedPaymentKey::from_basepoint(
364+
secp_ctx,
365+
basepoint,
366+
&per_commitment_point,
367+
);
368+
// Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
369+
let add_tweak = basepoint.derive_add_tweak(&per_commitment_point);
370+
(Some(get_revokeable_redeemscript(
371+
&revocation_pubkey,
372+
*to_self_delay,
373+
&payment_key,
374+
)), Some(add_tweak))
375+
} else {
376+
(None, None)
377+
};
378+
318379
bitcoin::psbt::Input {
319-
witness_utxo: Some(descriptor.output.clone()),
380+
witness_utxo: Some(output.clone()),
381+
witness_script,
382+
proprietary: add_tweak.map(|add_tweak| {vec![(
383+
bitcoin::psbt::raw::ProprietaryKey {
384+
// A non standard namespace for spendable outputs, used to store the tweak needed
385+
// to derive the private key
386+
prefix: "LDK_spendable_output".as_bytes().to_vec(),
387+
subtype: 0,
388+
key: "add_tweak".as_bytes().to_vec(),
389+
},
390+
add_tweak.to_vec(),
391+
)].into_iter().collect()}).unwrap_or_default(),
320392
..Default::default()
321393
}
322394
},
323395
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
324-
// TODO we could add the witness script as well
396+
let witness_script = if let Some(true) = descriptor.channel_transaction_parameters.as_ref().and_then(|channel_params| {
397+
Some(channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx())}) {
398+
descriptor.witness_script()
399+
} else {
400+
descriptor.script_pubkey()
401+
};
402+
// With simplified derivation, the private payment key is equal to private payment basepoint,
403+
// so add tweak is not needed.
325404
bitcoin::psbt::Input {
326405
witness_utxo: Some(descriptor.output.clone()),
406+
witness_script,
327407
..Default::default()
328408
}
329409
},
@@ -346,7 +426,7 @@ impl SpendableOutputDescriptor {
346426
/// does not match the one we can spend.
347427
///
348428
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
349-
pub fn create_spendable_outputs_psbt(descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>) -> Result<(PartiallySignedTransaction, u64), ()> {
429+
pub fn create_spendable_outputs_psbt(secp_ctx: &Secp256k1<secp256k1::All>, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>) -> Result<(PartiallySignedTransaction, u64), ()> {
350430
let mut input = Vec::with_capacity(descriptors.len());
351431
let mut input_value = 0;
352432
let mut witness_weight = 0;
@@ -413,7 +493,7 @@ impl SpendableOutputDescriptor {
413493
let expected_max_weight =
414494
transaction_utils::maybe_add_change_output(&mut tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
415495

416-
let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
496+
let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx)).collect::<Vec<_>>();
417497
let psbt = PartiallySignedTransaction {
418498
inputs: psbt_inputs,
419499
outputs: vec![Default::default(); tx.output.len()],
@@ -1604,7 +1684,7 @@ impl KeysManager {
16041684
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
16051685
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
16061686
pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
1607-
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime)?;
1687+
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(&self.secp_ctx, descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime)?;
16081688
psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;
16091689

16101690
let spend_tx = psbt.extract_tx();

0 commit comments

Comments
 (0)