Skip to content

Commit ead76c6

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 1d782e5 commit ead76c6

File tree

5 files changed

+49
-51
lines changed

5 files changed

+49
-51
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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};
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 Transaction, 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
@@ -378,6 +383,40 @@ impl ChannelKeys for InMemoryChannelKeys {
378383
local_commitment_tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
379384
}
380385

386+
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>) {
387+
if htlc_tx.input.len() != 1 { return; }
388+
if htlc_tx.input[0].witness.len() != 0 { return; }
389+
390+
let htlc_redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
391+
392+
if let Ok(our_htlc_key) = chan_utils::derive_private_key(secp_ctx, per_commitment_point, &self.htlc_base_key) {
393+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
394+
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
395+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
396+
397+
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
398+
399+
if local_tx { // b, then a
400+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
401+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
402+
} else {
403+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
404+
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
405+
}
406+
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
407+
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
408+
409+
if htlc.offered {
410+
htlc_tx.input[0].witness.push(Vec::new());
411+
assert!(preimage.is_none());
412+
} else {
413+
htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
414+
}
415+
416+
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
417+
} else { return; }
418+
}
419+
381420
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
382421
if closing_tx.input.len() != 1 { return Err(()); }
383422
if closing_tx.input[0].witness.len() != 0 { return Err(()); }

lightning/src/ln/chan_utils.rs

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ use bitcoin::blockdata::script::{Script,Builder};
66
use bitcoin::blockdata::opcodes;
77
use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction, SigHashType};
88
use bitcoin::consensus::encode::{self, Decodable, Encodable};
9-
use bitcoin::util::bip143;
109

1110
use bitcoin_hashes::{Hash, HashEngine};
1211
use bitcoin_hashes::sha256::Hash as Sha256;
1312
use bitcoin_hashes::ripemd160::Hash as Ripemd160;
1413
use bitcoin_hashes::hash160::Hash as Hash160;
1514
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
1615

17-
use ln::channelmanager::{PaymentHash, PaymentPreimage};
16+
use ln::channelmanager::PaymentHash;
1817
use ln::msgs::DecodeError;
1918
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
2019
use util::byte_utils;
@@ -355,7 +354,7 @@ impl_writeable!(HTLCOutputInCommitment, 1 + 8 + 4 + 32 + 5, {
355354
});
356355

357356
#[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 {
357+
pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey) -> Script {
359358
let payment_hash160 = Ripemd160::hash(&htlc.payment_hash.0[..]).into_inner();
360359
if htlc.offered {
361360
Builder::new().push_opcode(opcodes::all::OP_DUP)
@@ -475,43 +474,6 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
475474
}
476475
}
477476

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-
515477
#[derive(Clone)]
516478
/// We use this to track local commitment transactions and put off signing them until we are ready
517479
/// 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
@@ -1694,23 +1694,15 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
16941694
if htlc.offered {
16951695
log_trace!(self, "Broadcasting HTLC-Timeout transaction against local commitment transactions");
16961696
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);
1697-
let (our_sig, htlc_script) = match
1698-
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) {
1699-
Ok(res) => res,
1700-
Err(_) => continue,
1701-
};
1697+
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);
17021698

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

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