Skip to content

Raise APIError from send_payment #173

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
44 changes: 24 additions & 20 deletions src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ impl ChannelManager {
}

/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
fn build_onion_payloads(route: &Route, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), HandleError> {
fn build_onion_payloads(route: &Route, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
let mut cur_value_msat = 0u64;
let mut cur_cltv = starting_htlc_offset;
let mut last_short_channel_id = 0;
Expand All @@ -625,11 +625,11 @@ impl ChannelManager {
};
cur_value_msat += hop.fee_msat;
if cur_value_msat >= 21000000 * 100000000 * 1000 {
return Err(HandleError{err: "Channel fees overflowed?!", action: None});
return Err(APIError::RouteError{err: "Channel fees overflowed?!"});
}
cur_cltv += hop.cltv_expiry_delta as u32;
if cur_cltv >= 500000000 {
return Err(HandleError{err: "Channel CLTV overflowed?!", action: None});
return Err(APIError::RouteError{err: "Channel CLTV overflowed?!"});
}
last_short_channel_id = hop.short_channel_id;
}
Expand All @@ -656,7 +656,7 @@ impl ChannelManager {
}

const ZERO:[u8; 21*65] = [0; 21*65];
fn construct_onion_packet(mut payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, associated_data: &[u8; 32]) -> Result<msgs::OnionPacket, HandleError> {
fn construct_onion_packet(mut payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, associated_data: &[u8; 32]) -> msgs::OnionPacket {
let mut buf = Vec::with_capacity(21*65);
buf.resize(21*65, 0);

Expand Down Expand Up @@ -697,12 +697,12 @@ impl ChannelManager {
hmac.raw_result(&mut hmac_res);
}

Ok(msgs::OnionPacket{
msgs::OnionPacket{
version: 0,
public_key: Ok(onion_keys.first().unwrap().ephemeral_pubkey),
hop_data: packet_data,
hmac: hmac_res,
})
}
}

/// Encrypts a failure packet. raw_packet can either be a
Expand Down Expand Up @@ -973,14 +973,16 @@ impl ChannelManager {
/// payment") and prevent double-sends yourself.
/// See-also docs on Channel::send_htlc_and_commit.
/// May generate a SendHTLCs event on success, which should be relayed.
pub fn send_payment(&self, route: Route, payment_hash: [u8; 32]) -> Result<(), HandleError> {
/// Raises APIError::RoutError when invalid route or forward parameter
/// (cltv_delta, fee, node public key) is specified
pub fn send_payment(&self, route: Route, payment_hash: [u8; 32]) -> Result<(), APIError> {
if route.hops.len() < 1 || route.hops.len() > 20 {
return Err(HandleError{err: "Route didn't go anywhere/had bogus size", action: None});
return Err(APIError::RouteError{err: "Route didn't go anywhere/had bogus size"});
}
let our_node_id = self.get_our_node_id();
for (idx, hop) in route.hops.iter().enumerate() {
if idx != route.hops.len() - 1 && hop.pubkey == our_node_id {
return Err(HandleError{err: "Route went through us but wasn't a simple rebalance loop to us", action: None});
return Err(APIError::RouteError{err: "Route went through us but wasn't a simple rebalance loop to us"});
Copy link
Collaborator

Choose a reason for hiding this comment

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

whitespace err here.

}
}

Expand All @@ -992,34 +994,32 @@ impl ChannelManager {

let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;

//TODO: This should return something other than HandleError, that's really intended for
//p2p-returns only.
let onion_keys = secp_call!(ChannelManager::construct_onion_keys(&self.secp_ctx, &route, &session_priv),
HandleError{err: "Pubkey along hop was maliciously selected", action: Some(msgs::ErrorAction::IgnoreError)});
APIError::RouteError{err: "Pubkey along hop was maliciously selected"});
let (onion_payloads, htlc_msat, htlc_cltv) = ChannelManager::build_onion_payloads(&route, cur_height)?;
let onion_packet = ChannelManager::construct_onion_packet(onion_payloads, onion_keys, &payment_hash)?;
let onion_packet = ChannelManager::construct_onion_packet(onion_payloads, onion_keys, &payment_hash);

let (first_hop_node_id, (update_add, commitment_signed, chan_monitor)) = {
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = channel_state_lock.borrow_parts();

let id = match channel_state.short_to_id.get(&route.hops.first().unwrap().short_channel_id) {
None => return Err(HandleError{err: "No channel available with first hop!", action: None}),
Some(id) => id.clone()
None => return Err(APIError::RouteError{err: "No channel available with first hop!"}),
Some(id) => id.clone(),
};

let res = {
let chan = channel_state.by_id.get_mut(&id).unwrap();
if chan.get_their_node_id() != route.hops.first().unwrap().pubkey {
return Err(HandleError{err: "Node ID mismatch on first hop!", action: None});
return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
}
if !chan.is_live() {
return Err(HandleError{err: "Peer for first hop currently disconnected!", action: None});
return Err(APIError::RouteError{err: "Peer for first hop currently disconnected!"});
}
chan.send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
route: route.clone(),
session_priv: session_priv.clone(),
}, onion_packet)?
}, onion_packet).map_err(|he| APIError::RouteError{err: he.err})?
};

let first_hop_node_id = route.hops.first().unwrap().pubkey;
Expand Down Expand Up @@ -2193,6 +2193,7 @@ mod tests {
use util::test_utils;
use util::events::{Event, EventsProvider};
use util::logger::Logger;
use util::errors::APIError;

use bitcoin::util::hash::Sha256dHash;
use bitcoin::blockdata::block::{Block, BlockHeader};
Expand Down Expand Up @@ -2340,7 +2341,7 @@ mod tests {
},
);

let packet = ChannelManager::construct_onion_packet(payloads, onion_keys, &[0x42; 32]).unwrap();
let packet = ChannelManager::construct_onion_packet(payloads, onion_keys, &[0x42; 32]);
// Just check the final packet encoding, as it includes all the per-hop vectors in it
// anyway...
assert_eq!(packet.encode(), hex::decode("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a716a996c7845c93d90e4ecbb9bde4ece2f69425c99e4bc820e44485455f135edc0d10f7d61ab590531cf08000179a333a347f8b4072f216400406bdf3bf038659793d4a1fd7b246979e3150a0a4cb052c9ec69acf0f48c3d39cd55675fe717cb7d80ce721caad69320c3a469a202f1e468c67eaf7a7cd8226d0fd32f7b48084dca885d56047694762b67021713ca673929c163ec36e04e40ca8e1c6d17569419d3039d9a1ec866abe044a9ad635778b961fc0776dc832b3a451bd5d35072d2269cf9b040f6b7a7dad84fb114ed413b1426cb96ceaf83825665ed5a1d002c1687f92465b49ed4c7f0218ff8c6c7dd7221d589c65b3b9aaa71a41484b122846c7c7b57e02e679ea8469b70e14fe4f70fee4d87b910cf144be6fe48eef24da475c0b0bcc6565ae82cd3f4e3b24c76eaa5616c6111343306ab35c1fe5ca4a77c0e314ed7dba39d6f1e0de791719c241a939cc493bea2bae1c1e932679ea94d29084278513c77b899cc98059d06a27d171b0dbdf6bee13ddc4fc17a0c4d2827d488436b57baa167544138ca2e64a11b43ac8a06cd0c2fba2d4d900ed2d9205305e2d7383cc98dacb078133de5f6fb6bed2ef26ba92cea28aafc3b9948dd9ae5559e8bd6920b8cea462aa445ca6a95e0e7ba52961b181c79e73bd581821df2b10173727a810c92b83b5ba4a0403eb710d2ca10689a35bec6c3a708e9e92f7d78ff3c5d9989574b00c6736f84c199256e76e19e78f0c98a9d580b4a658c84fc8f2096c2fbea8f5f8c59d0fdacb3be2802ef802abbecb3aba4acaac69a0e965abd8981e9896b1f6ef9d60f7a164b371af869fd0e48073742825e9434fc54da837e120266d53302954843538ea7c6c3dbfb4ff3b2fdbe244437f2a153ccf7bdb4c92aa08102d4f3cff2ae5ef86fab4653595e6a5837fa2f3e29f27a9cde5966843fb847a4a61f1e76c281fe8bb2b0a181d096100db5a1a5ce7a910238251a43ca556712eaadea167fb4d7d75825e440f3ecd782036d7574df8bceacb397abefc5f5254d2722215c53ff54af8299aaaad642c6d72a14d27882d9bbd539e1cc7a527526ba89b8c037ad09120e98ab042d3e8652b31ae0e478516bfaf88efca9f3676ffe99d2819dcaeb7610a626695f53117665d267d3f7abebd6bbd6733f645c72c389f03855bdf1e4b8075b516569b118233a0f0971d24b83113c0b096f5216a207ca99a7cddc81c130923fe3d91e7508c9ac5f2e914ff5dccab9e558566fa14efb34ac98d878580814b94b73acbfde9072f30b881f7f0fff42d4045d1ace6322d86a97d164aa84d93a60498065cc7c20e636f5862dc81531a88c60305a2e59a985be327a6902e4bed986dbf4a0b50c217af0ea7fdf9ab37f9ea1a1aaa72f54cf40154ea9b269f1a7c09f9f43245109431a175d50e2db0132337baa0ef97eed0fcf20489da36b79a1172faccc2f7ded7c60e00694282d93359c4682135642bc81f433574aa8ef0c97b4ade7ca372c5ffc23c7eddd839bab4e0f14d6df15c9dbeab176bec8b5701cf054eb3072f6dadc98f88819042bf10c407516ee58bce33fbe3b3d86a54255e577db4598e30a135361528c101683a5fcde7e8ba53f3456254be8f45fe3a56120ae96ea3773631fcb3873aa3abd91bcff00bd38bd43697a2e789e00da6077482e7b1b1a677b5afae4c54e6cbdf7377b694eb7d7a5b913476a5be923322d3de06060fd5e819635232a2cf4f0731da13b8546d1d6d4f8d75b9fce6c2341a71b0ea6f780df54bfdb0dd5cd9855179f602f9172307c7268724c3618e6817abd793adc214a0dc0bc616816632f27ea336fb56dfd").unwrap());
Expand Down Expand Up @@ -2818,7 +2819,10 @@ mod tests {
};

let err = origin_node.node.send_payment(route, our_payment_hash).err().unwrap();
assert_eq!(err.err, "Cannot send value that would put us over our max HTLC value in flight");
match err {
APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
_ => panic!("Unknown error variants"),
};
}

fn send_payment(origin: &Node, expected_route: &[&Node], recv_value: u64) {
Expand Down
6 changes: 5 additions & 1 deletion src/util/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ pub enum APIError {
/// For example, this may be returned if the feerate implies we cannot open a channel at the
/// requested value, but opening a larger channel would succeed.
FeeRateTooHigh {err: String, feerate: u64},

/// Invalid route or parameters (cltv_delta, fee, pubkey) was specified
RouteError {err: &'static str},
}

impl fmt::Debug for APIError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
APIError::APIMisuseError {ref err} => f.write_str(err),
APIError::FeeRateTooHigh {ref err, ref feerate} => write!(f, "{} feerate: {}", err, feerate)
APIError::FeeRateTooHigh {ref err, ref feerate} => write!(f, "{} feerate: {}", err, feerate),
APIError::RouteError {ref err} => f.write_str(err),
}
}
}