Skip to content

Commit 45a558b

Browse files
authored
Merge pull request #716 from TheBlueMatt/2020-09-fee-tests
(Fix and) Test that txn pay at least a minimum relay fee in functional tests
2 parents 937d1d8 + 3219926 commit 45a558b

File tree

3 files changed

+63
-17
lines changed

3 files changed

+63
-17
lines changed

lightning/src/ln/channel.rs

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,8 +1054,30 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
10541054
}
10551055

10561056
#[inline]
1057-
fn get_closing_transaction_weight(a_scriptpubkey: &Script, b_scriptpubkey: &Script) -> u64 {
1058-
(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)
1057+
fn get_closing_transaction_weight(&self, a_scriptpubkey: Option<&Script>, b_scriptpubkey: Option<&Script>) -> u64 {
1058+
let mut ret =
1059+
(4 + // version
1060+
1 + // input count
1061+
36 + // prevout
1062+
1 + // script length (0)
1063+
4 + // sequence
1064+
1 + // output count
1065+
4 // lock time
1066+
)*4 + // * 4 for non-witness parts
1067+
2 + // witness marker and flag
1068+
1 + // witness element count
1069+
4 + // 4 element lengths (2 sigs, multisig dummy, and witness script)
1070+
self.get_funding_redeemscript().len() as u64 + // funding witness script
1071+
2*(1 + 71); // two signatures + sighash type flags
1072+
if let Some(spk) = a_scriptpubkey {
1073+
ret += ((8+1) + // output values and script length
1074+
spk.len() as u64) * 4; // scriptpubkey and witness multiplier
1075+
}
1076+
if let Some(spk) = b_scriptpubkey {
1077+
ret += ((8+1) + // output values and script length
1078+
spk.len() as u64) * 4; // scriptpubkey and witness multiplier
1079+
}
1080+
ret
10591081
}
10601082

10611083
#[inline]
@@ -2880,13 +2902,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
28802902
if self.feerate_per_kw > proposed_feerate {
28812903
proposed_feerate = self.feerate_per_kw;
28822904
}
2883-
let tx_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
2905+
let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
28842906
let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000;
28852907

28862908
let (closing_tx, total_fee_satoshis) = self.build_closing_transaction(proposed_total_fee_satoshis, false);
28872909
let sig = self.holder_keys
28882910
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
28892911
.ok();
2912+
assert!(closing_tx.get_weight() as u64 <= tx_weight);
28902913
if sig.is_none() { return None; }
28912914

28922915
self.last_sent_closing_fee = Some((proposed_feerate, total_fee_satoshis, sig.clone().unwrap()));
@@ -3007,7 +3030,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30073030
if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() {
30083031
return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned()));
30093032
}
3010-
if msg.fee_satoshis > 21000000 * 10000000 { //this is required to stop potential overflow in build_closing_transaction
3033+
if msg.fee_satoshis > 21_000_000 * 1_0000_0000 { //this is required to stop potential overflow in build_closing_transaction
30113034
return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned()));
30123035
}
30133036

@@ -3031,9 +3054,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30313054
},
30323055
};
30333056

