X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fpackage.rs;h=a86add4b9e56efbd1606da736011db882b1e399d;hb=0dfcacd22c23f69b6526c9c6507d21427a2b7ccb;hp=2e1e50371dc569d4a1f22b937da2ec7e661bbae6;hpb=c40ebf18e5b5c65944907cc5ef94a271edadf604;p=rust-lightning diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 2e1e5037..a86add4b 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -21,7 +21,7 @@ use bitcoin::hash_types::Txid; use bitcoin::secp256k1::key::{SecretKey,PublicKey}; use ln::PaymentPreimage; -use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, HTLC_OUTPUT_IN_COMMITMENT_SIZE}; +use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment}; use ln::chan_utils; use ln::msgs::DecodeError; use chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; @@ -31,9 +31,11 @@ use util::byte_utils; use util::logger::Logger; use util::ser::{Readable, Writer, Writeable}; -use std::cmp; -use std::mem; -use std::ops::Deref; +use io; +use prelude::*; +use core::cmp; +use core::mem; +use core::ops::Deref; const MAX_ALLOC_SIZE: usize = 64*1024; @@ -86,14 +88,14 @@ impl RevokedOutput { } } -impl_writeable!(RevokedOutput, 33*3 + 32 + 8 + 8 + 2, { - per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, - per_commitment_key, - weight, - amount, - on_counterparty_tx_csv +impl_writeable_tlv_based!(RevokedOutput, { + (0, per_commitment_point, required), + (2, counterparty_delayed_payment_base_key, required), + (4, counterparty_htlc_base_key, required), + (6, per_commitment_key, required), + (8, weight, required), + (10, amount, required), + (12, on_counterparty_tx_csv, required), }); /// A struct to describe a revoked offered output and corresponding information to generate a @@ -130,14 +132,14 @@ impl RevokedHTLCOutput { } } -impl_writeable!(RevokedHTLCOutput, 33*3 + 32 + 8 + 8 + HTLC_OUTPUT_IN_COMMITMENT_SIZE, { - per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, - per_commitment_key, - weight, - amount, - htlc +impl_writeable_tlv_based!(RevokedHTLCOutput, { + (0, per_commitment_point, required), + (2, counterparty_delayed_payment_base_key, required), + (4, counterparty_htlc_base_key, required), + (6, per_commitment_key, required), + (8, weight, required), + (10, amount, required), + (12, htlc, required), }); /// A struct to describe a HTLC output on a counterparty commitment transaction. @@ -167,12 +169,12 @@ impl CounterpartyOfferedHTLCOutput { } } -impl_writeable!(CounterpartyOfferedHTLCOutput, 33*3 + 32 + HTLC_OUTPUT_IN_COMMITMENT_SIZE, { - per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, - preimage, - htlc +impl_writeable_tlv_based!(CounterpartyOfferedHTLCOutput, { + (0, per_commitment_point, required), + (2, counterparty_delayed_payment_base_key, required), + (4, counterparty_htlc_base_key, required), + (6, preimage, required), + (8, htlc, required), }); /// A struct to describe a HTLC output on a counterparty commitment transaction. @@ -198,11 +200,11 @@ impl CounterpartyReceivedHTLCOutput { } } -impl_writeable!(CounterpartyReceivedHTLCOutput, 33*3 + HTLC_OUTPUT_IN_COMMITMENT_SIZE, { - per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, - htlc +impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, { + (0, per_commitment_point, required), + (2, counterparty_delayed_payment_base_key, required), + (4, counterparty_htlc_base_key, required), + (6, htlc, required), }); /// A struct to describe a HTLC output on holder commitment transaction. @@ -213,20 +215,32 @@ impl_writeable!(CounterpartyReceivedHTLCOutput, 33*3 + HTLC_OUTPUT_IN_COMMITMENT pub(crate) struct HolderHTLCOutput { preimage: Option, amount: u64, + /// Defaults to 0 for HTLC-Success transactions, which have no expiry + cltv_expiry: u32, } impl HolderHTLCOutput { - pub(crate) fn build(preimage: Option, amount: u64) -> Self { + pub(crate) fn build_offered(amount: u64, cltv_expiry: u32) -> Self { HolderHTLCOutput { - preimage, - amount + preimage: None, + amount, + cltv_expiry, + } + } + + pub(crate) fn build_accepted(preimage: PaymentPreimage, amount: u64) -> Self { + HolderHTLCOutput { + preimage: Some(preimage), + amount, + cltv_expiry: 0, } } } -impl_writeable!(HolderHTLCOutput, 0, { - preimage, - amount +impl_writeable_tlv_based!(HolderHTLCOutput, { + (0, amount, required), + (2, cltv_expiry, required), + (4, preimage, option) }); /// A struct to describe the channel output on the funding transaction. @@ -245,8 +259,8 @@ impl HolderFundingOutput { } } -impl_writeable!(HolderFundingOutput, 0, { - funding_redeemscript +impl_writeable_tlv_based!(HolderFundingOutput, { + (0, funding_redeemscript, required), }); /// A wrapper encapsulating all in-protocol differing outputs types. @@ -273,8 +287,8 @@ impl PackageSolvingData { // Note: Currently, amounts of holder outputs spending witnesses aren't used // as we can't malleate spending package to increase their feerate. This // should change with the remaining anchor output patchset. - PackageSolvingData::HolderHTLCOutput(..) => { 0 }, - PackageSolvingData::HolderFundingOutput(..) => { 0 }, + PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() }, + PackageSolvingData::HolderFundingOutput(..) => { unreachable!() }, }; amt } @@ -287,8 +301,8 @@ impl PackageSolvingData { // Note: Currently, weights of holder outputs spending witnesses aren't used // as we can't malleate spending package to increase their feerate. This // should change with the remaining anchor output patchset. - PackageSolvingData::HolderHTLCOutput(..) => { debug_assert!(false); 0 }, - PackageSolvingData::HolderFundingOutput(..) => { debug_assert!(false); 0 }, + PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() }, + PackageSolvingData::HolderFundingOutput(..) => { unreachable!() }, }; weight } @@ -374,68 +388,32 @@ impl PackageSolvingData { _ => { panic!("API Error!"); } } } -} - -impl Writeable for PackageSolvingData { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - PackageSolvingData::RevokedOutput(ref revoked_outp) => { - 0u8.write(writer)?; - revoked_outp.write(writer)?; - }, - PackageSolvingData::RevokedHTLCOutput(ref revoked_outp) => { - 1u8.write(writer)?; - revoked_outp.write(writer)?; - }, - PackageSolvingData::CounterpartyOfferedHTLCOutput(ref counterparty_outp) => { - 2u8.write(writer)?; - counterparty_outp.write(writer)?; - }, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref counterparty_outp) => { - 3u8.write(writer)?; - counterparty_outp.write(writer)?; - }, - PackageSolvingData::HolderHTLCOutput(ref holder_outp) => { - 4u8.write(writer)?; - holder_outp.write(writer)?; - }, - PackageSolvingData::HolderFundingOutput(ref funding_outp) => { - 5u8.write(writer)?; - funding_outp.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for PackageSolvingData { - fn read(reader: &mut R) -> Result { - let byte = ::read(reader)?; - let solving_data = match byte { - 0 => { - PackageSolvingData::RevokedOutput(Readable::read(reader)?) - }, - 1 => { - PackageSolvingData::RevokedHTLCOutput(Readable::read(reader)?) - }, - 2 => { - PackageSolvingData::CounterpartyOfferedHTLCOutput(Readable::read(reader)?) - }, - 3 => { - PackageSolvingData::CounterpartyReceivedHTLCOutput(Readable::read(reader)?) - }, - 4 => { - PackageSolvingData::HolderHTLCOutput(Readable::read(reader)?) - }, - 5 => { - PackageSolvingData::HolderFundingOutput(Readable::read(reader)?) - } - _ => return Err(DecodeError::UnknownVersion) + fn absolute_tx_timelock(&self, output_conf_height: u32) -> u32 { + // Get the absolute timelock at which this output can be spent given the height at which + // this output was confirmed. We use `output_conf_height + 1` as a safe default as we can + // be confirmed in the next block and transactions with time lock `current_height + 1` + // always propagate. + let absolute_timelock = match self { + PackageSolvingData::RevokedOutput(_) => output_conf_height + 1, + PackageSolvingData::RevokedHTLCOutput(_) => output_conf_height + 1, + PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => output_conf_height + 1, + PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, output_conf_height + 1), + PackageSolvingData::HolderHTLCOutput(ref outp) => cmp::max(outp.cltv_expiry, output_conf_height + 1), + PackageSolvingData::HolderFundingOutput(_) => output_conf_height + 1, }; - Ok(solving_data) + absolute_timelock } } +impl_writeable_tlv_based_enum!(PackageSolvingData, ; + (0, RevokedOutput), + (1, RevokedHTLCOutput), + (2, CounterpartyOfferedHTLCOutput), + (3, CounterpartyReceivedHTLCOutput), + (4, HolderHTLCOutput), + (5, HolderFundingOutput), +); + /// A malleable package might be aggregated with other packages to save on fees. /// A untractable package has been counter-signed and aggregable will break cached counterparty /// signatures. @@ -576,13 +554,19 @@ impl PackageTemplate { } self.height_timer = cmp::min(self.height_timer, merge_from.height_timer); } - pub(crate) fn package_amount(&self) -> u64 { + /// Gets the amount of all outptus being spent by this package, only valid for malleable + /// packages. + fn package_amount(&self) -> u64 { let mut amounts = 0; for (_, outp) in self.inputs.iter() { amounts += outp.amount(); } amounts } + pub(crate) fn package_timelock(&self) -> u32 { + self.inputs.iter().map(|(_, outp)| outp.absolute_tx_timelock(self.height_original)) + .max().expect("There must always be at least one output to spend in a PackageTemplate") + } pub(crate) fn package_weight(&self, destination_script: &Script) -> usize { let mut inputs_weight = 0; let mut witnesses_weight = 2; // count segwit flags @@ -620,17 +604,18 @@ impl PackageTemplate { }); } for (i, (outpoint, out)) in self.inputs.iter().enumerate() { - log_trace!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); + log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; } } - log_trace!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid()); + log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid()); return Some(bumped_tx); }, PackageMalleability::Untractable => { + debug_assert_eq!(value, 0, "value is ignored for non-malleable packages, should be zero to ensure callsites are correct"); if let Some((outpoint, outp)) = self.inputs.first() { if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) { - log_trace!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); - log_trace!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid()); + log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); + log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid()); return Some(final_tx); } return None; @@ -652,10 +637,12 @@ impl PackageTemplate { current_height + LOW_FREQUENCY_BUMP_INTERVAL } /// Returns value in satoshis to be included as package outgoing output amount and feerate with which package finalization should be done. - pub(crate) fn compute_package_output(&self, predicted_weight: usize, input_amounts: u64, fee_estimator: &F, logger: &L) -> Option<(u64, u64)> + pub(crate) fn compute_package_output(&self, predicted_weight: usize, fee_estimator: &F, logger: &L) -> Option<(u64, u64)> where F::Target: FeeEstimator, L::Target: Logger, { + debug_assert!(self.malleability == PackageMalleability::Malleable, "The package output is fixed for non-malleable packages"); + let input_amounts = self.package_amount(); // If old feerate is 0, first iteration of this claim, use normal fee calculation if self.feerate_previous != 0 { if let Some((new_fee, feerate)) = feerate_bump(predicted_weight, input_amounts, self.feerate_previous, fee_estimator, logger) { @@ -697,22 +684,24 @@ impl PackageTemplate { } impl Writeable for PackageTemplate { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&byte_utils::be64_to_array(self.inputs.len() as u64))?; for (ref outpoint, ref rev_outp) in self.inputs.iter() { outpoint.write(writer)?; rev_outp.write(writer)?; } - self.soonest_conf_deadline.write(writer)?; - self.feerate_previous.write(writer)?; - self.height_timer.write(writer)?; - self.height_original.write(writer)?; + write_tlv_fields!(writer, { + (0, self.soonest_conf_deadline, required), + (2, self.feerate_previous, required), + (4, self.height_original, required), + (6, self.height_timer, option) + }); Ok(()) } } impl Readable for PackageTemplate { - fn read(reader: &mut R) -> Result { + fn read(reader: &mut R) -> Result { let inputs_count = ::read(reader)?; let mut inputs: Vec<(BitcoinOutPoint, PackageSolvingData)> = Vec::with_capacity(cmp::min(inputs_count as usize, MAX_ALLOC_SIZE / 128)); for _ in 0..inputs_count { @@ -730,10 +719,16 @@ impl Readable for PackageTemplate { PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) }, } } else { return Err(DecodeError::InvalidValue); }; - let soonest_conf_deadline = Readable::read(reader)?; - let feerate_previous = Readable::read(reader)?; - let height_timer = Readable::read(reader)?; - let height_original = Readable::read(reader)?; + let mut soonest_conf_deadline = 0; + let mut feerate_previous = 0; + let mut height_timer = None; + let mut height_original = 0; + read_tlv_fields!(reader, { + (0, soonest_conf_deadline, required), + (2, feerate_previous, required), + (4, height_original, required), + (6, height_timer, option), + }); Ok(PackageTemplate { inputs, malleability, @@ -802,13 +797,13 @@ fn feerate_bump(predicted_weight: usize, input_amounts: u64, // ...else just increase the previous feerate by 25% (because that's a nice number) let new_fee = previous_feerate * (predicted_weight as u64) / 750; if input_amounts <= new_fee { - log_trace!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts); + log_warn!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts); return None; } new_fee } } else { - log_trace!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts); + log_warn!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts); return None; }; @@ -867,7 +862,7 @@ mod tests { () => { { let preimage = PaymentPreimage([2;32]); - PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build(Some(preimage), 0)) + PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0)) } } }