Add OnchainTxHandler::get_fully_signed_htlc
[rust-lightning] / lightning / src / ln / onchaintx.rs
index 2270cdb7c6683e8f82ba71f9bbdb2ed235355a3d..8cb9bfb397ac4f14a2450f7ebc5709d43e58ab69 100644 (file)
@@ -10,12 +10,14 @@ use bitcoin::util::bip143;
 
 use bitcoin_hashes::sha256d::Hash as Sha256dHash;
 
-use secp256k1::Secp256k1;
+use secp256k1::{Secp256k1, Signature};
 use secp256k1;
 
 use ln::msgs::DecodeError;
 use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest};
-use ln::chan_utils::{HTLCType, LocalCommitmentTransaction};
+use ln::channelmanager::{HTLCSource, PaymentPreimage};
+use ln::chan_utils;
+use ln::chan_utils::{HTLCType, LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment};
 use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
 use chain::keysinterface::ChannelKeys;
 use util::logger::Logger;
@@ -47,6 +49,15 @@ enum OnchainEvent {
        }
 }
 
+/// Cache public keys and feerate used to compute any HTLC transaction.
+/// We only keep state for latest 2 commitment transactions as we should
+/// never have to generate HTLC txn for revoked local commitment
+struct HTLCTxCache {
+       local_keys: TxCreationKeys,
+       feerate_per_kw: u64,
+       per_htlc: HashMap<u32, (HTLCOutputInCommitment, Option<Signature>)>
+}
+
 /// Higher-level cache structure needed to re-generate bumped claim txn if needed
 #[derive(Clone, PartialEq)]
 pub struct ClaimTxBumpMaterial {
@@ -144,6 +155,9 @@ pub struct OnchainTxHandler<ChanSigner: ChannelKeys> {
        funding_redeemscript: Script,
        local_commitment: Option<LocalCommitmentTransaction>,
        prev_local_commitment: Option<LocalCommitmentTransaction>,
+       current_htlc_cache: Option<HTLCTxCache>,
+       prev_htlc_cache: Option<HTLCTxCache>,
+       local_csv: u16,
 
        key_storage: ChanSigner,
 
@@ -187,6 +201,38 @@ impl<ChanSigner: ChannelKeys + Writeable> OnchainTxHandler<ChanSigner> {
                self.local_commitment.write(writer)?;
                self.prev_local_commitment.write(writer)?;
 
+               macro_rules! serialize_htlc_cache {
+                       ($cache: expr) => {
+                               $cache.local_keys.write(writer)?;
+                               $cache.feerate_per_kw.write(writer)?;
+                               writer.write_all(&byte_utils::be64_to_array($cache.per_htlc.len() as u64))?;
+                               for (_, &(ref htlc, ref sig)) in $cache.per_htlc.iter() {
+                                       htlc.write(writer)?;
+                                       if let &Some(ref their_sig) = sig {
+                                               1u8.write(writer)?;
+                                               writer.write_all(&their_sig.serialize_compact())?;
+                                       } else {
+                                               0u8.write(writer)?;
+                                       }
+                               }
+                       }
+               }
+
+               if let Some(ref current) = self.current_htlc_cache {
+                       writer.write_all(&[1; 1])?;
+                       serialize_htlc_cache!(current);
+               } else {
+                       writer.write_all(&[0; 1])?;
+               }
+
+               if let Some(ref prev) = self.prev_htlc_cache {
+                       writer.write_all(&[1; 1])?;
+                       serialize_htlc_cache!(prev);
+               } else {
+                       writer.write_all(&[0; 1])?;
+               }
+               self.local_csv.write(writer)?;
+
                self.key_storage.write(writer)?;
 
                writer.write_all(&byte_utils::be64_to_array(self.pending_claim_requests.len() as u64))?;
@@ -231,6 +277,48 @@ impl<ChanSigner: ChannelKeys + Readable> ReadableArgs<Arc<Logger>> for OnchainTx
                let local_commitment = Readable::read(reader)?;
                let prev_local_commitment = Readable::read(reader)?;
 
+               macro_rules! read_htlc_cache {
+                       () => {
+                               {
+                                       let local_keys = Readable::read(reader)?;
+                                       let feerate_per_kw = Readable::read(reader)?;
+                                       let htlcs_count: u64 = Readable::read(reader)?;
+                                       let mut per_htlc = HashMap::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32));
+                                       for _ in 0..htlcs_count {
+                                               let htlc: HTLCOutputInCommitment = Readable::read(reader)?;
+                                               let sigs = match <u8 as Readable>::read(reader)? {
+                                                       0 => None,
+                                                       1 => Some(Readable::read(reader)?),
+                                                       _ => return Err(DecodeError::InvalidValue),
+                                               };
+                                               per_htlc.insert(htlc.transaction_output_index.unwrap(), (htlc, sigs));
+                                       }
+                                       HTLCTxCache {
+                                               local_keys,
+                                               feerate_per_kw,
+                                               per_htlc
+                                       }
+                               }
+                       }
+               }
+
+               let current_htlc_cache = match <u8 as Readable>::read(reader)? {
+                       0 => None,
+                       1 => {
+                               Some(read_htlc_cache!())
+                       }
+                       _ => return Err(DecodeError::InvalidValue),
+               };
+
+               let prev_htlc_cache = match <u8 as Readable>::read(reader)? {
+                       0 => None,
+                       1 => {
+                               Some(read_htlc_cache!())
+                       }
+                       _ => return Err(DecodeError::InvalidValue),
+               };
+               let local_csv = Readable::read(reader)?;
+
                let key_storage = Readable::read(reader)?;
 
                let pending_claim_requests_len: u64 = Readable::read(reader)?;
@@ -281,6 +369,9 @@ impl<ChanSigner: ChannelKeys + Readable> ReadableArgs<Arc<Logger>> for OnchainTx
                        funding_redeemscript,
                        local_commitment,
                        prev_local_commitment,
+                       current_htlc_cache,
+                       prev_htlc_cache,
+                       local_csv,
                        key_storage,
                        claimable_outpoints,
                        pending_claim_requests,
@@ -292,7 +383,7 @@ impl<ChanSigner: ChannelKeys + Readable> ReadableArgs<Arc<Logger>> for OnchainTx
 }
 
 impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