3057+
let closing_tx_max_weight = self.get_closing_transaction_weight(
3058+
if let Some(oup) = closing_tx.output.get(0) { Some(&oup.script_pubkey) } else { None },
3059+
if let Some(oup) = closing_tx.output.get(1) { Some(&oup.script_pubkey) } else { None });
30343060
if let Some((_, last_fee, sig)) = self.last_sent_closing_fee {
30353061
if last_fee == msg.fee_satoshis {
30363062
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
3063+
assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
3064+
debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
30373065
self.channel_state = ChannelState::ShutdownComplete as u32;
30383066
self.update_time_counter += 1;
30393067
return Ok((None, Some(closing_tx)));
@@ -3042,11 +3070,12 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30423070

30433071
macro_rules! propose_new_feerate {
30443072
($new_feerate: expr) => {
3045-
let closing_tx_max_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
3046-
let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * closing_tx_max_weight / 1000, false);
3073+
let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
3074+
let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * tx_weight / 1000, false);
30473075
let sig = self.holder_keys
30483076
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
30493077
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
3078+
assert!(closing_tx.get_weight() as u64 <= tx_weight);
30503079
self.last_sent_closing_fee = Some(($new_feerate, used_total_fee, sig.clone()));
30513080
return Ok((Some(msgs::ClosingSigned {
30523081
channel_id: self.channel_id,
@@ -3056,10 +3085,10 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30563085
}
30573086
}
30583087

3059-
let proposed_sat_per_kw = msg.fee_satoshis * 1000 / closing_tx.get_weight() as u64;
3088+
let mut min_feerate = 253;
30603089
if self.channel_outbound {
30613090
let max_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
3062-
if (proposed_sat_per_kw as u32) > max_feerate {
3091+
if (msg.fee_satoshis as u64) > max_feerate as u64 * closing_tx_max_weight / 1000 {
30633092
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
30643093
if max_feerate <= last_feerate {
30653094
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,21 +3097,23 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30683097
propose_new_feerate!(max_feerate);
30693098
}
30703099
} else {
3071-
let min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
3072-
if (proposed_sat_per_kw as u32) < min_feerate {
3073-
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
3074-
if min_feerate >= last_feerate {
3075-
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)));
3076-
}
3100+
min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
3101+
}
3102+
if (msg.fee_satoshis as u64) < min_feerate as u64 * closing_tx_max_weight / 1000 {
3103+
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
3104+
if min_feerate >= last_feerate {
3105+
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)));
30773106
}
3078-
propose_new_feerate!(min_feerate);
30793107
}
3108+
propose_new_feerate!(min_feerate);
30803109
}
30813110

30823111
let sig = self.holder_keys
30833112
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
30843113
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
30853114
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
3115+
assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
3116+
debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
30863117

30873118
self.channel_state = ChannelState::ShutdownComplete as u32;
30883119
self.update_time_counter += 1;

lightning/src/ln/functional_test_utils.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,14 +505,26 @@ pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a
505505
macro_rules! check_spends {
506506
($tx: expr, $($spends_txn: expr),*) => {
507507
{
508-
$tx.verify(|out_point| {
508+
let get_output = |out_point: &bitcoin::blockdata::transaction::OutPoint| {
509509
$(
510510
if out_point.txid == $spends_txn.txid() {
511511
return $spends_txn.output.get(out_point.vout as usize).cloned()
512512
}
513513
)*
514514
None
515-
}).unwrap();
515+
};
516+
let mut total_value_in = 0;
517+
for input in $tx.input.iter() {
518+
total_value_in += get_output(&input.previous_output).unwrap().value;
519+
}
520+
let mut total_value_out = 0;
521+
for output in $tx.output.iter() {
522+
total_value_out += output.value;
523+
}
524+
let min_fee = ($tx.get_weight() as u64 + 3) / 4; // One sat per vbyte (ie per weight/4, rounded up)
525+
// Input amount - output amount = fee, so check that out + min_fee is smaller than input
526+
assert!(total_value_out + min_fee <= total_value_in);
527+
$tx.verify(get_output).unwrap();
516528
}
517529
}
518530
}

lightning/src/ln/functional_tests.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4708,6 +4708,7 @@ macro_rules! check_spendable_outputs {
47084708
input: vec![input],
47094709
output: vec![outp],
47104710
};
4711+
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
47114712
let secp_ctx = Secp256k1::new();
47124713
let keys = $keysinterface.derive_channel_keys($chan_value, key_derivation_params.0, key_derivation_params.1);
47134714
let remotepubkey = keys.pubkeys().payment_point;
@@ -4742,6 +4743,7 @@ macro_rules! check_spendable_outputs {
47424743

47434744
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
47444745
let witness_script = chan_utils::get_revokeable_redeemscript(revocation_pubkey, *to_self_delay, &delayed_payment_pubkey);
4746+
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 1 + witness_script.len() + 1 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
47454747
let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
47464748
let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
47474749
spend_tx.input[0].witness.push(local_delayedsig.serialize_der().to_vec());
@@ -4769,6 +4771,7 @@ macro_rules! check_spendable_outputs {
47694771
input: vec![input],
47704772
output: vec![outp.clone()],
47714773
};
4774+
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
47724775
let secret = {
47734776
match ExtendedPrivKey::new_master(Network::Testnet, &$node.node_seed) {
47744777
Ok(master_key) => {

0 commit comments

Comments
 (0)