Skip to content

Commit

Permalink
Use ChannelPublicKeys in create_spendable_outputs_psbt to derive th…
Browse files Browse the repository at this point in the history
…e basepoint and the key tweak.

Implement StaticPaymentOutput to PSBT input conversion.
  • Loading branch information
yellowred committed Dec 19, 2023
1 parent 9b5a79f commit ed57652
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 31 deletions.
1 change: 0 additions & 1 deletion lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4255,7 +4255,6 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
revocation_pubkey: broadcasted_holder_revokable_script.2,
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
delayed_payment_basepoint: Some(self.onchain_tx_handler.signer.pubkeys().delayed_payment_basepoint),
}));
}
}
Expand Down
29 changes: 13 additions & 16 deletions lightning/src/ln/channel_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ macro_rules! basepoint_impl {
pub fn to_public_key(&self) -> PublicKey {
self.0
}

/// Derives a per-commitment-transaction (eg an htlc key or delayed_payment key) private key addition tweak
/// from a basepoint and a per_commitment_point:
/// `privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`
pub fn derive_add_tweak(
&self,
per_commitment_point: &PublicKey,
) -> [u8; 32] {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&self.to_public_key().serialize());
Sha256::from_engine(sha).to_byte_array()
}
}

impl From<PublicKey> for $BasepointT {
Expand Down Expand Up @@ -226,22 +239,6 @@ impl RevocationKey {
key_read_write!(RevocationKey);


/// Derives a per-commitment-transaction (eg an htlc key or delayed_payment key) private key addition tweak
/// from a delayed payment basepoint and a per_commitment_point:
/// `privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`
/// TODO(oleg): refactor after migration to LDK v119
pub fn derive_add_tweak(
per_commitment_point: &PublicKey,
basepoint: &DelayedPaymentBasepoint,
) -> Vec<u8> {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&basepoint.to_public_key().serialize());
let res = Sha256::from_engine(sha).to_byte_array();
res.to_vec()
}


#[cfg(test)]
mod test {
use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
Expand Down
57 changes: 43 additions & 14 deletions lightning/src/sign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr;
use bitcoin::{secp256k1, Sequence, Witness, Txid};

use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::util::transaction_utils;
use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
use crate::chain::transaction::OutPoint;
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::ln::{chan_utils, PaymentPreimage};
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction, get_revokeable_redeemscript};
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint, derive_add_tweak};
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint, PaymentBasepoint};
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
#[cfg(taproot)]
use crate::ln::msgs::PartialSignatureWithNonce;
Expand Down Expand Up @@ -103,8 +103,6 @@ pub struct DelayedPaymentOutputDescriptor {
pub channel_keys_id: [u8; 32],
/// The value of the channel which this output originated from, possibly indirectly.
pub channel_value_satoshis: u64,
/// Channel base key used to generate a witness data to spend this output.
pub delayed_payment_basepoint: Option<DelayedPaymentBasepoint>
}

impl DelayedPaymentOutputDescriptor {
Expand All @@ -124,7 +122,6 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
(8, revocation_pubkey, required),
(10, channel_keys_id, required),
(12, channel_value_satoshis, required),
(14, delayed_payment_basepoint, option),
});

pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
Expand Down Expand Up @@ -309,7 +306,7 @@ impl SpendableOutputDescriptor {
///
/// This is not exported to bindings users as there is no standard serialization for an input.
/// See [`Self::create_spendable_outputs_psbt`] instead.
pub fn to_psbt_input<T: secp256k1::Signing>(&self, secp_ctx: &Secp256k1<T>) -> bitcoin::psbt::Input {
pub fn to_psbt_input<T: secp256k1::Signing>(&self, secp_ctx: &Secp256k1<T>, channel_public_keys: Option<&ChannelPublicKeys>) -> bitcoin::psbt::Input {
match self {
SpendableOutputDescriptor::StaticOutput { output, .. } => {
// Is a standard P2WPKH, no need for witness script
Expand All @@ -319,14 +316,18 @@ impl SpendableOutputDescriptor {
}
},
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
let (witness_script, add_tweak) = if let Some(basepoint) = descriptor.delayed_payment_basepoint.as_ref() {
let delayed_payment_basepoint = channel_public_keys.map(|keys| DelayedPaymentBasepoint::from(
keys.delayed_payment_basepoint,
));

let (witness_script, add_tweak) = if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
let payment_key = DelayedPaymentKey::from_basepoint(
secp_ctx,
basepoint,
&descriptor.per_commitment_point,
);
// Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
let add_tweak = derive_add_tweak(&descriptor.per_commitment_point, basepoint);
let add_tweak = basepoint.derive_add_tweak(&descriptor.per_commitment_point);
(Some(get_revokeable_redeemscript(
&descriptor.revocation_pubkey,
descriptor.to_self_delay,
Expand All @@ -336,7 +337,6 @@ impl SpendableOutputDescriptor {
(None, None)
};


bitcoin::psbt::Input {
witness_utxo: Some(descriptor.output.clone()),
witness_script,
Expand All @@ -346,15 +346,43 @@ impl SpendableOutputDescriptor {
subtype: 0,
key: "add_tweak".as_bytes().to_vec(),
},
add_tweak,
add_tweak.to_vec(),
)].into_iter().collect()}).unwrap_or_default(),
..Default::default()
}
},
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
// TODO we could add the witness script as well
// Use simplified derivation, assuming `option_static_remotekey` or `option_anchors` is negotiated.
// `remote_payment_basepoint` is used to produce add tweak which is needed in order to produce the signing key.
let remote_payment_basepoint = channel_public_keys.map(|keys|
PaymentBasepoint::from(keys.payment_point)
);

let witness_script = match remote_payment_basepoint {
Some(ref basepoint) => {
// We cannot always assume that `channel_parameters` is set, so can't just call
// `self.channel_parameters()` or anything that relies on it
let supports_anchors_zero_fee_htlc_tx = descriptor.channel_transaction_parameters.as_ref()
.map(|features| features.channel_type_features.supports_anchors_zero_fee_htlc_tx())
.unwrap_or(false);

let witness_script = if supports_anchors_zero_fee_htlc_tx {
chan_utils::get_to_countersignatory_with_anchors_redeemscript(&basepoint.to_public_key())
} else {
ScriptBuf::new_p2pkh(&bitcoin::PublicKey::new(basepoint.to_public_key()).pubkey_hash())
};

// With simplified derivation, the private payment key is equal to private payment basepoint,
// so add tweak is not needed.
Some(witness_script)
},
_ => None,
};


bitcoin::psbt::Input {
witness_utxo: Some(descriptor.output.clone()),
witness_script,
..Default::default()
}
},
Expand All @@ -378,7 +406,7 @@ impl SpendableOutputDescriptor {
/// does not match the one we can spend.
///
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
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), ()> {
pub fn create_spendable_outputs_psbt(descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>, channel_public_keys: Option<&ChannelPublicKeys>) -> Result<(PartiallySignedTransaction, u64), ()> {
let secp_ctx = Secp256k1::new();
let mut input = Vec::with_capacity(descriptors.len());
let mut input_value = 0;
Expand Down Expand Up @@ -446,7 +474,7 @@ impl SpendableOutputDescriptor {
let expected_max_weight =
transaction_utils::maybe_add_change_output(&mut tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;

let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx)).collect::<Vec<_>>();
let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx, channel_public_keys)).collect::<Vec<_>>();
let psbt = PartiallySignedTransaction {
inputs: psbt_inputs,
outputs: vec![Default::default(); tx.output.len()],
Expand Down Expand Up @@ -1649,7 +1677,8 @@ impl KeysManager {
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
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, ()> {
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime)?;
// TODO: provide channel keys to construct witness script
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime, None)?;
psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;

let spend_tx = psbt.extract_tx();
Expand Down

0 comments on commit ed57652

Please sign in to comment.