Skip to content

Commit 1107ab0

Browse files
author
Antoine Riard
committed
Move HTLC tx generation in OnchainTxHandler
HTLC Transaction can't be bumped without sighash changes so their gneeration is one-time for nwo. We move them in OnchainTxHandler for simplifying ChannelMonitor and to prepare storage of keys material behind one external signer interface. Some tests break due to change in transaction broadcaster order. Number of transactions may vary because of temporary anti-duplicata tweak can't dissociate between 2- broadcast from different origins (ChannelMonitor, ChannelManager) and 2-broadcast from same component.
1 parent 8369541 commit 1107ab0

File tree

7 files changed

+228
-274
lines changed

7 files changed

+228
-274
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 4 additions & 35 deletions
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, SigHashType};
5+
use bitcoin::blockdata::transaction::{Transaction, OutPoint, TxOut};
66
use bitcoin::blockdata::script::{Script, Builder};
77
use bitcoin::blockdata::opcodes;
88
use bitcoin::network::constants::Network;
@@ -235,8 +235,7 @@ pub trait ChannelKeys : Send+Clone {
235235
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
236236
/// HTLC-Success transaction, preimage must be set!
237237
/// TODO: should be merged with sign_local_commitment as a slice of HTLC transactions to sign
238-
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>);
239-
238+
fn sign_htlc_transaction<T: secp256k1::Signing>(&self, local_commitment_tx: &mut LocalCommitmentTransaction, htlc_index: u32, preimage: Option<PaymentPreimage>, local_csv: u16, secp_ctx: &Secp256k1<T>);
240239
/// Create a signature for a (proposed) closing transaction.
241240
///
242241
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
@@ -373,38 +372,8 @@ impl ChannelKeys for InMemoryChannelKeys {
373372
local_commitment_tx.add_local_sig(&self.funding_key, funding_redeemscript, channel_value_satoshis, secp_ctx);
374373
}
375374

376-
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>) {
377-
if htlc_tx.input.len() != 1 { return; }
378-
if htlc_tx.input[0].witness.len() != 0 { return; }
379-
380-
let htlc_redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
381-
382-
if let Ok(our_htlc_key) = chan_utils::derive_private_key(secp_ctx, per_commitment_point, &self.htlc_base_key) {
383-
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
384-
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
385-
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
386-
387-
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
388-
389-
if local_tx { // b, then a
390-
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
391-
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
392-
} else {
393-
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
394-
htlc_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
395-
}
396-
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
397-
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
398-
399-
if htlc.offered {
400-
htlc_tx.input[0].witness.push(Vec::new());
401-
assert!(preimage.is_none());
402-
} else {
403-
htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
404-
}
405-
406-
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
407-
} else { return; }
375+
fn sign_htlc_transaction<T: secp256k1::Signing>(&self, local_commitment_tx: &mut LocalCommitmentTransaction, htlc_index: u32, preimage: Option<PaymentPreimage>, local_csv: u16, secp_ctx: &Secp256k1<T>) {
376+
local_commitment_tx.add_htlc_sig(&self.htlc_base_key, htlc_index, preimage, local_csv, secp_ctx);
408377
}
409378

410379
fn sign_closing_transaction<T: secp256k1::Signing>(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {

lightning/src/ln/chan_utils.rs

Lines changed: 105 additions & 5 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;
17+
use ln::channelmanager::{PaymentHash, PaymentPreimage};
1818
use ln::msgs::DecodeError;
1919
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
2020
use util::byte_utils;
@@ -23,6 +23,10 @@ use secp256k1::key::{SecretKey, PublicKey};
2323
use secp256k1::{Secp256k1, Signature};
2424
use secp256k1;
2525

26+
use std::{cmp, mem};
27+
28+
const MAX_ALLOC_SIZE: usize = 64*1024;
29+
2630
pub(super) const HTLC_SUCCESS_TX_WEIGHT: u64 = 703;
2731
pub(super) const HTLC_TIMEOUT_TX_WEIGHT: u64 = 663;
2832

@@ -480,7 +484,11 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
480484
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
481485
/// just pass in the SecretKeys required.
482486
pub struct LocalCommitmentTransaction {
483-
tx: Transaction
487+
tx: Transaction,
488+
//TODO: modify Channel methods to integrate HTLC material at LocalCommitmentTransaction generation to drop Option here
489+
local_keys: Option<TxCreationKeys>,
490+
feerate_per_kw: Option<u64>,
491+
per_htlc: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<Transaction>)>
484492
}
485493
impl LocalCommitmentTransaction {
486494
#[cfg(test)]
@@ -499,7 +507,11 @@ impl LocalCommitmentTransaction {
499507
input: vec![dummy_input],
500508
output: Vec::new(),
501509
lock_time: 0,
502-
} }
510+
},
511+
local_keys: None,
512+
feerate_per_kw: None,
513+
per_htlc: Vec::new()
514+
}
503515
}
504516

505517
/// Generate a new LocalCommitmentTransaction based on a raw commitment transaction,
@@ -520,7 +532,11 @@ impl LocalCommitmentTransaction {
520532
tx.input[0].witness.push(Vec::new());
521533
}
522534

523-
Self { tx }
535+
Self { tx,
536+
local_keys: None,
537+
feerate_per_kw: None,
538+
per_htlc: Vec::new()
539+
}
524540
}
525541

526542
/// Get the txid of the local commitment transaction contained in this
@@ -577,6 +593,67 @@ impl LocalCommitmentTransaction {
577593
assert!(self.has_local_sig());
578594
&self.tx
579595
}
596+
597+
/// Set HTLC cache to generate any local HTLC transaction spending one of htlc ouput
598+
/// from this local commitment transaction
599+
pub(crate) fn set_htlc_cache(&mut self, local_keys: TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<Transaction>)>) {
600+
self.local_keys = Some(local_keys);
601+
self.feerate_per_kw = Some(feerate_per_kw);
602+
self.per_htlc = htlc_outputs;
603+
}
604+
605+
/// Add local signature for a htlc transaction, do nothing if a cached signed transaction is
606+
/// already present
607+
pub fn add_htlc_sig<T: secp256k1::Signing>(&mut self, htlc_base_key: &SecretKey, htlc_index: u32, preimage: Option<PaymentPreimage>, local_csv: u16, secp_ctx: &Secp256k1<T>) {
608+
if self.local_keys.is_none() || self.feerate_per_kw.is_none() { return; }
609+
let local_keys = self.local_keys.as_ref().unwrap();
610+
let txid = self.txid();
611+
for this_htlc in self.per_htlc.iter_mut() {
612+
if this_htlc.0.transaction_output_index.unwrap() == htlc_index {
613+
if this_htlc.2.is_some() { return; } // we already have a cached htlc transaction at provided index
614+
let mut htlc_tx = build_htlc_transaction(&txid, self.feerate_per_kw.unwrap(), local_csv, &this_htlc.0, &local_keys.a_delayed_payment_key, &local_keys.revocation_key);
615+
if !this_htlc.0.offered && preimage.is_none() { return; } // if we don't have preimage for HTLC-Success, don't try to generate
616+
let htlc_secret = if !this_htlc.0.offered { preimage } else { None }; // if we have a preimage for HTLC-Timeout, don't use it that's likely a duplicate HTLC hash
617+
if this_htlc.1.is_none() { return; } // we don't have any remote signature for this htlc
618+
if htlc_tx.input.len() != 1 { return; }
619+
if htlc_tx.input[0].witness.len() != 0 { return; }
620+
621+
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc.0, &local_keys.a_htlc_key, &local_keys.b_htlc_key, &local_keys.revocation_key);
622+
623+
if let Ok(our_htlc_key) = derive_private_key(secp_ctx, &local_keys.per_commitment_point, htlc_base_key) {
624+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, this_htlc.0.amount_msat / 1000)[..]);
625+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
626+
627+
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
628+
629+
htlc_tx.input[0].witness.push(this_htlc.1.unwrap().serialize_der().to_vec());
630+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
631+
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
632+
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
633+
634+
if this_htlc.0.offered {
635+
htlc_tx.input[0].witness.push(Vec::new());
636+
assert!(htlc_secret.is_none());
637+
} else {
638+
htlc_tx.input[0].witness.push(htlc_secret.unwrap().0.to_vec());
639+
}
640+
641+
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
642+
643+
this_htlc.2 = Some(htlc_tx);
644+
} else { return; }
645+
}
646+
}
647+
}
648+
/// Expose raw htlc transaction, guarante witness is complete if non-empty
649+
pub fn htlc_with_valid_witness(&self, htlc_index: u32) -> &Option<Transaction> {
650+
for this_htlc in self.per_htlc.iter() {
651+
if this_htlc.0.transaction_output_index.unwrap() == htlc_index {
652+
return &this_htlc.2;
653+
}
654+
}
655+
&None
656+
}
580657
}
581658
impl PartialEq for LocalCommitmentTransaction {
582659
// We dont care whether we are signed in equality comparison
@@ -592,6 +669,14 @@ impl Writeable for LocalCommitmentTransaction {
592669
_ => panic!("local tx must have been well-formed!"),
593670
}
594671
}
672+
self.local_keys.write(writer)?;
673+
self.feerate_per_kw.write(writer)?;
674+
writer.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?;
675+
for &(ref htlc, ref sig, ref htlc_tx) in self.per_htlc.iter() {
676+
htlc.write(writer)?;
677+
sig.write(writer)?;
678+
htlc_tx.write(writer)?;
679+
}
595680
Ok(())
596681
}
597682
}
@@ -604,12 +689,27 @@ impl Readable for LocalCommitmentTransaction {
604689
_ => return Err(DecodeError::InvalidValue),
605690
},
606691
};
692+
let local_keys = Readable::read(reader)?;
693+
let feerate_per_kw = Readable::read(reader)?;
694+
let htlcs_count: u64 = Readable::read(reader)?;
695+
let mut per_htlc = Vec::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / mem::size_of::<(HTLCOutputInCommitment, Option<Signature>, Option<Transaction>)>()));
696+
for _ in 0..htlcs_count {
697+
let htlc: HTLCOutputInCommitment = Readable::read(reader)?;
698+
let sigs = Readable::read(reader)?;
699+
let htlc_tx = Readable::read(reader)?;
700+
per_htlc.push((htlc, sigs, htlc_tx));
701+
}
607702

