Skip to content

Commit fb54c85

Browse files
author
Antoine Riard
committed
Integrate OnchainTxHandler::HTLCache inside LocalCommitmentTransaction
Provide a safe API from which to withdraw local HTLC transaction built against a parent local commitment transaction all in one place.
1 parent 635be08 commit fb54c85

File tree

6 files changed

+173
-202
lines changed

6 files changed

+173
-202
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 4 additions & 34 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;
@@ -225,7 +225,7 @@ pub trait ChannelKeys : Send+Clone {
225225

226226
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
227227
/// 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>);
228+
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>);
229229

230230
/// Create a signature for a (proposed) closing transaction.
231231
///
@@ -358,38 +358,8 @@ impl ChannelKeys for InMemoryChannelKeys {
358358
local_commitment_tx.add_local_sig(&self.funding_key, funding_redeemscript, channel_value_satoshis, secp_ctx);
359359
}
360360

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; }
361+
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>) {
362+
local_commitment_tx.add_htlc_sig(&self.htlc_base_key, htlc_index, preimage, local_csv, secp_ctx);
393363
}
394364

395365
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: 102 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,11 @@ use secp256k1::key::{SecretKey, PublicKey};
2323
use secp256k1::{Secp256k1, Signature};
2424
use secp256k1;
2525

26+
use std::collections::HashMap;
27+
use std::cmp;
28+
29+
const MAX_ALLOC_SIZE: usize = 64*1024;
30+
2631
pub(super) const HTLC_SUCCESS_TX_WEIGHT: u64 = 703;
2732
pub(super) const HTLC_TIMEOUT_TX_WEIGHT: u64 = 663;
2833

@@ -480,7 +485,11 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
480485
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
481486
/// just pass in the SecretKeys required.
482487
pub struct LocalCommitmentTransaction {
483-
tx: Transaction
488+
tx: Transaction,
489+
//TODO: modify Channel methods to integrate HTLC material at LocalCommitmentTransaction generation to drop Option here
490+
local_keys: Option<TxCreationKeys>,
491+
feerate_per_kw: Option<u64>,
492+
per_htlc: HashMap<u32, (HTLCOutputInCommitment, Option<Signature>, Option<Transaction>)>
484493
}
485494
impl LocalCommitmentTransaction {
486495
#[cfg(test)]
@@ -495,7 +504,11 @@ impl LocalCommitmentTransaction {
495504
input: vec![dummy_input],
496505
output: Vec::new(),
497506
lock_time: 0,
498-
} }
507+
},
508+
local_keys: None,
509+
feerate_per_kw: None,
510+
per_htlc: HashMap::new()
511+
}
499512
}
500513

501514
/// Generate a new LocalCommitmentTransaction based on a raw commitment transaction, remote signature and both parties keys
@@ -515,7 +528,11 @@ impl LocalCommitmentTransaction {
515528
tx.input[0].witness.push(Vec::new());
516529
}
517530

518-
Self { tx }
531+
Self { tx,
532+
local_keys: None,
533+
feerate_per_kw: None,
534+
per_htlc: HashMap::new()
535+
}
519536
}
520537

