X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fpackage.rs;h=bd983ecd9163589b19b12f85199c9d851d0fb5f0;hb=2eb7db9ace08b57bfedbdac2b26961897b032e0b;hp=311937a121a0822eb01cea3bfbd4e8d6b851cd8f;hpb=df829a85263e0bc675ca696c0f6ff0a49d89e623;p=rust-lightning diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 311937a12..bd983ecd9 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -213,21 +213,33 @@ impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, { 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_tlv_based!(HolderHTLCOutput, { (0, amount), + (2, cltv_expiry), }, { - (2, preimage), + (4, preimage), }, {}); /// A struct to describe the channel output on the funding transaction. @@ -274,8 +286,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 } @@ -288,8 +300,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 } @@ -375,68 +387,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) => std::cmp::max(outp.htlc.cltv_expiry, output_conf_height + 1), + PackageSolvingData::HolderHTLCOutput(ref outp) => std::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. @@ -577,13 +553,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 @@ -628,6 +610,7 @@ impl PackageTemplate { 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); @@ -653,10 +636,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) { @@ -874,7 +859,7 @@ mod tests { () => { { let preimage = PaymentPreimage([2;32]); - PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build(Some(preimage), 0)) + PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0)) } } }