608703
if tx.input.len() != 1 {
609704
// Ensure tx didn't hit the 0-input ambiguity case.
610705
return Err(DecodeError::InvalidValue);
611706
}
612-
Ok(Self { tx })
707+
Ok(Self {
708+
tx,
709+
local_keys,
710+
feerate_per_kw,
711+
per_htlc,
712+
})
613713
}
614714
}
615715

lightning/src/ln/channel.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4475,6 +4475,7 @@ mod tests {
44754475

44764476
let mut unsigned_tx: (Transaction, Vec<HTLCOutputInCommitment>);
44774477

4478+
let mut localtx;
44784479
macro_rules! test_commitment {
44794480
( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
44804481
unsigned_tx = {
@@ -4489,7 +4490,7 @@ mod tests {
44894490
let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &redeemscript, chan.channel_value_satoshis)[..]).unwrap();
44904491
secp_ctx.verify(&sighash, &their_signature, chan.their_funding_pubkey()).unwrap();
44914492

4492-
let mut localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), &their_signature, &PublicKey::from_secret_key(&secp_ctx, chan.local_keys.funding_key()), chan.their_funding_pubkey());
4493+
localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), &their_signature, &PublicKey::from_secret_key(&secp_ctx, chan.local_keys.funding_key()), chan.their_funding_pubkey());
44934494
chan_keys.sign_local_commitment(&mut localtx, &redeemscript, chan.channel_value_satoshis, &chan.secp_ctx);
44944495

44954496
assert_eq!(serialize(localtx.with_valid_witness())[..],
@@ -4498,11 +4499,11 @@ mod tests {
44984499
}
44994500

45004501
macro_rules! test_htlc_output {
4501-
( $htlc_idx: expr, $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr ) => {
4502+
( $htlc_idx: expr, $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
45024503
let remote_signature = Signature::from_der(&hex::decode($their_sig_hex).unwrap()[..]).unwrap();
45034504

45044505
let ref htlc = unsigned_tx.1[$htlc_idx];
4505-
let mut htlc_tx = chan.build_htlc_transaction(&unsigned_tx.0.txid(), &htlc, true, &keys, chan.feerate_per_kw);
4506+
let htlc_tx = chan.build_htlc_transaction(&unsigned_tx.0.txid(), &htlc, true, &keys, chan.feerate_per_kw);
45064507
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
45074508
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
45084509
secp_ctx.verify(&htlc_sighash, &remote_signature, &keys.b_htlc_key).unwrap();
@@ -4519,8 +4520,12 @@ mod tests {
45194520
assert!(preimage.is_some());
45204521
}
45214522

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);
4523-
assert_eq!(serialize(&htlc_tx)[..],
4523+
let mut per_htlc = Vec::new();
4524+
per_htlc.push((htlc.clone(), Some(remote_signature), None));
4525+
localtx.set_htlc_cache(keys.clone(), chan.feerate_per_kw, per_htlc);
4526+
chan_keys.sign_htlc_transaction(&mut localtx, $htlc_idx, preimage, chan.their_to_self_delay, &chan.secp_ctx);
4527+
4528+
assert_eq!(serialize(localtx.htlc_with_valid_witness($htlc_idx).as_ref().unwrap())[..],
45244529
hex::decode($tx_hex).unwrap()[..]);
45254530
};
45264531
}

0 commit comments

Comments
 (0)