521538
/// Get the txid of the local commitment transaction contained in this LocalCommitmentTransaction
@@ -567,6 +584,63 @@ impl LocalCommitmentTransaction {
567584
assert!(self.has_local_sig());
568585
&self.tx
569586
}
587+
588+
/// Set HTLC cache to generate any local HTLC transaction spending one of htlc ouput
589+
/// from this local commitment transaction
590+
pub fn set_htlc_cache(&mut self, local_keys: TxCreationKeys, feerate_per_kw: u64, htlc_outputs: HashMap<u32, (HTLCOutputInCommitment, Option<Signature>, Option<Transaction>)>) {
591+
self.local_keys = Some(local_keys);
592+
self.feerate_per_kw = Some(feerate_per_kw);
593+
self.per_htlc = htlc_outputs;
594+
}
595+
596+
/// Add local signature for a htlc transaction, do nothing if a cached signed transaction is
597+
/// already present
598+
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>) {
599+
if self.local_keys.is_none() || self.feerate_per_kw.is_none() { return; }
600+
let local_keys = self.local_keys.as_ref().unwrap();
601+
let txid = self.txid();
602+
if let Some(ref mut this_htlc) = self.per_htlc.get_mut(&htlc_index) {
603+
if this_htlc.2.is_some() { return; } // we already have a cached htlc transaction at provided index
604+
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);
605+
if !this_htlc.0.offered && preimage.is_none() { return; } // if we don't have preimage for HTLC-Success, don't try to generate
606+
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
607+
if this_htlc.1.is_none() { return; } // we don't have any remote signature for this htlc
608+
if htlc_tx.input.len() != 1 { return; }
609+
if htlc_tx.input[0].witness.len() != 0 { return; }
610+
611+
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);
612+
613+
if let Ok(our_htlc_key) = derive_private_key(secp_ctx, &local_keys.per_commitment_point, htlc_base_key) {
614+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, this_htlc.0.amount_msat / 1000)[..]);
615+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
616+
617+
htlc_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
618+
619+
htlc_tx.input[0].witness.push(this_htlc.1.unwrap().serialize_der().to_vec());
620+
htlc_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
621+
htlc_tx.input[0].witness[1].push(SigHashType::All as u8);
622+
htlc_tx.input[0].witness[2].push(SigHashType::All as u8);
623+
624+
if this_htlc.0.offered {
625+
htlc_tx.input[0].witness.push(Vec::new());
626+
assert!(htlc_secret.is_none());
627+
} else {
628+
htlc_tx.input[0].witness.push(htlc_secret.unwrap().0.to_vec());
629+
}
630+
631+
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
632+
633+
this_htlc.2 = Some(htlc_tx);
634+
} else { return; }
635+
}
636+
}
637+
/// Expose raw htlc transaction, guarante witness is complete if non-empty
638+
pub fn htlc_with_valid_witness(&self, htlc_index: u32) -> &Option<Transaction> {
639+
if let Some(ref this_htlc) = self.per_htlc.get(&htlc_index) {
640+
return &this_htlc.2;
641+
}
642+
&None
643+
}
570644
}
571645
impl PartialEq for LocalCommitmentTransaction {
572646
// We dont care whether we are signed in equality comparison
@@ -582,6 +656,14 @@ impl Writeable for LocalCommitmentTransaction {
582656
_ => panic!("local tx must have been well-formed!"),
583657
}
584658
}
659+
self.local_keys.write(writer)?;
660+
self.feerate_per_kw.write(writer)?;
661+
writer.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?;
662+
for (ref _index, &(ref htlc, ref sig, ref htlc_tx)) in self.per_htlc.iter() {
663+
htlc.write(writer)?;
664+
sig.write(writer)?;
665+
htlc_tx.write(writer)?;
666+
}
585667
Ok(())
586668
}
587669
}
@@ -594,12 +676,27 @@ impl Readable for LocalCommitmentTransaction {
594676
_ => return Err(DecodeError::InvalidValue),
595677
},
596678
};
679+
let local_keys = Readable::read(reader)?;
680+
let feerate_per_kw = Readable::read(reader)?;
681+
let htlcs_count: u64 = Readable::read(reader)?;
682+
let mut per_htlc = HashMap::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32));
683+
for _ in 0..htlcs_count {
684+
let htlc: HTLCOutputInCommitment = Readable::read(reader)?;
685+
let sigs = Readable::read(reader)?;
686+
let htlc_tx = Readable::read(reader)?;
687+
per_htlc.insert(htlc.transaction_output_index.unwrap(), (htlc, sigs, htlc_tx));
688+
}
597689

598690
if tx.input.len() != 1 {
599691
// Ensure tx didn't hit the 0-input ambiguity case.
600692
return Err(DecodeError::InvalidValue);
601693
}
602-
Ok(Self { tx })
694+
Ok(Self {
695+
tx,
696+
local_keys,
697+
feerate_per_kw,
698+
per_htlc,
699+
})
603700
}
604701
}
605702

