Skip to content

Commit f0f2cd3

Browse files
author
Antoine Riard
committed
Remove signing htlc transaction from ChannelMonitor
Extend external signer interface to sign HTLC transactions on its behalf without seckey passing. This move will allow us to remove key access access from ChannelMonitor hot memory in further work. HTLC transactions should stay half-signed by remote until we need to broadcast them for timing-out/claiming HTLCs onchain.
1 parent 2c2202b commit f0f2cd3

File tree

5 files changed

+51
-51
lines changed

5 files changed

+51
-51
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! spendable on-chain outputs which the user owns and is responsible for using just as any other
33
//! on-chain output which is theirs.
44
5-
use bitcoin::blockdata::transaction::{Transaction, OutPoint, TxOut};
5+
use bitcoin::blockdata::transaction::{Transaction, OutPoint, TxOut, SigHashType};
66
use bitcoin::blockdata::script::{Script, Builder};
77
use bitcoin::blockdata::opcodes;
88
use bitcoin::network::constants::Network;
@@ -25,6 +25,7 @@ use util::ser::{Writeable, Writer, Readable};
2525

2626
use ln::chan_utils;
2727
use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, LocalCommitmentTransaction};
28+
use ln::channelmanager::PaymentPreimage;
2829
use ln::msgs;
2930

3031
use std::sync::Arc;
@@ -222,6 +223,11 @@ pub trait ChannelKeys : Send+Clone {
222223
/// making the callee generate it via some util function we expose)!
223224
fn sign_local_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, local_commitment_tx: &mut LocalCommitmentTransaction, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>);
224225

226+
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
227+
/// HTLC-Success transaction, preimage must be set!
228+
/// TODO: should be merged with sign_local_commitment as a slice of HTLC transactions to sign
229+
fn sign_htlc_transaction<T: secp256k1::Signing>(&self, htlc_tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1<T>);
230+
225231
/// Create a signature for a (proposed) closing transaction.
226232
///
227233
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
@@ -353,6 +359,40 @@ impl ChannelKeys for InMemoryChannelKeys {
353359
local_commitment_tx.add_local_sig(&self.funding_key, funding_redeemscript, channel_value_satoshis, secp_ctx);
354360
}
355361

362+
fn sign_htlc_transaction<T: secp256k1::Signing>(&self, htlc_tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1<T>) {
363+
if htlc_tx.input.len() != 1 { return; }
364+
if htlc_tx.input[0].witness.len() != 0 { return; }
365+
366+
let htlc_redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
367+
368+
if let Ok(our_htlc_key) = chan_utils::derive_private_key(secp_ctx, per_commitment_point, &self.htlc_base_key) {
369+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
370+
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
371+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
372+
373+
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
374+
375+
if local_tx { // b, then a
376+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
377+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
378+
} else {
379+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
380+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
381+
}
382+
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
383+
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
384+
385+
if htlc.offered {
386+
htlc_tx.input[0].witness.push(Vec::new());
387+
assert!(preimage.is_none());
388+
} else {
389+
htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
390+
}
391+
392+
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
393+
} else { return; }
394+
}
395+
356396
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
357397
if closing_tx.input.len() != 1 { return Err(()); }
358398
if closing_tx.input[0].witness.len() != 0 { return Err(()); }

lightning/src/ln/chan_utils.rs

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use bitcoin_hashes::ripemd160::Hash as Ripemd160;
1414
use bitcoin_hashes::hash160::Hash as Hash160;
1515
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
1616

17-
use ln::channelmanager::{PaymentHash, PaymentPreimage};
17+
use ln::channelmanager::PaymentHash;
1818
use ln::msgs::DecodeError;
1919
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
2020
use util::byte_utils;
@@ -355,7 +355,7 @@ impl_writeable!(HTLCOutputInCommitment, 1 + 8 + 4 + 32 + 5, {
355355
});
356356

357357
#[inline]
358-
pub(super) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey) -> Script {
358+
pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey) -> Script {
359359
let payment_hash160 = Ripemd160::hash(&htlc.payment_hash.0[..]).into_inner();
360360
if htlc.offered {
361361
Builder::new().push_opcode(opcodes::all::OP_DUP)
@@ -475,43 +475,6 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
475475
}
476476
}
477477

