mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Fix feerate calculation on closing transactions
This resolves a number of bugs around how we calculate feerates on closing transactions: * We previously calculated the weight wrong both by always counting two outputs instead of counting the number of outputs that actually exist in the closing transaction and by not counting the witness redeemscript. * We use assertions to check the calculated weight matches what we actually build (with debug_assertions for variable-length sigs). * As an additional sanity check, we really should check that the transaction had at least min-relay-fee when we were the channel initator.
This commit is contained in:
parent
6366fc7154
commit
b43a7e3ef3
1 changed files with 45 additions and 14 deletions
|
@ -1054,8 +1054,30 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_closing_transaction_weight(a_scriptpubkey: &Script, b_scriptpubkey: &Script) -> u64 {
|
fn get_closing_transaction_weight(&self, a_scriptpubkey: Option<&Script>, b_scriptpubkey: Option<&Script>) -> u64 {
|
||||||
(4 + 1 + 36 + 4 + 1 + 1 + 2*(8+1) + 4 + a_scriptpubkey.len() as u64 + b_scriptpubkey.len() as u64)*4 + 2 + 1 + 1 + 2*(1 + 72)
|
let mut ret =
|
||||||
|
(4 + // version
|
||||||
|
1 + // input count
|
||||||
|
36 + // prevout
|
||||||
|
1 + // script length (0)
|
||||||
|
4 + // sequence
|
||||||
|
1 + // output count
|
||||||
|
4 // lock time
|
||||||
|
)*4 + // * 4 for non-witness parts
|
||||||
|
2 + // witness marker and flag
|
||||||
|
1 + // witness element count
|
||||||
|
4 + // 4 element lengths (2 sigs, multisig dummy, and witness script)
|
||||||
|
self.get_funding_redeemscript().len() as u64 + // funding witness script
|
||||||
|
2*(1 + 71); // two signatures + sighash type flags
|
||||||
|
if let Some(spk) = a_scriptpubkey {
|
||||||
|
ret += ((8+1) + // output values and script length
|
||||||
|
spk.len() as u64) * 4; // scriptpubkey and witness multiplier
|
||||||
|
}
|
||||||
|
if let Some(spk) = b_scriptpubkey {
|
||||||
|
ret += ((8+1) + // output values and script length
|
||||||
|
spk.len() as u64) * 4; // scriptpubkey and witness multiplier
|
||||||
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2880,13 +2902,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
if self.feerate_per_kw > proposed_feerate {
|
if self.feerate_per_kw > proposed_feerate {
|
||||||
proposed_feerate = self.feerate_per_kw;
|
proposed_feerate = self.feerate_per_kw;
|
||||||
}
|
}
|
||||||
let tx_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
|
let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
|
||||||
let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000;
|
let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000;
|
||||||
|
|
||||||
let (closing_tx, total_fee_satoshis) = self.build_closing_transaction(proposed_total_fee_satoshis, false);
|
let (closing_tx, total_fee_satoshis) = self.build_closing_transaction(proposed_total_fee_satoshis, false);
|
||||||
let sig = self.holder_keys
|
let sig = self.holder_keys
|
||||||
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
||||||
.ok();
|
.ok();
|
||||||
|
assert!(closing_tx.get_weight() as u64 <= tx_weight);
|
||||||
if sig.is_none() { return None; }
|
if sig.is_none() { return None; }
|
||||||
|
|
||||||
self.last_sent_closing_fee = Some((proposed_feerate, total_fee_satoshis, sig.clone().unwrap()));
|
self.last_sent_closing_fee = Some((proposed_feerate, total_fee_satoshis, sig.clone().unwrap()));
|
||||||
|
@ -3031,9 +3054,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let closing_tx_max_weight = self.get_closing_transaction_weight(
|
||||||
|
if let Some(oup) = closing_tx.output.get(0) { Some(&oup.script_pubkey) } else { None },
|
||||||
|
if let Some(oup) = closing_tx.output.get(1) { Some(&oup.script_pubkey) } else { None });
|
||||||
if let Some((_, last_fee, sig)) = self.last_sent_closing_fee {
|
if let Some((_, last_fee, sig)) = self.last_sent_closing_fee {
|
||||||
if last_fee == msg.fee_satoshis {
|
if last_fee == msg.fee_satoshis {
|
||||||
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
|
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
|
||||||
|
assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
|
||||||
|
debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
|
||||||
self.channel_state = ChannelState::ShutdownComplete as u32;
|
self.channel_state = ChannelState::ShutdownComplete as u32;
|
||||||
self.update_time_counter += 1;
|
self.update_time_counter += 1;
|
||||||
return Ok((None, Some(closing_tx)));
|
return Ok((None, Some(closing_tx)));
|
||||||
|
@ -3042,11 +3070,12 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
|
|
||||||
macro_rules! propose_new_feerate {
|
macro_rules! propose_new_feerate {
|
||||||
($new_feerate: expr) => {
|
($new_feerate: expr) => {
|
||||||
let closing_tx_max_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
|
let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
|
||||||
let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * closing_tx_max_weight / 1000, false);
|
let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * tx_weight / 1000, false);
|
||||||
let sig = self.holder_keys
|
let sig = self.holder_keys
|
||||||
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
||||||
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
|
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
|
||||||
|
assert!(closing_tx.get_weight() as u64 <= tx_weight);
|
||||||
self.last_sent_closing_fee = Some(($new_feerate, used_total_fee, sig.clone()));
|
self.last_sent_closing_fee = Some(($new_feerate, used_total_fee, sig.clone()));
|
||||||
return Ok((Some(msgs::ClosingSigned {
|
return Ok((Some(msgs::ClosingSigned {
|
||||||
channel_id: self.channel_id,
|
channel_id: self.channel_id,
|
||||||
|
@ -3056,10 +3085,10 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let proposed_sat_per_kw = msg.fee_satoshis * 1000 / closing_tx.get_weight() as u64;
|
let mut min_feerate = 253;
|
||||||
if self.channel_outbound {
|
if self.channel_outbound {
|
||||||
let max_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
|
let max_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
|
||||||
if (proposed_sat_per_kw as u32) > max_feerate {
|
if (msg.fee_satoshis as u64) > max_feerate as u64 * closing_tx_max_weight / 1000 {
|
||||||
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
|
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
|
||||||
if max_feerate <= last_feerate {
|
if max_feerate <= last_feerate {
|
||||||
return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something higher ({}) than our Normal feerate ({})", last_feerate, max_feerate)));
|
return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something higher ({}) than our Normal feerate ({})", last_feerate, max_feerate)));
|
||||||
|
@ -3068,8 +3097,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
propose_new_feerate!(max_feerate);
|
propose_new_feerate!(max_feerate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
|
min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
|
||||||
if (proposed_sat_per_kw as u32) < min_feerate {
|
}
|
||||||
|
if (msg.fee_satoshis as u64) < min_feerate as u64 * closing_tx_max_weight / 1000 {
|
||||||
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
|
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
|
||||||
if min_feerate >= last_feerate {
|
if min_feerate >= last_feerate {
|
||||||
return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something lower ({}) than our Background feerate ({}).", last_feerate, min_feerate)));
|
return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something lower ({}) than our Background feerate ({}).", last_feerate, min_feerate)));
|
||||||
|
@ -3077,12 +3107,13 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
}
|
}
|
||||||
propose_new_feerate!(min_feerate);
|
propose_new_feerate!(min_feerate);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let sig = self.holder_keys
|
let sig = self.holder_keys
|
||||||
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
|
||||||
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
|
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
|
||||||
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
|
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
|
||||||
|
assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
|
||||||
|
debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
|
||||||
|
|
||||||
self.channel_state = ChannelState::ShutdownComplete as u32;
|
self.channel_state = ChannelState::ShutdownComplete as u32;
|
||||||
self.update_time_counter += 1;
|
self.update_time_counter += 1;
|
||||||
|
|
Loading…
Add table
Reference in a new issue