-       pub(super) fn new(destination_script: Script, keys: ChanSigner, funding_redeemscript: Script, logger: Arc<Logger>) -> Self {
+       pub(super) fn new(destination_script: Script, keys: ChanSigner, funding_redeemscript: Script, local_csv: u16, logger: Arc<Logger>) -> Self {
 
                let key_storage = keys;
 
@@ -301,6 +392,9 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
                        funding_redeemscript,
                        local_commitment: None,
                        prev_local_commitment: None,
+                       current_htlc_cache: None,
+                       prev_htlc_cache: None,
+                       local_csv,
                        key_storage,
                        pending_claim_requests: HashMap::new(),
                        claimable_outpoints: HashMap::new(),
@@ -756,7 +850,7 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
                }
        }
 
-       pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) -> Result<(), ()> {
+       pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), ()> {
                // To prevent any unsafe state discrepancy between offchain and onchain, once local
                // commitment transaction has been signed due to an event (either block height for
                // HTLC-timeout or channel force-closure), don't allow any further update of local
@@ -767,6 +861,55 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
                }
                self.prev_local_commitment = self.local_commitment.take();
                self.local_commitment = Some(tx);
+               self.prev_htlc_cache = self.current_htlc_cache.take();
+               let mut per_htlc = HashMap::with_capacity(htlc_outputs.len());
+               for htlc in htlc_outputs {
+                       if htlc.0.transaction_output_index.is_some() { // Discard dust HTLC as we will never have to generate onchain tx for them
+                               per_htlc.insert(htlc.0.transaction_output_index.unwrap(), (htlc.0, htlc.1));
+                       }
+               }
+               self.current_htlc_cache = Some(HTLCTxCache {
+                       local_keys,
+                       feerate_per_kw,
+                       per_htlc
+               });
                Ok(())
        }
+
+       pub(super) fn get_fully_signed_local_tx(&mut self, channel_value_satoshis: u64) -> Option<Transaction> {
+               if let Some(ref mut local_commitment) = self.local_commitment {
+                       self.key_storage.sign_local_commitment(local_commitment, &self.funding_redeemscript, channel_value_satoshis, &self.secp_ctx);
+                       return Some(local_commitment.with_valid_witness().clone());
+               }
+               None
+       }
+
+       #[cfg(test)]
+       pub(super) fn get_fully_signed_copy_local_tx(&mut self, channel_value_satoshis: u64) -> Option<Transaction> {
+               if let Some(ref mut local_commitment) = self.local_commitment {
+                       let mut local_commitment = local_commitment.clone();
+                       self.key_storage.unsafe_sign_local_commitment(&mut local_commitment, &self.funding_redeemscript, channel_value_satoshis, &self.secp_ctx);
+                       return Some(local_commitment.with_valid_witness().clone());
+               }
+               None
+       }
+
+       pub(super) fn get_fully_signed_htlc_tx(&mut self, txid: Sha256dHash, htlc_index: u32, preimage: Option<PaymentPreimage>) -> Option<Transaction> {
+               //TODO: store preimage in OnchainTxHandler
+               if let Some(ref local_commitment) = self.local_commitment {
+                       if local_commitment.txid() == txid {
+                               if let Some(ref htlc_cache) = self.current_htlc_cache {
+                                       if let Some(htlc) = htlc_cache.per_htlc.get(&htlc_index) {
+                                               if !htlc.0.offered && preimage.is_none() { return None; }; // If we don't have preimage for HTLC-Success, don't try to generate
+                                               let htlc_secret = if !htlc.0.offered { preimage } else { None }; // If we have a preimage for a HTLC-Timeout, don't use it that's likely a duplicate HTLC hash
+                                               let mut htlc_tx = chan_utils::build_htlc_transaction(&txid, htlc_cache.feerate_per_kw, self.local_csv, &htlc.0, &htlc_cache.local_keys.a_delayed_payment_key, &htlc_cache.local_keys.revocation_key);
+                                               self.key_storage.sign_htlc_transaction(&mut htlc_tx, htlc.1.as_ref().unwrap(), &htlc_secret, &htlc.0, &htlc_cache.local_keys.a_htlc_key, &htlc_cache.local_keys.b_htlc_key, &htlc_cache.local_keys.revocation_key, &htlc_cache.local_keys.per_commitment_point, &self.secp_ctx);
+                                               return Some(htlc_tx);
+
+                                       }
+                               }
+                       }
+               }
+               None
+       }
 }