mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Merge pull request #231 from philipr-za/philip-204-check-commitment-transaction-fee
Check funder can afford commitment transaction fee when receiving update_fee
This commit is contained in:
commit
86944d34a1
2 changed files with 91 additions and 61 deletions
|
@ -355,8 +355,9 @@ const UNCONF_THRESHOLD: u32 = 6;
|
|||
const BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7; //TODO?
|
||||
/// The amount of time we're willing to wait to claim money back to us
|
||||
const MAX_LOCAL_BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 14;
|
||||
const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
|
||||
const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
|
||||
/// Exposing these two constants for use in test in ChannelMonitor
|
||||
pub const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
|
||||
pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
|
||||
const SPENDING_INPUT_FOR_A_OUTPUT_WEIGHT: u64 = 79; // prevout: 36, nSequence: 4, script len: 1, witness lengths: (3+1)/4, sig: 73/4, if-selector: 1, redeemScript: (6 ops + 2*33 pubkeys + 1*2 delay)/4
|
||||
const B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT: u64 = 104; // prevout: 40, nSequence: 4, script len: 1, witness lengths: 3/4, sig: 73/4, pubkey: 33/4, output: 31 (TODO: Wrong? Useless?)
|
||||
/// Maximmum `funding_satoshis` value, according to the BOLT #2 specification
|
||||
|
@ -1663,7 +1664,9 @@ impl Channel {
|
|||
|
||||
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
|
||||
|
||||
let mut update_fee = false;
|
||||
let feerate_per_kw = if !self.channel_outbound && self.pending_update_fee.is_some() {
|
||||
update_fee = true;
|
||||
self.pending_update_fee.unwrap()
|
||||
} else {
|
||||
self.feerate_per_kw
|
||||
|
@ -1674,6 +1677,16 @@ impl Channel {
|
|||
let local_sighash = Message::from_slice(&bip143::SighashComponents::new(&local_commitment_tx.0).sighash_all(&local_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
|
||||
secp_call!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid commitment tx signature from peer", self.channel_id());
|
||||
|
||||
//If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction
|
||||
if update_fee {
|
||||
let num_htlcs = local_commitment_tx.1.len();
|
||||
let total_fee: u64 = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
|
||||
|
||||
if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + self.their_channel_reserve_satoshis {
|
||||
return Err(HandleError { err: "Funding remote cannot afford proposed new fee", action: Some(ErrorAction::DisconnectPeer { msg: None }) });
|
||||
}
|
||||
}
|
||||
|
||||
if msg.htlc_signatures.len() != local_commitment_tx.1.len() {
|
||||
return Err(HandleError{err: "Got wrong number of HTLC signatures from remote", action: None});
|
||||
}
|
||||
|
@ -1687,7 +1700,7 @@ impl Channel {
|
|||
let mut htlc_tx = self.build_htlc_transaction(&local_commitment_txid, htlc, true, &local_keys, feerate_per_kw);
|
||||
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &local_keys);
|
||||
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
|
||||
secp_call!(self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key), "Invalid HTLC tx siganture from peer", self.channel_id());
|
||||
secp_call!(self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key), "Invalid HTLC tx signature from peer", self.channel_id());
|
||||
let htlc_sig = if htlc.offered {
|
||||
let htlc_sig = self.sign_htlc_transaction(&mut htlc_tx, &msg.htlc_signatures[idx], &None, htlc, &local_keys)?;
|
||||
new_local_commitment_txn.push(htlc_tx);
|
||||
|
@ -1715,6 +1728,7 @@ impl Channel {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 {
|
||||
// This is a response to our post-monitor-failed unfreeze messages, so we can clear the
|
||||
// monitor_pending_order requirement as we won't re-send the monitor_pending messages.
|
||||
|
@ -2199,7 +2213,6 @@ impl Channel {
|
|||
return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish"));
|
||||
}
|
||||
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
|
||||
|
||||
self.pending_update_fee = Some(msg.feerate_per_kw as u64);
|
||||
self.channel_update_count += 1;
|
||||
Ok(())
|
||||
|
|
|
@ -3211,6 +3211,7 @@ mod tests {
|
|||
use chain::chaininterface::{ChainListener, ChainWatchInterface};
|
||||
use chain::keysinterface::{KeysInterface, SpendableOutputDescriptor};
|
||||
use chain::keysinterface;
|
||||
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
|
||||
use ln::channelmanager::{ChannelManager,ChannelManagerReadArgs,OnionKeys,PaymentFailReason,RAACommitmentOrder};
|
||||
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr, CLTV_CLAIM_BUFFER, HTLC_FAIL_TIMEOUT_BLOCKS, ManyChannelMonitor};
|
||||
use ln::router::{Route, RouteHop, Router};
|
||||
|
@ -3503,6 +3504,17 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr, $channel_id: expr) => {
|
||||
{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&$channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn create_chan_between_nodes_with_value_init(node_a: &Node, node_b: &Node, channel_value: u64, push_msat: u64) -> Transaction {
|
||||
node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42).unwrap();
|
||||
node_b.node.handle_open_channel(&node_a.node.get_our_node_id(), &get_event_msg!(node_a, MessageSendEvent::SendOpenChannel, node_b.node.get_our_node_id())).unwrap();
|
||||
|
@ -4136,14 +4148,6 @@ mod tests {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
// balancing
|
||||
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
|
||||
|
||||
|
@ -4165,7 +4169,7 @@ mod tests {
|
|||
// (6) RAA is delivered ->
|
||||
|
||||
// First nodes[0] generates an update_fee
|
||||
nodes[0].node.update_fee(channel_id, get_feerate!(nodes[0]) + 20).unwrap();
|
||||
nodes[0].node.update_fee(channel_id, get_feerate!(nodes[0], channel_id) + 20).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
let events_0 = nodes[0].node.get_and_clear_pending_msg_events();
|
||||
|
@ -4254,19 +4258,11 @@ mod tests {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
// balancing
|
||||
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
|
||||
|
||||
// First nodes[0] generates an update_fee
|
||||
nodes[0].node.update_fee(channel_id, get_feerate!(nodes[0]) + 20).unwrap();
|
||||
nodes[0].node.update_fee(channel_id, get_feerate!(nodes[0], channel_id) + 20).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
let events_0 = nodes[0].node.get_and_clear_pending_msg_events();
|
||||
|
@ -4312,14 +4308,6 @@ mod tests {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
// A B
|
||||
// update_fee/commitment_signed ->
|
||||
// .- send (1) RAA and (2) commitment_signed
|
||||
|
@ -4340,7 +4328,7 @@ mod tests {
|
|||
// revoke_and_ack ->
|
||||
|
||||
// First nodes[0] generates an update_fee
|
||||
let initial_feerate = get_feerate!(nodes[0]);
|
||||
let initial_feerate = get_feerate!(nodes[0], channel_id);
|
||||
nodes[0].node.update_fee(channel_id, initial_feerate + 20).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
|
@ -4424,16 +4412,8 @@ mod tests {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
let feerate = get_feerate!(nodes[0]);
|
||||
nodes[0].node.update_fee(channel_id, feerate+20).unwrap();
|
||||
let feerate = get_feerate!(nodes[0], channel_id);
|
||||
nodes[0].node.update_fee(channel_id, feerate+25).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
let events_0 = nodes[0].node.get_and_clear_pending_msg_events();
|
||||
|
@ -4464,24 +4444,69 @@ mod tests {
|
|||
check_added_monitors!(nodes[1], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_fee_that_funder_cannot_afford() {
|
||||
let mut nodes = create_network(2);
|
||||
let channel_value = 1888;
|
||||
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, channel_value, 700000);
|
||||
let channel_id = chan.2;
|
||||
|
||||
let feerate = 260;
|
||||
nodes[0].node.update_fee(channel_id, feerate).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
let update_msg = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
|
||||
|
||||
nodes[1].node.handle_update_fee(&nodes[0].node.get_our_node_id(), &update_msg.update_fee.unwrap()).unwrap();
|
||||
|
||||
commitment_signed_dance!(nodes[1], nodes[0], update_msg.commitment_signed, false);
|
||||
|
||||
//Confirm that the new fee based on the last local commitment txn is what we expected based on the feerate of 260 set above.
|
||||
//This value results in a fee that is exactly what the funder can afford (277 sat + 1000 sat channel reserve)
|
||||
{
|
||||
let chan_lock = nodes[1].node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
|
||||
//We made sure neither party's funds are below the dust limit so -2 non-HTLC txns from number of outputs
|
||||
let num_htlcs = chan.last_local_commitment_txn[0].output.len() - 2;
|
||||
let total_fee: u64 = feerate * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
|
||||
let mut actual_fee = chan.last_local_commitment_txn[0].output.iter().fold(0, |acc, output| acc + output.value);
|
||||
actual_fee = channel_value - actual_fee;
|
||||
assert_eq!(total_fee, actual_fee);
|
||||
} //drop the mutex
|
||||
|
||||
//Add 2 to the previous fee rate to the final fee increases by 1 (with no HTLCs the fee is essentially
|
||||
//fee_rate*(724/1000) so the increment of 1*0.724 is rounded back down)
|
||||
nodes[0].node.update_fee(channel_id, feerate+2).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
let update2_msg = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
|
||||
|
||||
nodes[1].node.handle_update_fee(&nodes[0].node.get_our_node_id(), &update2_msg.update_fee.unwrap());
|
||||
|
||||
//While producing the commitment_signed response after handling a received update_fee request the
|
||||
//check to see if the funder, who sent the update_fee request, can afford the new fee (funder_balance >= fee+channel_reserve)
|
||||
//Should produce and error.
|
||||
let err = nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &update2_msg.commitment_signed).unwrap_err();
|
||||
|
||||
assert!(match err.err {
|
||||
"Funding remote cannot afford proposed new fee" => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
//clear the message we could not handle
|
||||
nodes[1].node.get_and_clear_pending_msg_events();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_fee_with_fundee_update_add_htlc() {
|
||||
let mut nodes = create_network(2);
|
||||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
// balancing
|
||||
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
|
||||
|
||||
let feerate = get_feerate!(nodes[0]);
|
||||
let feerate = get_feerate!(nodes[0], channel_id);
|
||||
nodes[0].node.update_fee(channel_id, feerate+20).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
|
@ -4579,14 +4604,6 @@ mod tests {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let channel_id = chan.2;
|
||||
|
||||
macro_rules! get_feerate {
|
||||
($node: expr) => {{
|
||||
let chan_lock = $node.node.channel_state.lock().unwrap();
|
||||
let chan = chan_lock.by_id.get(&channel_id).unwrap();
|
||||
chan.get_feerate()
|
||||
}}
|
||||
}
|
||||
|
||||
// A B
|
||||
// (1) update_fee/commitment_signed ->
|
||||
// <- (2) revoke_and_ack
|
||||
|
@ -4602,7 +4619,7 @@ mod tests {
|
|||
// revoke_and_ack ->
|
||||
|
||||
// Create and deliver (1)...
|
||||
let feerate = get_feerate!(nodes[0]);
|
||||
let feerate = get_feerate!(nodes[0], channel_id);
|
||||
nodes[0].node.update_fee(channel_id, feerate+20).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
|
||||
|
@ -4676,8 +4693,8 @@ mod tests {
|
|||
check_added_monitors!(nodes[1], 1);
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
assert_eq!(get_feerate!(nodes[0]), feerate + 30);
|
||||
assert_eq!(get_feerate!(nodes[1]), feerate + 30);
|
||||
assert_eq!(get_feerate!(nodes[0], channel_id), feerate + 30);
|
||||
assert_eq!(get_feerate!(nodes[1], channel_id), feerate + 30);
|
||||
close_channel(&nodes[0], &nodes[1], &chan.2, chan.3, true);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue