-
Notifications
You must be signed in to change notification settings - Fork 412
Check funder can afford commitment transaction fee when receiving update_fee #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
TheBlueMatt
merged 1 commit into
lightningdevkit:master
from
philipr-za:philip-204-check-commitment-transaction-fee
Nov 20, 2018
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -357,8 +357,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 | ||
|
@@ -1653,7 +1654,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 | ||
|
@@ -1664,6 +1667,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 }) }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should really be an error-message-generating Err, but I'll fix it in #254 since I'm rewriting this stuff there anyway. |
||
} | ||
} | ||
|
||
if msg.htlc_signatures.len() != local_commitment_tx.1.len() { | ||
return Err(HandleError{err: "Got wrong number of HTLC signatures from remote", action: None}); | ||
} | ||
|
@@ -1677,7 +1690,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); | ||
|
@@ -1705,6 +1718,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. | ||
|
@@ -2188,7 +2202,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(()) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3212,6 +3212,7 @@ mod tests { | |
use chain::chaininterface::{ChainListener, ChainWatchInterface}; | ||
use chain::keysinterface::KeysInterface; | ||
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}; | ||
|
@@ -3501,6 +3502,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(); | ||
|
@@ -4134,14 +4146,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); | ||
|
||
|
@@ -4163,7 +4167,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(); | ||
|
@@ -4252,19 +4256,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(); | ||
|
@@ -4310,14 +4306,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 | ||
|
@@ -4338,7 +4326,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); | ||
|
||
|
@@ -4422,16 +4410,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(); | ||
|
@@ -4462,24 +4442,69 @@ mod tests { | |
check_added_monitors!(nodes[1], 1); | ||
} | ||
|
||
#[test] | ||
fn test_update_fee_that_funder_cannot_afford() { | ||
let mut nodes = create_network(2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This mut is unnecessary. |
||
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); | ||
|
||
|
@@ -4577,14 +4602,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 | ||
|
@@ -4600,7 +4617,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); | ||
|
||
|
@@ -4674,8 +4691,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); | ||
} | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to add a comment noting these are just exposed for channelmanager tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.