lightning/src/ln/channel.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4299,6 +4299,7 @@ mod tests {
42994299
use bitcoin_hashes::hash160::Hash as Hash160;
43004300
use bitcoin_hashes::Hash;
43014301
use std::sync::Arc;
4302+
use std::collections::HashMap;
43024303
use rand::{thread_rng,Rng};
43034304

43044305
struct TestFeeEstimator {
@@ -4475,6 +4476,7 @@ mod tests {
44754476

44764477
let mut unsigned_tx: (Transaction, Vec<HTLCOutputInCommitment>);
44774478

4479+
let mut localtx;
44784480
macro_rules! test_commitment {
44794481
( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
44804482
unsigned_tx = {
@@ -4489,7 +4491,7 @@ mod tests {
44894491
let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &redeemscript, chan.channel_value_satoshis)[..]).unwrap();
44904492
secp_ctx.verify(&sighash, &their_signature, chan.their_funding_pubkey()).unwrap();
44914493

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());
4494+
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());
44934495
chan_keys.sign_local_commitment(&mut localtx, &redeemscript, chan.channel_value_satoshis, &chan.secp_ctx);
44944496

44954497
assert_eq!(serialize(localtx.with_valid_witness())[..],
@@ -4498,11 +4500,11 @@ mod tests {
44984500
}
44994501

45004502
macro_rules! test_htlc_output {
4501-
( $htlc_idx: expr, $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr ) => {
4503+
( $htlc_idx: expr, $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
45024504
let remote_signature = Signature::from_der(&hex::decode($their_sig_hex).unwrap()[..]).unwrap();
45034505

45044506
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);
4507+
let htlc_tx = chan.build_htlc_transaction(&unsigned_tx.0.txid(), &htlc, true, &keys, chan.feerate_per_kw);
45064508
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
45074509
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
45084510
secp_ctx.verify(&htlc_sighash, &remote_signature, &keys.b_htlc_key).unwrap();
@@ -4519,8 +4521,12 @@ mod tests {
45194521
assert!(preimage.is_some());
45204522
}
45214523

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)[..],
4524+
let mut per_htlc = HashMap::new();
4525+
per_htlc.insert($htlc_idx, (htlc.clone(), Some(remote_signature), None));
4526+
localtx.set_htlc_cache(keys.clone(), chan.feerate_per_kw, per_htlc);
4527+
chan_keys.sign_htlc_transaction(&mut localtx, $htlc_idx, preimage, chan.their_to_self_delay, &chan.secp_ctx);
4528+
4529+
assert_eq!(serialize(&localtx.htlc_with_valid_witness($htlc_idx).clone().unwrap())[..],
45244530
hex::decode($tx_hex).unwrap()[..]);
45254531
};
45264532
}

lightning/src/ln/channelmonitor.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
12511251
/// is important that any clones of this channel monitor (including remote clones) by kept
12521252
/// up-to-date as our local commitment transaction is updated.
12531253
/// Panics if set_their_to_self_delay has never been called.
1254-
pub(super) fn provide_latest_local_commitment_tx_info(&mut self, commitment_tx: LocalCommitmentTransaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), MonitorUpdateError> {
1254+
pub(super) fn provide_latest_local_commitment_tx_info(&mut self, mut commitment_tx: LocalCommitmentTransaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), MonitorUpdateError> {
12551255
if self.their_to_self_delay.is_none() {
12561256
return Err(MonitorUpdateError("Got a local commitment tx info update before we'd set basic information about the channel"));
12571257
}
@@ -1268,7 +1268,14 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
12681268
htlc_outputs: htlc_outputs.clone(),
12691269
});
12701270
self.current_local_commitment_number = 0xffff_ffff_ffff - ((((commitment_tx.without_valid_witness().input[0].sequence as u64 & 0xffffff) << 3*8) | (commitment_tx.without_valid_witness().lock_time as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor);
1271-
self.onchain_tx_handler.provide_latest_local_tx(commitment_tx, local_keys, feerate_per_kw, htlc_outputs);
1271+
let mut htlcs = HashMap::with_capacity(htlc_outputs.len());
1272+
for htlc in htlc_outputs {
1273+
if let Some(htlc_index) = htlc.0.transaction_output_index {
1274+
htlcs.insert(htlc_index as u32, (htlc.0, htlc.1, None));
1275+
}
1276+
}
1277+
commitment_tx.set_htlc_cache(local_keys, feerate_per_kw, htlcs);
1278+
self.onchain_tx_handler.provide_latest_local_tx(commitment_tx);
12721279
Ok(())
12731280
}
12741281

0 commit comments

Comments
 (0)