Integrate PackageTemplate
[rust-lightning] / lightning / src / ln / package.rs
index 078f0f6388ff7d2b6c2eda82b1a9cc55761060d9..220a3df14bb5dcbdf2ad830931f6e40abc81b0ed 100644 (file)
@@ -25,6 +25,7 @@ use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, HTLC_OUTPUT_IN_COMM
 use ln::chan_utils;
 use ln::msgs::DecodeError;
 use ln::onchaintx::OnchainTxHandler;
+use chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
 use chain::keysinterface::Sign;
 use util::byte_utils;
 use util::logger::Logger;
@@ -706,89 +707,106 @@ impl Readable for PackageTemplate {
        }
 }
 
-/// Utilities for computing witnesses weight and feerate computation for onchain operation
-#[derive(PartialEq, Clone, Copy)]
-pub(crate) enum InputDescriptors {
-       RevokedOfferedHTLC,
-       RevokedReceivedHTLC,
-       OfferedHTLC,
-       ReceivedHTLC,
-       RevokedOutput, // either a revoked to_holder output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output
-}
-
-impl Writeable for InputDescriptors {
-       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
-               match self {
-                       &InputDescriptors::RevokedOfferedHTLC => {
-                               writer.write_all(&[0; 1])?;
-                       },
-                       &InputDescriptors::RevokedReceivedHTLC => {
-                               writer.write_all(&[1; 1])?;
-                       },
-                       &InputDescriptors::OfferedHTLC => {
-                               writer.write_all(&[2; 1])?;
-                       },
-                       &InputDescriptors::ReceivedHTLC => {
-                               writer.write_all(&[3; 1])?;
-                       }
-                       &InputDescriptors::RevokedOutput => {
-                               writer.write_all(&[4; 1])?;
+/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
+/// weight. We start with the highest priority feerate returned by the node's fee estimator then
+/// fall-back to lower priorities until we have enough value available to suck from.
+///
+/// If the proposed fee is less than the available spent output's values, we return the proposed
+/// fee and the corresponding updated feerate. If the proposed fee is equal or more than the
+/// available spent output's values, we return nothing
+fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predicted_weight: usize, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
+       where F::Target: FeeEstimator,
+             L::Target: Logger,
+{
+       let mut updated_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64;
+       let mut fee = updated_feerate * (predicted_weight as u64) / 1000;
+       if input_amounts <= fee {
+               updated_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal) as u64;
+               fee = updated_feerate * (predicted_weight as u64) / 1000;
+               if input_amounts <= fee {
+                       updated_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) as u64;
+                       fee = updated_feerate * (predicted_weight as u64) / 1000;
+                       if input_amounts <= fee {
+                               log_error!(logger, "Failed to generate an on-chain punishment tx as even low priority fee ({} sat) was more than the entire claim balance ({} sat)",
+                                       fee, input_amounts);
+                               None
+                       } else {
+                               log_warn!(logger, "Used low priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
+                                       input_amounts);
+                               Some((fee, updated_feerate))
                        }
+               } else {
+                       log_warn!(logger, "Used medium priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
+                               input_amounts);
+                       Some((fee, updated_feerate))
                }
-               Ok(())
+       } else {
+               Some((fee, updated_feerate))
        }
 }
 
-impl Readable for InputDescriptors {
-       fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
-               let input_descriptor = match <u8 as Readable>::read(reader)? {
-                       0 => {
-                               InputDescriptors::RevokedOfferedHTLC
-                       },
-                       1 => {
-                               InputDescriptors::RevokedReceivedHTLC
-                       },
-                       2 => {
-                               InputDescriptors::OfferedHTLC
-                       },
-                       3 => {
-                               InputDescriptors::ReceivedHTLC
-                       },
-                       4 => {
-                               InputDescriptors::RevokedOutput
+/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
+/// weight. If feerates proposed by the fee-estimator have been increasing since last fee-bumping
+/// attempt, use them. Otherwise, blindly bump the feerate by 25% of the previous feerate. We also
+/// verify that those bumping heuristics respect BIP125 rules 3) and 4) and if required adjust
+/// the new fee to meet the RBF policy requirement.
+fn feerate_bump<F: Deref, L: Deref>(predicted_weight: usize, input_amounts: u64, previous_feerate: u64, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
+       where F::Target: FeeEstimator,
+             L::Target: Logger,
+{
+       // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
+       let new_fee = if let Some((new_fee, _)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
+               let updated_feerate = new_fee / (predicted_weight as u64 * 1000);
+               if updated_feerate > previous_feerate {
+                       new_fee
+               } else {
+                       // ...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);
+                               return None;
                        }
-                       _ => return Err(DecodeError::InvalidValue),
-               };
-               Ok(input_descriptor)
-       }
+                       new_fee
+               }
+       } else {
+               log_trace!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts);
+               return None;
+       };
+
+       let previous_fee = previous_feerate * (predicted_weight as u64) / 1000;
+       let min_relay_fee = MIN_RELAY_FEE_SAT_PER_1000_WEIGHT * (predicted_weight as u64) / 1000;
+       // BIP 125 Opt-in Full Replace-by-Fee Signaling
+       //      * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
+       //      * 4. The replacement transaction must also pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting.
+       let new_fee = if new_fee < previous_fee + min_relay_fee {
+               new_fee + previous_fee + min_relay_fee - new_fee
+       } else {
+               new_fee
+       };
+       Some((new_fee, new_fee * 1000 / (predicted_weight as u64)))
 }
 
-pub(crate) fn get_witnesses_weight(inputs: &[InputDescriptors]) -> usize {
-       let mut tx_weight = 2; // count segwit flags
-       for inp in inputs {
-               // We use expected weight (and not actual) as signatures and time lock delays may vary
-               tx_weight +=  match inp {
-                       // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script
-                       &InputDescriptors::RevokedOfferedHTLC => {
-                               1 + 1 + 73 + 1 + 33 + 1 + 133
-                       },
-                       // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script
-                       &InputDescriptors::RevokedReceivedHTLC => {
-                               1 + 1 + 73 + 1 + 33 + 1 + 139
-                       },
-                       // number_of_witness_elements + sig_length + counterpartyhtlc_sig  + preimage_length + preimage + witness_script_length + witness_script
-                       &InputDescriptors::OfferedHTLC => {
-                               1 + 1 + 73 + 1 + 32 + 1 + 133
-                       },
-                       // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script
-                       &InputDescriptors::ReceivedHTLC => {
-                               1 + 1 + 73 + 1 + 1 + 1 + 139
-                       },
-                       // number_of_witness_elements + sig_length + revocation_sig + true_length + op_true + witness_script_length + witness_script
-                       &InputDescriptors::RevokedOutput => {
-                               1 + 1 + 73 + 1 + 1 + 1 + 77
-                       },
-               };
+/// Deduce a new proposed fee from the claiming transaction output value.
+/// If the new proposed fee is superior to the consumed outpoint's value, burn everything in miner's
+/// fee to deter counterparties attacker.
+pub(crate) fn compute_output_value<F: Deref, L: Deref>(predicted_weight: usize, input_amounts: u64, previous_feerate: u64, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
+       where F::Target: FeeEstimator,
+             L::Target: Logger,
+{
+       // If old feerate is 0, first iteration of this claim, use normal fee calculation
+       if previous_feerate != 0 {
+               if let Some((new_fee, feerate)) = feerate_bump(predicted_weight, input_amounts, previous_feerate, fee_estimator, logger) {
+                       // If new computed fee is superior at the whole claimable amount burn all in fees
+                       if new_fee > input_amounts {
+                               return Some((0, feerate));
+                       } else {
+                               return Some((input_amounts - new_fee, feerate));
+                       }
+               }
+       } else {
+               if let Some((new_fee, feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
+                               return Some((input_amounts - new_fee, feerate));
+               }
        }
-       tx_weight
+       None
 }