Skip to content

Implement DisconnectPeer event + test #43

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions fuzz/fuzz_targets/full_stack_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,16 @@ impl BroadcasterInterface for TestBroadcaster {
#[derive(Clone, PartialEq, Eq, Hash)]
struct Peer {
id: u8,
connected: bool,
}
impl SocketDescriptor for Peer {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, _resume_read: bool) -> usize {
assert!(write_offset < data.len());
data.len() - write_offset
}
fn disconnect_socket(&mut self) {
self.connected = false;
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry didn't get you on this one, disconnect_socket is used by the network provider to notify that we want to disconnect the peer, right ? So what do we want here is setting a bool in SocketDescriptor object and let the network provider disconnect peer, block further disconnect_event and remove peer infos. Why do we need RefCell ?

}

#[inline]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/msg_targets/gen_target.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
for target in CommitmentSigned FundingCreated FundingLocked FundingSigned OpenChannel RevokeAndACK Shutdown UpdateAddHTLC UpdateFailHTLC UpdateFailMalformedHTLC UpdateFee UpdateFulfillHTLC AcceptChannel ClosingSigned; do
for target in CommitmentSigned FundingCreated FundingLocked FundingSigned OpenChannel RevokeAndACK Shutdown UpdateAddHTLC UpdateFailHTLC UpdateFailMalformedHTLC UpdateFee UpdateFulfillHTLC AcceptChannel ClosingSigned ErrorMessage; do
tn=$(echo $target | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\L\2/g')
fn=msg_$(echo $tn | tr '[:upper:]' '[:lower:]')_target.rs
cat msg_target_template.txt | sed s/MSG_TARGET/$target/ > $fn
Expand Down
48 changes: 48 additions & 0 deletions fuzz/fuzz_targets/msg_targets/msg_error_message_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.

extern crate lightning;

use lightning::ln::msgs;
use lightning::util::reset_rng_state;

use lightning::ln::msgs::{MsgEncodable, MsgDecodable};

mod utils;

#[inline]
pub fn do_test(data: &[u8]) {
reset_rng_state();
test_msg!(msgs::ErrorMessage, data);
}

#[cfg(feature = "afl")]
extern crate afl;
#[cfg(feature = "afl")]
fn main() {
afl::read_stdio_bytes(|data| {
do_test(&data);
});
}

#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}

#[cfg(test)]
mod tests {
use utils::extend_vec_from_hex;
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00", &mut a);
super::do_test(&a);
}
}
25 changes: 14 additions & 11 deletions src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ macro_rules! secp_call {
match $res {
Ok(key) => key,
//TODO: make the error a parameter
Err(_) => return Err(HandleError{err: $err, action: Some(msgs::ErrorAction::DisconnectPeer{})})
Err(_) => return Err(HandleError{err: $err, action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })})
}
};
}
Expand Down Expand Up @@ -433,10 +433,10 @@ impl Channel {

fn check_remote_fee(fee_estimator: &FeeEstimator, feerate_per_kw: u32) -> Result<(), HandleError> {
if (feerate_per_kw as u64) < fee_estimator.get_est_sat_per_vbyte(ConfirmationTarget::Background) * 250 {
return Err(HandleError{err: "Peer's feerate much too low", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Peer's feerate much too low", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if (feerate_per_kw as u64) > fee_estimator.get_est_sat_per_vbyte(ConfirmationTarget::HighPriority) * 375 { // 375 = 250 * 1.5x
return Err(HandleError{err: "Peer's feerate much too high", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Peer's feerate much too high", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
Ok(())
}
Expand All @@ -448,29 +448,32 @@ impl Channel {
pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, announce_publicly: bool) -> Result<Channel, HandleError> {
// Check sanity of message fields:
if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
return Err(HandleError{err: "funding value > 2^24", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "funding value > 2^24", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.channel_reserve_satoshis > msg.funding_satoshis {
return Err(HandleError{err: "Bogus channel_reserve_satoshis", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Bogus channel_reserve_satoshis", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.push_msat > (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "push_msat more than highest possible value", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "push_msat more than highest possible value", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.dust_limit_satoshis > msg.funding_satoshis {
return Err(HandleError{err: "Peer never wants payout outputs?", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Peer never wants payout outputs?", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.max_htlc_value_in_flight_msat > msg.funding_satoshis * 1000 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase error here - this if was removed on master.

return Err(HandleError{err: "Bogus max_htlc_value_in_flight_satoshis", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "Minimum htlc value is full channel value", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Minimum htlc value is full channel value", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
return Err(HandleError{err: "They wanted our payments to be delayed by a needlessly long period", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "They wanted our payments to be delayed by a needlessly long period", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if msg.max_accepted_htlcs < 1 {
return Err(HandleError{err: "0 max_accpted_htlcs makes for a useless channel", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "0 max_accpted_htlcs makes for a useless channel", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
if (msg.channel_flags & 254) != 0 {
return Err(HandleError{err: "unknown channel_flags", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "unknown channel_flags", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}

// Convert things into internal flags and prep our state:
Expand Down
32 changes: 22 additions & 10 deletions src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,16 @@ impl ChannelManager {
/// Call this upon creation of a funding transaction for the given channel.
/// Panics if a funding transaction has already been provided for this channel.
pub fn funding_transaction_generated(&self, temporary_channel_id: &[u8; 32], funding_txo: OutPoint) {

macro_rules! add_pending_event {
($event: expr) => {
{
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push($event);
}
}
}

let (chan, msg, chan_monitor) = {
let mut channel_state = self.channel_state.lock().unwrap();
match channel_state.by_id.remove(temporary_channel_id) {
Expand All @@ -684,10 +694,15 @@ impl ChannelManager {
Ok(funding_msg) => {
(chan, funding_msg.0, funding_msg.1)
},
Err(_e) => {
//TODO: Push e to pendingevents
Err(e) => {
mem::drop(channel_state);
add_pending_event!(events::Event::DisconnectPeer {
node_id: chan.get_their_node_id(),
msg: if let Some(msgs::ErrorAction::DisconnectPeer { msg } ) = e.action { msg } else { None },
});

return;
}
},
}
},
None => return
Expand All @@ -696,13 +711,10 @@ impl ChannelManager {
if let Err(_e) = self.monitor.add_update_monitor(chan_monitor.get_funding_txo().unwrap(), chan_monitor) {
unimplemented!(); // maybe remove from claimable_htlcs?
}
{
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push(events::Event::SendFundingCreated {
node_id: chan.get_their_node_id(),
msg: msg,
});
}
add_pending_event!(events::Event::SendFundingCreated {
node_id: chan.get_their_node_id(),
msg: msg,
});

let mut channel_state = self.channel_state.lock().unwrap();
channel_state.by_id.insert(chan.channel_id(), chan);
Expand Down
45 changes: 44 additions & 1 deletion src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ pub struct Init {
pub local_features: LocalFeatures,
}

pub struct ErrorMessage {
pub channel_id: [u8; 32],
pub data: String,
}

pub struct Ping {
pub ponglen: u16,
pub byteslen: u16,
Expand Down Expand Up @@ -372,9 +377,15 @@ pub enum ErrorAction {
msg: UpdateFailHTLC
},
/// The peer took some action which made us think they were useless. Disconnect them.
DisconnectPeer,
DisconnectPeer {
msg: Option<ErrorMessage>
},
/// The peer did something harmless that we weren't able to process, just log and ignore
IgnoreError,
/// The peer did something incorrect. Tell them.
SendErrorMessage {
msg: ErrorMessage
},
}

pub struct HandleError { //TODO: rename me
Expand Down Expand Up @@ -1562,3 +1573,35 @@ impl MsgEncodable for OnionErrorPacket {
res
}
}

impl MsgEncodable for ErrorMessage {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a TODO for MsgDecodable for ErrorMessage? We should obviously also support receiving them.

fn encode(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(34 + self.data.len());
res.extend_from_slice(&self.channel_id);
res.extend_from_slice(&byte_utils::be16_to_array(self.data.len() as u16));
res.extend_from_slice(&self.data.as_bytes());
res
}
}

impl MsgDecodable for ErrorMessage {
fn decode(v: &[u8]) -> Result<Self,DecodeError> {
if v.len() < 34 {
return Err(DecodeError::WrongLength);
}
let len = byte_utils::slice_to_be16(&v[33..34]);
let mut data = String::new();
if len > 0 {
data = String::from_utf8(v[35..len as usize].to_vec()).unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() here can panic if the message contains non-utf-8 crap.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, some of the indexing is off in the decoder here.

if len != data.len() as u16 {
return Err(DecodeError::WrongLength);
}
}
let mut channel_id = [0; 32];
channel_id[..].copy_from_slice(&v[0..32]);
Ok(Self {
channel_id,
data: data,
})
}
}
10 changes: 5 additions & 5 deletions src/ln/peer_channel_encryptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl PeerChannelEncryptor {

let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce, h);
if !chacha.decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]) {
return Err(HandleError{err: "Bad MAC", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Bad MAC", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}
Ok(())
}
Expand Down Expand Up @@ -195,11 +195,11 @@ impl PeerChannelEncryptor {
assert_eq!(act.len(), 50);

if act[0] != 0 {
return Err(HandleError{err: "Unknown handshake version number", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Unknown handshake version number", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}

let their_pub = match PublicKey::from_slice(secp_ctx, &act[1..34]) {
Err(_) => return Err(HandleError{err: "Invalid public key", action: Some(msgs::ErrorAction::DisconnectPeer{})}),
Err(_) => return Err(HandleError{err: "Invalid public key", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })}),
Ok(key) => key,
};

Expand Down Expand Up @@ -349,14 +349,14 @@ impl PeerChannelEncryptor {
panic!("Requested act at wrong step");
}
if act_three[0] != 0 {
return Err(HandleError{err: "Unknown handshake version number", action: Some(msgs::ErrorAction::DisconnectPeer{})});
return Err(HandleError{err: "Unknown handshake version number", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
}

let mut their_node_id = [0; 33];
PeerChannelEncryptor::decrypt_with_ad(&mut their_node_id, 1, &temp_k2.unwrap(), &bidirectional_state.h, &act_three[1..50])?;
self.their_node_id = Some(match PublicKey::from_slice(&self.secp_ctx, &their_node_id) {
Ok(key) => key,
Err(_) => return Err(HandleError{err: "Bad node_id from peer", action: Some(msgs::ErrorAction::DisconnectPeer{})}),
Err(_) => return Err(HandleError{err: "Bad node_id from peer", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })}),
});

let mut sha = Sha256::new();
Expand Down
Loading