Skip to content

Commit d9c4a4f

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 54af4dd commit d9c4a4f

File tree

5 files changed

+50
-51
lines changed

5 files changed

+50
-51
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 40 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,10 @@ 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 (ie htlc.offered is false), preimage must be set!
228+
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>);
229+
225230
/// Create a signature for a (proposed) closing transaction.
226231
///
227232
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
@@ -353,6 +358,40 @@ impl ChannelKeys for InMemoryChannelKeys {
353358
local_commitment_tx.add_local_sig(&self.funding_key, funding_redeemscript, channel_value_satoshis, secp_ctx);
354359
}
355360

361+
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>) {
362+
if htlc_tx.input.len() != 1 { return; }
363+
if htlc_tx.input[0].witness.len() != 0 { return; }
364+
365+
let htlc_redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
366+
367+
if let Ok(our_htlc_key) = chan_utils::derive_private_key(secp_ctx, per_commitment_point, &self.htlc_base_key) {
368+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
369+
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
370+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
371+
372+
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
373+
374+
if local_tx { // b, then a
375+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
376+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
377+
} else {
378+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
379+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
380+
}
381+
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
382+
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
383+
384+
if htlc.offered {
385+
htlc_tx.input[0].witness.push(Vec::new());
386+
assert!(preimage.is_none());
387+
} else {
388+
htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
389+
}
390+
391+
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
392+
} else { return; }
393+
}
394+
356395
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
357396
if closing_tx.input.len() != 1 { return Err(()); }
358397
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
@@ -1695,23 +1695,15 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
16951695
if htlc.offered {
16961696
log_trace!(self, "Broadcasting HTLC-Timeout transaction against local commitment transactions");
16971697
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);
1698-
let (our_sig, htlc_script) = match
1699-
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) {
1700-
Ok(res) => res,
1701-
Err(_) => continue,
1702-
};
1698+
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);
17031699

17041700
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_timeout_tx.input[0].previous_output.vout, htlc_timeout_tx.input[0].previous_output.txid);
17051701
res.push(htlc_timeout_tx);
17061702
} else {
17071703
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
17081704
log_trace!(self, "Broadcasting HTLC-Success transaction against local commitment transactions");
17091705
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);
1710-
let (our_sig, htlc_script) = match
1711-
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) {
1712-
Ok(res) => res,
1713-
Err(_) => continue,
1714-
};
1706+
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);
17151707

17161708
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_success_tx.input[0].previous_output.vout, htlc_success_tx.input[0].previous_output.txid);
17171709
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

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

88+
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>) {
89+
self.inner.sign_htlc_transaction(htlc_tx, their_sig, preimage, htlc, a_htlc_key, b_htlc_key, revocation_key, per_commitment_point, secp_ctx);
90+
}
91+
8792
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
8893
Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())
8994
}

0 commit comments

Comments
 (0)