478-
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
479-
/// HTLC-Success transaction (ie htlc.offered is false), preimage must be set!
480-
pub(crate) fn sign_htlc_transaction<T: secp256k1::Signing>(tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey, per_commitment_point: &PublicKey, htlc_base_key: &SecretKey, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Script), ()> {
481-
if tx.input.len() != 1 { return Err(()); }
482-
if tx.input[0].witness.len() != 0 { return Err(()); }
483-
484-
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
485-
486-
let our_htlc_key = derive_private_key(secp_ctx, per_commitment_point, htlc_base_key).map_err(|_| ())?;
487-
let sighash = hash_to_message!(&bip143::SighashComponents::new(&tx).sighash_all(&tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
488-
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
489-
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
490-
491-
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
492-
493-
if local_tx { // b, then a
494-
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
495-
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
496-
} else {
497-
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
498-
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
499-
}
500-
tx.input[0].witness[1].push(SigHashType::All as u8);
501-
tx.input[0].witness[2].push(SigHashType::All as u8);
502-
503-
if htlc.offered {
504-
tx.input[0].witness.push(Vec::new());
505-
assert!(preimage.is_none());
506-
} else {
507-
tx.input[0].witness.push(preimage.unwrap().0.to_vec());
508-
}
509-
510-
tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
511-
512-
Ok((our_sig, htlc_redeemscript))
513-
}
514-
515478
#[derive(Clone)]
516479
/// We use this to track local commitment transactions and put off signing them until we are ready
517480
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we

lightning/src/ln/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4519,7 +4519,7 @@ mod tests {
45194519
assert!(preimage.is_some());
45204520
}
45214521

4522-
chan_utils::sign_htlc_transaction(&mut htlc_tx, &remote_signature, &preimage, &htlc, &keys.a_htlc_key, &keys.b_htlc_key, &keys.revocation_key, &keys.per_commitment_point, chan.local_keys.htlc_base_key(), &chan.secp_ctx).unwrap();
4522+
chan_keys.sign_htlc_transaction(&mut htlc_tx, &remote_signature, &preimage, &htlc, &keys.a_htlc_key, &keys.b_htlc_key, &keys.revocation_key, &keys.per_commitment_point, &chan.secp_ctx);
45234523
assert_eq!(serialize(&htlc_tx)[..],
45244524
hex::decode($tx_hex).unwrap()[..]);
45254525
};

lightning/src/ln/channelmonitor.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,23 +1702,15 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
17021702
if htlc.offered {
17031703
log_trace!(self, "Broadcasting HTLC-Timeout transaction against local commitment transactions");
17041704
let mut htlc_timeout_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
1705-
let (our_sig, htlc_script) = match
1706-
chan_utils::sign_htlc_transaction(&mut htlc_timeout_tx, their_sig, &None, htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, &self.onchain_detection.keys.htlc_base_key(), &self.secp_ctx) {
1707-
Ok(res) => res,
1708-
Err(_) => continue,
1709-
};
1705+
self.onchain_detection.keys.sign_htlc_transaction(&mut htlc_timeout_tx, their_sig, &None, htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, &self.secp_ctx);
17101706

17111707
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_timeout_tx.input[0].previous_output.vout, htlc_timeout_tx.input[0].previous_output.txid);
17121708
res.push(htlc_timeout_tx);
17131709
} else {
17141710
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
17151711
log_trace!(self, "Broadcasting HTLC-Success transaction against local commitment transactions");
17161712
let mut htlc_success_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
1717-
let (our_sig, htlc_script) = match
1718-
chan_utils::sign_htlc_transaction(&mut htlc_success_tx, their_sig, &Some(*payment_preimage), htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, &self.onchain_detection.keys.htlc_base_key(), &self.secp_ctx) {
1719-
Ok(res) => res,
1720-
Err(_) => continue,
1721-
};
1713+
self.onchain_detection.keys.sign_htlc_transaction(&mut htlc_success_tx, their_sig, &Some(*payment_preimage), htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, &self.secp_ctx);
17221714

17231715
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_success_tx.input[0].previous_output.vout, htlc_success_tx.input[0].previous_output.txid);
17241716
res.push(htlc_success_tx);

lightning/src/util/enforcing_trait_impls.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys, ChannelPublicKeys, LocalCommitmentTransaction};
2+
use ln::channelmanager::PaymentPreimage;
23
use ln::msgs;
34
use chain::keysinterface::{ChannelKeys, InMemoryChannelKeys};
45

@@ -83,6 +84,10 @@ impl ChannelKeys for EnforcingChannelKeys {
8384
self.inner.sign_local_commitment(local_commitment_tx, funding_redeemscript, channel_value_satoshis, secp_ctx)
8485
}
8586

87+
fn sign_htlc_transaction<T: secp256k1::Signing>(&self, htlc_tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1<T>) {
88+
self.inner.sign_htlc_transaction(htlc_tx, their_sig, preimage, htlc, a_htlc_key, b_htlc_key, revocation_key, per_commitment_point, secp_ctx);
89+
}
90+
8691
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
8792
Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())
8893
}

0 commit comments

Comments
 (0)