From cf6b8c5d1d39ffabe47cc5a65ca84753573cfc88 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 21 Jun 2020 16:53:29 +0900 Subject: [PATCH 01/45] Improve error message. ... for ChannelError and APIMisuseError Before this commit, When rl returns error, we don't know The actual parameter which caused the error. By returning parameterised `String` instead of predefined `&'static str`, We can give a caller improved error message. --- lightning/src/ln/channel.rs | 357 +++++++++++---------- lightning/src/ln/channelmanager.rs | 117 +++---- lightning/src/ln/functional_test_utils.rs | 6 +- lightning/src/ln/functional_tests.rs | 10 +- lightning/src/ln/msgs.rs | 4 +- lightning/src/ln/peer_channel_encryptor.rs | 11 +- lightning/src/routing/network_graph.rs | 21 +- lightning/src/routing/router.rs | 10 +- lightning/src/util/errors.rs | 4 +- lightning/src/util/test_utils.rs | 6 +- 10 files changed, 279 insertions(+), 267 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index c95d6e73fe2..6823458e247 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -34,6 +34,7 @@ use std; use std::default::Default; use std::{cmp,mem,fmt}; use std::ops::Deref; +use bitcoin::hashes::hex::ToHex; #[cfg(test)] pub struct ChannelValueStat { @@ -409,17 +410,17 @@ pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24; /// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our /// channel_id in ChannelManager. pub(super) enum ChannelError { - Ignore(&'static str), - Close(&'static str), - CloseDelayBroadcast(&'static str), + Ignore(String), + Close(String), + CloseDelayBroadcast(String), } impl fmt::Debug for ChannelError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &ChannelError::Ignore(e) => write!(f, "Ignore : {}", e), - &ChannelError::Close(e) => write!(f, "Close : {}", e), - &ChannelError::CloseDelayBroadcast(e) => write!(f, "CloseDelayBroadcast : {}", e) + &ChannelError::Ignore(ref e) => write!(f, "Ignore : {}", e), + &ChannelError::Close(ref e) => write!(f, "Close : {}", e), + &ChannelError::CloseDelayBroadcast(ref e) => write!(f, "CloseDelayBroadcast : {}", e) } } } @@ -433,6 +434,7 @@ macro_rules! secp_check { }; } + impl Channel { // Convert constants + channel value to limits: fn get_our_max_htlc_value_in_flight_msat(channel_value_satoshis: u64) -> u64 { @@ -460,17 +462,15 @@ impl Channel { let chan_keys = keys_provider.get_channel_keys(false, channel_value_satoshis); if channel_value_satoshis >= MAX_FUNDING_SATOSHIS { - return Err(APIError::APIMisuseError{err: "funding value > 2^24"}); + return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than {}, it was {}", MAX_FUNDING_SATOSHIS, channel_value_satoshis)}); } - - if push_msat > channel_value_satoshis * 1000 { - return Err(APIError::APIMisuseError{err: "push value > channel value"}); + let channel_value_msat = channel_value_satoshis * 1000; + if push_msat > channel_value_msat { + return Err(APIError::APIMisuseError { err: format!("push value ({}) was larger than channel_value ({})", push_msat, channel_value_msat) }); } if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT { - return Err(APIError::APIMisuseError{err: "Configured with an unreasonable our_to_self_delay putting user funds at risks"}); + return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", config.own_channel_config.our_to_self_delay)}); } - - let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); if Channel::::get_remote_channel_reserve_satoshis(channel_value_satoshis) < Channel::::derive_our_dust_limit_satoshis(background_feerate) { return Err(APIError::FeeRateTooHigh{err: format!("Not enough reserve above dust limit can be found at current fee rate({})", background_feerate), feerate: background_feerate}); @@ -558,11 +558,13 @@ impl Channel { fn check_remote_fee(fee_estimator: &F, feerate_per_kw: u32) -> Result<(), ChannelError> where F::Target: FeeEstimator { - if feerate_per_kw < fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) { - return Err(ChannelError::Close("Peer's feerate much too low")); + let lower_limit = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); + if feerate_per_kw < lower_limit { + return Err(ChannelError::Close(format!("Peer's feerate much too low. actual: {}. our expected lower_limit: {}", feerate_per_kw, lower_limit))); } - if feerate_per_kw as u64 > fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64 * 2 { - return Err(ChannelError::Close("Peer's feerate much too high")); + let upper_limit = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64 * 2; + if feerate_per_kw as u64 > upper_limit { + return Err(ChannelError::Close(format!("Peer's feerate much too high. actual: {}. our expected upper limit {}", feerate_per_kw, upper_limit))); } Ok(()) } @@ -585,61 +587,64 @@ impl Channel { let mut local_config = (*config).channel_options.clone(); if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT { - return Err(ChannelError::Close("Configured with an unreasonable our_to_self_delay putting user funds at risks")); + return Err(ChannelError::Close(format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", config.own_channel_config.our_to_self_delay))); } // Check sanity of message fields: if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS { - return Err(ChannelError::Close("funding value > 2^24")); + return Err(ChannelError::Close(format!("funding must be smaller than {}. It was {}", msg.funding_satoshis, MAX_FUNDING_SATOSHIS))); } if msg.channel_reserve_satoshis > msg.funding_satoshis { - return Err(ChannelError::Close("Bogus channel_reserve_satoshis")); + return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis({}). must be not greater than funding_satoshis: {}", msg.channel_reserve_satoshis, msg.funding_satoshis))); } - if msg.push_msat > (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 { - return Err(ChannelError::Close("push_msat larger than funding value")); + let funding_value = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000; + if msg.push_msat > funding_value { + return Err(ChannelError::Close(format!("push_msat {} was larger than funding value {}", msg.push_msat, funding_value))); } if msg.dust_limit_satoshis > msg.funding_satoshis { - return Err(ChannelError::Close("Peer never wants payout outputs?")); + return Err(ChannelError::Close(format!("dust_limit_satoshis {} was larger than funding_satoshis {}. Peer never wants payout outputs?", msg.dust_limit_satoshis, msg.funding_satoshis))); } if msg.dust_limit_satoshis > msg.channel_reserve_satoshis { - return Err(ChannelError::Close("Bogus; channel reserve is less than dust limit")); + return Err(ChannelError::Close(format!("Bogus; channel reserve({}) is less than dust limit ({})", msg.channel_reserve_satoshis, msg.dust_limit_satoshis))); } - if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 { - return Err(ChannelError::Close("Minimum htlc value is full channel value")); + let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000; + if msg.htlc_minimum_msat >= full_channel_value_msat { + return Err(ChannelError::Close(format!("Minimum htlc value ({}) is full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat))); } Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; - if msg.to_self_delay > config.peer_channel_config_limits.their_to_self_delay || msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT { - return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period")); + let max_to_self_delay = u16::min(config.peer_channel_config_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT); + if msg.to_self_delay > max_to_self_delay { + return Err(ChannelError::Close(format!("They wanted our payments to be delayed by a needlessly long period. upper limit: {}. actual: {}", max_to_self_delay, msg.to_self_delay))); } if msg.max_accepted_htlcs < 1 { - return Err(ChannelError::Close("0 max_accpted_htlcs makes for a useless channel")); + return Err(ChannelError::Close("0 max_accpted_htlcs makes for a useless channel".to_owned())); } if msg.max_accepted_htlcs > 483 { - return Err(ChannelError::Close("max_accpted_htlcs > 483")); + return Err(ChannelError::Close(format!("max_accpted_htlcs was {}. it must not be larger than 483", msg.max_accepted_htlcs))); } // Now check against optional parameters as set by config... if msg.funding_satoshis < config.peer_channel_config_limits.min_funding_satoshis { - return Err(ChannelError::Close("funding satoshis is less than the user specified limit")); + return Err(ChannelError::Close(format!("funding satoshis ({}) is less than the user specified limit ({})", msg.funding_satoshis, config.peer_channel_config_limits.min_funding_satoshis))); } if msg.htlc_minimum_msat > config.peer_channel_config_limits.max_htlc_minimum_msat { - return Err(ChannelError::Close("htlc minimum msat is higher than the user specified limit")); + return Err(ChannelError::Close(format!("htlc minimum msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, config.peer_channel_config_limits.max_htlc_minimum_msat))); } if msg.max_htlc_value_in_flight_msat < config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat { - return Err(ChannelError::Close("max htlc value in flight msat is less than the user specified limit")); + return Err(ChannelError::Close(format!("max htlc value in flight msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat))); } if msg.channel_reserve_satoshis > config.peer_channel_config_limits.max_channel_reserve_satoshis { - return Err(ChannelError::Close("channel reserve satoshis is higher than the user specified limit")); + return Err(ChannelError::Close(format!("channel reserve satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, config.peer_channel_config_limits.max_channel_reserve_satoshis))); } if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs { - return Err(ChannelError::Close("max accepted htlcs is less than the user specified limit")); + return Err(ChannelError::Close(format!("max accepted htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, config.peer_channel_config_limits.min_max_accepted_htlcs))); } if msg.dust_limit_satoshis < config.peer_channel_config_limits.min_dust_limit_satoshis { - return Err(ChannelError::Close("dust limit satoshis is less than the user specified limit")); + return Err(ChannelError::Close(format!("dust limit satoshis ({}) is less than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.min_dust_limit_satoshis))); } if msg.dust_limit_satoshis > config.peer_channel_config_limits.max_dust_limit_satoshis { - return Err(ChannelError::Close("dust limit satoshis is greater than the user specified limit")); + return Err(ChannelError::Close(format!("dust limit satoshis ({}) is greater than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.max_dust_limit_satoshis))); } // Convert things into internal flags and prep our state: @@ -647,7 +652,7 @@ impl Channel { let their_announce = if (msg.channel_flags & 1) == 1 { true } else { false }; if config.peer_channel_config_limits.force_announced_channel_preference { if local_config.announced_channel != their_announce { - return Err(ChannelError::Close("Peer tried to open channel but their announcement preference is different from ours")); + return Err(ChannelError::Close("Peer tried to open channel but their announcement preference is different from ours".to_owned())); } } // we either accept their preference or the preferences match @@ -658,26 +663,27 @@ impl Channel { let our_dust_limit_satoshis = Channel::::derive_our_dust_limit_satoshis(background_feerate); let remote_channel_reserve_satoshis = Channel::::get_remote_channel_reserve_satoshis(msg.funding_satoshis); if remote_channel_reserve_satoshis < our_dust_limit_satoshis { - return Err(ChannelError::Close("Suitable channel reserve not found. aborting")); + return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). our_dusts_limit_satoshis is ({}) .aborting", remote_channel_reserve_satoshis, our_dust_limit_satoshis))); } if msg.channel_reserve_satoshis < our_dust_limit_satoshis { - return Err(ChannelError::Close("channel_reserve_satoshis too small")); + return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is small than our dust limit ({})", msg.channel_reserve_satoshis, our_dust_limit_satoshis))); } if remote_channel_reserve_satoshis < msg.dust_limit_satoshis { - return Err(ChannelError::Close("Dust limit too high for the channel reserve we require the remote to keep")); + return Err(ChannelError::Close(format!("Dust limit ({}) too high for the channel reserve we require the remote to keep ({})", msg.dust_limit_satoshis, remote_channel_reserve_satoshis))); } // check if the funder's amount for the initial commitment tx is sufficient // for full fee payment let funders_amount_msat = msg.funding_satoshis * 1000 - msg.push_msat; - if funders_amount_msat < background_feerate as u64 * COMMITMENT_TX_BASE_WEIGHT { - return Err(ChannelError::Close("Insufficient funding amount for initial commitment")); + let lower_limit = background_feerate as u64 * COMMITMENT_TX_BASE_WEIGHT; + if funders_amount_msat < lower_limit { + return Err(ChannelError::Close(format!("Insufficient funding amount ({}) for initial commitment. must be at least ({})", funders_amount_msat, lower_limit))); } let to_local_msat = msg.push_msat; let to_remote_msat = funders_amount_msat - background_feerate as u64 * COMMITMENT_TX_BASE_WEIGHT; if to_local_msat <= msg.channel_reserve_satoshis * 1000 && to_remote_msat <= remote_channel_reserve_satoshis * 1000 { - return Err(ChannelError::Close("Insufficient funding amount for initial commitment")); + return Err(ChannelError::Close("Insufficient funding amount for initial commitment".to_owned())); } let their_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() { @@ -691,12 +697,12 @@ impl Channel { None // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. Fail the channel } else { - return Err(ChannelError::Close("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format")); + return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format. script: ({})", script.to_bytes().to_hex()))); } }, // Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel &OptionalField::Absent => { - return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out")); + return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out".to_owned())); } } } else { None }; @@ -1123,7 +1129,7 @@ impl Channel { let htlc_basepoint = &self.local_keys.pubkeys().htlc_basepoint; let their_pubkeys = self.their_pubkeys.as_ref().unwrap(); - Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &their_pubkeys.revocation_basepoint, &their_pubkeys.htlc_basepoint), "Local tx keys generation got bogus keys")) + Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &their_pubkeys.revocation_basepoint, &their_pubkeys.htlc_basepoint), "Local tx keys generation got bogus keys".to_owned())) } #[inline] @@ -1137,7 +1143,7 @@ impl Channel { let htlc_basepoint = &self.local_keys.pubkeys().htlc_basepoint; let their_pubkeys = self.their_pubkeys.as_ref().unwrap(); - Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &self.their_cur_commitment_point.unwrap(), &their_pubkeys.delayed_payment_basepoint, &their_pubkeys.htlc_basepoint, revocation_basepoint, htlc_basepoint), "Remote tx keys generation got bogus keys")) + Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &self.their_cur_commitment_point.unwrap(), &their_pubkeys.delayed_payment_basepoint, &their_pubkeys.htlc_basepoint, revocation_basepoint, htlc_basepoint), "Remote tx keys generation got bogus keys".to_owned())) } /// Gets the redeemscript for the funding transaction output (ie the funding transaction output @@ -1200,7 +1206,7 @@ impl Channel { } } if pending_idx == std::usize::MAX { - return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID")); + return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned())); } // Now update local state: @@ -1310,14 +1316,14 @@ impl Channel { }, _ => { debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to"); - return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID")); + return Err(ChannelError::Ignore(format!("Unable to find a pending HTLC which matched the given HTLC ID ({})", htlc.htlc_id))); } } pending_idx = idx; } } if pending_idx == std::usize::MAX { - return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID")); + return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned())); } // Now update local state: @@ -1327,13 +1333,13 @@ impl Channel { &HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => { if htlc_id_arg == htlc_id { debug_assert!(false, "Tried to fail an HTLC that was already fulfilled"); - return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID")); + return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned())); } }, &HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => { if htlc_id_arg == htlc_id { debug_assert!(false, "Tried to fail an HTLC that was already failed"); - return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID")); + return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned())); } }, _ => {} @@ -1363,60 +1369,63 @@ impl Channel { pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, config: &UserConfig, their_features: InitFeatures) -> Result<(), ChannelError> { // Check sanity of message fields: if !self.channel_outbound { - return Err(ChannelError::Close("Got an accept_channel message from an inbound peer")); + return Err(ChannelError::Close("Got an accept_channel message from an inbound peer".to_owned())); } if self.channel_state != ChannelState::OurInitSent as u32 { - return Err(ChannelError::Close("Got an accept_channel message at a strange time")); + return Err(ChannelError::Close("Got an accept_channel message at a strange time".to_owned())); } if msg.dust_limit_satoshis > 21000000 * 100000000 { - return Err(ChannelError::Close("Peer never wants payout outputs?")); + return Err(ChannelError::Close(format!("Peer never wants payout outputs? dust_limit_satoshis was {}", msg.dust_limit_satoshis))); } if msg.channel_reserve_satoshis > self.channel_value_satoshis { - return Err(ChannelError::Close("Bogus channel_reserve_satoshis")); + return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis({}). must not be greater than ({})", msg.channel_reserve_satoshis, self.channel_value_satoshis))); } if msg.dust_limit_satoshis > msg.channel_reserve_satoshis { - return Err(ChannelError::Close("Bogus channel_reserve and dust_limit")); + return Err(ChannelError::Close(format!("Bogus channel_reserve({}) and dust_limit ({})", msg.dust_limit_satoshis, msg.channel_reserve_satoshis))); } if msg.channel_reserve_satoshis < self.our_dust_limit_satoshis { - return Err(ChannelError::Close("Peer never wants payout outputs?")); + return Err(ChannelError::Close(format!("Peer never wants payout outputs? channel_reserve_satoshis was ({}). our_dust_limit is ({})", msg.channel_reserve_satoshis, self.our_dust_limit_satoshis))); } - if msg.dust_limit_satoshis > Channel::::get_remote_channel_reserve_satoshis(self.channel_value_satoshis) { - return Err(ChannelError::Close("Dust limit is bigger than our channel reverse")); + let remote_reserve = Channel::::get_remote_channel_reserve_satoshis(self.channel_value_satoshis); + if msg.dust_limit_satoshis > remote_reserve { + return Err(ChannelError::Close(format!("Dust limit ({}) is bigger than our channel reverse ({})", msg.dust_limit_satoshis, remote_reserve))); } - if msg.htlc_minimum_msat >= (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000 { - return Err(ChannelError::Close("Minimum htlc value is full channel value")); + let full_channel_value_msat = (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000; + if msg.htlc_minimum_msat >= full_channel_value_msat { + return Err(ChannelError::Close(format!("Minimum htlc value({}) is full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat))); } - if msg.to_self_delay > config.peer_channel_config_limits.their_to_self_delay || msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT { - return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period")); + let max_delay_acceptable = u16::min(config.peer_channel_config_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT); + if msg.to_self_delay > max_delay_acceptable { + return Err(ChannelError::Close(format!("They wanted our payments to be delayed by a needlessly long period. upper limit: {}. actual: {}", msg.to_self_delay, max_delay_acceptable))); } if msg.max_accepted_htlcs < 1 { - return Err(ChannelError::Close("0 max_accepted_htlcs makes for a useless channel")); + return Err(ChannelError::Close("0 max_accepted_htlcs makes for a useless channel".to_owned())); } if msg.max_accepted_htlcs > 483 { - return Err(ChannelError::Close("max_accepted_htlcs > 483")); + return Err(ChannelError::Close(format!("max_accepted_htlcs was {} it must not be larger than 483", msg.max_accepted_htlcs))); } // Now check against optional parameters as set by config... if msg.htlc_minimum_msat > config.peer_channel_config_limits.max_htlc_minimum_msat { - return Err(ChannelError::Close("htlc minimum msat is higher than the user specified limit")); + return Err(ChannelError::Close(format!("htlc minimum msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, config.peer_channel_config_limits.max_htlc_minimum_msat))); } if msg.max_htlc_value_in_flight_msat < config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat { - return Err(ChannelError::Close("max htlc value in flight msat is less than the user specified limit")); + return Err(ChannelError::Close(format!("max htlc value in flight msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat))); } if msg.channel_reserve_satoshis > config.peer_channel_config_limits.max_channel_reserve_satoshis { - return Err(ChannelError::Close("channel reserve satoshis is higher than the user specified limit")); + return Err(ChannelError::Close(format!("channel reserve satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, config.peer_channel_config_limits.max_channel_reserve_satoshis))); } if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs { - return Err(ChannelError::Close("max accepted htlcs is less than the user specified limit")); + return Err(ChannelError::Close(format!("max accepted htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, config.peer_channel_config_limits.min_max_accepted_htlcs))); } if msg.dust_limit_satoshis < config.peer_channel_config_limits.min_dust_limit_satoshis { - return Err(ChannelError::Close("dust limit satoshis is less than the user specified limit")); + return Err(ChannelError::Close(format!("dust limit satoshis ({}) is less than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.min_dust_limit_satoshis))); } if msg.dust_limit_satoshis > config.peer_channel_config_limits.max_dust_limit_satoshis { - return Err(ChannelError::Close("dust limit satoshis is greater than the user specified limit")); + return Err(ChannelError::Close(format!("dust limit satoshis ({}) is greater than the user specified limit ({})", msg.dust_limit_satoshis, config.peer_channel_config_limits.max_dust_limit_satoshis))); } if msg.minimum_depth > config.peer_channel_config_limits.max_minimum_depth { - return Err(ChannelError::Close("We consider the minimum depth to be unreasonably large")); + return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. expected minimum: ({}). actual: ({})", msg.minimum_depth, config.peer_channel_config_limits.max_minimum_depth))); } let their_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() { @@ -1430,12 +1439,12 @@ impl Channel { None // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. Fail the channel } else { - return Err(ChannelError::Close("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format")); + return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format. scirptpubkey: ({})", script.to_bytes().to_hex()))); } }, // Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel &OptionalField::Absent => { - return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out")); + return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out".to_owned())); } } } else { None }; @@ -1476,14 +1485,14 @@ impl Channel { // They sign the "local" commitment transaction... log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {}", log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.their_funding_pubkey().serialize()), encode::serialize_hex(&local_initial_commitment_tx), log_bytes!(local_sighash[..]), encode::serialize_hex(&funding_script)); - secp_check!(self.secp_ctx.verify(&local_sighash, &sig, self.their_funding_pubkey()), "Invalid funding_created signature from peer"); + secp_check!(self.secp_ctx.verify(&local_sighash, &sig, self.their_funding_pubkey()), "Invalid funding_created signature from peer".to_owned()); let localtx = LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, sig.clone(), &self.local_keys.pubkeys().funding_pubkey, self.their_funding_pubkey(), local_keys, self.feerate_per_kw, Vec::new()); let remote_keys = self.build_remote_transaction_keys()?; let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw, logger).0; let remote_signature = self.local_keys.sign_remote_commitment(self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) - .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?.0; + .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0; // We sign the "remote" commitment transaction, allowing them to broadcast the tx if they wish. Ok((remote_initial_commitment_tx, localtx, remote_signature)) @@ -1495,13 +1504,13 @@ impl Channel { pub fn funding_created(&mut self, msg: &msgs::FundingCreated, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> where L::Target: Logger { if self.channel_outbound { - return Err(ChannelError::Close("Received funding_created for an outbound channel?")); + return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned())); } if self.channel_state != (ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32) { // BOLT 2 says that if we disconnect before we send funding_signed we SHOULD NOT // remember the channel, so it's safe to just send an error_message here and drop the // channel. - return Err(ChannelError::Close("Received funding_created after we got the channel!")); + return Err(ChannelError::Close("Received funding_created after we got the channel!".to_owned())); } if self.commitment_secrets.get_min_seen_secret() != (1 << 48) || self.cur_remote_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER || @@ -1558,10 +1567,10 @@ impl Channel { /// If this call is successful, broadcast the funding transaction (and not before!) pub fn funding_signed(&mut self, msg: &msgs::FundingSigned, logger: &L) -> Result, ChannelError> where L::Target: Logger { if !self.channel_outbound { - return Err(ChannelError::Close("Received funding_signed for an inbound channel?")); + return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned())); } if self.channel_state & !(ChannelState::MonitorUpdateFailed as u32) != ChannelState::FundingCreated as u32 { - return Err(ChannelError::Close("Received funding_signed in strange state!")); + return Err(ChannelError::Close("Received funding_signed in strange state!".to_owned())); } if self.commitment_secrets.get_min_seen_secret() != (1 << 48) || self.cur_remote_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER || @@ -1582,7 +1591,7 @@ impl Channel { // They sign the "local" commitment transaction, allowing us to broadcast the tx if we wish. if let Err(_) = self.secp_ctx.verify(&local_sighash, &msg.signature, their_funding_pubkey) { - return Err(ChannelError::Close("Invalid funding_signed signature from peer")); + return Err(ChannelError::Close("Invalid funding_signed signature from peer".to_owned())); } let their_pubkeys = self.their_pubkeys.as_ref().unwrap(); @@ -1619,7 +1628,7 @@ impl Channel { pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), ChannelError> { if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent funding_locked when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent funding_locked when we needed a channel_reestablish".to_owned())); } let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); @@ -1637,12 +1646,12 @@ impl Channel { (self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32)) { if self.their_cur_commitment_point != Some(msg.next_per_commitment_point) { - return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point")); + return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point".to_owned())); } // They probably disconnected/reconnected and re-sent the funding_locked, which is required return Ok(()); } else { - return Err(ChannelError::Close("Peer sent a funding_locked at a strange time")); + return Err(ChannelError::Close("Peer sent a funding_locked at a strange time".to_owned())); } self.their_prev_commitment_point = self.their_cur_commitment_point; @@ -1765,28 +1774,28 @@ impl Channel { // If the remote has sent a shutdown prior to adding this HTLC, then they are in violation of the spec. let remote_sent_shutdown = (self.channel_state & (ChannelState::ChannelFunded as u32 | ChannelState::RemoteShutdownSent as u32)) != (ChannelState::ChannelFunded as u32); if remote_sent_shutdown { - return Err(ChannelError::Close("Got add HTLC message when channel was not in an operational state")); + return Err(ChannelError::Close("Got add HTLC message when channel was not in an operational state".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent update_add_htlc when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent update_add_htlc when we needed a channel_reestablish".to_owned())); } if msg.amount_msat > self.channel_value_satoshis * 1000 { - return Err(ChannelError::Close("Remote side tried to send more than the total value of the channel")); + return Err(ChannelError::Close("Remote side tried to send more than the total value of the channel".to_owned())); } if msg.amount_msat == 0 { - return Err(ChannelError::Close("Remote side tried to send a 0-msat HTLC")); + return Err(ChannelError::Close("Remote side tried to send a 0-msat HTLC".to_owned())); } if msg.amount_msat < self.our_htlc_minimum_msat { - return Err(ChannelError::Close("Remote side tried to send less than our minimum HTLC value")); + return Err(ChannelError::Close(format!("Remote side tried to send less than our minimum HTLC value. lower limit: ({}). actual: ({})", msg.amount_msat, self.our_htlc_minimum_msat))); } let (inbound_htlc_count, htlc_inbound_value_msat) = self.get_inbound_pending_htlc_stats(); if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 { - return Err(ChannelError::Close("Remote tried to push more than our max accepted HTLCs")); + return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", OUR_MAX_HTLCS))); } - // Check our_max_htlc_value_in_flight_msat - if htlc_inbound_value_msat + msg.amount_msat > Channel::::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis) { - return Err(ChannelError::Close("Remote HTLC add would put them over our max HTLC value")); + let our_max_htlc_value_in_flight_msat = Channel::::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis); + if htlc_inbound_value_msat + msg.amount_msat > our_max_htlc_value_in_flight_msat { + return Err(ChannelError::Close(format!("Remote HTLC add would put them over our max HTLC value ({})", our_max_htlc_value_in_flight_msat))); } // Check remote_channel_reserve_satoshis (we're getting paid, so they have to at least meet // the reserve_satoshis we told them to always have as direct payment so that they lose @@ -1814,7 +1823,7 @@ impl Channel { let pending_remote_value_msat = self.channel_value_satoshis * 1000 - pending_value_to_self_msat; if pending_remote_value_msat < msg.amount_msat { - return Err(ChannelError::Close("Remote HTLC add would overdraw remaining funds")); + return Err(ChannelError::Close("Remote HTLC add would overdraw remaining funds".to_owned())); } // Check that the remote can afford to pay for this HTLC on-chain at the current @@ -1824,13 +1833,13 @@ impl Channel { self.next_remote_commit_tx_fee_msat(1) }; if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat { - return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees")); + return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned())); }; let chan_reserve_msat = Channel::::get_remote_channel_reserve_satoshis(self.channel_value_satoshis) * 1000; if pending_remote_value_msat - msg.amount_msat - remote_commit_tx_fee_msat < chan_reserve_msat { - return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value")); + return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value".to_owned())); } if !self.channel_outbound { @@ -1854,15 +1863,15 @@ impl Channel { // +1 for this HTLC. let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(1); if self.value_to_self_msat < self.local_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat { - return Err(ChannelError::Close("Cannot receive value that would put us under local channel reserve value")); + return Err(ChannelError::Close("Cannot receive value that would put us under local channel reserve value".to_owned())); } } if self.next_remote_htlc_id != msg.htlc_id { - return Err(ChannelError::Close("Remote skipped HTLC ID")); + return Err(ChannelError::Close(format!("Remote skipped HTLC ID ({})", self.next_remote_htlc_id))); } if msg.cltv_expiry >= 500000000 { - return Err(ChannelError::Close("Remote provided CLTV expiry in seconds instead of block height")); + return Err(ChannelError::Close("Remote provided CLTV expiry in seconds instead of block height".to_owned())); } if self.channel_state & ChannelState::LocalShutdownSent as u32 != 0 { @@ -1892,30 +1901,30 @@ impl Channel { None => {}, Some(payment_hash) => if payment_hash != htlc.payment_hash { - return Err(ChannelError::Close("Remote tried to fulfill HTLC with an incorrect preimage")); + return Err(ChannelError::Close(format!("Remote tried to fulfill HTLC ({}) with an incorrect preimage", htlc_id))); } }; match htlc.state { OutboundHTLCState::LocalAnnounced(_) => - return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")), + return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) before it had been committed", htlc_id))), OutboundHTLCState::Committed => { htlc.state = OutboundHTLCState::RemoteRemoved(fail_reason); }, OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) | OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) | OutboundHTLCState::RemoteRemoved(_) => - return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")), + return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) that they'd already fulfilled/failed", htlc_id))), } return Ok(&htlc.source); } } - Err(ChannelError::Close("Remote tried to fulfill/fail an HTLC we couldn't find")) + Err(ChannelError::Close("Remote tried to fulfill/fail an HTLC we couldn't find".to_owned())) } pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { - return Err(ChannelError::Close("Got fulfill HTLC message when channel was not in an operational state")); + return Err(ChannelError::Close("Got fulfill HTLC message when channel was not in an operational state".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish".to_owned())); } let payment_hash = PaymentHash(Sha256::hash(&msg.payment_preimage.0[..]).into_inner()); @@ -1924,10 +1933,10 @@ impl Channel { pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { - return Err(ChannelError::Close("Got fail HTLC message when channel was not in an operational state")); + return Err(ChannelError::Close("Got fail HTLC message when channel was not in an operational state".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent update_fail_htlc when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent update_fail_htlc when we needed a channel_reestablish".to_owned())); } self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))?; @@ -1936,10 +1945,10 @@ impl Channel { pub fn update_fail_malformed_htlc(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { - return Err(ChannelError::Close("Got fail malformed HTLC message when channel was not in an operational state")); + return Err(ChannelError::Close("Got fail malformed HTLC message when channel was not in an operational state".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent update_fail_malformed_htlc when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent update_fail_malformed_htlc when we needed a channel_reestablish".to_owned())); } self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))?; @@ -1951,13 +1960,13 @@ impl Channel { L::Target: Logger { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { - return Err((None, ChannelError::Close("Got commitment signed message when channel was not in an operational state"))); + return Err((None, ChannelError::Close("Got commitment signed message when channel was not in an operational state".to_owned()))); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err((None, ChannelError::Close("Peer sent commitment_signed when we needed a channel_reestablish"))); + return Err((None, ChannelError::Close("Peer sent commitment_signed when we needed a channel_reestablish".to_owned()))); } if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK == BOTH_SIDES_SHUTDOWN_MASK && self.last_sent_closing_fee.is_some() { - return Err((None, ChannelError::Close("Peer sent commitment_signed after we'd started exchanging closing_signeds"))); + return Err((None, ChannelError::Close("Peer sent commitment_signed after we'd started exchanging closing_signeds".to_owned()))); } let funding_script = self.get_funding_redeemscript(); @@ -1981,7 +1990,7 @@ impl Channel { let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_commitment_tx.0).sighash_all(&local_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]); log_trace!(logger, "Checking commitment tx signature {} by key {} against tx {} (sighash {}) with redeemscript {}", log_bytes!(msg.signature.serialize_compact()[..]), log_bytes!(self.their_funding_pubkey().serialize()), encode::serialize_hex(&local_commitment_tx.0), log_bytes!(local_sighash[..]), encode::serialize_hex(&funding_script)); if let Err(_) = self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey()) { - return Err((None, ChannelError::Close("Invalid commitment tx signature from peer"))); + return Err((None, ChannelError::Close("Invalid commitment tx signature from peer".to_owned()))); } //If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction @@ -1991,12 +2000,12 @@ impl Channel { let remote_reserve_we_require = Channel::::get_remote_channel_reserve_satoshis(self.channel_value_satoshis); if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + remote_reserve_we_require { - return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee"))); + return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned()))); } } if msg.htlc_signatures.len() != local_commitment_tx.1 { - return Err((None, ChannelError::Close("Got wrong number of HTLC signatures from remote"))); + return Err((None, ChannelError::Close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), local_commitment_tx.1)))); } // TODO: Merge these two, sadly they are currently both required to be passed separately to @@ -2010,7 +2019,7 @@ impl Channel { let htlc_sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]); log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {}", log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(local_keys.b_htlc_key.serialize()), encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript)); if let Err(_) = self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key) { - return Err((None, ChannelError::Close("Invalid HTLC tx signature from peer"))); + return Err((None, ChannelError::Close("Invalid HTLC tx signature from peer".to_owned()))); } htlcs_without_source.push((htlc.clone(), Some(msg.htlc_signatures[idx]))); htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source)); @@ -2090,7 +2099,7 @@ impl Channel { } // TODO: Call maybe_propose_first_closing_signed on restoration (or call it here and // re-send the message on restoration) - return Err((Some(monitor_update), ChannelError::Ignore("Previous monitor update failure prevented generation of RAA"))); + return Err((Some(monitor_update), ChannelError::Ignore("Previous monitor update failure prevented generation of RAA".to_owned()))); } let (our_commitment_signed, closing_signed) = if need_our_commitment && (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == 0 { @@ -2248,18 +2257,18 @@ impl Channel { L::Target: Logger, { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { - return Err(ChannelError::Close("Got revoke/ACK message when channel was not in an operational state")); + return Err(ChannelError::Close("Got revoke/ACK message when channel was not in an operational state".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent revoke_and_ack when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent revoke_and_ack when we needed a channel_reestablish".to_owned())); } if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK == BOTH_SIDES_SHUTDOWN_MASK && self.last_sent_closing_fee.is_some() { - return Err(ChannelError::Close("Peer sent revoke_and_ack after we'd started exchanging closing_signeds")); + return Err(ChannelError::Close("Peer sent revoke_and_ack after we'd started exchanging closing_signeds".to_owned())); } if let Some(their_prev_commitment_point) = self.their_prev_commitment_point { - if PublicKey::from_secret_key(&self.secp_ctx, &secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret")) != their_prev_commitment_point { - return Err(ChannelError::Close("Got a revoke commitment secret which didn't correspond to their current pubkey")); + if PublicKey::from_secret_key(&self.secp_ctx, &secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned())) != their_prev_commitment_point { + return Err(ChannelError::Close("Got a revoke commitment secret which didn't correspond to their current pubkey".to_owned())); } } @@ -2271,11 +2280,11 @@ impl Channel { // lot of work, and there's some chance this is all a misunderstanding anyway. // We have to do *something*, though, since our signer may get mad at us for otherwise // jumping a remote commitment number, so best to just force-close and move on. - return Err(ChannelError::Close("Received an unexpected revoke_and_ack")); + return Err(ChannelError::Close("Received an unexpected revoke_and_ack".to_owned())); } self.commitment_secrets.provide_secret(self.cur_remote_commitment_transaction_number + 1, msg.per_commitment_secret) - .map_err(|_| ChannelError::Close("Previous secrets did not match new one"))?; + .map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?; self.latest_monitor_update_id += 1; let mut monitor_update = ChannelMonitorUpdate { update_id: self.latest_monitor_update_id, @@ -2647,10 +2656,10 @@ impl Channel { where F::Target: FeeEstimator { if self.channel_outbound { - return Err(ChannelError::Close("Non-funding remote tried to update channel fee")); + return Err(ChannelError::Close("Non-funding remote tried to update channel fee".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish".to_owned())); } Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; self.pending_update_fee = Some(msg.feerate_per_kw); @@ -2732,23 +2741,23 @@ impl Channel { // While BOLT 2 doesn't indicate explicitly we should error this channel here, it // almost certainly indicates we are going to end up out-of-sync in some way, so we // just close here instead of trying to recover. - return Err(ChannelError::Close("Peer sent a loose channel_reestablish not after reconnect")); + return Err(ChannelError::Close("Peer sent a loose channel_reestablish not after reconnect".to_owned())); } if msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_local_commitment_number == 0 { - return Err(ChannelError::Close("Peer sent a garbage channel_reestablish")); + return Err(ChannelError::Close("Peer sent a garbage channel_reestablish".to_owned())); } if msg.next_remote_commitment_number > 0 { match msg.data_loss_protect { OptionalField::Present(ref data_loss) => { if self.local_keys.commitment_secret(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1) != data_loss.your_last_per_commitment_secret { - return Err(ChannelError::Close("Peer sent a garbage channel_reestablish with secret key not matching the commitment height provided")); + return Err(ChannelError::Close("Peer sent a garbage channel_reestablish with secret key not matching the commitment height provided".to_owned())); } if msg.next_remote_commitment_number > INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number { return Err(ChannelError::CloseDelayBroadcast( - "We have fallen behind - we have received proof that if we broadcast remote is going to claim our funds - we can't do any automated broadcasting" + "We have fallen behind - we have received proof that if we broadcast remote is going to claim our funds - we can't do any automated broadcasting".to_owned() )); } }, @@ -2772,7 +2781,7 @@ impl Channel { if self.channel_state & (ChannelState::OurFundingLocked as u32) == 0 || self.channel_state & (ChannelState::MonitorUpdateFailed as u32) != 0 { if msg.next_remote_commitment_number != 0 { - return Err(ChannelError::Close("Peer claimed they saw a revoke_and_ack but we haven't sent funding_locked yet")); + return Err(ChannelError::Close("Peer claimed they saw a revoke_and_ack but we haven't sent funding_locked yet".to_owned())); } // Short circuit the whole handler as there is nothing we can resend them return Ok((None, None, None, None, RAACommitmentOrder::CommitmentFirst, shutdown_msg)); @@ -2799,7 +2808,7 @@ impl Channel { Some(self.get_last_revoke_and_ack()) } } else { - return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction")); + return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction".to_owned())); }; // We increment cur_remote_commitment_transaction_number only upon receipt of @@ -2853,7 +2862,7 @@ impl Channel { return Ok((resend_funding_locked, required_revoke, Some(self.get_last_commitment_update(logger)), None, self.resend_order.clone(), shutdown_msg)); } else { - return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction")); + return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction".to_owned())); } } @@ -2891,17 +2900,17 @@ impl Channel { where F::Target: FeeEstimator { if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent shutdown when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent shutdown when we needed a channel_reestablish".to_owned())); } if self.channel_state < ChannelState::FundingSent as u32 { // Spec says we should fail the connection, not the channel, but that's nonsense, there // are plenty of reasons you may want to fail a channel pre-funding, and spec says you // can do that via error message without getting a connection fail anyway... - return Err(ChannelError::Close("Peer sent shutdown pre-funding generation")); + return Err(ChannelError::Close("Peer sent shutdown pre-funding generation".to_owned())); } for htlc in self.pending_inbound_htlcs.iter() { if let InboundHTLCState::RemoteAnnounced(_) = htlc.state { - return Err(ChannelError::Close("Got shutdown with remote pending HTLCs")); + return Err(ChannelError::Close("Got shutdown with remote pending HTLCs".to_owned())); } } assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0); @@ -2909,17 +2918,17 @@ impl Channel { // BOLT 2 says we must only send a scriptpubkey of certain standard forms, which are up to // 34 bytes in length, so don't let the remote peer feed us some super fee-heavy script. if self.channel_outbound && msg.scriptpubkey.len() > 34 { - return Err(ChannelError::Close("Got shutdown_scriptpubkey of absurd length from remote peer")); + return Err(ChannelError::Close(format!("Got shutdown_scriptpubkey ({}) of absurd length from remote peer", msg.scriptpubkey.to_bytes().to_hex()))); } //Check shutdown_scriptpubkey form as BOLT says we must if !msg.scriptpubkey.is_p2pkh() && !msg.scriptpubkey.is_p2sh() && !msg.scriptpubkey.is_v0_p2wpkh() && !msg.scriptpubkey.is_v0_p2wsh() { - return Err(ChannelError::Close("Got a nonstandard scriptpubkey from remote peer")); + return Err(ChannelError::Close(format!("Got a nonstandard scriptpubkey ({}) from remote peer", msg.scriptpubkey.to_bytes().to_hex()))); } if self.their_shutdown_scriptpubkey.is_some() { if Some(&msg.scriptpubkey) != self.their_shutdown_scriptpubkey.as_ref() { - return Err(ChannelError::Close("Got shutdown request with a scriptpubkey which did not match their previous scriptpubkey")); + return Err(ChannelError::Close(format!("Got shutdown request with a scriptpubkey ({}) which did not match their previous scriptpubkey.", msg.scriptpubkey.to_bytes().to_hex()))); } } else { self.their_shutdown_scriptpubkey = Some(msg.scriptpubkey.clone()); @@ -2989,22 +2998,22 @@ impl Channel { where F::Target: FeeEstimator { if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK != BOTH_SIDES_SHUTDOWN_MASK { - return Err(ChannelError::Close("Remote end sent us a closing_signed before both sides provided a shutdown")); + return Err(ChannelError::Close("Remote end sent us a closing_signed before both sides provided a shutdown".to_owned())); } if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { - return Err(ChannelError::Close("Peer sent closing_signed when we needed a channel_reestablish")); + return Err(ChannelError::Close("Peer sent closing_signed when we needed a channel_reestablish".to_owned())); } if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() { - return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs")); + return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned())); } if msg.fee_satoshis > 21000000 * 10000000 { //this is required to stop potential overflow in build_closing_transaction - return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee")); + return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned())); } let funding_redeemscript = self.get_funding_redeemscript(); let (mut closing_tx, used_total_fee) = self.build_closing_transaction(msg.fee_satoshis, false); if used_total_fee != msg.fee_satoshis { - return Err(ChannelError::Close("Remote sent us a closing_signed with a fee greater than the value they can claim")); + return Err(ChannelError::Close(format!("Remote sent us a closing_signed with a fee greater than the value they can claim. actual: {}", msg.fee_satoshis))); } let mut sighash = hash_to_message!(&bip143::SighashComponents::new(&closing_tx).sighash_all(&closing_tx.input[0], &funding_redeemscript, self.channel_value_satoshis)[..]); @@ -3017,7 +3026,7 @@ impl Channel { // limits, so check for that case by re-checking the signature here. closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0; sighash = hash_to_message!(&bip143::SighashComponents::new(&closing_tx).sighash_all(&closing_tx.input[0], &funding_redeemscript, self.channel_value_satoshis)[..]); - secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, self.their_funding_pubkey()), "Invalid closing tx signature from peer"); + secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, self.their_funding_pubkey()), "Invalid closing tx signature from peer".to_owned()); }, }; @@ -3036,7 +3045,7 @@ impl Channel { let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * closing_tx_max_weight / 1000, false); let our_sig = self.local_keys .sign_closing_transaction(&closing_tx, &self.secp_ctx) - .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction"))?; + .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?; self.last_sent_closing_fee = Some(($new_feerate, used_total_fee, our_sig.clone())); return Ok((Some(msgs::ClosingSigned { channel_id: self.channel_id, @@ -3052,7 +3061,7 @@ impl Channel { if (proposed_sat_per_kw as u32) > our_max_feerate { if let Some((last_feerate, _, _)) = self.last_sent_closing_fee { if our_max_feerate <= last_feerate { - return Err(ChannelError::Close("Unable to come to consensus about closing feerate, remote wanted something higher than our Normal feerate")); + return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something higher ({}) than our Normal feerate ({})", last_feerate, our_max_feerate))); } } propose_new_feerate!(our_max_feerate); @@ -3062,7 +3071,7 @@ impl Channel { if (proposed_sat_per_kw as u32) < our_min_feerate { if let Some((last_feerate, _, _)) = self.last_sent_closing_fee { if our_min_feerate >= last_feerate { - return Err(ChannelError::Close("Unable to come to consensus about closing feerate, remote wanted something lower than our Background feerate")); + return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something lower ({}) than our Background feerate. ({})", last_feerate, our_min_feerate))); } } propose_new_feerate!(our_min_feerate); @@ -3071,7 +3080,7 @@ impl Channel { let our_sig = self.local_keys .sign_closing_transaction(&closing_tx, &self.secp_ctx) - .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction"))?; + .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?; self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &our_sig); self.channel_state = ChannelState::ShutdownComplete as u32; @@ -3513,7 +3522,7 @@ impl Channel { let remote_keys = self.build_remote_transaction_keys()?; let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw, logger).0; Ok(self.local_keys.sign_remote_commitment(self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) - .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?.0) + .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0) } /// Updates channel state with knowledge of the funding transaction's txid/index, and generates @@ -3571,13 +3580,13 @@ impl Channel { /// https://github.com/lightningnetwork/lightning-rfc/issues/468 pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: BlockHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), ChannelError> { if !self.config.announced_channel { - return Err(ChannelError::Ignore("Channel is not available for public announcements")); + return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned())); } if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 { - return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement until the channel funding has been locked")); + return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement until the channel funding has been locked".to_owned())); } if (self.channel_state & (ChannelState::LocalShutdownSent as u32 | ChannelState::ShutdownComplete as u32)) != 0 { - return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement once the channel is closing")); + return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement once the channel is closing".to_owned())); } let were_node_one = our_node_id.serialize()[..] < self.their_node_id.serialize()[..]; @@ -3594,7 +3603,7 @@ impl Channel { }; let sig = self.local_keys.sign_channel_announcement(&msg, &self.secp_ctx) - .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement"))?; + .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?; Ok((msg, sig)) } @@ -3662,19 +3671,19 @@ impl Channel { /// If an Err is returned, it's a ChannelError::Ignore! pub fn send_htlc(&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource, onion_routing_packet: msgs::OnionPacket) -> Result, ChannelError> { if (self.channel_state & (ChannelState::ChannelFunded as u32 | BOTH_SIDES_SHUTDOWN_MASK)) != (ChannelState::ChannelFunded as u32) { - return Err(ChannelError::Ignore("Cannot send HTLC until channel is fully established and we haven't started shutting down")); + return Err(ChannelError::Ignore("Cannot send HTLC until channel is fully established and we haven't started shutting down".to_owned())); } - - if amount_msat > self.channel_value_satoshis * 1000 { - return Err(ChannelError::Ignore("Cannot send more than the total value of the channel")); + let channel_total_msat = self.channel_value_satoshis * 1000; + if amount_msat > channel_total_msat { + return Err(ChannelError::Ignore(format!("Cannot send amount {}, because it is more than the total value of the channel {}", amount_msat, channel_total_msat))); } if amount_msat == 0 { - return Err(ChannelError::Ignore("Cannot send 0-msat HTLC")); + return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned())); } if amount_msat < self.their_htlc_minimum_msat { - return Err(ChannelError::Ignore("Cannot send less than their minimum HTLC value")); + return Err(ChannelError::Ignore(format!("Cannot send less than their minimum HTLC value ({})", self.their_htlc_minimum_msat))); } if (self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) != 0 { @@ -3684,16 +3693,16 @@ impl Channel { // disconnected during the time the previous hop was doing the commitment dance we may // end up getting here after the forwarding delay. In any case, returning an // IgnoreError will get ChannelManager to do the right thing and fail backwards now. - return Err(ChannelError::Ignore("Cannot send an HTLC while disconnected/frozen for channel monitor update")); + return Err(ChannelError::Ignore("Cannot send an HTLC while disconnected/frozen for channel monitor update".to_owned())); } let (outbound_htlc_count, htlc_outbound_value_msat) = self.get_outbound_pending_htlc_stats(); if outbound_htlc_count + 1 > self.their_max_accepted_htlcs as u32 { - return Err(ChannelError::Ignore("Cannot push more than their max accepted HTLCs")); + return Err(ChannelError::Ignore(format!("Cannot push more than their max accepted HTLCs ({})", self.their_max_accepted_htlcs))); } // Check their_max_htlc_value_in_flight_msat if htlc_outbound_value_msat + amount_msat > self.their_max_htlc_value_in_flight_msat { - return Err(ChannelError::Ignore("Cannot send value that would put us over the max HTLC value in flight our peer will accept")); + return Err(ChannelError::Ignore(format!("Cannot send value that would put us over the max HTLC value in flight our peer will accept ({})", self.their_max_htlc_value_in_flight_msat))); } if !self.channel_outbound { @@ -3704,13 +3713,13 @@ impl Channel { // 1 additional HTLC corresponding to this HTLC. let remote_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(1); if remote_balance_msat < remote_chan_reserve_msat + remote_commit_tx_fee_msat { - return Err(ChannelError::Ignore("Cannot send value that would put them under remote channel reserve value")); + return Err(ChannelError::Ignore("Cannot send value that would put them under remote channel reserve value".to_owned())); } } let pending_value_to_self_msat = self.value_to_self_msat - htlc_outbound_value_msat; if pending_value_to_self_msat < amount_msat { - return Err(ChannelError::Ignore("Cannot send value that would overdraw remaining funds")); + return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. amount: {}, pending value to self {}", amount_msat, pending_value_to_self_msat))); } // The `+1` is for the HTLC currently being added to the commitment tx and @@ -3719,14 +3728,14 @@ impl Channel { 2 * self.next_local_commit_tx_fee_msat(1 + 1) } else { 0 }; if pending_value_to_self_msat - amount_msat < local_commit_tx_fee_msat { - return Err(ChannelError::Ignore("Cannot send value that would not leave enough to pay for fees")); + return Err(ChannelError::Ignore(format!("Cannot send value that would not leave enough to pay for fees. pending value to self: {}. local_commit_tx_fee {}", pending_value_to_self_msat, local_commit_tx_fee_msat))); } // Check self.local_channel_reserve_satoshis (the amount we must keep as // reserve for the remote to have something to claim if we misbehave) let chan_reserve_msat = self.local_channel_reserve_satoshis * 1000; if pending_value_to_self_msat - amount_msat - local_commit_tx_fee_msat < chan_reserve_msat { - return Err(ChannelError::Ignore("Cannot send value that would put us under local channel reserve value")); + return Err(ChannelError::Ignore(format!("Cannot send value that would put us under local channel reserve value ({})", chan_reserve_msat))); } // Now update local state: @@ -3866,7 +3875,7 @@ impl Channel { } let res = self.local_keys.sign_remote_commitment(feerate_per_kw, &remote_commitment_tx.0, &remote_keys, &htlcs, self.our_to_self_delay, &self.secp_ctx) - .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?; + .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?; signature = res.0; htlc_signatures = res.1; @@ -3910,20 +3919,20 @@ impl Channel { pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, PaymentHash)>), APIError> { for htlc in self.pending_outbound_htlcs.iter() { if let OutboundHTLCState::LocalAnnounced(_) = htlc.state { - return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first"}); + return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first".to_owned()}); } } if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK != 0 { if (self.channel_state & ChannelState::LocalShutdownSent as u32) == ChannelState::LocalShutdownSent as u32 { - return Err(APIError::APIMisuseError{err: "Shutdown already in progress"}); + return Err(APIError::APIMisuseError{err: "Shutdown already in progress".to_owned()}); } else if (self.channel_state & ChannelState::RemoteShutdownSent as u32) == ChannelState::RemoteShutdownSent as u32 { - return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote"}); + return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote".to_owned()}); } } assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0); if self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32) != 0 { - return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected or we're waiting on a monitor update, maybe force-close instead?"}); + return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected or we're waiting on a monitor update, maybe force-close instead?".to_owned()}); } let our_closing_script = self.get_closing_scriptpubkey(); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 28c6430bbc2..5acae167318 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -51,6 +51,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; use std::marker::{Sync, Send}; use std::ops::Deref; +use bitcoin::hashes::hex::ToHex; // We hold various information about HTLC relay in the HTLC objects in Channel itself: // @@ -192,10 +193,10 @@ struct MsgHandleErrInternal { } impl MsgHandleErrInternal { #[inline] - fn send_err_msg_no_close(err: &'static str, channel_id: [u8; 32]) -> Self { + fn send_err_msg_no_close(err: String, channel_id: [u8; 32]) -> Self { Self { err: LightningError { - err, + err: err.clone(), action: msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id, @@ -207,10 +208,10 @@ impl MsgHandleErrInternal { } } #[inline] - fn ignore_no_close(err: &'static str) -> Self { + fn ignore_no_close(err: String) -> Self { Self { err: LightningError { - err, + err: err.clone(), action: msgs::ErrorAction::IgnoreError, }, shutdown_finish: None, @@ -221,10 +222,10 @@ impl MsgHandleErrInternal { Self { err, shutdown_finish: None } } #[inline] - fn from_finish_shutdown(err: &'static str, channel_id: [u8; 32], shutdown_res: ShutdownResult, channel_update: Option) -> Self { + fn from_finish_shutdown(err: String, channel_id: [u8; 32], shutdown_res: ShutdownResult, channel_update: Option) -> Self { Self { err: LightningError { - err, + err: err.clone(), action: msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id, @@ -240,11 +241,11 @@ impl MsgHandleErrInternal { Self { err: match err { ChannelError::Ignore(msg) => LightningError { - err: msg, + err: msg.clone(), action: msgs::ErrorAction::IgnoreError, }, ChannelError::Close(msg) => LightningError { - err: msg, + err: msg.clone(), action: msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id, @@ -253,7 +254,7 @@ impl MsgHandleErrInternal { }, }, ChannelError::CloseDelayBroadcast(msg) => LightningError { - err: msg, + err: msg.clone(), action: msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id, @@ -632,7 +633,7 @@ macro_rules! handle_monitor_err { // splitting hairs we'd prefer to claim payments that were to us, but we haven't // given up the preimage yet, so might as well just wait until the payment is // retried, avoiding the on-chain fees. - let res: Result<(), _> = Err(MsgHandleErrInternal::from_finish_shutdown("ChannelMonitor storage failure", channel_id, chan.force_shutdown(true), $self.get_channel_update(&chan).ok())); + let res: Result<(), _> = Err(MsgHandleErrInternal::from_finish_shutdown("ChannelMonitor storage failure".to_owned(), channel_id, chan.force_shutdown(true), $self.get_channel_update(&chan).ok())); res }, ChannelMonitorUpdateErr::TemporaryFailure => { @@ -655,7 +656,7 @@ macro_rules! handle_monitor_err { debug_assert!($action_type == RAACommitmentOrder::CommitmentFirst || !$resend_commitment); } $entry.get_mut().monitor_update_failed($resend_raa, $resend_commitment, $failed_forwards, $failed_fails); - Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore("Failed to update ChannelMonitor"), *$entry.key())) + Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore("Failed to update ChannelMonitor".to_owned()), *$entry.key())) }, } } @@ -757,7 +758,7 @@ impl /// greater than channel_value_satoshis * 1k or channel_value_satoshis is < 1000. pub fn create_channel(&self, their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Option) -> Result<(), APIError> { if channel_value_satoshis < 1000 { - return Err(APIError::APIMisuseError { err: "channel_value must be at least 1000 satoshis" }); + return Err(APIError::APIMisuseError { err: format!("channel_value must be at least 1000 satoshis. it was {}", channel_value_satoshis) }); } let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration }; @@ -769,7 +770,7 @@ impl match channel_state.by_id.entry(channel.channel_id()) { hash_map::Entry::Occupied(_) => { if cfg!(feature = "fuzztarget") { - return Err(APIError::APIMisuseError { err: "Fuzzy bad RNG" }); + return Err(APIError::APIMisuseError { err: "Fuzzy bad RNG".to_owned() }); } else { panic!("RNG is bad???"); } @@ -855,7 +856,7 @@ impl (failed_htlcs, Some(chan_entry.remove_entry().1)) } else { (failed_htlcs, None) } }, - hash_map::Entry::Vacant(_) => return Err(APIError::ChannelUnavailable{err: "No such channel"}) + hash_map::Entry::Vacant(_) => return Err(APIError::ChannelUnavailable{err: "No such channel".to_owned()}) } }; for htlc_source in failed_htlcs.drain(..) { @@ -1201,7 +1202,7 @@ impl /// May be called with channel_state already locked! fn get_channel_update(&self, chan: &Channel) -> Result { let short_channel_id = match chan.get_short_channel_id() { - None => return Err(LightningError{err: "Channel not yet established", action: msgs::ErrorAction::IgnoreError}), + None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}), Some(id) => id, }; @@ -1246,7 +1247,7 @@ impl let err: Result<(), _> = loop { let mut channel_lock = self.channel_state.lock().unwrap(); let id = match channel_lock.short_to_id.get(&path.first().unwrap().short_channel_id) { - None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}), + None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}), Some(id) => id.clone(), }; @@ -1257,7 +1258,7 @@ impl return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"}); } if !chan.get().is_live() { - return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"}); + return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()}); } break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute { path: path.clone(), @@ -2213,7 +2214,7 @@ impl fn internal_open_channel(&self, their_node_id: &PublicKey, their_features: InitFeatures, msg: &msgs::OpenChannel) -> Result<(), MsgHandleErrInternal> { if msg.chain_hash != self.genesis_hash { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash", msg.temporary_channel_id.clone())); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash".to_owned(), msg.temporary_channel_id.clone())); } let channel = Channel::new_from_req(&self.fee_estimator, &self.keys_manager, their_node_id.clone(), their_features, msg, 0, &self.default_configuration) @@ -2221,7 +2222,7 @@ impl let mut channel_state_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_state_lock; match channel_state.by_id.entry(channel.channel_id()) { - hash_map::Entry::Occupied(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!", msg.temporary_channel_id.clone())), + hash_map::Entry::Occupied(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!".to_owned(), msg.temporary_channel_id.clone())), hash_map::Entry::Vacant(entry) => { channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel { node_id: their_node_id.clone(), @@ -2240,12 +2241,12 @@ impl match channel_state.by_id.entry(msg.temporary_channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.temporary_channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.temporary_channel_id)); } try_chan_entry!(self, chan.get_mut().accept_channel(&msg, &self.default_configuration, their_features), channel_state, chan); (chan.get().get_value_satoshis(), chan.get().get_funding_redeemscript().to_v0_p2wsh(), chan.get().get_user_id()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.temporary_channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.temporary_channel_id)) } }; let mut pending_events = self.pending_events.lock().unwrap(); @@ -2265,11 +2266,11 @@ impl match channel_state.by_id.entry(msg.temporary_channel_id.clone()) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.temporary_channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.temporary_channel_id)); } (try_chan_entry!(self, chan.get_mut().funding_created(msg, &self.logger), channel_state, chan), chan.remove()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.temporary_channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.temporary_channel_id)) } }; // Because we have exclusive ownership of the channel here we can release the channel_state @@ -2281,7 +2282,7 @@ impl // channel, not the temporary_channel_id. This is compatible with ourselves, but the // spec is somewhat ambiguous here. Not a huge deal since we'll send error messages for // any messages referencing a previously-closed channel anyway. - return Err(MsgHandleErrInternal::from_finish_shutdown("ChannelMonitor storage failure", funding_msg.channel_id, chan.force_shutdown(true), None)); + return Err(MsgHandleErrInternal::from_finish_shutdown("ChannelMonitor storage failure".to_owned(), funding_msg.channel_id, chan.force_shutdown(true), None)); }, ChannelMonitorUpdateErr::TemporaryFailure => { // There's no problem signing a counterparty's funding transaction if our monitor @@ -2296,7 +2297,7 @@ impl let channel_state = &mut *channel_state_lock; match channel_state.by_id.entry(funding_msg.channel_id) { hash_map::Entry::Occupied(_) => { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Already had channel with the new channel_id", funding_msg.channel_id)) + return Err(MsgHandleErrInternal::send_err_msg_no_close("Already had channel with the new channel_id".to_owned(), funding_msg.channel_id)) }, hash_map::Entry::Vacant(e) => { channel_state.pending_msg_events.push(events::MessageSendEvent::SendFundingSigned { @@ -2316,7 +2317,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let monitor = match chan.get_mut().funding_signed(&msg, &self.logger) { Ok(update) => update, @@ -2327,7 +2328,7 @@ impl } (chan.get().get_funding_txo().unwrap(), chan.get().get_user_id()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } }; let mut pending_events = self.pending_events.lock().unwrap(); @@ -2344,7 +2345,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } try_chan_entry!(self, chan.get_mut().funding_locked(&msg), channel_state, chan); if let Some(announcement_sigs) = self.get_announcement_sigs(chan.get()) { @@ -2365,7 +2366,7 @@ impl } Ok(()) }, - hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } } @@ -2377,7 +2378,7 @@ impl match channel_state.by_id.entry(msg.channel_id.clone()) { hash_map::Entry::Occupied(mut chan_entry) => { if chan_entry.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let (shutdown, closing_signed, dropped_htlcs) = try_chan_entry!(self, chan_entry.get_mut().shutdown(&self.fee_estimator, &msg), channel_state, chan_entry); if let Some(msg) = shutdown { @@ -2399,7 +2400,7 @@ impl (dropped_htlcs, Some(chan_entry.remove_entry().1)) } else { (dropped_htlcs, None) } }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } }; for htlc_source in dropped_htlcs.drain(..) { @@ -2423,7 +2424,7 @@ impl match channel_state.by_id.entry(msg.channel_id.clone()) { hash_map::Entry::Occupied(mut chan_entry) => { if chan_entry.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let (closing_signed, tx) = try_chan_entry!(self, chan_entry.get_mut().closing_signed(&self.fee_estimator, &msg), channel_state, chan_entry); if let Some(msg) = closing_signed { @@ -2444,7 +2445,7 @@ impl (tx, Some(chan_entry.remove_entry().1)) } else { (tx, None) } }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } }; if let Some(broadcast_tx) = tx { @@ -2478,7 +2479,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let create_pending_htlc_status = |chan: &Channel, pending_forward_info: PendingHTLCStatus, error_code: u16| { @@ -2521,7 +2522,7 @@ impl }; try_chan_entry!(self, chan.get_mut().update_add_htlc(&msg, pending_forward_info, create_pending_htlc_status, &self.logger), channel_state, chan); }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } Ok(()) } @@ -2533,11 +2534,11 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } try_chan_entry!(self, chan.get_mut().update_fulfill_htlc(&msg), channel_state, chan) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } }; self.claim_funds_internal(channel_lock, htlc_source, msg.payment_preimage.clone()); @@ -2550,11 +2551,11 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } try_chan_entry!(self, chan.get_mut().update_fail_htlc(&msg, HTLCFailReason::LightningError { err: msg.reason.clone() }), channel_state, chan); }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } Ok(()) } @@ -2565,16 +2566,16 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } if (msg.failure_code & 0x8000) == 0 { - let chan_err: ChannelError = ChannelError::Close("Got update_fail_malformed_htlc with BADONION not set"); + let chan_err: ChannelError = ChannelError::Close("Got update_fail_malformed_htlc with BADONION not set".to_owned()); try_chan_entry!(self, Err(chan_err), channel_state, chan); } try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() }), channel_state, chan); Ok(()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } } @@ -2584,7 +2585,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let (revoke_and_ack, commitment_signed, closing_signed, monitor_update) = match chan.get_mut().commitment_signed(&msg, &self.fee_estimator, &self.logger) { @@ -2626,7 +2627,7 @@ impl } Ok(()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } } @@ -2672,7 +2673,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let was_frozen_for_monitor = chan.get().is_awaiting_monitor_update(); let (commitment_update, pending_forwards, pending_failures, closing_signed, monitor_update) = @@ -2680,7 +2681,7 @@ impl if let Err(e) = self.monitor.update_monitor(chan.get().get_funding_txo().unwrap(), monitor_update) { if was_frozen_for_monitor { assert!(commitment_update.is_none() && closing_signed.is_none() && pending_forwards.is_empty() && pending_failures.is_empty()); - return Err(MsgHandleErrInternal::ignore_no_close("Previous monitor update failure prevented responses to RAA")); + return Err(MsgHandleErrInternal::ignore_no_close("Previous monitor update failure prevented responses to RAA".to_owned())); } else { return_monitor_err!(self, e, channel_state, chan, RAACommitmentOrder::CommitmentFirst, false, commitment_update.is_some(), pending_forwards, pending_failures); } @@ -2699,7 +2700,7 @@ impl } (pending_forwards, pending_failures, chan.get().get_short_channel_id().expect("RAA should only work on a short-id-available channel")) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } }; for failure in pending_failures.drain(..) { @@ -2716,11 +2717,11 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } try_chan_entry!(self, chan.get_mut().update_fee(&self.fee_estimator, &msg), channel_state, chan); }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } Ok(()) } @@ -2732,10 +2733,10 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } if !chan.get().is_usable() { - return Err(MsgHandleErrInternal::from_no_close(LightningError{err: "Got an announcement_signatures before we were ready for it", action: msgs::ErrorAction::IgnoreError})); + return Err(MsgHandleErrInternal::from_no_close(LightningError{err: "Got an announcement_signatures before we were ready for it".to_owned(), action: msgs::ErrorAction::IgnoreError})); } let our_node_id = self.get_our_node_id(); @@ -2746,7 +2747,7 @@ impl let msghash = hash_to_message!(&Sha256dHash::hash(&announcement.encode()[..])[..]); if self.secp_ctx.verify(&msghash, &msg.node_signature, if were_node_one { &announcement.node_id_2 } else { &announcement.node_id_1 }).is_err() || self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, if were_node_one { &announcement.bitcoin_key_2 } else { &announcement.bitcoin_key_1 }).is_err() { - let chan_err: ChannelError = ChannelError::Close("Bad announcement_signatures node_signature"); + let chan_err: ChannelError = ChannelError::Close("Bad announcement_signatures node_signature".to_owned()); try_chan_entry!(self, Err(chan_err), channel_state, chan); } @@ -2763,7 +2764,7 @@ impl update_msg: self.get_channel_update(chan.get()).unwrap(), // can only fail if we're not in a ready state }); }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } Ok(()) } @@ -2775,7 +2776,7 @@ impl match channel_state.by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan) => { if chan.get().get_their_node_id() != *their_node_id { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } let (funding_locked, revoke_and_ack, commitment_update, monitor_update_opt, mut order, shutdown) = try_chan_entry!(self, chan.get_mut().channel_reestablish(msg, &self.logger), channel_state, chan); @@ -2834,7 +2835,7 @@ impl } Ok(()) }, - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } } @@ -2851,16 +2852,16 @@ impl let channel_state = &mut *channel_state_lock; match channel_state.by_id.entry(channel_id) { - hash_map::Entry::Vacant(_) => return Err(APIError::APIMisuseError{err: "Failed to find corresponding channel"}), + hash_map::Entry::Vacant(_) => return Err(APIError::APIMisuseError{err: format!("Failed to find corresponding channel for id {}", channel_id.to_hex())}), hash_map::Entry::Occupied(mut chan) => { if !chan.get().is_outbound() { - return Err(APIError::APIMisuseError{err: "update_fee cannot be sent for an inbound channel"}); + return Err(APIError::APIMisuseError{err: "update_fee cannot be sent for an inbound channel".to_owned()}); } if chan.get().is_awaiting_monitor_update() { return Err(APIError::MonitorUpdateFailed); } if !chan.get().is_live() { - return Err(APIError::ChannelUnavailable{err: "Channel is either not yet fully established or peer is currently disconnected"}); + return Err(APIError::ChannelUnavailable{err: "Channel is either not yet fully established or peer is currently disconnected".to_owned()}); } their_node_id = chan.get().get_their_node_id(); if let Some((update_fee, commitment_signed, monitor_update)) = diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 24fe127d70e..d9b9b17582e 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -278,14 +278,14 @@ macro_rules! unwrap_send_err { match &$res { &Err(PaymentSendFailure::AllFailedRetrySafe(ref fails)) if $all_failed => { assert_eq!(fails.len(), 1); - match fails[0] { + match &fails[0] { $type => { $check }, _ => panic!(), } }, &Err(PaymentSendFailure::PartialFailure(ref fails)) if !$all_failed => { assert_eq!(fails.len(), 1); - match fails[0] { + match &fails[0] { Err($type) => { $check }, _ => panic!(), } @@ -973,7 +973,7 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou let (_, our_payment_hash) = get_payment_preimage_hash!(origin_node); unwrap_send_err!(origin_node.node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept")); + assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); } pub fn send_payment<'a, 'b, 'c>(origin: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64, expected_value: u64) { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 5e8edb3ca5e..a5ec173b248 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1322,7 +1322,7 @@ fn holding_cell_htlc_counting() { let net_graph_msg_handler = &nodes[1].net_graph_msg_handler; let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap(); unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash_1, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot push more than their max accepted HTLCs")); + assert!(err.contains("Cannot push more than their max accepted HTLCs"))); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); } @@ -1540,7 +1540,7 @@ fn test_basic_channel_reserve() { let err = nodes[0].node.send_payment(&route, our_payment_hash, &None).err().unwrap(); match err { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { - match fails[0] { + match &fails[0] { APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us under local channel reserve value"), _ => panic!("Unexpected error variant"), @@ -1930,7 +1930,7 @@ fn test_channel_reserve_holding_cell_htlcs() { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_0 + 1); assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept")); + assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept (10000000)")); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); } @@ -2105,7 +2105,7 @@ fn test_channel_reserve_holding_cell_htlcs() { let err = nodes[0].node.send_payment(&route, our_payment_hash, &None).err().unwrap(); match err { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { - match fails[0] { + match &fails[0] { APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us under local channel reserve value"), _ => panic!("Unexpected error variant"), @@ -6470,7 +6470,7 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() { let net_graph_msg_handler = &nodes[0].net_graph_msg_handler; let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000000, 500000001, &logger).unwrap(); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::RouteError { err }, - assert_eq!(err, "Channel CLTV overflowed?!")); + assert_eq!(err, &"Channel CLTV overflowed?")); } #[test] diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 24e8120de6c..bd5d23504b1 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -461,7 +461,7 @@ pub enum ErrorAction { /// An Err type for failure to process messages. pub struct LightningError { /// A human-readable message describing the error - pub err: &'static str, + pub err: String, /// The action which should be taken against the offending peer. pub action: ErrorAction, } @@ -701,7 +701,7 @@ impl fmt::Display for DecodeError { impl fmt::Debug for LightningError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.err) + f.write_str(self.err.as_str()) } } diff --git a/lightning/src/ln/peer_channel_encryptor.rs b/lightning/src/ln/peer_channel_encryptor.rs index 152426577ad..68f3c505b9b 100644 --- a/lightning/src/ln/peer_channel_encryptor.rs +++ b/lightning/src/ln/peer_channel_encryptor.rs @@ -11,6 +11,7 @@ use bitcoin::secp256k1; use util::chacha20poly1305rfc::ChaCha20Poly1305RFC; use util::byte_utils; +use bitcoin::hashes::hex::ToHex; // Sha256("Noise_XK_secp256k1_ChaChaPoly_SHA256") const NOISE_CK: [u8; 32] = [0x26, 0x40, 0xf5, 0x2e, 0xeb, 0xcd, 0x9e, 0x88, 0x29, 0x58, 0x95, 0x1c, 0x79, 0x42, 0x50, 0xee, 0xdb, 0x28, 0x00, 0x2c, 0x05, 0xd7, 0xdc, 0x2e, 0xa0, 0xf1, 0x95, 0x40, 0x60, 0x42, 0xca, 0xf1]; @@ -139,7 +140,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(LightningError{err: "Bad MAC", action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); + return Err(LightningError{err: "Bad MAC".to_owned(), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); } Ok(()) } @@ -193,11 +194,11 @@ impl PeerChannelEncryptor { assert_eq!(act.len(), 50); if act[0] != 0 { - return Err(LightningError{err: "Unknown handshake version number", action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); + return Err(LightningError{err: format!("Unknown handshake version number {}", act[0]), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); } let their_pub = match PublicKey::from_slice(&act[1..34]) { - Err(_) => return Err(LightningError{err: "Invalid public key", action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), + Err(_) => return Err(LightningError{err: format!("Invalid public key {}", &act[1..34].to_hex()), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), Ok(key) => key, }; @@ -330,14 +331,14 @@ impl PeerChannelEncryptor { panic!("Requested act at wrong step"); } if act_three[0] != 0 { - return Err(LightningError{err: "Unknown handshake version number", action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); + return Err(LightningError{err: format!("Unknown handshake version number {}", act_three[0]), action: 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(&their_node_id) { Ok(key) => key, - Err(_) => return Err(LightningError{err: "Bad node_id from peer", action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), + Err(_) => return Err(LightningError{err: format!("Bad node_id from peer, {}", &their_node_id.to_hex()), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), }); let mut sha = Sha256::engine(); diff --git a/lightning/src/routing/network_graph.rs b/lightning/src/routing/network_graph.rs index b6fb7a94884..9ef239f5657 100644 --- a/lightning/src/routing/network_graph.rs +++ b/lightning/src/routing/network_graph.rs @@ -22,6 +22,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::BTreeMap; use std::collections::btree_map::Entry as BtreeEntry; use std::ops::Deref; +use bitcoin::hashes::hex::ToHex; /// Receives and validates network updates from peers, /// stores authentic and relevant data as a network graph. @@ -74,7 +75,7 @@ macro_rules! secp_verify_sig { ( $secp_ctx: expr, $msg: expr, $sig: expr, $pubkey: expr ) => { match $secp_ctx.verify($msg, $sig, $pubkey) { Ok(_) => {}, - Err(_) => return Err(LightningError{err: "Invalid signature from remote node", action: ErrorAction::IgnoreError}), + Err(_) => return Err(LightningError{err: "Invalid signature from remote node".to_owned(), action: ErrorAction::IgnoreError}), } }; } @@ -86,7 +87,7 @@ impl RoutingMessageHandler for N fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result { if msg.contents.node_id_1 == msg.contents.node_id_2 || msg.contents.bitcoin_key_1 == msg.contents.bitcoin_key_2 { - return Err(LightningError{err: "Channel announcement node had a channel with itself", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError}); } let checked_utxo = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) { @@ -97,7 +98,7 @@ impl RoutingMessageHandler for N .push_opcode(opcodes::all::OP_PUSHNUM_2) .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); if script_pubkey != expected_script { - return Err(LightningError{err: "Channel announcement keys didn't match on-chain script", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: format!("Channel announcement key({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.to_hex()), action: ErrorAction::IgnoreError}); } //TODO: Check if value is worth storing, use it to inform routing, and compare it //to the new HTLC max field in channel_update @@ -108,10 +109,10 @@ impl RoutingMessageHandler for N false }, Err(ChainError::NotWatched) => { - return Err(LightningError{err: "Channel announced on an unknown chain", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError}); }, Err(ChainError::UnknownTx) => { - return Err(LightningError{err: "Channel announced without corresponding UTXO entry", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError}); }, }; let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, checked_utxo, Some(&self.secp_ctx)); @@ -522,11 +523,11 @@ impl NetworkGraph { } match self.nodes.get_mut(&msg.contents.node_id) { - None => Err(LightningError{err: "No existing channels for node_announcement", action: ErrorAction::IgnoreError}), + None => Err(LightningError{err: "No existing channels for node_announcement".to_owned(), action: ErrorAction::IgnoreError}), Some(node) => { if let Some(node_info) = node.announcement_info.as_ref() { if node_info.last_update >= msg.contents.timestamp { - return Err(LightningError{err: "Update older than last processed update", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Update older than last processed update".to_owned(), action: ErrorAction::IgnoreError}); } } @@ -588,7 +589,7 @@ impl NetworkGraph { Self::remove_channel_in_nodes(&mut self.nodes, &entry.get(), msg.contents.short_channel_id); *entry.get_mut() = chan_info; } else { - return Err(LightningError{err: "Already have knowledge of channel", action: ErrorAction::IgnoreError}) + return Err(LightningError{err: "Already have knowledge of channel".to_owned(), action: ErrorAction::IgnoreError}) } }, BtreeEntry::Vacant(entry) => { @@ -656,13 +657,13 @@ impl NetworkGraph { let chan_was_enabled; match self.channels.get_mut(&msg.contents.short_channel_id) { - None => return Err(LightningError{err: "Couldn't find channel for update", action: ErrorAction::IgnoreError}), + None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}), Some(channel) => { macro_rules! maybe_update_channel_info { ( $target: expr, $src_node: expr) => { if let Some(existing_chan_info) = $target.as_ref() { if existing_chan_info.last_update >= msg.contents.timestamp { - return Err(LightningError{err: "Update older than last processed update", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Update older than last processed update".to_owned(), action: ErrorAction::IgnoreError}); } chan_was_enabled = existing_chan_info.enabled; } else { diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index dfdfb5e8f87..c00298feab7 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -165,11 +165,11 @@ pub fn get_route(our_node_id: &PublicKey, network: &NetworkGraph, targ // TODO: Obviously *only* using total fee cost sucks. We should consider weighting by // uptime/success in using a node in the past. if *target == *our_node_id { - return Err(LightningError{err: "Cannot generate a route to ourselves", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError}); } if final_value_msat > 21_000_000 * 1_0000_0000 * 1000 { - return Err(LightningError{err: "Cannot generate a route of more value than all existing satoshis", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Cannot generate a route of more value than all existing satoshis".to_owned(), action: ErrorAction::IgnoreError}); } // We do a dest-to-source Dijkstra's sorting by each node's distance from the destination @@ -209,7 +209,7 @@ pub fn get_route(our_node_id: &PublicKey, network: &NetworkGraph, targ first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone())); } if first_hop_targets.is_empty() { - return Err(LightningError{err: "Cannot route when there are no outbound routes away from us", action: ErrorAction::IgnoreError}); + return Err(LightningError{err: "Cannot route when there are no outbound routes away from us".to_owned(), action: ErrorAction::IgnoreError}); } } @@ -374,7 +374,7 @@ pub fn get_route(our_node_id: &PublicKey, network: &NetworkGraph, targ let new_entry = match dist.remove(&res.last().unwrap().pubkey) { Some(hop) => hop.3, - None => return Err(LightningError{err: "Failed to find a non-fee-overflowing path to the given destination", action: ErrorAction::IgnoreError}), + None => return Err(LightningError{err: "Failed to find a non-fee-overflowing path to the given destination".to_owned(), action: ErrorAction::IgnoreError}), }; res.last_mut().unwrap().fee_msat = new_entry.fee_msat; res.last_mut().unwrap().cltv_expiry_delta = new_entry.cltv_expiry_delta; @@ -395,7 +395,7 @@ pub fn get_route(our_node_id: &PublicKey, network: &NetworkGraph, targ } } - Err(LightningError{err: "Failed to find a path to the given destination", action: ErrorAction::IgnoreError}) + Err(LightningError{err: "Failed to find a path to the given destination".to_owned(), action: ErrorAction::IgnoreError}) } #[cfg(test)] diff --git a/lightning/src/util/errors.rs b/lightning/src/util/errors.rs index 1b29916e0f5..7119c3a023a 100644 --- a/lightning/src/util/errors.rs +++ b/lightning/src/util/errors.rs @@ -9,7 +9,7 @@ pub enum APIError { /// are documented, but generally indicates some precondition of a function was violated. APIMisuseError { /// A human-readable error message - err: &'static str + err: String }, /// Due to a high feerate, we were unable to complete the request. /// For example, this may be returned if the feerate implies we cannot open a channel at the @@ -31,7 +31,7 @@ pub enum APIError { /// peer, channel at capacity, channel shutting down, etc. ChannelUnavailable { /// A human-readable error message - err: &'static str + err: String }, /// An attempt to call add/update_monitor returned an Err (ie you did this!), causing the /// attempted action to fail. diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 2134c26f9f6..9d194e05a3f 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -240,15 +240,15 @@ impl TestRoutingMessageHandler { } impl msgs::RoutingMessageHandler for TestRoutingMessageHandler { fn handle_node_announcement(&self, _msg: &msgs::NodeAnnouncement) -> Result { - Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) } fn handle_channel_announcement(&self, _msg: &msgs::ChannelAnnouncement) -> Result { self.chan_anns_recvd.fetch_add(1, Ordering::AcqRel); - Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) } fn handle_channel_update(&self, _msg: &msgs::ChannelUpdate) -> Result { self.chan_upds_recvd.fetch_add(1, Ordering::AcqRel); - Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) } fn handle_htlc_fail_channel_update(&self, _update: &msgs::HTLCFailChannelUpdate) {} fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, Option, Option)> { From 1aa951ed6883d08e64f8d3509474528ee6f33c70 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 2 Jul 2020 14:44:17 +0900 Subject: [PATCH 02/45] Update tests according to tparameterised errors. TestLogger now has two additional methods 1. `assert_log_contains` which checks the logged messsage has entry which includes the specified string as a substring. 2. `aasert_log_regex` mostly same with above but it is more flexible that caller specifies regex which has to be satisfied instead of just a substring. --- lightning/Cargo.toml | 1 + lightning/src/ln/functional_tests.rs | 75 ++++++++++++++-------------- lightning/src/ln/onion_utils.rs | 4 +- lightning/src/util/test_utils.rs | 16 ++++++ 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index 3826123a0fb..ab2bd0499cf 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -28,3 +28,4 @@ features = ["bitcoinconsensus"] [dev-dependencies] hex = "0.3" +regex = "1.3.9" diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index a5ec173b248..ec8d7af155d 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -77,10 +77,11 @@ fn test_insane_channel_opens() { nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &message_mutator(open_channel_message.clone())); let msg_events = nodes[1].node.get_and_clear_pending_msg_events(); assert_eq!(msg_events.len(), 1); + let expected_regex = regex::Regex::new(expected_error_str).unwrap(); if let MessageSendEvent::HandleError { ref action, .. } = msg_events[0] { match action { &ErrorAction::SendErrorMessage { .. } => { - nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), expected_error_str.to_string(), 1); + nodes[1].logger.assert_log_regex("lightning::ln::channelmanager".to_string(), expected_regex, 1); }, _ => panic!("unexpected event!"), } @@ -91,23 +92,23 @@ fn test_insane_channel_opens() { use ln::channelmanager::MAX_LOCAL_BREAKDOWN_TIMEOUT; // Test all mutations that would make the channel open message insane - insane_open_helper("funding value > 2^24", |mut msg| { msg.funding_satoshis = MAX_FUNDING_SATOSHIS; msg }); + insane_open_helper(format!("funding must be smaller than {}. It was {}", MAX_FUNDING_SATOSHIS, MAX_FUNDING_SATOSHIS).as_str(), |mut msg| { msg.funding_satoshis = MAX_FUNDING_SATOSHIS; msg }); insane_open_helper("Bogus channel_reserve_satoshis", |mut msg| { msg.channel_reserve_satoshis = msg.funding_satoshis + 1; msg }); - insane_open_helper("push_msat larger than funding value", |mut msg| { msg.push_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 + 1; msg }); + insane_open_helper(r"push_msat \d+ was larger than funding value \d+", |mut msg| { msg.push_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 + 1; msg }); insane_open_helper("Peer never wants payout outputs?", |mut msg| { msg.dust_limit_satoshis = msg.funding_satoshis + 1 ; msg }); - insane_open_helper("Bogus; channel reserve is less than dust limit", |mut msg| { msg.dust_limit_satoshis = msg.channel_reserve_satoshis + 1; msg }); + insane_open_helper(r"Bogus; channel reserve\(\d+\) is less than dust limit \(\d+\)", |mut msg| { msg.dust_limit_satoshis = msg.channel_reserve_satoshis + 1; msg }); - insane_open_helper("Minimum htlc value is full channel value", |mut msg| { msg.htlc_minimum_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000; msg }); + insane_open_helper(r"Minimum htlc value \(\d+\) is full channel value \(\d+\)", |mut msg| { msg.htlc_minimum_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000; msg }); insane_open_helper("They wanted our payments to be delayed by a needlessly long period", |mut msg| { msg.to_self_delay = MAX_LOCAL_BREAKDOWN_TIMEOUT + 1; msg }); insane_open_helper("0 max_accpted_htlcs makes for a useless channel", |mut msg| { msg.max_accepted_htlcs = 0; msg }); - insane_open_helper("max_accpted_htlcs > 483", |mut msg| { msg.max_accepted_htlcs = 484; msg }); + insane_open_helper("max_accpted_htlcs was 484. it must not be larger than 483", |mut msg| { msg.max_accepted_htlcs = 484; msg }); } #[test] @@ -1270,7 +1271,7 @@ fn fake_network_test() { route_over_limit(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 3000000); let events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 0); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); //TODO: Test that routes work again here as we've been notified that the channel is full @@ -1324,7 +1325,7 @@ fn holding_cell_htlc_counting() { unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash_1, &None), true, APIError::ChannelUnavailable { err }, assert!(err.contains("Cannot push more than their max accepted HTLCs"))); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); - nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); + nodes[1].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); } // This should also be true if we try to forward a payment. @@ -1542,14 +1543,14 @@ fn test_basic_channel_reserve() { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { match &fails[0] { APIError::ChannelUnavailable{err} => - assert_eq!(err, "Cannot send value that would put us under local channel reserve value"), + assert!(err.contains("Cannot send value that would put us under local channel reserve value")), _ => panic!("Unexpected error variant"), } }, _ => panic!("Unexpected error variant"), } assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 1); send_payment(&nodes[0], &vec![&nodes[1]], max_can_send, max_can_send); } @@ -1930,9 +1931,9 @@ fn test_channel_reserve_holding_cell_htlcs() { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_0 + 1); assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept (10000000)")); + assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); } // channel reserve is bigger than their_max_htlc_value_in_flight_msat so loop to deplete @@ -1994,7 +1995,7 @@ fn test_channel_reserve_holding_cell_htlcs() { { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_2 + 1); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us under local channel reserve value")); + assert!(err.contains("Cannot send value that would put us under local channel reserve value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); } @@ -2020,9 +2021,9 @@ fn test_channel_reserve_holding_cell_htlcs() { { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_22+1); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us under local channel reserve value")); + assert!(err.contains("Cannot send value that would put us under local channel reserve value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 2); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 2); } let (route_22, our_payment_hash_22, our_payment_preimage_22) = get_route_and_payment_hash!(recv_value_22); @@ -2107,14 +2108,14 @@ fn test_channel_reserve_holding_cell_htlcs() { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { match &fails[0] { APIError::ChannelUnavailable{err} => - assert_eq!(err, "Cannot send value that would put us under local channel reserve value"), + assert!(err.contains("Cannot send value that would put us under local channel reserve value")), _ => panic!("Unexpected error variant"), } }, _ => panic!("Unexpected error variant"), } assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 3); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 3); } send_payment(&nodes[0], &vec![&nodes[1], &nodes[2]][..], recv_value_3, recv_value_3); @@ -6404,9 +6405,9 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() { route.paths[0][0].fee_msat = 100; unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send less than their minimum HTLC value")); + assert!(err.contains("Cannot send less than their minimum HTLC value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send less than their minimum HTLC value".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send less than their minimum HTLC value".to_string(), 1); } #[test] @@ -6427,7 +6428,7 @@ fn test_update_add_htlc_bolt2_sender_zero_value_msat() { assert_eq!(err, "Cannot send 0-msat HTLC")); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send 0-msat HTLC".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send 0-msat HTLC".to_string(), 1); } #[test] @@ -6514,10 +6515,10 @@ fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_num_and_htlc_id_increment() let net_graph_msg_handler = &nodes[0].net_graph_msg_handler; let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap(); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot push more than their max accepted HTLCs")); + assert!(err.contains("Cannot push more than their max accepted HTLCs"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); } #[test] @@ -6538,10 +6539,10 @@ fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_value_in_flight() { let logger = test_utils::TestLogger::new(); let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], max_in_flight+1, TEST_FINAL_CLTV, &logger).unwrap(); unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, - assert_eq!(err, "Cannot send value that would put us over the max HTLC value in flight our peer will accept")); + assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); - nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); + nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); send_payment(&nodes[0], &[&nodes[1]], max_in_flight, max_in_flight); } @@ -6573,7 +6574,7 @@ fn test_update_add_htlc_bolt2_receiver_check_amount_received_more_than_min() { nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]); assert!(nodes[1].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[1], true).unwrap(); - assert_eq!(err_msg.data, "Remote side tried to send less than our minimum HTLC value"); + assert!(err_msg.data.contains("Remote side tried to send less than our minimum HTLC value")); check_added_monitors!(nodes[1], 1); } @@ -6653,7 +6654,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() { assert!(nodes[1].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[1], true).unwrap(); - assert_eq!(err_msg.data, "Remote tried to push more than our max accepted HTLCs"); + assert!(err_msg.data.contains("Remote tried to push more than our max accepted HTLCs")); check_added_monitors!(nodes[1], 1); } @@ -6678,7 +6679,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_in_flight_msat() { assert!(nodes[1].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[1], true).unwrap(); - assert_eq!(err_msg.data,"Remote HTLC add would put them over our max HTLC value"); + assert!(err_msg.data.contains("Remote HTLC add would put them over our max HTLC value")); check_added_monitors!(nodes[1], 1); } @@ -6752,7 +6753,7 @@ fn test_update_add_htlc_bolt2_receiver_check_repeated_id_ignore() { assert!(nodes[1].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[1], true).unwrap(); - assert_eq!(err_msg.data, "Remote skipped HTLC ID"); + assert!(err_msg.data.contains("Remote skipped HTLC ID")); check_added_monitors!(nodes[1], 1); } @@ -6785,7 +6786,7 @@ fn test_update_fulfill_htlc_bolt2_update_fulfill_htlc_before_commitment() { assert!(nodes[0].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[0], true).unwrap(); - assert_eq!(err_msg.data, "Remote tried to fulfill/fail HTLC before it had been committed"); + assert!(regex::Regex::new(r"Remote tried to fulfill/fail HTLC \(\d+\) before it had been committed").unwrap().is_match(err_msg.data.as_str())); check_added_monitors!(nodes[0], 1); } @@ -6818,7 +6819,7 @@ fn test_update_fulfill_htlc_bolt2_update_fail_htlc_before_commitment() { assert!(nodes[0].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[0], true).unwrap(); - assert_eq!(err_msg.data, "Remote tried to fulfill/fail HTLC before it had been committed"); + assert!(regex::Regex::new(r"Remote tried to fulfill/fail HTLC \(\d+\) before it had been committed").unwrap().is_match(err_msg.data.as_str())); check_added_monitors!(nodes[0], 1); } @@ -6852,7 +6853,7 @@ fn test_update_fulfill_htlc_bolt2_update_fail_malformed_htlc_before_commitment() assert!(nodes[0].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[0], true).unwrap(); - assert_eq!(err_msg.data, "Remote tried to fulfill/fail HTLC before it had been committed"); + assert!(regex::Regex::new(r"Remote tried to fulfill/fail HTLC \(\d+\) before it had been committed").unwrap().is_match(err_msg.data.as_str())); check_added_monitors!(nodes[0], 1); } @@ -6934,7 +6935,7 @@ fn test_update_fulfill_htlc_bolt2_wrong_preimage() { assert!(nodes[0].node.list_channels().is_empty()); let err_msg = check_closed_broadcast!(nodes[0], true).unwrap(); - assert_eq!(err_msg.data, "Remote tried to fulfill HTLC with an incorrect preimage"); + assert!(regex::Regex::new(r"Remote tried to fulfill HTLC \(\d+\) with an incorrect preimage").unwrap().is_match(err_msg.data.as_str())); check_added_monitors!(nodes[0], 1); } @@ -7328,7 +7329,7 @@ fn test_upfront_shutdown_script() { node_0_shutdown.scriptpubkey = Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script().to_p2sh(); // Test we enforce upfront_scriptpbukey if by providing a diffrent one at closing that we disconnect peer nodes[2].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown); - assert_eq!(check_closed_broadcast!(nodes[2], true).unwrap().data, "Got shutdown request with a scriptpubkey which did not match their previous scriptpubkey"); + assert!(regex::Regex::new(r"Got shutdown request with a scriptpubkey \([A-Fa-f0-9]+\) which did not match their previous scriptpubkey.").unwrap().is_match(check_closed_broadcast!(nodes[2], true).unwrap().data.as_str())); check_added_monitors!(nodes[2], 1); // We test that in case of peer committing upfront to a script, if it doesn't change at closing, we sign @@ -7409,7 +7410,7 @@ fn test_user_configurable_csv_delay() { let keys_manager: Arc> = Arc::new(test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet)); if let Err(error) = Channel::new_outbound(&&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), 1000000, 1000000, 0, &low_our_to_self_config) { match error { - APIError::APIMisuseError { err } => { assert_eq!(err, "Configured with an unreasonable our_to_self_delay putting user funds at risks"); }, + APIError::APIMisuseError { err } => { assert!(regex::Regex::new(r"Configured with an unreasonable our_to_self_delay \(\d+\) putting user funds at risks").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), } } else { assert!(false) } @@ -7420,7 +7421,7 @@ fn test_user_configurable_csv_delay() { open_channel.to_self_delay = 200; if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), InitFeatures::known(), &open_channel, 0, &low_our_to_self_config) { match error { - ChannelError::Close(err) => { assert_eq!(err, "Configured with an unreasonable our_to_self_delay putting user funds at risks"); }, + ChannelError::Close(err) => { assert!(regex::Regex::new(r"Configured with an unreasonable our_to_self_delay \(\d+\) putting user funds at risks").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), } } else { assert!(false); } @@ -7434,7 +7435,7 @@ fn test_user_configurable_csv_delay() { if let MessageSendEvent::HandleError { ref action, .. } = nodes[0].node.get_and_clear_pending_msg_events()[0] { match action { &ErrorAction::SendErrorMessage { ref msg } => { - assert_eq!(msg.data,"They wanted our payments to be delayed by a needlessly long period"); + assert!(regex::Regex::new(r"They wanted our payments to be delayed by a needlessly long period\. upper limit: \d+\. actual: \d+").unwrap().is_match(msg.data.as_str())); }, _ => { assert!(false); } } @@ -7446,7 +7447,7 @@ fn test_user_configurable_csv_delay() { open_channel.to_self_delay = 200; if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), InitFeatures::known(), &open_channel, 0, &high_their_to_self_config) { match error { - ChannelError::Close(err) => { assert_eq!(err, "They wanted our payments to be delayed by a needlessly long period"); }, + ChannelError::Close(err) => { assert!(regex::Regex::new(r"They wanted our payments to be delayed by a needlessly long period\. upper limit: \d+\. actual: \d+").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), } } else { assert!(false); } diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 36a6ccabc07..08e9e6a88ce 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -146,11 +146,11 @@ pub(super) fn build_onion_payloads(path: &Vec, total_msat: u64, paymen }); cur_value_msat += hop.fee_msat; if cur_value_msat >= 21000000 * 100000000 * 1000 { - return Err(APIError::RouteError{err: "Channel fees overflowed?!"}); + return Err(APIError::RouteError{err: "Channel fees overflowed?"}); } cur_cltv += hop.cltv_expiry_delta as u32; if cur_cltv >= 500000000 { - return Err(APIError::RouteError{err: "Channel CLTV overflowed?!"}); + return Err(APIError::RouteError{err: "Channel CLTV overflowed?"}); } last_short_channel_id = hop.short_channel_id; } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 9d194e05a3f..c13fcf3f64c 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -300,6 +300,22 @@ impl TestLogger { let log_entries = self.lines.lock().unwrap(); assert_eq!(log_entries.get(&(module, line)), Some(&count)); } + + pub fn assert_log_contains(&self, module: String, line: String, count: usize) { + let log_entries = self.lines.lock().unwrap(); + let l = log_entries.iter().find_map(|((m, l), c)| { + if m == &module && l.contains(line.as_str()) { Some(c) } else { None } + }); + assert_eq!(l.unwrap(), &count) + } + + pub fn assert_log_regex(&self, module: String, line: regex::Regex, count: usize) { + let log_entries = self.lines.lock().unwrap(); + let l = log_entries.iter().find_map(|((m, l), c)| { + if m == &module && line.is_match(l) { Some(c) } else { None } + }); + assert_eq!(l.unwrap(), &count) + } } impl Logger for TestLogger { From a949cd14c5a72267091268ca5debb992b07dedd1 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 2 Jul 2020 15:42:04 +0900 Subject: [PATCH 03/45] Update tests to pass in rustc 1.22.0 * Downgrade the `regex` crate version to the latest which works fine in rustc 1.22.0 * Be explicit about how to match against reference since old rustc is not smart enough to handle implicit reference coercion. * use separate `find` and `map` instead of `find_map` . Since it is not supported in old rust. --- lightning/Cargo.toml | 2 +- lightning/src/lib.rs | 1 + lightning/src/ln/functional_test_utils.rs | 6 +++--- lightning/src/ln/functional_tests.rs | 26 ++++++++++++----------- lightning/src/routing/router.rs | 24 ++++++++++----------- lightning/src/util/test_utils.rs | 14 ++++++------ 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index ab2bd0499cf..e1b9462668b 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -28,4 +28,4 @@ features = ["bitcoinconsensus"] [dev-dependencies] hex = "0.3" -regex = "1.3.9" +regex = "0.1.80" diff --git a/lightning/src/lib.rs b/lightning/src/lib.rs index 72017e46553..96000a753fe 100644 --- a/lightning/src/lib.rs +++ b/lightning/src/lib.rs @@ -20,6 +20,7 @@ extern crate bitcoin; #[cfg(test)] extern crate hex; +#[cfg(test)] extern crate regex; #[macro_use] pub mod util; diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index d9b9b17582e..2c994c12c8a 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -278,14 +278,14 @@ macro_rules! unwrap_send_err { match &$res { &Err(PaymentSendFailure::AllFailedRetrySafe(ref fails)) if $all_failed => { assert_eq!(fails.len(), 1); - match &fails[0] { + match fails[0] { $type => { $check }, _ => panic!(), } }, &Err(PaymentSendFailure::PartialFailure(ref fails)) if !$all_failed => { assert_eq!(fails.len(), 1); - match &fails[0] { + match fails[0] { Err($type) => { $check }, _ => panic!(), } @@ -972,7 +972,7 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou } let (_, our_payment_hash) = get_payment_preimage_hash!(origin_node); - unwrap_send_err!(origin_node.node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(origin_node.node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index ec8d7af155d..346222280d3 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -43,6 +43,8 @@ use bitcoin::hashes::Hash; use bitcoin::secp256k1::{Secp256k1, Message}; use bitcoin::secp256k1::key::{PublicKey,SecretKey}; +use regex; + use std::collections::{BTreeSet, HashMap, HashSet}; use std::default::Default; use std::sync::{Arc, Mutex}; @@ -1322,7 +1324,7 @@ fn holding_cell_htlc_counting() { { let net_graph_msg_handler = &nodes[1].net_graph_msg_handler; let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap(); - unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash_1, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash_1, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot push more than their max accepted HTLCs"))); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); nodes[1].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot push more than their max accepted HTLCs".to_string(), 1); @@ -1542,7 +1544,7 @@ fn test_basic_channel_reserve() { match err { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { match &fails[0] { - APIError::ChannelUnavailable{err} => + &APIError::ChannelUnavailable{ref err} => assert!(err.contains("Cannot send value that would put us under local channel reserve value")), _ => panic!("Unexpected error variant"), } @@ -1752,7 +1754,7 @@ fn test_chan_reserve_violation_outbound_htlc_inbound_chan() { }; let (route, our_payment_hash, _) = get_route_and_payment_hash!(1000); - unwrap_send_err!(nodes[1].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[1].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, "Cannot send value that would put them under remote channel reserve value")); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send value that would put them under remote channel reserve value".to_string(), 1); @@ -1930,7 +1932,7 @@ fn test_channel_reserve_holding_cell_htlcs() { { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_0 + 1); assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat)); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us over the max HTLC value in flight our peer will accept".to_string(), 1); @@ -1994,7 +1996,7 @@ fn test_channel_reserve_holding_cell_htlcs() { let recv_value_2 = stat01.value_to_self_msat - amt_msat_1 - stat01.channel_reserve_msat - total_fee_msat - commit_tx_fee_2_htlcs; { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_2 + 1); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send value that would put us under local channel reserve value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); } @@ -2020,7 +2022,7 @@ fn test_channel_reserve_holding_cell_htlcs() { // test with outbound holding cell amount > 0 { let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_22+1); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send value that would put us under local channel reserve value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put us under local channel reserve value".to_string(), 2); @@ -2107,7 +2109,7 @@ fn test_channel_reserve_holding_cell_htlcs() { match err { PaymentSendFailure::AllFailedRetrySafe(ref fails) => { match &fails[0] { - APIError::ChannelUnavailable{err} => + &APIError::ChannelUnavailable{ref err} => assert!(err.contains("Cannot send value that would put us under local channel reserve value")), _ => panic!("Unexpected error variant"), } @@ -6404,7 +6406,7 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() { let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap(); route.paths[0][0].fee_msat = 100; - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send less than their minimum HTLC value"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send less than their minimum HTLC value".to_string(), 1); @@ -6424,7 +6426,7 @@ fn test_update_add_htlc_bolt2_sender_zero_value_msat() { let logger = test_utils::TestLogger::new(); let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap(); route.paths[0][0].fee_msat = 0; - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, "Cannot send 0-msat HTLC")); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); @@ -6470,7 +6472,7 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() { let net_graph_msg_handler = &nodes[0].net_graph_msg_handler; let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000000, 500000001, &logger).unwrap(); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::RouteError { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::RouteError { ref err }, assert_eq!(err, &"Channel CLTV overflowed?")); } @@ -6514,7 +6516,7 @@ fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_num_and_htlc_id_increment() let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]); let net_graph_msg_handler = &nodes[0].net_graph_msg_handler; let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap(); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot push more than their max accepted HTLCs"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); @@ -6538,7 +6540,7 @@ fn test_update_add_htlc_bolt2_sender_exceed_max_htlc_value_in_flight() { let net_graph_msg_handler = &nodes[0].net_graph_msg_handler; let logger = test_utils::TestLogger::new(); let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, &[], max_in_flight+1, TEST_FINAL_CLTV, &logger).unwrap(); - unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { err }, + unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err }, assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index c00298feab7..883d0015fbb 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -881,7 +881,7 @@ mod tests { assert_eq!(route.paths[0][0].fee_msat, 200); assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1); assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.paths[0][1].pubkey, node3); assert_eq!(route.paths[0][1].short_channel_id, 13); @@ -945,7 +945,7 @@ mod tests { assert_eq!(route.paths[0][0].fee_msat, 200); assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1); assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.paths[0][1].pubkey, node3); assert_eq!(route.paths[0][1].short_channel_id, 13); @@ -1008,7 +1008,7 @@ mod tests { assert_eq!(route.paths[0][0].fee_msat, 200); assert_eq!(route.paths[0][0].cltv_expiry_delta, (13 << 8) | 1); assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.paths[0][1].pubkey, node3); assert_eq!(route.paths[0][1].short_channel_id, 13); @@ -1082,8 +1082,8 @@ mod tests { assert_eq!(route.paths[0][4].short_channel_id, 8); assert_eq!(route.paths[0][4].fee_msat, 100); assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::::new()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly // Simple test with outbound channel to 4 to test that last_hops and first_hops connect let our_chans = vec![channelmanager::ChannelDetails { @@ -1105,14 +1105,14 @@ mod tests { assert_eq!(route.paths[0][0].fee_msat, 0); assert_eq!(route.paths[0][0].cltv_expiry_delta, (8 << 8) | 1); assert_eq!(route.paths[0][0].node_features.le_flags(), &vec![0b11]); - assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + assert_eq!(route.paths[0][0].channel_features.le_flags(), &Vec::::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.paths[0][1].pubkey, node7); assert_eq!(route.paths[0][1].short_channel_id, 8); assert_eq!(route.paths[0][1].fee_msat, 100); assert_eq!(route.paths[0][1].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][1].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][1].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0][1].node_features.le_flags(), &Vec::::new()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0][1].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly last_hops[0].fees.base_msat = 1000; @@ -1147,8 +1147,8 @@ mod tests { assert_eq!(route.paths[0][3].short_channel_id, 10); assert_eq!(route.paths[0][3].fee_msat, 100); assert_eq!(route.paths[0][3].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][3].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0][3].node_features.le_flags(), &Vec::::new()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly // ...but still use 8 for larger payments as 6 has a variable feerate let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &node7, None, &last_hops, 2000, 42, Arc::clone(&logger)).unwrap(); @@ -1188,7 +1188,7 @@ mod tests { assert_eq!(route.paths[0][4].short_channel_id, 8); assert_eq!(route.paths[0][4].fee_msat, 2000); assert_eq!(route.paths[0][4].cltv_expiry_delta, 42); - assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet - assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly + assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::::new()); // We dont pass flags in from invoices yet + assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index c13fcf3f64c..30351275d9b 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -22,6 +22,8 @@ use bitcoin::hash_types::{Txid, BlockHash}; use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, Signature}; +use regex; + use std::time::Duration; use std::sync::Mutex; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -303,17 +305,17 @@ impl TestLogger { pub fn assert_log_contains(&self, module: String, line: String, count: usize) { let log_entries = self.lines.lock().unwrap(); - let l = log_entries.iter().find_map(|((m, l), c)| { - if m == &module && l.contains(line.as_str()) { Some(c) } else { None } - }); + let l = log_entries.iter().find(|&(&(ref m, ref l), _c)| { + m == &module && l.contains(line.as_str()) + }).map(|(&(ref _m, ref _l),c) | { c }); assert_eq!(l.unwrap(), &count) } pub fn assert_log_regex(&self, module: String, line: regex::Regex, count: usize) { let log_entries = self.lines.lock().unwrap(); - let l = log_entries.iter().find_map(|((m, l), c)| { - if m == &module && line.is_match(l) { Some(c) } else { None } - }); + let l = log_entries.iter().find(|&(&(ref m, ref l), _c)| { + m == &module && line.is_match(&l) + }).map(|(&(ref _m, ref _l),c) | { c }); assert_eq!(l.unwrap(), &count) } } From e9ebd8d341e57913e42e1b14e2bfc174d4ddf12d Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 4 Jul 2020 19:33:56 +0900 Subject: [PATCH 04/45] Update error message For when other peer has sent a invalid `announcement_signatures` --- lightning/src/ln/channelmanager.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5acae167318..cd479df3a0c 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -2745,10 +2745,21 @@ impl let were_node_one = announcement.node_id_1 == our_node_id; let msghash = hash_to_message!(&Sha256dHash::hash(&announcement.encode()[..])[..]); - if self.secp_ctx.verify(&msghash, &msg.node_signature, if were_node_one { &announcement.node_id_2 } else { &announcement.node_id_1 }).is_err() || - self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, if were_node_one { &announcement.bitcoin_key_2 } else { &announcement.bitcoin_key_1 }).is_err() { - let chan_err: ChannelError = ChannelError::Close("Bad announcement_signatures node_signature".to_owned()); - try_chan_entry!(self, Err(chan_err), channel_state, chan); + { + let their_node_key = if were_node_one { &announcement.node_id_2 } else { &announcement.node_id_1 }; + let their_bitcoin_key = if were_node_one { &announcement.bitcoin_key_2 } else { &announcement.bitcoin_key_1 }; + match (self.secp_ctx.verify(&msghash, &msg.node_signature, their_node_key), + self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, their_bitcoin_key)) { + (Err(e), _) => { + let chan_err: ChannelError = ChannelError::Close(format!("Bad announcement_signatures. node_signature: {:?}. announcement_signatures was {:?}. their_node_key: {:?}", e, &announcement, their_node_key)); + try_chan_entry!(self, Err(chan_err), channel_state, chan); + }, + (_, Err(e)) => { + let chan_err: ChannelError = ChannelError::Close(format!("Bad announcement_signatures. bitcoin_signature: {:?}, announcement_signatures was {:?}. their_bitcoin_key: ({:?})", e, &announcement, their_bitcoin_key)); + try_chan_entry!(self, Err(chan_err), channel_state, chan); + }, + _ => {} + } } let our_node_sig = self.secp_ctx.sign(&msghash, &self.our_network_key); From 13edc1f437343ff7cdf80ce8467c048b2c5ae3dc Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 16 Mar 2020 11:38:20 +0900 Subject: [PATCH 05/45] start writing bindings WIP: expose ChannelManager from bindings crate to ffi update bindings udpate bindings to not expose result type as FFI update prepare FFIResult finish tests for FFIResult prepare thread_handle prepare is_null prepare all FFI handler type avoid all compiler error by using ARc separate adaptor types into adaptors folder add BufferToSmall error case to FFIResult kind Add new entrypoints for fetching and testing last result for FFI add entrypoint for testing remove input argument from test ffi update update update update update amend update udpate broadcaster_wrapper ffi interface update cleanup: rename variables and remove unused deps remove unused cbindgen.toml delete outdated readme message do not specify repr(C) to Record type Add `UnwandSafe` and `RefUnwindSafe` trait bound * `Logger` and `ChainWatchInterface` were trait which has to cross ffi boundary. for a foreign language to catch a panic caused in `rust-lightning` these two traits must be `UnwindSafe` so explicitly add. Fix miscs * Use usize everywhere for len * Use `OutPoint` from lightning instead of bitcoin udpate comment start writing ffi interface for testing logger fix bug that FFITransaction points to garbage location Remove unused `&Self` parameter from FFI delegate. update add entrypoint for releasing channel manager remove unused test function fix bug in FFIChainWatchInterface Constructor Revert "remove unused test function" This reverts commit 4a1cccb6e8c0290b685f6003ca8ee8ba7ad71718. add new entrypoint send_payment fix bug in broadcaster stop receiving SafeHandle twice take everything as delegate in constructor * Since otherwise it sometimes crashes by GC in C# side. cleanup stop using debug_assertions as a feature remove some unused imports and implement Error trait for APIError Simplify FFIRoute * When passing struct through FFI boundary, if the object has nested array it will cause headache for preventing it from GCed. So we just pass binary serialized object and (de)serialize after passing. In this case, `FFIRoute`. * Prepare Debug trait for Route object. add entrpoint for getting pending events from chanman update: FFI PeerManager create/dispose methods add deep_copy method to array_struct update remove unnecessary dirty tests update: add process_event function for FFI remove repr(C) from lightning pcakge fix build error in lightning update ffi for BasicMPP fix some compiler warning return Bool from read_event Simplify ChainWatchInterface * delegate some `FFIChainWatchInteface`'s work to `ChainWatchInterfaceUtil` * remove now-useless ChannelMonitor and its related functions, objects refactor create_channel_manager to be more generic fix bug in deep_copy return internal_error directly when converting FFI pub/secret key stop using wacky deep_copy method return act_one when calling new_outbound from C# stop using FFIBytes for return value of new_outbound simplify array_struct by not using generics expose PeerManager::get_peer_node_ids to ffi fix build error after rebasing to master give channel maanger from outside world when initializing PeerManager add many ffi entrypoints for ChannelManager --- Cargo.toml | 1 + bindings/Cargo.toml | 23 ++ bindings/README.md | 4 + bindings/src/adaptors/mod.rs | 478 ++++++++++++++++++++++++ bindings/src/adaptors/primitives.rs | 270 +++++++++++++ bindings/src/block_notirifer.rs | 4 + bindings/src/broadcaster.rs | 80 ++++ bindings/src/channelmanager.rs | 276 ++++++++++++++ bindings/src/error.rs | 379 +++++++++++++++++++ bindings/src/fee_estimator.rs | 22 ++ bindings/src/ffi_test_utils.rs | 17 + bindings/src/handle/mod.rs | 230 ++++++++++++ bindings/src/handle/thread_bound.rs | 343 +++++++++++++++++ bindings/src/is_null.rs | 46 +++ bindings/src/lazy_static/inline_lazy.rs | 52 +++ bindings/src/lazy_static/mod.rs | 113 ++++++ bindings/src/lib.rs | 71 ++++ bindings/src/logger.rs | 33 ++ bindings/src/peermanager.rs | 204 ++++++++++ bindings/src/test_utils.rs | 20 + bindings/src/utils/macros.rs | 101 +++++ bindings/src/utils/mod.rs | 54 +++ bindings/src/utils/option_extensions.rs | 40 ++ lightning/src/chain/chaininterface.rs | 3 +- lightning/src/ln/channelmanager.rs | 9 + lightning/src/ln/channelmonitor.rs | 2 +- lightning/src/ln/features.rs | 1 - lightning/src/ln/msgs.rs | 9 +- lightning/src/routing/network_graph.rs | 3 +- lightning/src/util/config.rs | 6 + lightning/src/util/errors.rs | 8 + lightning/src/util/logger.rs | 4 +- lightning/src/util/ser.rs | 23 ++ 33 files changed, 2921 insertions(+), 8 deletions(-) create mode 100644 bindings/Cargo.toml create mode 100644 bindings/README.md create mode 100644 bindings/src/adaptors/mod.rs create mode 100644 bindings/src/adaptors/primitives.rs create mode 100644 bindings/src/block_notirifer.rs create mode 100644 bindings/src/broadcaster.rs create mode 100644 bindings/src/channelmanager.rs create mode 100644 bindings/src/error.rs create mode 100644 bindings/src/fee_estimator.rs create mode 100644 bindings/src/ffi_test_utils.rs create mode 100644 bindings/src/handle/mod.rs create mode 100644 bindings/src/handle/thread_bound.rs create mode 100644 bindings/src/is_null.rs create mode 100644 bindings/src/lazy_static/inline_lazy.rs create mode 100644 bindings/src/lazy_static/mod.rs create mode 100644 bindings/src/lib.rs create mode 100644 bindings/src/logger.rs create mode 100644 bindings/src/peermanager.rs create mode 100644 bindings/src/test_utils.rs create mode 100644 bindings/src/utils/macros.rs create mode 100644 bindings/src/utils/mod.rs create mode 100644 bindings/src/utils/option_extensions.rs diff --git a/Cargo.toml b/Cargo.toml index 222de893aa4..073b35763ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "lightning", "lightning-net-tokio", + "bindings", ] # Our tests do actual crypo and lots of work, the tradeoff for -O1 is well worth it diff --git a/bindings/Cargo.toml b/bindings/Cargo.toml new file mode 100644 index 00000000000..f5bd0e49818 --- /dev/null +++ b/bindings/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "bindings" +version = "0.1.0" +authors = ["joe.miyamoto "] +edition = "2018" + +[lib] +crate-type=["staticlib", "cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lightning = { path = "../lightning" } +bitcoin = "0.23" +bitcoin_hashes = "0.7" + +# We want to include this only for debug build, but following line does not work. +# [target.'cfg(debug_assertions)'.dependencies] +# see https://github.com/rust-lang/cargo/issues/7634 +# as a workaround, we just included it as an usual dependency +hex = "0.3" +rand = "0.7.3" + diff --git a/bindings/README.md b/bindings/README.md new file mode 100644 index 00000000000..f7b9096fbe8 --- /dev/null +++ b/bindings/README.md @@ -0,0 +1,4 @@ +# FFI binding interface for rust-lighting + +Currently, it only supports C#, other languages are untested. + diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs new file mode 100644 index 00000000000..219f8b79aaa --- /dev/null +++ b/bindings/src/adaptors/mod.rs @@ -0,0 +1,478 @@ +use std::{ + io::Error, + ffi::{CString}, + hash::{Hasher, Hash}, + ffi::CStr, + ptr::NonNull +}; + +use bitcoin::hash_types::{BlockHash, Txid}; +use bitcoin::{blockdata::transaction::Transaction, blockdata::script::Script, blockdata::block::Block, consensus::serialize as bitcoin_serialize, Network}; +use bitcoin::secp256k1; + +use lightning::{ + chain::chaininterface::{BroadcasterInterface, FeeEstimator, ConfirmationTarget, ChainWatchInterface, ChainError}, + util::logger::{Logger, Record, Level}, + util::ser::{Writeable, Writer}, + ln::peer_handler::SocketDescriptor, + ln::msgs::ErrorAction, + chain::chaininterface::ChainWatchInterfaceUtil, + ln::msgs::{ErrorMessage, RoutingMessageHandler, HTLCFailChannelUpdate, ChannelAnnouncement, NodeAnnouncement, LightningError, ChannelUpdate} +}; + +pub mod primitives; +use primitives::*; +use std::sync::Arc; +use lightning::util::ser::Readable; +use std::io::Read; +use lightning::ln::msgs::DecodeError; + +type cstr = NonNull; + +#[derive(PartialOrd, PartialEq, Eq, Ord, Debug, Copy, Clone)] +#[repr(u8)] +pub enum Bool { False = 0, True = 1 } +impl Bool { + pub fn to_bool(&self) -> bool { *self == Bool::True } +} + +impl From for Bool { + fn from(v: bool) -> Self { + if v { Bool::True } else { Bool::False } + } +} + +pub mod broadcaster_fn { + use crate::adaptors::primitives::FFITransaction; + pub type BroadcastTransactionPtr = extern "cdecl" fn(tx: *const FFITransaction); +} + +#[repr(C)] +pub struct FFIBroadCaster { + pub broadcast_transaction_ptr: broadcaster_fn::BroadcastTransactionPtr, +} + +// these are necessary for `SimpleChannelManager` to have `ManyChannelMonitor` impl. +// TODO: we may need to use AtomicPtr type for inner func +unsafe_impl!("We don't mutate inner function pointer during execution" => impl Send for FFIBroadCaster {}); +unsafe_impl!("We don't mutate inner function pointer during execution" => impl Sync for FFIBroadCaster {}); + +impl BroadcasterInterface for FFIBroadCaster { + fn broadcast_transaction(&self, tx: &Transaction) { + let v = bitcoin_serialize(tx); + let ffi_tx = FFITransaction::from(v.as_slice()); + (self.broadcast_transaction_ptr)(&ffi_tx as *const FFITransaction) + } +} + +#[repr(C)] +pub enum FFIConfirmationTarget { + /// We are happy with this transaction confirming slowly when feerate drops some. + Background, + /// We'd like this transaction to confirm without major delay, but 12-18 blocks is fine. + Normal, + /// We'd like this transaction to confirm in the next few blocks. + HighPriority, +} + +impl From for FFIConfirmationTarget { + fn from(target: ConfirmationTarget) -> FFIConfirmationTarget { + match target { + ConfirmationTarget::Background => FFIConfirmationTarget::Background, + ConfirmationTarget::Normal => FFIConfirmationTarget::Normal, + ConfirmationTarget::HighPriority => FFIConfirmationTarget::HighPriority, + } + } +} + +pub mod fee_estimator_fn { + use super::{FFIConfirmationTarget}; + pub type GetEstSatPer1000WeightPtr = extern "cdecl" fn (FFIConfirmationTarget) -> u64; +} + +#[repr(C)] +#[derive(Clone)] +pub struct FFIFeeEstimator { + pub get_est_sat_per_1000_weight_ptr: fee_estimator_fn::GetEstSatPer1000WeightPtr, +} + +impl FeeEstimator for FFIFeeEstimator { + fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u64 { + (self.get_est_sat_per_1000_weight_ptr)(confirmation_target.into()) + } +} +unsafe_impl!("We don't mutate inner function pointer during execution" => impl Send for FFIFeeEstimator {}); +unsafe_impl!("We don't mutate inner function pointer during execution" => impl Sync for FFIFeeEstimator {}); + +#[repr(C)] +#[derive(Clone, Copy)] +pub enum FFINetwork { + MainNet = 0, + TestNet = 1, + RegTest = 2, +} + +impl FFINetwork { + pub fn to_network(&self) -> bitcoin::network::constants::Network { + match self { + FFINetwork::MainNet => { bitcoin::network::constants::Network::Bitcoin }, + FFINetwork::TestNet => { bitcoin::network::constants::Network::Testnet }, + FFINetwork::RegTest => { bitcoin::network::constants::Network::Regtest }, + } + } +} + +#[derive(Debug)] +#[repr(C)] +pub enum FFILogLevel { + ///Designates logger being silent + Off, + /// Designates very serious errors + Error, + /// Designates hazardous situations + Warn, + /// Designates useful information + Info, + /// Designates lower priority information + Debug, + /// Designates very low priority, often extremely verbose, information + Trace, +} + +impl From for FFILogLevel { + fn from(level: Level) -> FFILogLevel { + match level { + Level::Off => FFILogLevel::Off, + Level::Error => FFILogLevel::Error, + Level::Warn => FFILogLevel::Warn, + Level::Info => FFILogLevel::Info, + Level::Debug => FFILogLevel::Debug, + Level::Trace => FFILogLevel::Trace + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFILogRecord { + /// The verbosity level of the message. + pub level: FFILogLevel, + /// The message body. + pub args: cstr, + /// The module path of the message. + pub module_path: cstr, + /// The source file containing the message. + pub file: cstr, + /// The line containing the message. + pub line: u32, +} + +pub mod ffilogger_fn { + use super::{FFILogRecord}; + pub type LogExtern = extern "cdecl" fn(record: *const FFILogRecord); +} + +#[repr(C)] +pub struct FFILogger { + pub log_ptr: ffilogger_fn::LogExtern, +} + +impl Logger for FFILogger { + fn log(&self, rec: &Record) { + let args = CString::new(std::fmt::format(rec.args)).unwrap_or(CString::new("Record.args contains null char in the middle").unwrap()); + let module_path = CString::new(rec.module_path).unwrap_or(CString::new("Record.module_path contains null char in the middle").unwrap()); + let file = CString::new(rec.file).unwrap_or(CString::new("Record.file contains null char in the middle").unwrap()); + let ffi_record = + FFILogRecord { + level: rec.level.into(), + args: NonNull::new(args.as_ptr() as *mut _).unwrap(), + module_path: NonNull::new(module_path.as_ptr() as *mut _).unwrap(), + file: NonNull::new(file.as_ptr() as *mut _).unwrap(), + line: rec.line, + }; + (self.log_ptr)(&ffi_record as *const _); + } +} + +pub mod chain_watch_interface_fn { + use super::*; + pub type InstallWatchTxPtr = extern "cdecl" fn(*const FFISha256dHash, script_pub_key: *const FFIScript); + pub type InstallWatchOutpointPtr = extern "cdecl" fn(outpoint: *const FFIOutPoint, out_script: *const FFIScript); + pub type WatchAllTxnPtr = extern "cdecl" fn(); + pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const FFISha256dHash, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script: *mut FFITxOut); +} + +#[repr(C)] +pub struct FFIChainWatchInterface { + pub install_watch_tx_ptr: chain_watch_interface_fn::InstallWatchTxPtr, + pub install_watch_outpoint_ptr: chain_watch_interface_fn::InstallWatchOutpointPtr, + pub watch_all_txn_ptr: chain_watch_interface_fn::WatchAllTxnPtr, + pub get_chain_utxo_ptr: chain_watch_interface_fn::GetChainUtxoPtr, + util: ChainWatchInterfaceUtil +} +impl FFIChainWatchInterface { + pub fn new( + install_watch_tx: chain_watch_interface_fn::InstallWatchTxPtr, + install_watch_outpoint: chain_watch_interface_fn::InstallWatchOutpointPtr, + watch_all_txn: chain_watch_interface_fn::WatchAllTxnPtr, + get_chain_utxo: chain_watch_interface_fn::GetChainUtxoPtr, + network: Network, + logger: Arc + ) -> FFIChainWatchInterface { + FFIChainWatchInterface{ + install_watch_tx_ptr: install_watch_tx, + install_watch_outpoint_ptr: install_watch_outpoint, + watch_all_txn_ptr: watch_all_txn, + get_chain_utxo_ptr: get_chain_utxo, + util: ChainWatchInterfaceUtil::new(network) + } + } +} + +impl ChainWatchInterface for FFIChainWatchInterface { + fn install_watch_tx(&self, txid: &Txid, script_pub_key: &Script) { + self.util.install_watch_tx(txid, script_pub_key); + let spk_vec = bitcoin_serialize(script_pub_key); + let ffi_spk = FFIScript::from(spk_vec.as_slice()); + (self.install_watch_tx_ptr)(&txid.into() as *const _, &ffi_spk as *const _) + } + fn install_watch_outpoint(&self, outpoint: (Txid, u32), out_script: &Script) { + self.util.install_watch_outpoint(outpoint, out_script); + let txid: FFISha256dHash = outpoint.0.into(); + let ffi_outpoint = FFIOutPoint { txid: txid, index: outpoint.1 as u16 }; + let out_script_vec = bitcoin_serialize(out_script); + let ffi_outscript = FFIScript::from(out_script_vec.as_slice()); + (self.install_watch_outpoint_ptr)(&ffi_outpoint as *const _, &ffi_outscript as *const _) + } + fn watch_all_txn(&self) { + self.util.watch_all_txn(); + (self.watch_all_txn_ptr)() + } + fn get_chain_utxo(&self, genesis_hash: BlockHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { + let err = std::ptr::null_mut(); + let tx_out = std::ptr::null_mut(); + (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err, tx_out); + if err.is_null() { + let tx_out: FFITxOut = unsafe_block!("We know the caller has set the value into the tx_out" => (*tx_out).clone()); + Ok((tx_out.script_pubkey.to_script(), tx_out.value)) + } + else { + let e = unsafe_block!("we know the error is not a null pointer" => (*err).clone()); + Err(e.into()) + } + } + fn filter_block<'a>(&self, block: &'a Block) -> (Vec<&'a Transaction>, Vec) { + self.util.filter_block(block) + } + fn reentered(&self) -> usize { + self.util.reentered() + } +} + +pub mod socket_descriptor_fn { + use super::FFIBytes; + pub type SendData = extern "cdecl" fn (data: FFIBytes, resume_read: u8) -> usize; + pub type DisconnectSocket = extern "cdecl" fn (); +} + +#[repr(C)] +#[derive(Clone)] +pub struct FFISocketDescriptor { + pub index: usize, + pub send_data_ptr: socket_descriptor_fn::SendData, + pub disconnect_socket_ptr: socket_descriptor_fn::DisconnectSocket, +} + +impl PartialEq for FFISocketDescriptor { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} +impl Eq for FFISocketDescriptor {} +impl Hash for FFISocketDescriptor { + fn hash(&self, state: &mut H) { + self.index.hash(state) + } +} + +impl SocketDescriptor for FFISocketDescriptor { + fn send_data(&mut self, data: &[u8], resume_read: bool) -> usize { + let r: FFIBytes = data.into(); + (self.send_data_ptr)(r, resume_read as u8) + } + + fn disconnect_socket(&mut self) { + (self.disconnect_socket_ptr)() + } +} + +#[repr(u8)] +#[derive(Debug,Clone)] +pub enum FFIErrorActionType { + DisconnectPeer = 0u8, + /// 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, +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct FFIErrorMsg { + pub channel_id: [u8; 32], + pub data: cstr, +} + +impl From for ErrorMessage { + fn from(msg: FFIErrorMsg) -> Self { + let data = unsafe_block!("We know pointer is non-null" => CStr::from_ptr(msg.data.as_ptr()) ); + ErrorMessage { + data: data.to_str().unwrap().to_owned(), + channel_id: msg.channel_id + } + } +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct FFIErrorAction { + ty: FFIErrorActionType, + payload: Option<*const FFIErrorMsg> +} + +impl From for ErrorAction { + fn from(x: FFIErrorAction) -> Self { + match x.ty { + FFIErrorActionType::DisconnectPeer => { + ErrorAction::DisconnectPeer { + msg: x.payload.map(|msg| { + From::from(unsafe_block!("`from` conversion consumes x" => (*msg).clone())) + }) + } + }, + FFIErrorActionType::IgnoreError => { + ErrorAction::IgnoreError + }, + FFIErrorActionType::SendErrorMessage => { + match x.payload { + None => panic!(format!("Inconsistent enum {:?}", x)), + Some(msg) => { + let msg = unsafe_block!("`from` conversion consumes x" => (*msg).clone()).into(); + ErrorAction::SendErrorMessage { msg } + } + } + } + } + } +} + +#[derive(Clone)] +#[repr(C)] +pub struct FFILightningError { + /// A human-readable message describing the error + pub err: cstr, + /// The action which should be taken against the offending peer. + pub action: FFIErrorAction, +} + +impl From for LightningError { + fn from(v: FFILightningError) -> Self { + let err = unsafe_block!("We know error msg is non-null c string" => CStr::from_ptr(v.err.as_ptr()) ); + LightningError { + err: err.to_str().unwrap(), + action: v.action.into() + } + } +} + +pub mod routing_msg_descriptor_fn { + use super::*; + use crate::adaptors::primitives::PublicKey; + + /// Handle an incoming node_announcement message, returning true if it should be forwarded on, + /// false or returning an Err otherwise. + pub type HandleNodeAnnouncement = extern "cdecl" fn (msg: *const FFIBytes, error: *mut FFILightningError) -> Bool; + /// Handle a channel_announcement message, returning true if it should be forwarded on, false + /// or returning an Err otherwise. + pub type HandleChannelAnnouncement = extern "cdecl" fn (msg: *const FFIBytes, error: *mut FFILightningError) -> Bool; + /// Handle an incoming channel_update message, returning true if it should be forwarded on, + /// false or returning an Err otherwise. + pub type HandleChannelUpdate = extern "cdecl" fn (msg: *const FFIBytes, error: *mut FFILightningError) -> Bool; + /// Handle some updates to the route graph that we learned due to an outbound failed payment. + pub type HandleHTLCFailChannelUpdate = extern "cdecl" fn (update: *const FFIBytes); + /// Gets a subset of the channel announcements and updates required to dump our routing table + /// to a remote node, starting at the short_channel_id indicated by starting_point and + /// including the batch_amount entries immediately higher in numerical value than starting_point. + /// Return type is serialized `Vec<(ChannelAnnouncement, ChannelUpdate, ChannelUpdate)>` + pub type GetNextChannelAnnouncements = extern "cdecl" fn (starting_point: u64, batch_amount: u8) -> FFIBytes; + /// Gets a subset of the node announcements required to dump our routing table to a remote node, + /// starting at the node *after* the provided publickey and including batch_amount entries + /// immediately higher (as defined by ::cmp) than starting_point. + /// If None is provided for starting_point, we start at the first node. + /// Return type is binary serialized `Vec` . + pub type GetNextNodeAnnouncements = extern "cdecl" fn (starting_point: Option<*const PublicKey>, batch_amount: u8) -> FFIBytes; + /// Returns whether a full sync should be requested from a peer. + pub type ShouldRequestFullSync = extern "cdecl" fn (node_id: NonNull) -> Bool; +} + +pub struct FFIRoutingMsgHandler { + pub handle_node_announcement_ptr: routing_msg_descriptor_fn::HandleNodeAnnouncement, + pub handle_channel_announcement_ptr: routing_msg_descriptor_fn::HandleChannelAnnouncement, + pub handle_channel_update_ptr: routing_msg_descriptor_fn::HandleChannelUpdate, + pub handle_htlc_fail_channel_update_ptr: routing_msg_descriptor_fn::HandleHTLCFailChannelUpdate, + pub get_next_channel_announcements_ptr: routing_msg_descriptor_fn::GetNextChannelAnnouncements, + pub get_next_node_announcements_ptr: routing_msg_descriptor_fn::GetNextNodeAnnouncements, + pub should_request_full_sync_ptr: routing_msg_descriptor_fn::ShouldRequestFullSync +} + +pub struct VecWriter(pub Vec); +impl Writer for VecWriter { + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + self.0.extend_from_slice(buf); + Ok(()) + } + + fn size_hint(&mut self, size: usize) { + self.0.reserve_exact(size) + } +} + +impl RoutingMessageHandler for FFIRoutingMsgHandler { + fn handle_node_announcement(&self, msg: &NodeAnnouncement) -> Result { + let mut w = VecWriter(Vec::new()); + msg.write(&mut w); + let bytes = FFIBytes::from(w.0.into_boxed_slice()); + let e = std::ptr::null_mut(); + let is_success = (self.handle_node_announcement_ptr)(&bytes as *const _, e); + if e.is_null() { + Ok(is_success.to_bool()) + } else { + let e = unsafe_block!("we know the error is not a null pointer" => (*e).clone()); + Err(e.into()) + } + } + + fn handle_channel_announcement(&self, msg: &ChannelAnnouncement) -> Result { + unimplemented!() + } + + fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result { + unimplemented!() + } + + fn handle_htlc_fail_channel_update(&self, update: &HTLCFailChannelUpdate) { + unimplemented!() + } + + fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { + unimplemented!() + } + + fn get_next_node_announcements(&self, starting_point: Option<&secp256k1::PublicKey>, batch_amount: u8) -> Vec { + unimplemented!() + } + + fn should_request_full_sync(&self, node_id: &secp256k1::PublicKey) -> bool { + unimplemented!() + } +} diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs new file mode 100644 index 00000000000..079a66d2286 --- /dev/null +++ b/bindings/src/adaptors/primitives.rs @@ -0,0 +1,270 @@ +use std::{ + convert::{TryFrom, TryInto}, + fmt::{Formatter, Error} +}; + +use bitcoin::{ + hash_types::{Txid, BlockHash}, + blockdata::script::Script, + hashes::Hash, + Transaction, + secp256k1 +}; +use lightning::{ + chain::chaininterface::ChainError, + ln::channelmanager::{PaymentHash, PaymentSecret}, + routing::router::{Route}, + util::ser::{Readable, Writeable}, + util::events::Event, + chain::transaction::OutPoint +}; +use crate::{ + FFIResult, + is_null::IsNull +}; + +macro_rules! array_struct{ + ( + $(#[$meta:meta])* + $name:ident) => { + $(#[$meta])* + #[derive(Clone)] + #[repr(C)] + pub struct $name { + ptr: *const u8, + len: usize, + } + unsafe_impl!("Simulate `Unique`" => impl<'a> Send for $name {}); + unsafe_impl!("Simulate `Unique`" => impl<'a> Sync for $name {}); + impl $name { + fn new(ptr: *const u8, len: usize) -> Self { $name{ ptr: ptr, len: len } } + } + impl From<&[u8]> for $name { + fn from(slice: &[u8]) -> Self { + $name::new( + slice.as_ptr(), + slice.len(), + ) + } + } + + impl From> for $name { + fn from(slice: Box<[u8]>) -> Self { + $name::new( + slice.as_ptr(), + slice.len(), + ) + } + } + + + impl std::fmt::Debug for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + hex::encode(self).fmt(f) + } + } + + impl AsRef<[u8]> for $name { + fn as_ref(&self) -> &[u8] { + unsafe_block!("" => std::slice::from_raw_parts(self.ptr, self.len)) + } + } + + impl IsNull for $name { + fn is_null(&self) -> bool { + self.ptr.is_null() + } + } + } +} + +#[doc="The length must be [the same as a byte number of secp256k1 secret key](secp256k1::constants::SECRET_KEY_SIZE)"] +array_struct!(SecretKey); + +impl TryFrom for secp256k1::SecretKey { + type Error = FFIResult; + + fn try_from(value: SecretKey) -> Result { + let s = value.as_ref(); + secp256k1::SecretKey::from_slice(s).map_err(|e| FFIResult::internal_error().context(e)) + } +} + + +#[doc="The length must be [the same as a byte number of secp256k1 public key] `secp256k1::constants::PUBLIC_KEY_SIZE`"] +array_struct!(PublicKey); + +impl TryFrom for secp256k1::PublicKey { + type Error = FFIResult; + + fn try_from(value: PublicKey) -> Result { + let s = value.as_ref(); + secp256k1::PublicKey::from_slice(s).map_err(|e| FFIResult::internal_error().context(e)) + } +} + +#[doc="256 bit seed to initialize [ChannelManager](lightning::ln::channelmanager::ChannelManager)"] +array_struct!(Seed); + +array_struct!(FFISha256dHash); + +impl TryFrom for PaymentHash { + type Error = FFIResult; + + fn try_from(ffi_hash: FFISha256dHash) -> Result { + let s = unsafe_block!("" => std::slice::from_raw_parts(ffi_hash.ptr, ffi_hash.len)); + let s:[u8; 32] = s.try_into().map_err(|_| FFIResult::invalid_data_length())?; + Ok(PaymentHash(s)) + } +} + +impl From<&Txid> for FFISha256dHash { + fn from(hash: &Txid) -> Self { + let v = hash.encode(); + FFISha256dHash::from(v.into_boxed_slice()) + } +} + +impl From for FFISha256dHash { + fn from(hash: Txid) -> Self { + let v = hash.encode(); + FFISha256dHash::from(v.into_boxed_slice()) + } +} + +impl TryFrom for Txid { + type Error = bitcoin::hashes::Error; + fn try_from(hash: FFISha256dHash) -> Result { + let slice = unsafe_block!("We know it points to valid buffer" => std::slice::from_raw_parts(hash.ptr, hash.len)); + let v = bitcoin::hashes::sha256d::Hash::from_slice(slice)?; + Ok(v.into()) + } +} + +impl From<&BlockHash> for FFISha256dHash { + fn from(hash: &BlockHash) -> Self { + let v = hash.encode(); + FFISha256dHash::from(v.into_boxed_slice()) + } +} + +impl From for FFISha256dHash { + fn from(hash: BlockHash) -> Self { + let v = hash.encode(); + FFISha256dHash::from(v.into_boxed_slice()) + } +} + +array_struct!(FFISecret); + +impl TryFrom for PaymentSecret { + type Error = FFIResult; + + fn try_from(ffi_secret: FFISecret) -> Result { + let s = unsafe_block!("" => std::slice::from_raw_parts(ffi_secret.ptr, ffi_secret.len)); + let s:[u8; 32] = s.try_into().map_err(|_| FFIResult::invalid_data_length())?; + Ok(PaymentSecret(s)) + } +} + +array_struct!(FFIScript); +impl FFIScript { + pub fn to_script(&self) -> Script { + unimplemented!() + } +} + +#[derive(Clone)] +#[repr(C)] +pub struct FFIOutPoint { + pub txid: FFISha256dHash, + pub index: u16, +} + +impl TryFrom for OutPoint { + type Error = bitcoin::hashes::Error; + fn try_from(value: FFIOutPoint) -> Result { + let txid = value.txid.try_into()?; + Ok(OutPoint{ txid, index: value.index }) + } +} + +impl From for FFIOutPoint { + fn from(value: OutPoint) -> Self { + FFIOutPoint { txid: value.txid.into(), index: value.index } + } +} + +impl IsNull for FFIOutPoint { + fn is_null(&self) -> bool { + false + } +} + +#[derive(Clone)] +#[repr(C)] +pub struct FFITxOut { + pub value: u64, + pub script_pubkey: FFIScript, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub enum FFIChainError { + /// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash) + NotSupported, + /// Chain isn't the one watched + NotWatched, + /// Tx doesn't exist or is unconfirmed + UnknownTx, +} + +impl From for ChainError { + fn from(e: FFIChainError) -> Self { + match e { + FFIChainError::NotSupported => ChainError::NotSupported, + FFIChainError::NotWatched => ChainError::NotWatched, + FFIChainError::UnknownTx => ChainError::UnknownTx, + } + } +} + +array_struct!(FFIRoute); + +impl TryFrom for Route { + type Error = FFIResult; + + fn try_from(value: FFIRoute) -> Result { + let mut slice = value.as_ref(); + ::read(&mut slice).map_err(|_| FFIResult::deserialization_failure()) + } +} + +array_struct!(FFITransaction); +array_struct!(FFIBlock); +array_struct!(FFIEvents); + +/// General purpose byte array which has to cross ffi-boundary +array_struct!(FFIBytes); + +/// For `ChainWatchInterface::filter_block` +impl TryFrom for (Vec<&Transaction>, Vec) { + type Error = FFIResult; + + fn try_from(bytes: FFIBytes) -> Result { + unimplemented!() + } +} + +impl From> for FFIEvents { + + fn from(value: Vec) -> Self { + let len = value.len(); + let mut result_vec: Vec = Vec::with_capacity(len * std::mem::size_of::()); + for e in value { + result_vec.extend(e.encode()); + } + let r = result_vec.into_boxed_slice(); + r.into() + } +} diff --git a/bindings/src/block_notirifer.rs b/bindings/src/block_notirifer.rs new file mode 100644 index 00000000000..339e1c5a3d5 --- /dev/null +++ b/bindings/src/block_notirifer.rs @@ -0,0 +1,4 @@ + +use crate::handle::HandleShared; + +pub type FFIBlockNotifierHandle = HandleShared<'a, FFIBlockNotifier>; \ No newline at end of file diff --git a/bindings/src/broadcaster.rs b/bindings/src/broadcaster.rs new file mode 100644 index 00000000000..bcd67fc8034 --- /dev/null +++ b/bindings/src/broadcaster.rs @@ -0,0 +1,80 @@ +//! functions for managing broadcaster. +//! Usually, you don't have to have a handler to the broadcaster in a wrapper side. Just create +//! ChannelMonitor or ChannelManager and hold a reference to it. +//! However, sometimes it is useful for testing. + +use std::sync::Arc; + +use bitcoin::blockdata::transaction::Transaction; +use lightning::chain::chaininterface::BroadcasterInterface; +use crate::adaptors::*; +use crate::handle::{Ref, Out, HandleShared}; +use crate::error::FFIResult; + +pub type FFIBroadCasterHandle<'a> = HandleShared<'a, FFIBroadCaster>; + +#[cfg(debug_assertions)] +#[repr(C)] +pub struct BroadcasterWrapper { + broadcaster: Arc, +} + +#[cfg(debug_assertions)] +impl BroadcasterWrapper { + pub fn broadcast(&self, tx: &Transaction) { + self.broadcaster.as_ref().broadcast_transaction(&tx) + } +} + +#[cfg(debug_assertions)] +type BroadcasterWrapperHandle<'a> = HandleShared<'a, BroadcasterWrapper>; + + +ffi! { + fn create_broadcaster(broadcast_transaction_ptr: Ref, out: Out) -> FFIResult { + let broadcast_transaction = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); + let broadcaster = FFIBroadCaster{ broadcast_transaction_ptr: *broadcast_transaction }; + unsafe_block!("" => out.init(FFIBroadCasterHandle::alloc(broadcaster))); + FFIResult::ok() + } + + fn release_broadcaster(handle: FFIBroadCasterHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIBroadCasterHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } +} + +/// Useful for testing low-level interoperability. +#[cfg(debug_assertions)] +ffi! { + fn ffi_test_broadcaster(broadcaster_ptr: FFIBroadCasterHandle) -> FFIResult { + let broadcaster = unsafe_block!("" => broadcaster_ptr.as_ref()); + let tx: Transaction = bitcoin::consensus::deserialize(&hex::decode("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")?)?; + broadcaster.broadcast_transaction(&tx); + FFIResult::ok() + } + + fn create_broadcaster_wrapper(fn_ptr: Ref, out: Out) -> FFIResult { + // Test if passing dependent object by handle will be safe. + let broadcaster_fn = unsafe_block!("" => fn_ptr.as_ref()); + let broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcaster_fn }); + let wrapper_raw = BroadcasterWrapper{ broadcaster: broadcaster }; + unsafe_block!("" => out.init(BroadcasterWrapperHandle::alloc(wrapper_raw))); + FFIResult::ok() + } + + fn test_broadcaster_wrapper(wrapper_handle: BroadcasterWrapperHandle) -> FFIResult { + let wrapper = unsafe_block!("" => wrapper_handle.as_ref()); + let tx: Transaction = bitcoin::consensus::deserialize(&hex::decode("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")?)?; + wrapper.broadcast(&tx); + FFIResult::ok() + } + + fn release_broadcaster_wrapper(handle: BroadcasterWrapperHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => BroadcasterWrapperHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } + +} diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs new file mode 100644 index 00000000000..336f8cd09fd --- /dev/null +++ b/bindings/src/channelmanager.rs @@ -0,0 +1,276 @@ +use std::{ + convert::{TryInto}, + sync::Arc, + time::{SystemTime, UNIX_EPOCH} +}; + +use bitcoin::secp256k1; +use lightning::{ + util::{ + config::UserConfig, + events::EventsProvider + }, + ln::{ + channelmanager::{ChannelManager, PaymentHash}, + channelmonitor::SimpleManyChannelMonitor + }, + chain::{ + keysinterface::{KeysManager, InMemoryChannelKeys}, + transaction::OutPoint + }, + routing::router::Route, +}; + +use crate::{ + error::FFIResult, + handle::{Out, Ref, HandleShared}, + adaptors::{ + primitives::{ + PublicKey, + FFISha256dHash, + FFIRoute, + FFIEvents, + FFISecret, + FFIOutPoint + }, + *, + } +}; +use lightning::ln::channelmanager::{PaymentSecret, PaymentPreimage}; + +pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; +pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; +pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; + +fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let payment_hash: &FFISha256dHash = unsafe_block!("" => payment_hash.as_ref()); + let payment_hash: PaymentHash = payment_hash.clone().try_into()?; + Ok(chan_man.fail_htlc_backwards(&payment_hash, payment_secret)) +} + +fn create_channel_inner(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Option, handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let their_network_key = their_network_key.try_into()?; + chan_man.create_channel(their_network_key, channel_value_satoshis, push_msat, user_id, override_config)?; + FFIResult::ok() +} + +fn claim_funds_inner(payment_preimage: Ref<[u8; 32]>, payment_secret: Option, expected_amount: u64, handle: FFIArcChannelManagerHandle) -> bool { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let payment_preimage: &[u8;32] = unsafe_block!("" => payment_preimage.as_ref()); + let payment_preimage = PaymentPreimage(payment_preimage.clone()); + + chan_man.claim_funds(payment_preimage, &payment_secret, expected_amount) +} + + +pub(crate) fn construct_channel_manager( + seed_ptr: Ref, + seed_len: usize, + ffi_network: FFINetwork, + cfg: Ref, + + install_watch_tx: &chain_watch_interface_fn::InstallWatchTxPtr, + install_watch_outpoint: &chain_watch_interface_fn::InstallWatchOutpointPtr, + watch_all_txn: &chain_watch_interface_fn::WatchAllTxnPtr, + get_chain_utxo: &chain_watch_interface_fn::GetChainUtxoPtr, + + broadcast_transaction_ptr: Ref, + log_ref: &ffilogger_fn::LogExtern, + get_est_sat_per_1000_weight_ptr: Ref, + cur_block_height: usize, + +) -> Result { + let seed_slice = unsafe_block!("The seed lives as long as `create_channel_manager` and the length is within the seed" => seed_ptr.as_bytes(seed_len)); + let network = ffi_network.to_network(); + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let mut seed: [u8; 32] = Default::default(); + seed.copy_from_slice(seed_slice); + + let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); + + let chain_watch_interface_arc = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx, + *install_watch_outpoint, + *watch_all_txn, + *get_chain_utxo, + network, + logger_arc.clone() + )); + + let broadcast_ref = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); + let broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcast_ref }); + + let fee_est_fn_ref = unsafe_block!("" => get_est_sat_per_1000_weight_ptr.as_ref()); + let fee_est = FFIFeeEstimator{ get_est_sat_per_1000_weight_ptr: *fee_est_fn_ref }; + + let keyman = Arc::new(KeysManager::new(&seed, network, now.as_secs(), now.subsec_nanos())); + let cfg = unsafe_block!("" => cfg.as_ref()); + + let monitor = + Arc::new(FFIManyChannelMonitor::new(chain_watch_interface_arc, broadcaster.clone(), logger_arc.clone(), Arc::new(fee_est.clone()))); + + ChannelManager::new( + network, + Arc::new(fee_est), + monitor, + broadcaster, + logger_arc, + keyman, + cfg.clone(), + cur_block_height + ) +} + +ffi! { + + fn create_channel_manager( + seed_ptr: Ref, + seed_len: usize, + network_ref: Ref, + cfg: Ref, + + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + + broadcast_transaction_ptr: Ref, + log_ptr: Ref, + get_est_sat_per_1000_weight_ptr: Ref, + cur_block_height: usize, + chan_man: Out) -> FFIResult { + if (seed_len != 32) { + return FFIResult::invalid_data_length(); + } + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + let network = unsafe_block!("" => *network_ref.as_ref()); + let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let chan_man_raw = + construct_channel_manager( + seed_ptr, + seed_len, + network, + cfg, + install_watch_tx_ref, + install_watch_outpoint_ref, + watch_all_txn_ref, + get_chain_utxo_ref, + + broadcast_transaction_ptr, + log_ref, + get_est_sat_per_1000_weight_ptr, + cur_block_height, + )?; + unsafe_block!("We know chan_man is not null by wrapper macro. And we know `Out` is writable" => chan_man.init(HandleShared::alloc(chan_man_raw))); + FFIResult::ok() + } + + fn create_channel(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, handle: FFIArcChannelManagerHandle) -> FFIResult { + create_channel_inner(their_network_key, channel_value_satoshis, push_msat, user_id, None, handle) + } + + fn create_channel_with_custom_config(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { + let override_config = unsafe_block!("We know it points to valid UserConfig" => override_config.as_ref()); + create_channel_inner(their_network_key, channel_value_satoshis, push_msat, user_id, Some(override_config.clone()), handle) + } + + fn close_channel(channel_id: Ref<[u8; 32]>, handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let channel_id: &[u8; 32] = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); + chan_man.close_channel(channel_id)?; + FFIResult::ok() + } + + fn force_close_channel(channel_id: Ref<[u8; 32]>, handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let channel_id: &[u8; 32] = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); + chan_man.force_close_channel(channel_id); + FFIResult::ok() + } + + fn force_close_all_channels(handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + chan_man.force_close_all_channels(); + FFIResult::ok() + } + + fn process_pending_htlc_forwards(handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + chan_man.process_pending_htlc_forwards(); + FFIResult::ok() + } + + fn timer_chan_freshness_every_min(handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + chan_man.timer_chan_freshness_every_min(); + FFIResult::ok() + } + + fn fail_htlc_backwards(payment_hash: Ref, payment_secret: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let payment_secret: &FFISecret = unsafe_block!("it points to valid data and lives as long as the function call" => payment_secret.as_ref()); + let payment_secret: Option = Some(payment_secret.clone().try_into()?); + let r = fail_htlc_backwards_inner(payment_hash, &payment_secret, handle)?; + unsafe_block!("We know out parameter is writable" => result.init(r.into())); + FFIResult::ok() + } + + fn fail_htlc_backwards_without_secret(payment_hash: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let r = fail_htlc_backwards_inner(payment_hash, &None, handle)?; + unsafe_block!("We know out parameter is writable" => result.init(r.into())); + FFIResult::ok() + } + + fn send_payment(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref, payment_secret_ref: Ref) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let route_ffi: &FFIRoute = unsafe_block!("We know it points to valid route data" => route_ref.as_ref()); + let payment_hash_ffi: &FFISha256dHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()); + let payment_hash: PaymentHash = payment_hash_ffi.clone().try_into()?; + let payment_secret: &FFISecret = unsafe_block!("We know it points to valid payment_secret data or empty 32 bytes" => payment_secret_ref.as_ref()); + let maybe_secret = if payment_secret.as_ref().is_empty() { None } else { Some(payment_secret.clone().try_into()?) }; + let route: Route = route_ffi.clone().try_into()?; + chan_man.send_payment(&route, payment_hash, &maybe_secret)?; + FFIResult::ok() + } + + fn funding_transaction_generated(temporary_channel_id: Ref<[u8;32]>, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to a valid channel_manager" => handle.as_ref()); + let temporary_channel_id: &[u8; 32] = unsafe_block!("data lives as long as this function and it points to a valid value" => temporary_channel_id.as_ref()); + let funding_txo: OutPoint = funding_txo.try_into()?; + chan_man.funding_transaction_generated(temporary_channel_id, funding_txo); + FFIResult::ok() + } + + fn claim_funds(payment_preimage: Ref<[u8; 32]>, payment_secret: Ref<[u8; 32]>, expected_amount: u64, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let payment_secret: &[u8;32] = unsafe_block!("" => payment_secret.as_ref()); + let payment_secret: Option = Some(PaymentSecret(payment_secret.clone())); + let r = claim_funds_inner(payment_preimage, payment_secret, expected_amount, handle); + unsafe_block!("We know out parameter is writable" => result.init(r.into())); + FFIResult::ok() + } + + fn update_fee(channel_id: Ref<[u8; 32]>, feerate_per_kw: u64, handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let channel_id: &[u8;32] = unsafe_block!("" => channel_id.as_ref()); + chan_man.update_fee(channel_id.clone(), feerate_per_kw)?; + FFIResult::ok() + } + + fn get_and_clear_pending_events(handle: FFIArcChannelManagerHandle, events: Out) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let e = chan_man.get_and_clear_pending_events().try_into()?; + unsafe_block!("We know out parameter is writable" => events.init(e)); + FFIResult::ok() + } + + fn release_ffi_channel_manager(handle: FFIArcChannelManagerHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcChannelManagerHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } +} + diff --git a/bindings/src/error.rs b/bindings/src/error.rs new file mode 100644 index 00000000000..8ad3689286d --- /dev/null +++ b/bindings/src/error.rs @@ -0,0 +1,379 @@ +/// Error handling based on refs: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html + +use std::error::Error; +use std::cell::RefCell; +use std::ops::Try; +use std::fmt::Write; +use std::panic::{catch_unwind, UnwindSafe}; +use std::any::Any; +use std::sync::atomic::{AtomicU32, Ordering}; + +use lightning::util::errors::APIError; +use crate::utils::option_extensions::OptionMutExt; + +static LAST_ERR_ID: AtomicU32 = AtomicU32::new(0); + +fn next_err_id() -> u32 { + LAST_ERR_ID.fetch_add(1, Ordering::SeqCst) +} + +thread_local! { + static LAST_RESULT: RefCell> = RefCell::new(None); +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FFIResult { + kind: Kind, + id: u32, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Kind { + Ok, + EmptyPointerProvided, + InvalidDataLength, + /// Indicates we have passed byte array from wrapper which is not rust-lightning compatible. + DeserializationFailure, + /// Return this when buffer for allocating error message is too small. + /// When this is the last error, the caller should try to increase the buffer size and call the function again. + BufferTooSmall, + InternalError, +} + +impl From for FFIResult + where E: + Error +{ + fn from(e: E) -> Self { + FFIResult::internal_error().context(e) + } +} + +/// Allow casting standard `Result` to FFIResult +/// In this way, we assure +impl Try for FFIResult { + type Ok = Self; + type Error = Self; + + fn into_result(self) -> Result<::Ok, ::Error> { + match self.kind { + Kind::Ok => Ok(self), + _ => Err(self) + } + } + + fn from_error(result: Self::Error) -> Self { + if result.as_err().is_none() { + panic!(format!("attempted to return success code `{:?}` as an error", result)); + } + result + } + + fn from_ok(result: ::Ok) -> Self { + if result.as_err().is_some() { + panic!(format!("attempted to return error code `{:?}` as success", result)); + } + result + } +} + +fn format_error(err: &dyn Error) -> String { + let mut error_string = String::new(); + let mut source = err.source(); + writeln!(error_string, "Error: {}", err); + while let Some(parent_err) = source { + let _ = writeln!(error_string, "Caused by: {}.", parent_err); + source = parent_err.source(); + } + + if let Some(backtrace) = err.backtrace() { + let _ = writeln!(error_string, "backtrace: {}", backtrace); + } + error_string +} + +fn extract_panic(err: &Box) -> Option { + if let Some(e) = err.downcast_ref::() { + Some(e.to_string()) + } else if let Some(e) = err.downcast_ref::<&'static str>() { + Some((*e).to_owned()) + } else { + None + } +} + +impl FFIResult { + /// FFIResult is not a tagged union, so when an error has occured in rust-lightning side, + /// there is no way to return a type safe information to the caller side. + /// Instead, we set an error message to `LAST_RESULT` so that caller can see the error message. + pub(super) fn context(self, e: impl Error) -> Self { + assert!( + self.as_err().is_some(), + "context can only be attached to errors" + ); + let err = Some(format_error(&e)); + LAST_RESULT.with(|last_result| { + *last_result.borrow_mut() = Some(LastResult { value: self, err, }); + }); + self + } + + pub(super) fn ok() -> Self { + FFIResult { kind: Kind::Ok, id: 0 } + } + + pub fn is_ok(&self) -> bool { + self.kind == Kind::Ok + } + + pub(super) fn empty_pointer_provided() -> Self { + FFIResult { kind: Kind::EmptyPointerProvided, id: next_err_id() } + } + + pub fn is_empty_pointer_provided(&self) -> bool { + self.kind == Kind::EmptyPointerProvided + } + + pub(super) fn deserialization_failure() -> Self { + FFIResult { kind: Kind::DeserializationFailure, id: next_err_id() } + } + + pub fn is_deserialization_failure(&self) -> bool { + self.kind == Kind::DeserializationFailure + } + + pub(super) fn buffer_too_small() -> Self { + FFIResult { kind: Kind::BufferTooSmall, id: next_err_id() } + } + + pub fn is_buffer_too_small(&self) -> bool { + self.kind == Kind::BufferTooSmall + } + + pub(super) fn invalid_data_length() -> Self { + FFIResult { kind: Kind::InvalidDataLength, id: next_err_id() } + } + + pub fn is_invalid_data_length(&self) -> bool { + self.kind == Kind::InvalidDataLength + } + + pub(super) fn internal_error() -> Self { + FFIResult { kind: Kind::InternalError, id: next_err_id() } + } + + pub fn is_internal_error(&self) -> bool { + self.kind == Kind::InternalError + } + + /// Attempt to get a human-readable error message for a result. + /// If the result is successful then this method returns `None`. + pub fn as_err(&self) -> Option<&'static str> { + match self.kind { + Kind::Ok => None, + Kind::EmptyPointerProvided => Some("a required pointer argument was null"), + Kind::InvalidDataLength => Some("provided data buffer has invalid length"), + Kind::DeserializationFailure => Some("Failed to deserialize byte array passed to ffi"), + Kind::InternalError => Some("An internal error occured"), + Kind::BufferTooSmall => Some("buffer was too small"), + } + } + + + /// Call a function that returns a `FFIResult`, setting the thread local last result. + /// This method will also catch panics, so the function to call must be unwind safe. + pub(super) fn catch(f: impl FnOnce() -> Self + UnwindSafe) -> Self { + LAST_RESULT.with(|last_result| { + { + *last_result.borrow_mut() = None; + } + + match catch_unwind(f) { + // when no panic + Ok(ffi_result) => { + let extract_err = || ffi_result.as_err().map(Into::into); + last_result + .borrow_mut() + .map_mut(|last_result| { + last_result.value = ffi_result; + last_result.err.or_else_mut(extract_err); + }) + .get_or_insert_with(|| LastResult { + value: ffi_result, + err: extract_err(), + }) + .value + }, + // when panic + Err(e) => { + let extract_panic = || extract_panic(&e).map(|s| format!("Internal panic with {}", s)); + last_result + .borrow_mut() + .map_mut(|last_result| { + last_result.err.or_else_mut(extract_panic); + }) + .get_or_insert_with(|| LastResult { + value: FFIResult::internal_error(), + err: extract_panic(), + }) + .value + }, + } + }) + } + + /// Access the last result returned on the calling thread. + /// First argument for the closure is the last FFI result. + /// Second is its error message (if any). + pub(super) fn with_last_result(f: impl FnOnce(Option<(FFIResult, Option<&str>)>) -> R) -> R { + LAST_RESULT.with(|last_result| { + let last_result = last_result.borrow(); + let last_result_and_its_err_msg = last_result.as_ref().map(|r| { + let v = r.value; + let msg = v.as_err().and_then(|_| r.err.as_ref().map(|msg| msg.as_ref())); + (v, msg) + }); + f(last_result_and_its_err_msg) + }) + } +} + +#[derive(Debug)] +struct LastResult { + value: FFIResult, + err: Option, +} + +#[cfg(test)] +mod tests { + use std::{ + mem, + fmt, + thread, + }; + use std::fmt::Display; + use super::*; + + use crate::test_utils::*; + + #[derive(Debug)] + enum TestInnerError { + Variant + } + + impl fmt::Display for TestInnerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&format!("{:?}", self)) + } + } + impl Error for TestInnerError {} + #[derive(Debug)] + enum TestError { + Variant(TestInnerError) + } + impl Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&format!("{:?}", self)) + } + } + impl Error for TestError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match *self { + TestError::Variant(ref inner) => Some(inner), + } + } + } + + #[test] + fn ffi_result_is_u64_sized() { + assert_eq!(mem::size_of::(), mem::size_of::()); + } + + #[test] + fn ffi_result_err_is_none_if_kind_is_ok() { + thread::spawn(|| { + // Set the last result for this thread + // Normally you'd return from here + // But we're just going to leave the error + let _ = FFIResult::catch(|| { + FFIResult::internal_error().context(TestInnerError::Variant); + FFIResult::ok() + }); + + // We should have an error stored + LAST_RESULT.with(|last_result|{ + let lr = last_result.borrow(); + assert!(lr.as_ref().unwrap().err.is_some()); + }); + + // We don't surface that error in with_last_result + FFIResult::with_last_result(|last_result| { + let (_, err) = last_result.unwrap(); + + assert!(err.is_none()); + }); + }) + .join() + .unwrap() + } + + #[test] + fn last_result_check_ok() { + let result = FFIResult::catch(|| FFIResult::ok()); + + assert_eq!(Kind::Ok, result.kind); + + FFIResult::with_last_result(|last_result| { + assert_match!(Some((result, err)) = last_result => { + assert_eq!(Kind::Ok, result.kind); + + assert!(err.is_none()); + }); + }); + } + + #[test] + fn last_result_catch_err_carrier() { + let result = FFIResult::catch(|| { + Err(TestError::Variant(TestInnerError::Variant))?; + unreachable!() + }); + + assert_eq!(Kind::InternalError, result.kind); + + FFIResult::with_last_result(|last_result| { + assert_match!(Some((result, err)) = last_result => { + assert_eq!(Kind::InternalError, result.kind); + assert!(err.is_some()); + }); + }); + } + + #[test] + fn last_result_catch_err_return() { + let result = FFIResult::catch(|| FFIResult::empty_pointer_provided()); + assert_eq!(Kind::EmptyPointerProvided, result.kind); + FFIResult::with_last_result(|last_result| { + assert_match!(Some((result, err)) = last_result => { + assert_eq!(Kind::EmptyPointerProvided, result.kind); + assert!(err.is_some()); + }); + }); + } + + #[test] + fn last_result_catch_panic() { + let result = FFIResult::catch(|| panic!("something didn't work")); + assert_eq!(Kind::InternalError, result.kind); + + FFIResult::with_last_result(|last_result| { + assert_match!(Some((result, err)) = last_result => { + assert_eq!(Kind::InternalError, result.kind); + + assert!(err.is_some()); + }); + }); + } +} \ No newline at end of file diff --git a/bindings/src/fee_estimator.rs b/bindings/src/fee_estimator.rs new file mode 100644 index 00000000000..b1a56f5d3a1 --- /dev/null +++ b/bindings/src/fee_estimator.rs @@ -0,0 +1,22 @@ +use crate::adaptors::*; +use crate::handle::{Ref, Out, HandleShared}; +use crate::error::FFIResult; + +pub type FeeEstimatorHandle<'a> = HandleShared<'a, FFIFeeEstimator>; + +ffi! { + fn create_fee_estimator(fn_ref: Ref, out: Out) -> FFIResult { + let func = unsafe_block!("" => *fn_ref.as_ref()); + let fee_estimator = FFIFeeEstimator {get_est_sat_per_1000_weight_ptr: func}; + unsafe_block!("We know fee_estimator handle is not null by wrapper macro. And we know `Out` is writable" => + out.init(FeeEstimatorHandle::alloc(fee_estimator)) + ); + FFIResult::ok() + } + + fn release_fee_estimator(handle: FeeEstimatorHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FeeEstimatorHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } +} \ No newline at end of file diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs new file mode 100644 index 00000000000..00c70eac5d6 --- /dev/null +++ b/bindings/src/ffi_test_utils.rs @@ -0,0 +1,17 @@ +use std::sync::Arc; +use crate::error::FFIResult; + +/// These tests should be used for asserting that the wrapper code can see the expected +/// error messages when it fails (or succeeds). +ffi! { + fn ffi_test_error() -> FFIResult { + use std::io; + + FFIResult::internal_error().context(io::Error::new(io::ErrorKind::Other, "A test error.")) + } + + fn ffi_test_ok() -> FFIResult { + FFIResult::ok() + } +} + diff --git a/bindings/src/handle/mod.rs b/bindings/src/handle/mod.rs new file mode 100644 index 00000000000..17ce1af12af --- /dev/null +++ b/bindings/src/handle/mod.rs @@ -0,0 +1,230 @@ +use std::marker::PhantomData; +use std::panic::{RefUnwindSafe, UnwindSafe}; +use std::ptr; +use std::slice; +use std::sync::Arc; + +pub(crate) mod thread_bound; + +use self::thread_bound::ThreadBound; + +use super::is_null::IsNull; + +/* +The handles here are wrappers for `&T` and an exclusive `&mut T`. + +They protect from data races, but don't protect from use-after-free bugs. +The caller is expected to maintain that invariant, which in .NET can be achieved using +`SafeHandle`s. +*/ + +/** + * A shared handle that can be accessed concurrently by multiple threads. + * + * The interior value can be treated like `&T`. + * + * Consumers must ensure a handle is not used again after it has been deallocated. + */ +#[repr(transparent)] +pub struct HandleShared<'a, T: ?Sized>(*const T, PhantomData<&'a T>); + +unsafe_impl!("The handle is semantically `&T`" => impl<'a, T: ?Sized> Send for HandleShared<'a, T> where &'a T: Send {}); +unsafe_impl!("The handle is semantically `&T`" => impl<'a, T: ?Sized> Sync for HandleShared<'a, T> where &'a T: Sync {}); + +impl<'a, T: ?Sized + RefUnwindSafe> UnwindSafe for HandleShared<'a, T> {} +impl<'a, T> HandleShared<'a, T> +where + HandleShared<'a, T>: Send + Sync, +{ + pub(super) fn alloc(value: T) -> Self + where + T: 'static, + { + let v = Box::new(value); + HandleShared(Box::into_raw(v), PhantomData) + } + + pub(super) fn as_ref(&self) -> &T { + unsafe_block!("We own, the interioer value" => { &*self.0 }) + } + + unsafe_fn!("The pointer must be nonnull" => + pub fn as_arc(&self) -> Arc { + Arc::from_raw(self.0) + } + ); + + unsafe_fn!("There are no other live references and the handle won't be used again" => + pub(super) fn dealloc(handle: Self, f: impl FnOnce(T) -> R) -> R { + let v = Box::from_raw(handle.0 as *mut T); + f(*v) + } + ); +} + +/** + * A non-shared handle that cannot be accessed by multiple threads. + * + * The interior value can be treated like `&mut T`. + * + * The handle is bound to the thread that it was created on to ensure + * there's no possibility for data races. + * + * Note that, if this binary calls into a wrapper code (in case of C#, that is reverse PInvoke to the dll) + * then it's possible to mutably alias the handle from the same thread if the reverse + * call can re-enter the FFI using the same handle. This is technically undefined behavior. + * + * The handle _can_ be deallocated from a different thread than the one that created it. + * + * Consumers must ensure a handle is not used again after it has been deallocated. + */ +#[repr(transparent)] +pub struct HandleExclusive<'a, T: ?Sized>(*mut ThreadBound, PhantomData<&'a mut T>); + +unsafe_impl!("The handle is semantically `&mut T`" => impl<'a, T: ?Sized> Send for HandleExclusive<'a, T> where &'a mut ThreadBound: Send {}); +unsafe_impl!("The handle uses `ThreadBound` for synchronization" => impl<'a, T: ?Sized> Sync for HandleExclusive<'a, T> where &'a mut ThreadBound: Sync {}); +impl<'a, T: ?Sized + RefUnwindSafe> UnwindSafe for HandleExclusive<'a, T> {} +impl<'a, T> HandleExclusive<'a, T> +where + HandleExclusive<'a, T> : Send + Sync, +{ + pub(super) fn alloc(value: T) -> Self + where + T: 'static + { + let v = Box::new(ThreadBound::new(value)); + HandleExclusive(Box::into_raw(v), PhantomData) + } + pub(super) fn as_mut(&mut self) -> &mut T { + unsafe_block!("We own the interior value" => { &mut *(*self.0).get_raw() }) + } + + unsafe_fn!("There are no other live references and the handle won't be used again" => + pub(super) fn dealloc(handle: Self, f: impl FnOnce(T) -> R) -> R + where + T: Send, + { + let v = Box::from_raw(handle.0); + f(v.into_inner()) + } + ); +} + +/** + * An initialized parameter passed by shared reference. + */ +#[repr(transparent)] +pub struct Ref<'a, T: ?Sized>(*const T, PhantomData<&'a T>); + +impl<'a, T: ?Sized + RefUnwindSafe> UnwindSafe for Ref<'a, T> {} +unsafe_impl!("The handle is semantically `&mut T`" => impl<'a, T: ?Sized> Send for Ref<'a, T> where &'a T: Send{}); + +unsafe_impl!("The handle uses `ThreadBound` for synchronization" => impl<'a, T: ?Sized> Sync for Ref<'a, T> where &'a T: Sync {}); + +impl<'a, T: ?Sized> Ref<'a, T> { + unsafe_fn!("The pointer must be nonnull and will remain valid" => + pub fn as_ref(&self) -> &T { + &*self.0 + } + ); + + unsafe_fn!("The pointer must be nonnull" => + pub fn as_arc(&self) -> Arc { + Arc::from_raw(self.0) + } + ); +} + +impl<'a> Ref<'a, u8> { + unsafe_fn!("The pointer must be nonnull, the length is correct, and will remain valid" => + pub fn as_bytes(&self, len: usize) -> &[u8] { + slice::from_raw_parts(self.0, len) + } + ); +} + +/** +An initialized parameter passed by exclusive reference. +*/ +#[repr(transparent)] +pub struct RefMut<'a, T: ?Sized>(*mut T, PhantomData<&'a mut T>); + +impl<'a, T: ?Sized + RefUnwindSafe> UnwindSafe for RefMut<'a, T> {} + +unsafe_impl!("The handle is semantically `&mut T`" => impl<'a, T: ?Sized> Send for RefMut<'a, T> where &'a mut T: Send {}); +unsafe_impl!("The handle uses `ThreadBound` for synchronization" => impl<'a, T: ?Sized> Sync for RefMut<'a, T> where &'a mut T: Sync {}); + +impl<'a, T: ?Sized> RefMut<'a, T> { + unsafe_fn!("The pointer must be nonnull and will remain valid" => pub fn as_mut(&mut self) -> &mut T { + &mut *self.0 + }); +} + +impl<'a> RefMut<'a, u8> { + unsafe_fn!("The pointer must be nonnull, the length is correct, and will remain valid" => pub fn as_bytes_mut(&mut self, len: usize) -> &mut [u8] { + slice::from_raw_parts_mut(self.0, len) + }); +} + +/** +An uninitialized, assignable out parameter. +*/ +#[repr(transparent)] +pub struct Out<'a, T: ?Sized>(*mut T, PhantomData<&'a mut T>); + +impl<'a, T: ?Sized + RefUnwindSafe> UnwindSafe for Out<'a, T> {} + +unsafe_impl!("The handle is semantically `&mut T`" => impl<'a, T: ?Sized> Send for Out<'a, T> where &'a mut T: Send {}); +unsafe_impl!("The handle uses `ThreadBound` for synchronization" => impl<'a, T: ?Sized> Sync for Out<'a, T> where &'a mut T: Sync {}); + +impl<'a, T> Out<'a, T> { + unsafe_fn!("The pointer must be nonnull and valid for writes" => + pub fn init(&mut self, value: T) { + ptr::write(self.0, value); + } + ); +} + +impl<'a> Out<'a, u8> { + unsafe_fn!("The pointer must be nonnull, not overlap the slice, must be valid for the length of the slice, and valid for writes" => + pub fn init_bytes(&mut self, value: &[u8]) { + ptr::copy_nonoverlapping(value.as_ptr(), self.0, value.len()); + } + ); + + unsafe_fn!("The slice must never be read from and must be valid for the length of the slice" => + pub fn as_uninit_bytes_mut(&mut self, len: usize) -> &mut [u8] { + slice::from_raw_parts_mut(self.0, len) + } + ); +} + +impl<'a, T: ?Sized> IsNull for HandleExclusive<'a, T> { + fn is_null(&self) -> bool { + self.0.is_null() + } +} + +impl<'a, T: ?Sized + Sync> IsNull for HandleShared<'a, T> { + fn is_null(&self) -> bool { + self.0.is_null() + } +} + +impl<'a, T: ?Sized> IsNull for Ref<'a, T> { + fn is_null(&self) -> bool { + self.0.is_null() + } +} + +impl<'a, T: ?Sized + Sync> IsNull for RefMut<'a, T> { + fn is_null(&self) -> bool { + self.0.is_null() + } +} + +impl<'a, T: ?Sized> IsNull for Out<'a, T> { + fn is_null(&self) -> bool { + self.0.is_null() + } +} \ No newline at end of file diff --git a/bindings/src/handle/thread_bound.rs b/bindings/src/handle/thread_bound.rs new file mode 100644 index 00000000000..00d5864c747 --- /dev/null +++ b/bindings/src/handle/thread_bound.rs @@ -0,0 +1,343 @@ +use std::mem; +use std::panic::{UnwindSafe, RefUnwindSafe}; +use std::sync::Mutex; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::marker::PhantomData; +use std::cell::UnsafeCell; +use std::collections::HashMap; +use std::ops::{Deref, DerefMut}; + +use crate::lazy_static; + +type ThreadId = usize; +type ValueId = usize; + +static GLOBAL_ID: AtomicUsize = AtomicUsize::new(0); +thread_local!(static THREAD_ID: usize = next_thread_id()); + +fn next_thread_id() -> usize { + GLOBAL_ID.fetch_add(1, Ordering::SeqCst) +} + +fn get_thread_id() -> usize { + THREAD_ID.with(|x| *x) +} + +thread_local!(static VALUE_ID: UnsafeCell = UnsafeCell::new(0)); + +fn next_value_id() -> usize { + VALUE_ID.with(|x| { + unsafe_block!("The value never has overlapping mutable aliases" => { + let x = x.get(); + let next = *x; + *x += 1; + + next + }) + }) +} + +struct Registry(HashMap, Box)>)>); + +impl Drop for Registry { + fn drop(&mut self) { + // Remove this thread from the garbage list + let thread_id = get_thread_id(); + { + let mut garbage = GARBAGE.lock().expect("failed to lock garbage queue"); + let _ = garbage.remove(&thread_id); + } + + // Drop any remaining values in the registry + for (_, value) in self.0.iter() { + (value.1)(&value.0); + } + } +} + +thread_local!(static REGISTRY: UnsafeCell = UnsafeCell::new(Registry(Default::default()))); + +lazy_static! { + static ref GARBAGE: Mutex>> = Mutex::new(HashMap::new()); +} + +/** +A value that's bound to the thread it's created on. + +In pure rust, `Send` and `Sync` are good enough to protect against data races between threads. +But since this is a compile time contract, nothing stops from foreign caller to copy references to multiple +threads. +To prevent this, ThreadBound checks that it has not been sent across threads at runtime, and panics if it is. +It is pretty much the same to [fragile](https://docs.rs/fragile/1.0.0/fragile/) + +This is used for single-threaded handle for an FFI object, i.e. `HandleExclusive`. +*/ +pub struct ThreadBound { + thread_id: ThreadId, + inner: UnsafeCell, +} + +impl ThreadBound { + pub(super) fn new(inner: T) -> Self { + ThreadBound { + thread_id: get_thread_id(), + inner: UnsafeCell::new(inner), + } + } +} + +/* +We don't need to check the thread id when moving out of the inner +value so long as the inner value is itself `Send`. This allows +the .NET runtime to potentially finalize a value on another thread. +*/ +impl ThreadBound { + pub(super) fn into_inner(self) -> T { + self.inner.into_inner() + } +} + +impl ThreadBound { + fn check(&self) { + let current = get_thread_id(); + + if self.thread_id != current { + panic!("attempted to access resource from a different thread"); + } + } + + pub(super) fn get_raw(&self) -> *mut T { + self.check(); + self.inner.get() + } +} + +impl UnwindSafe for ThreadBound {} +impl RefUnwindSafe for ThreadBound {} + +unsafe_impl!("The inner value is safe to send to another thread" => impl Send for ThreadBound {}); +unsafe_impl!("The inner value can't actually be accessed concurrently" => impl Sync for ThreadBound {}); + +/** +A thread-bound value that can be safely dropped from a different thread. + +The value is allocated in thread-local storage. When dropping, if the value +is being accessed from a different thread it will be put onto a garbage queue +for cleanup instead of being moved onto the current thread. + +NOTE: We require `T: 'static` because the value may live as long +as the current thread +*/ +pub(crate) struct DeferredCleanup { + thread_id: ThreadId, + value_id: ValueId, + _m: PhantomData<*mut T>, +} + +impl Drop for DeferredCleanup { + fn drop(&mut self) { + if mem::needs_drop::() { + if self.is_valid() { + unsafe_block!("The value exists on the current thread" => { + self.into_inner_unchecked(); + }); + } else { + let mut garbage = GARBAGE.lock().expect("failed to lock garbage queue"); + let garbage = garbage.entry(self.thread_id).or_insert_with(|| Vec::new()); + + garbage.push(self.value_id); + } + } + } +} + +impl DeferredCleanup { + pub fn new(value: T) -> Self { + let thread_id = get_thread_id(); + let value_id = next_value_id(); + + // Check for any garbage that needs cleaning up + // If we can't acquire a lock to the global queue + // then we just continue on. + let garbage = { + GARBAGE + .try_lock() + .ok() + .and_then(|mut garbage| garbage.remove(&thread_id)) + }; + + if let Some(garbage) = garbage { + let remove = |value_id: ValueId| { + REGISTRY.with(|registry| { + unsafe_block!("The value never has overlapping mutable aliases" => { + let registry = &mut (*registry.get()).0; + registry.remove(&value_id) + }) + }) + }; + + for value_id in garbage { + if let Some((data, drop)) = remove(value_id) { + drop(&data); + } + } + } + + REGISTRY.with(|registry| { + unsafe_block!("The value never has overlapping mutable aliases" => { + (*registry.get()).0.insert( + value_id, + ( + UnsafeCell::new(Box::into_raw(Box::new(value)) as *mut _), + Box::new(|cell| { + let b: Box = Box::from_raw(*(cell.get() as *mut *mut T)); + mem::drop(b); + }), + ), + ); + }) + }); + + DeferredCleanup { + thread_id, + value_id, + _m: PhantomData, + } + } + + fn with_value>) -> R, R>(&self, f: F) -> R { + let current_thread = get_thread_id(); + + if current_thread != self.thread_id { + panic!("attempted to access resource from a different thread"); + } + + REGISTRY.with(|registry| { + unsafe_block!("There are no active mutable references" => { + let registry = &(*registry.get()).0; + + if let Some(item) = registry.get(&self.value_id) { + f(mem::transmute(&item.0)) + } else { + panic!("attempted to access resource from a different thread"); + } + }) + }) + } + + fn is_valid(&self) -> bool { + let current_thread = get_thread_id(); + let has_value = unsafe_block!("There are no active mutable references" => { + REGISTRY + .try_with(|registry| (*registry.get()).0.contains_key(&self.value_id)) + .unwrap_or(false) + }); + + self.thread_id == current_thread && has_value + } + + unsafe_fn!("The value must originate on the current thread" => fn into_inner_unchecked(&mut self) -> T { + let ptr = REGISTRY + .with(|registry| (*registry.get()).0.remove(&self.value_id)) + .unwrap() + .0 + .into_inner(); + let value = Box::from_raw(ptr as *mut T); + *value + }); +} + +unsafe_impl!( + "The inner value is pinned to the current thread and isn't actually sent. \ + Dropping from another thread will signal cleanup on the original" => + impl Send for DeferredCleanup {}); + +impl Deref for DeferredCleanup { + type Target = T; + + fn deref(&self) -> &T { + self.with_value(|value| unsafe_block!("The borrow of self protects the inner value" => { &*value.get() })) + } +} + +impl DerefMut for DeferredCleanup { + fn deref_mut(&mut self) -> &mut T { + self.with_value(|value| unsafe_block!("The borrow of self protects the inner value" => { &mut *value.get() })) + } +} + +#[cfg(test)] +mod tests { + use std::{ + sync::{ + Arc, + Mutex, + }, + thread, + }; + + use super::*; + + struct NeedsDrop(Arc>); + + impl Drop for NeedsDrop { + fn drop(&mut self) { + *self.0.lock().expect("failed to lock") = true; + } + } + + #[test] + fn garbage_queue_is_cleaned_when_thread_exits() { + let dropped = Arc::new(Mutex::new(false)); + + let (pre_clean_len, thread_id) = { + let dropped = dropped.clone(); + + thread::spawn(move || { + let dropped = dropped.clone(); + + let deferred = DeferredCleanup::new(NeedsDrop(dropped.clone())); + let thread_id = get_thread_id(); + + // Move the deferred into another thread + // This will signal that it needs to be dropped + thread::spawn(move || { + drop(deferred); + }) + .join() + .expect("failed to run inner thread"); + + // Ensure the value hasn't been dropped yet + { + assert!(!*dropped.lock().expect("failed to lock")); + } + + // Ensure we have garbage in the queue + let pre_clean_len = { + let garbage = GARBAGE.lock().expect("failed to lock garbage"); + garbage.get(&thread_id).map(|queue| queue.len()) + }; + + (pre_clean_len, thread_id) + }) + .join() + .expect("failed to run thread") + }; + + // Ensure the garbage has been cleaned + { + assert!(*dropped.lock().expect("failed to lock")); + } + + // Ensure we had some garbage to clean + assert_eq!(Some(1), pre_clean_len); + + // Ensure the queue for the thread is now gone + let len = { + let garbage = GARBAGE.lock().expect("failed to lock garbage"); + garbage.get(&thread_id).map(|queue| queue.len()) + }; + + assert_eq!(None, len); + } +} \ No newline at end of file diff --git a/bindings/src/is_null.rs b/bindings/src/is_null.rs new file mode 100644 index 00000000000..48adf3a2f12 --- /dev/null +++ b/bindings/src/is_null.rs @@ -0,0 +1,46 @@ +use std::fmt; + +#[derive(Debug)] +pub(super) struct Error { + pub(super) arg: &'static str, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&format!("argument `{}` was null", self.arg)) + } +} +impl std::error::Error for Error {} + +/** +Whether or not a value passed across an FFI boundary is null. +*/ +pub(super) trait IsNull { + fn is_null(&self) -> bool; +} + +macro_rules! never_null { + ($($t:ty),*) => { + $( + impl IsNull for $t { + fn is_null(&self) -> bool { + false + } + } + )* + } +} + +impl IsNull for *const T { + fn is_null(&self) -> bool { + <*const T>::is_null(*self) + } +} + +impl IsNull for *mut T { + fn is_null(&self) -> bool { + <*mut T>::is_null(*self) + } +} + +never_null!(usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); diff --git a/bindings/src/lazy_static/inline_lazy.rs b/bindings/src/lazy_static/inline_lazy.rs new file mode 100644 index 00000000000..c4cc07ff7f3 --- /dev/null +++ b/bindings/src/lazy_static/inline_lazy.rs @@ -0,0 +1,52 @@ +// Copyright 2016 lazy-static.rs Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use ::std::prelude::v1::*; +use ::std::cell::Cell; +use ::std::hint::unreachable_unchecked; +use ::std::sync::Once; +#[allow(deprecated)] +pub use ::std::sync::ONCE_INIT; + +// FIXME: Replace Option with MaybeUninit (stable since 1.36.0) +pub struct Lazy(Cell>, Once); + +impl Lazy { + #[allow(deprecated)] + pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT); + + #[inline(always)] + pub fn get(&'static self, f: F) -> &T + where + F: FnOnce() -> T, + { + self.1.call_once(|| { + self.0.set(Some(f())); + }); + + unsafe_block!("`self.0` is guaranteed to be `Some` by this point. The `Once` will catch and propagate panics. " => { + match *self.0.as_ptr() { + Some(ref x) => x, + None => { + debug_assert!(false, "attempted to derefence an uninitialized lazy static. This is a bug"); + + unreachable_unchecked() + }, + } + }) + } +} + +unsafe_impl!("" => impl Sync for Lazy {}); + +#[macro_export] +#[doc(hidden)] +macro_rules! __lazy_static_create { + ($NAME:ident, $T:ty) => { + static $NAME: crate::lazy_static::lazy::Lazy<$T> = crate::lazy_static::lazy::Lazy::INIT; + }; +} \ No newline at end of file diff --git a/bindings/src/lazy_static/mod.rs b/bindings/src/lazy_static/mod.rs new file mode 100644 index 00000000000..78345de4f50 --- /dev/null +++ b/bindings/src/lazy_static/mod.rs @@ -0,0 +1,113 @@ +// Copyright 2016 lazy-static.rs Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + + +/*! + * Polyfill of [rust-lang-nursery/lazy-static](https://github.com/rust-lang-nursery/lazy-static.rs) + * Original version is :746a0f02a84441157ca504c8679aba5c8269a9d0 + * Original lazy_static has feature `spin_no_std` for the user who does not want to depend on core lib. + * But we already do. So we do not support it in here. + */ + +#[path="inline_lazy.rs"] +#[doc(hidden)] +pub mod lazy; +pub use core::ops::Deref as __Deref; + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! __lazy_static_internal { + // optional visibility restrictions are wrapped in `()` to allow for + // explicitly passing otherwise implicit information about private items + ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N); + __lazy_static_internal!(@TAIL, $N : $T = $e); + lazy_static!($($t)*); + }; + (@TAIL, $N:ident : $T:ty = $e:expr) => { + impl crate::lazy_static::__Deref for $N { + type Target = $T; + fn deref(&self) -> &$T { + #[inline(always)] + fn __static_ref_initialize() -> $T { $e } + + #[inline(always)] + fn __stability() -> &'static $T { + __lazy_static_create!(LAZY, $T); + LAZY.get(__static_ref_initialize) + } + __stability() + } + } + impl crate::lazy_static::LazyStatic for $N { + fn initialize(lazy: &Self) { + let _ = &**lazy; + } + } + }; + // `vis` is wrapped in `()` to prevent parsing ambiguity + (@MAKE TY, $(#[$attr:meta])*, ($($vis:tt)*), $N:ident) => { + #[allow(missing_copy_implementations)] + #[allow(non_camel_case_types)] + #[allow(dead_code)] + $(#[$attr])* + $($vis)* struct $N {__private_field: ()} + #[doc(hidden)] + $($vis)* static $N: $N = $N {__private_field: ()}; + }; + () => () +} + +#[macro_export(local_inner_macros)] +macro_rules! lazy_static { + ($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + // use `()` to explicitly forward the information about private items + __lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*); + }; + ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*); + }; + ($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*); + }; + () => () +} + +/// Support trait for enabling a few common operation on lazy static values. +/// +/// This is implemented by each defined lazy static, and +/// used by the free functions in this crate. +pub trait LazyStatic { + #[doc(hidden)] + fn initialize(lazy: &Self); +} + +/// Takes a shared reference to a lazy static and initializes +/// it if it has not been already. +/// +/// This can be used to control the initialization point of a lazy static. +/// +/// Example: +/// +/// ```rust +/// use lazy_static::lazy_static; +/// +/// lazy_static! { +/// static ref BUFFER: Vec = (0..255).collect(); +/// } +/// +/// fn main() { +/// lazy_static::initialize(&BUFFER); +/// +/// // ... +/// work_with_initialized_data(&BUFFER); +/// } +/// # fn work_with_initialized_data(_: &[u8]) {} +/// ``` +pub fn initialize(lazy: &T) { + LazyStatic::initialize(lazy); +} \ No newline at end of file diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs new file mode 100644 index 00000000000..9256dfd0939 --- /dev/null +++ b/bindings/src/lib.rs @@ -0,0 +1,71 @@ +//! FFI interface for rust-lightning. + +// Unsafe is explicitly allowed through `unsafe_*` macros +#![deny(unsafe_code)] +// For converting rust-lightning results into FFI results +#![feature(try_trait)] +#![feature(backtrace)] + +extern crate bitcoin_hashes; +extern crate lightning; + +#[cfg(debug_assertions)] +extern crate hex; + +#[macro_use] +pub(crate) mod test_utils; +#[macro_use] +pub(crate) mod utils; + +#[macro_use] +pub(crate) mod lazy_static; + +pub(crate) mod is_null; + +mod adaptors; + +mod logger; +mod broadcaster; +mod fee_estimator; + +mod channelmanager; +mod peermanager; +mod error; +mod handle; + +#[cfg(debug_assertions)] +mod ffi_test_utils; + +pub use handle::*; +pub use error::*; + +ffi_no_catch! { + fn ffi_last_result( + message_buf: Out, + message_buf_len: usize, + actual_message_len: Out, + result: Out + ) -> FFIResult { + FFIResult::with_last_result(|last_result| { + let (value, error) = last_result.unwrap_or((FFIResult::ok(), None)); + + unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => result.init(value)); + + if let Some(error) = error { + let error = error.as_bytes(); + + unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => actual_message_len.init(error.len())); + + if message_buf_len < error.len() { + return FFIResult::buffer_too_small(); + } + + unsafe_block!("The buffer is valid for writes and the length is within the buffer" => message_buf.init_bytes(error)); + } else { + unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => actual_message_len.init(0)); + } + + FFIResult::ok() + }) + } +} diff --git a/bindings/src/logger.rs b/bindings/src/logger.rs new file mode 100644 index 00000000000..9cde1187c8c --- /dev/null +++ b/bindings/src/logger.rs @@ -0,0 +1,33 @@ +use crate::adaptors::*; +use crate::handle::{Ref, Out, HandleShared}; +use crate::error::FFIResult; +use lightning::util::logger::Logger; + +pub type FFILoggerHandle<'a> = HandleShared<'a, FFILogger>; + +ffi! { + fn create_logger(log_ref: Ref, out: Out) -> FFIResult { + let log = unsafe_block!("" => log_ref.as_ref()); + unsafe_block!("We know logger handle is not null by wrapper macro. And we know `Out` is writable" => out.init(FFILoggerHandle::alloc( FFILogger { log_ptr: *log }))); + FFIResult::ok() + } + + fn release_logger(handle: FFILoggerHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFILoggerHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } +} + +use lightning::util::logger::{Record, Level}; + +use std::fmt::Arguments; +/// Useful for testing low-level interoperability. +#[cfg(debug_assertions)] +ffi! { + fn test_logger(handle: FFILoggerHandle) -> FFIResult { + let logger: &FFILogger = unsafe_block!("" => handle.as_ref()); + logger.log(&Record::new(Level::Warn, std::format_args!("{}", "warn_msg"), "module_path", "logger.rs", 29)); + FFIResult::ok() + } +} diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs new file mode 100644 index 00000000000..e09177a4620 --- /dev/null +++ b/bindings/src/peermanager.rs @@ -0,0 +1,204 @@ +use std::{ + sync::atomic::{AtomicUsize, Ordering}, + convert::TryInto, + sync::Arc, + convert::TryFrom +}; + +use rand::{thread_rng, RngCore}; + +use bitcoin::secp256k1; + +use lightning::{ + ln::peer_handler::{PeerManager, MessageHandler}, + routing::network_graph::NetGraphMsgHandler, + util::config::UserConfig, + util::ser::Writeable +}; + +use crate::{ + channelmanager::{FFIArcChannelManager, construct_channel_manager}, + error::FFIResult, + handle::{Out, Ref, HandleShared}, + adaptors::*, + adaptors::primitives::{SecretKey, FFIBytes, PublicKey}, + utils::into_fixed_buffer +}; +use crate::channelmanager::FFIArcChannelManagerHandle; + +type FFISimpleArcPeerManager = PeerManager, Arc>; +type FFIArcPeerManagerHandle<'a> = HandleShared<'a, FFISimpleArcPeerManager>; + +lazy_static! { + static ref SOCKET_DESC_INDEX: AtomicUsize = AtomicUsize::new(0); +} + +fn construct_socket_desc ( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, +) -> FFISocketDescriptor { + let send_data_ref = unsafe_block!("" => send_data_ptr.as_ref()); + let disconnect_socket_ref = unsafe_block!("" => disconnect_socket_ptr.as_ref()); + let socket = FFISocketDescriptor { index, send_data_ptr: *send_data_ref, disconnect_socket_ptr: *disconnect_socket_ref }; + socket +} + + +ffi! { + fn create_peer_manager( + seed_ptr: Ref, + seed_len: usize, + network_ref: Ref, + cfg: Ref, + + chan_man: FFIArcChannelManagerHandle, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + log_ptr: Ref, + + our_node_secret_ptr: Ref, + our_node_id_ptr: Ref, + handle: Out + ) -> FFIResult { + if (seed_len != 32) { + return FFIResult::invalid_data_length(); + } + let network = unsafe_block!("" => *network_ref.as_ref()); + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + + let our_node_secret: secp256k1::SecretKey = { + let o = unsafe_block!("" => our_node_secret_ptr.as_ref()); + TryFrom::try_from(o.clone())? + }; + let our_node_id: secp256k1::PublicKey = { + let o = unsafe_block!("" => our_node_id_ptr.as_ref()); + TryFrom::try_from(o.clone())? + }; + + let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let logger_arc = Arc::new(FFILogger { log_ptr: *log_ref }); + let chain_watch_interface_arc = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx_ref, + *install_watch_outpoint_ref, + *watch_all_txn_ref, + *get_chain_utxo_ref, + network.to_network(), + logger_arc.clone() + )); + let route_handler = NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()); + let chan_man = unsafe_block!("It must point to valid ChannelManager" => chan_man.as_arc()); + let msg_handler = + MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; + let mut rng = thread_rng(); + let mut ephemeral_bytes = [0; 32]; + rng.fill_bytes(&mut ephemeral_bytes); + + let peer_man = + FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &ephemeral_bytes, logger_arc); + unsafe_block!("" => handle.init(FFIArcPeerManagerHandle::alloc(peer_man))); + FFIResult::ok() + } + + fn new_inbound_connection( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, + handle: FFIArcPeerManagerHandle + ) -> FFIResult { + let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + peer_man.new_inbound_connection(socket)?; + FFIResult::ok() + } + + fn new_outbound_connection( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, + their_node_id: Ref, + handle: FFIArcPeerManagerHandle, + initial_send: Out<[u8; 50]> + ) -> FFIResult { + let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let their_node_id: &PublicKey = unsafe_block!("" => their_node_id.as_ref()); + let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; + let act_one = peer_man.new_outbound_connection(their_node_id, socket)?; + let mut return_value = [0u8; 50]; + return_value.copy_from_slice(act_one.as_slice()); + unsafe_block!("We know `initial_send` points to valid FFIBytes to store pointers" => initial_send.init(return_value)); + FFIResult::ok() + } + + fn timer_tick_occured(handle: FFIArcPeerManagerHandle) -> FFIResult { + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + peer_man.timer_tick_occured(); + FFIResult::ok() + } + + fn write_buffer_space_avail( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, + handle: FFIArcPeerManagerHandle + ) -> FFIResult { + let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + peer_man.write_buffer_space_avail(&mut socket)?; + FFIResult::ok() + } + + fn read_event( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, + data_ref: Ref, + should_pause_read: Out, + handle: FFIArcPeerManagerHandle + ) -> FFIResult { + let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let data = unsafe_block!("data lives as long as this function and it points to valid value" => data_ref.as_ref()); + let should_pause = peer_man.read_event(&mut socket, data.as_ref())?; + unsafe_block!("We know it points to valid u8" => should_pause_read.init(if should_pause { Bool::True } else { Bool::False })); + FFIResult::ok() + } + + fn process_events(handle: FFIArcPeerManagerHandle) -> FFIResult { + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + peer_man.process_events(); + FFIResult::ok() + } + + fn socket_disconnected( + index: usize, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, + handle: FFIArcPeerManagerHandle) -> FFIResult { + let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + peer_man.socket_disconnected(&socket); + FFIResult::ok() + } + + fn get_peer_node_ids(buf_out: Out, buf_len: usize, actual_node_ids_len: Out, handle: FFIArcPeerManagerHandle) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as `get_peer_node_ids`, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let mut node_ids = peer_man.get_peer_node_ids(); + into_fixed_buffer(&mut node_ids, buf, &mut actual_node_ids_len) + } + + fn release_peer_manager(handle: FFIArcPeerManagerHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcPeerManagerHandle::dealloc(handle, |mut handle| { + FFIResult::ok() + })) + } + +} diff --git a/bindings/src/test_utils.rs b/bindings/src/test_utils.rs new file mode 100644 index 00000000000..3e986508f0f --- /dev/null +++ b/bindings/src/test_utils.rs @@ -0,0 +1,20 @@ +macro_rules! assert_match { + ($bind:pat = $bind_from:expr) => { + assert_match!($bind = $bind_from => ()) + }; + ($bind:pat = $bind_from:expr => $with:expr) => { + match $bind_from { + $bind => $with, + _ => panic!("assertion failed: unexpected value `{:?}`", $bind_from), + } + }; +} + +pub mod static_assert { + use std::panic::UnwindSafe; + + pub fn is_sync() {} + pub fn is_send() {} + + pub fn is_unwind_safe() {} +} diff --git a/bindings/src/utils/macros.rs b/bindings/src/utils/macros.rs new file mode 100644 index 00000000000..1e3d50c9980 --- /dev/null +++ b/bindings/src/utils/macros.rs @@ -0,0 +1,101 @@ +/** +Wrap an FFI function. + +This macro ensures all arguments satisfy `NotNull::not_null`. It's also a simple way to work +around not having a stable catch expression yet so we can handle early returns from ffi functions. +The macro doesn't support generics or argument patterns that are more complex than simple identifiers. + +A more advanced implementation could use a procedural macro, and generate bindings in high-level languages automatically. +*/ + +macro_rules! ffi { + ( + $($(#[$meta:meta])* + fn $name:ident ( $( $arg_ident:ident : $arg_ty:ty),* ) -> FFIResult $body:expr)*) => { + $( + $(#[$meta])* + #[allow(unsafe_code, unused_attributes)] + #[no_mangle] + pub unsafe extern "cdecl" fn $name( $($arg_ident : $arg_ty),* ) -> FFIResult { + #[allow(unused_mut)] + #[deny(unsafe_code)] + fn call( $(mut $arg_ident: $arg_ty),* ) -> FFIResult { + $( + if $crate::is_null::IsNull::is_null(&$arg_ident) { + return FFIResult::empty_pointer_provided().context($crate::is_null::Error { arg: stringify!($arg_ident) }); + } + )* + + $body + } + + FFIResult::catch(move || call( $($arg_ident),* )) + } + )* + }; +} + + +macro_rules! ffi_no_catch { + ($(fn $name:ident ( $( $arg_ident:ident : $arg_ty:ty),* ) -> FFIResult $body:expr)*) => { + $( + #[allow(unsafe_code, unused_attributes)] + #[no_mangle] + pub unsafe extern "cdecl" fn $name( $($arg_ident : $arg_ty),* ) -> FFIResult { + #[allow(unused_mut)] + #[deny(unsafe_code)] + fn call( $(mut $arg_ident: $arg_ty),* ) -> FFIResult { + $( + if $crate::is_null::IsNull::is_null(&$arg_ident) { + return FFIResult::empty_pointer_provided().context($crate::is_null::Error { arg: stringify!($arg_ident) }); + } + )* + + $body + } + + call( $($arg_ident),* ) + } + )* + }; +} + +/** +Allow a block of `unsafe` code with a reason. + +The macro will expand to an `unsafe` block. +*/ +macro_rules! unsafe_block { + ($reason:tt => $body:expr) => {{ + #[allow(unsafe_code)] + let __result = unsafe { $body }; + __result + }}; +} + +/** +Allow an `unsafe` function with a reason. + +The macro will expand to an `unsafe fn`. +*/ +macro_rules! unsafe_fn { + ($reason: tt => fn $name:ident $($body:tt)*) => { + unsafe_fn!($reason => pub(self) fn $name $($body)*); + }; + ($reason: tt => $publicity:vis fn $name:ident $($body:tt)*) => { + #[allow(unsafe_code)] + $publicity unsafe fn $name $($body)* + }; +} + +/** +Allow an `unsafe` trait implementation with a reason. + +The macro will expand to an `unsafe impl`. +*/ +macro_rules! unsafe_impl { + ($reason: tt => impl $($body:tt)*) => { + #[allow(unsafe_code)] + unsafe impl $($body)* + }; +} \ No newline at end of file diff --git a/bindings/src/utils/mod.rs b/bindings/src/utils/mod.rs new file mode 100644 index 00000000000..9db7f592e16 --- /dev/null +++ b/bindings/src/utils/mod.rs @@ -0,0 +1,54 @@ +use crate::{FFIResult, Out}; +use lightning::util::ser::{Writeable}; +use bitcoin_hashes::core::fmt::Formatter; + +#[macro_use] +pub(crate) mod macros; +pub(crate) mod option_extensions; + +#[derive(Debug)] +pub enum Error { + ZeroSizedBuf +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl std::error::Error for Error { +} + +/// Read an byte array payload to a given buffer. +/// When we want to return a variable length data (i.e. pointer data such as `Vec` or `&[T]`), +/// There is no straightforward way to do so. +/// Usually for a fixed size data, we return a value by writing it to an address +/// given by pointer from the wrapper side. But if the wrapper does not know the actual length +/// we want to return, The data may exceed the buffer that pointer points. +/// So in that case, we will return `FFIResult::BufferTooSmall` with actual length we want to write. +/// The wrapper must call the function again with a pointer points to a longer buffer. +pub (crate) fn into_fixed_buffer( + data: &mut T, + buf: &mut [u8], + actual_value_len: &mut Out +) -> FFIResult { + // A zero-sized input buffer will cause an infinite loop below. + // if we let it through. + if buf.len() == 0 { + Err(Error::ZeroSizedBuf)?; + } + + let data_vec = data.encode(); + let actual_len = data_vec.len(); + if (actual_len > buf.len()) + { + unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => actual_value_len.init(actual_len)); + FFIResult::buffer_too_small() + } else { + unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => actual_value_len.init(actual_len)); + let buf = &mut buf[..actual_len]; + buf.copy_from_slice(data_vec.as_ref()); + FFIResult::ok() + } +} diff --git a/bindings/src/utils/option_extensions.rs b/bindings/src/utils/option_extensions.rs new file mode 100644 index 00000000000..381f38bf0b8 --- /dev/null +++ b/bindings/src/utils/option_extensions.rs @@ -0,0 +1,40 @@ +pub(crate) trait OptionMutExt { + /** + Map and mutate an option in place. + */ + fn map_mut(&mut self, f: F) -> &mut Self + where + F: FnOnce(&mut T); + + /** + Replace an option if it doesn't contain a value. + */ + fn or_else_mut(&mut self, f: F) -> &mut Self + where + F: FnOnce() -> Option; +} + +impl OptionMutExt for Option { + fn map_mut(&mut self, f: F) -> &mut Self + where + F: FnOnce(&mut T), + { + match *self { + Some(ref mut t) => f(t), + None => (), + } + + self + } + + fn or_else_mut(&mut self, f: F) -> &mut Self + where + F: FnOnce() -> Option, + { + if self.is_none() { + *self = f(); + } + + self + } +} \ No newline at end of file diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 2e874296f5a..4fc1742f7b9 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::HashSet; use std::ops::Deref; use std::marker::PhantomData; +use std::panic::{UnwindSafe, RefUnwindSafe}; use std::ptr; /// Used to give chain error details upstream @@ -36,7 +37,7 @@ pub enum ChainError { /// Note that all of the functions implemented here *must* be reentrant-safe (obviously - they're /// called from inside the library in response to ChainListener events, P2P events, or timer /// events). -pub trait ChainWatchInterface: Sync + Send { +pub trait ChainWatchInterface: Sync + Send + UnwindSafe + RefUnwindSafe { /// Provides a txid/random-scriptPubKey-in-the-tx which much be watched for. fn install_watch_tx(&self, txid: &Txid, script_pub_key: &Script); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index cd479df3a0c..fa270b946ca 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -52,6 +52,7 @@ use std::time::Duration; use std::marker::{Sync, Send}; use std::ops::Deref; use bitcoin::hashes::hex::ToHex; +use std::fmt::{Formatter}; // We hold various information about HTLC relay in the HTLC objects in Channel itself: // @@ -521,6 +522,14 @@ pub enum PaymentSendFailure { PartialFailure(Vec>), } +impl std::fmt::Display for PaymentSendFailure { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl std::error::Error for PaymentSendFailure {} + macro_rules! handle_error { ($self: ident, $internal: expr, $their_node_id: expr) => { match $internal { diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index ce37944ca2f..2b177207c29 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -493,7 +493,7 @@ impl Writeable for InputMaterial { funding_redeemscript.write(writer)?; } } - Ok(()) + Ok(()) } } diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index c4a15fad0c7..529b1a3ef9b 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -371,7 +371,6 @@ impl Features { Features:: { flags, mark: PhantomData, } } - #[cfg(test)] /// Create a Features given a set of flags, in LE. pub fn from_le_bytes(flags: Vec) -> Features { Features { diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index bd5d23504b1..cdf1078de1f 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -65,8 +65,10 @@ pub struct Init { /// An error message to be sent or received from a peer #[derive(Clone)] pub struct ErrorMessage { - pub(crate) channel_id: [u8; 32], - pub(crate) data: String, + /// channel id which this message should be sent to, or received from. + pub channel_id: [u8; 32], + /// Actual error message. + pub data: String, } /// A ping message to be sent or received from a peer @@ -587,7 +589,7 @@ pub trait ChannelMessageHandler : events::MessageSendEventsProvider + Send + Syn } /// A trait to describe an object which can receive routing messages. -pub trait RoutingMessageHandler : Send + Sync { +pub trait RoutingMessageHandler : Send + Sync + UnwindSafe + RefUnwindSafe { /// Handle an incoming node_announcement message, returning true if it should be forwarded on, /// false or returning an Err otherwise. fn handle_node_announcement(&self, msg: &NodeAnnouncement) -> Result; @@ -656,6 +658,7 @@ mod fuzzy_internal_msgs { pub use self::fuzzy_internal_msgs::*; #[cfg(not(feature = "fuzztarget"))] pub(crate) use self::fuzzy_internal_msgs::*; +use std::panic::{RefUnwindSafe, UnwindSafe}; #[derive(Clone)] pub(crate) struct OnionPacket { diff --git a/lightning/src/routing/network_graph.rs b/lightning/src/routing/network_graph.rs index 9ef239f5657..1be4ee2074f 100644 --- a/lightning/src/routing/network_graph.rs +++ b/lightning/src/routing/network_graph.rs @@ -23,6 +23,7 @@ use std::collections::BTreeMap; use std::collections::btree_map::Entry as BtreeEntry; use std::ops::Deref; use bitcoin::hashes::hex::ToHex; +use std::panic::{RefUnwindSafe, UnwindSafe}; /// Receives and validates network updates from peers, /// stores authentic and relevant data as a network graph. @@ -80,7 +81,7 @@ macro_rules! secp_verify_sig { }; } -impl RoutingMessageHandler for NetGraphMsgHandler where C::Target: ChainWatchInterface, L::Target: Logger { +impl RoutingMessageHandler for NetGraphMsgHandler where C::Target: ChainWatchInterface, L::Target: Logger { fn handle_node_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result { self.network_graph.write().unwrap().update_node_from_announcement(msg, Some(&self.secp_ctx)) } diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index c76747cbbf8..64726a5a3e0 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -7,6 +7,9 @@ use ln::channelmanager::{BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT}; /// /// Default::default() provides sane defaults for most configurations /// (but currently with 0 relay fees!) +/// its bytes representation is c-compatible style. so that FFI consumer can +/// pass this from foreign language side. +#[repr(C)] #[derive(Clone, Debug)] pub struct UserConfig { /// Channel config that we propose to our counterparty. @@ -31,6 +34,7 @@ impl Default for UserConfig { /// /// Default::default() provides sane defaults. #[derive(Clone, Debug)] +#[repr(C)] pub struct ChannelHandshakeConfig { /// Confirmations we will wait for before considering the channel locked in. /// Applied only for inbound channels (see ChannelHandshakeLimits::max_minimum_depth for the @@ -83,6 +87,7 @@ impl Default for ChannelHandshakeConfig { /// field documentation. Note that this may result in barely-usable channels, but since they /// are applied mostly only to incoming channels that's not much of a problem. #[derive(Copy, Clone, Debug)] +#[repr(C)] pub struct ChannelHandshakeLimits { /// Minimum allowed satoshis when a channel is funded, this is supplied by the sender and so /// only applies to inbound channels. @@ -169,6 +174,7 @@ impl Default for ChannelHandshakeLimits { /// Options which apply on a per-channel basis and may change at runtime or based on negotiation /// with our counterparty. #[derive(Copy, Clone, Debug)] +#[repr(C)] pub struct ChannelConfig { /// Amount (in millionths of a satoshi) the channel will charge per transferred satoshi. /// This may be allowed to change at runtime in a later update, however doing so must result in diff --git a/lightning/src/util/errors.rs b/lightning/src/util/errors.rs index 7119c3a023a..58a38f6b9ce 100644 --- a/lightning/src/util/errors.rs +++ b/lightning/src/util/errors.rs @@ -50,6 +50,14 @@ impl fmt::Debug for APIError { } } +impl fmt::Display for APIError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(self, f) + } +} + +impl std::error::Error for APIError {} + #[inline] pub(crate) fn get_onion_debug_field(error_code: u16) -> (&'static str, usize) { match error_code & 0xff { diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs index 0d2de12d74a..ca9a31e8cdc 100644 --- a/lightning/src/util/logger.rs +++ b/lightning/src/util/logger.rs @@ -16,6 +16,8 @@ use std::cmp; use std::fmt; +use std::sync::Arc; +use std::panic::{UnwindSafe, RefUnwindSafe}; static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; @@ -115,7 +117,7 @@ impl<'a> Record<'a> { } /// A trait encapsulating the operations required of a logger -pub trait Logger: Sync + Send { +pub trait Logger: Sync + Send + UnwindSafe + RefUnwindSafe { /// Logs the `Record` fn log(&self, record: &Record); } diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 3d01a45f748..64d91660978 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -457,6 +457,29 @@ impl Readable for Vec { Ok(ret) } } + +impl Readable for Vec { + fn read(r: &mut R) -> Result { + let len: u16 = Readable::read(r)?; + let mut ret = Vec::with_capacity(len as usize); + for i in 0..(len as usize) { + let pk: PublicKey = Readable::read(r)?; + ret[i] = pk; + } + Ok(ret) + } +} + +impl Writeable for Vec { + fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + (self.len() as u16).write(w)?; + for i in self.iter() { + i.write(w)?; + } + Ok(()) + } +} + impl Writeable for Vec { #[inline] fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { From 578126bbd098fd10b551bd7e42a1afa08182756a Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Fri, 22 May 2020 13:05:27 +0900 Subject: [PATCH 06/45] impl Read/Write to ChannelDetails impl Readable/Writable to Vec * use fixed length array when possible remove unused low-level structs do not drop PeerManager::message_handler in PeerManager destructor --- bindings/Cargo.toml | 1 - bindings/src/adaptors/mod.rs | 44 ++--- bindings/src/adaptors/primitives.rs | 255 +++++++++++++++------------- bindings/src/block_notirifer.rs | 4 - bindings/src/broadcaster.rs | 80 --------- bindings/src/channelmanager.rs | 123 +++++++------- bindings/src/error.rs | 1 - bindings/src/fee_estimator.rs | 22 --- bindings/src/ffi_test_utils.rs | 5 +- bindings/src/lib.rs | 4 - bindings/src/logger.rs | 33 ---- bindings/src/peermanager.rs | 41 ++--- bindings/src/test_utils.rs | 12 -- bindings/src/utils/mod.rs | 2 +- lightning/src/ln/channelmanager.rs | 57 ++++++- lightning/src/util/logger.rs | 1 - 16 files changed, 293 insertions(+), 392 deletions(-) delete mode 100644 bindings/src/block_notirifer.rs delete mode 100644 bindings/src/broadcaster.rs delete mode 100644 bindings/src/fee_estimator.rs delete mode 100644 bindings/src/logger.rs diff --git a/bindings/Cargo.toml b/bindings/Cargo.toml index f5bd0e49818..90990e27479 100644 --- a/bindings/Cargo.toml +++ b/bindings/Cargo.toml @@ -19,5 +19,4 @@ bitcoin_hashes = "0.7" # see https://github.com/rust-lang/cargo/issues/7634 # as a workaround, we just included it as an usual dependency hex = "0.3" -rand = "0.7.3" diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 219f8b79aaa..b4ad090cea3 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -23,11 +23,8 @@ use lightning::{ pub mod primitives; use primitives::*; use std::sync::Arc; -use lightning::util::ser::Readable; -use std::io::Read; -use lightning::ln::msgs::DecodeError; -type cstr = NonNull; +type Cstr = NonNull; #[derive(PartialOrd, PartialEq, Eq, Ord, Debug, Copy, Clone)] #[repr(u8)] @@ -158,11 +155,11 @@ pub struct FFILogRecord { /// The verbosity level of the message. pub level: FFILogLevel, /// The message body. - pub args: cstr, + pub args: Cstr, /// The module path of the message. - pub module_path: cstr, + pub module_path: Cstr, /// The source file containing the message. - pub file: cstr, + pub file: Cstr, /// The line containing the message. pub line: u32, } @@ -196,10 +193,10 @@ impl Logger for FFILogger { pub mod chain_watch_interface_fn { use super::*; - pub type InstallWatchTxPtr = extern "cdecl" fn(*const FFISha256dHash, script_pub_key: *const FFIScript); + pub type InstallWatchTxPtr = extern "cdecl" fn(*const Bytes32, script_pub_key: *const FFIScript); pub type InstallWatchOutpointPtr = extern "cdecl" fn(outpoint: *const FFIOutPoint, out_script: *const FFIScript); pub type WatchAllTxnPtr = extern "cdecl" fn(); - pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const FFISha256dHash, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script: *mut FFITxOut); + pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const Bytes32, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script: *mut FFITxOut); } #[repr(C)] @@ -217,7 +214,7 @@ impl FFIChainWatchInterface { watch_all_txn: chain_watch_interface_fn::WatchAllTxnPtr, get_chain_utxo: chain_watch_interface_fn::GetChainUtxoPtr, network: Network, - logger: Arc + logger: Arc ) -> FFIChainWatchInterface { FFIChainWatchInterface{ install_watch_tx_ptr: install_watch_tx, @@ -234,11 +231,12 @@ impl ChainWatchInterface for FFIChainWatchInterface { self.util.install_watch_tx(txid, script_pub_key); let spk_vec = bitcoin_serialize(script_pub_key); let ffi_spk = FFIScript::from(spk_vec.as_slice()); - (self.install_watch_tx_ptr)(&txid.into() as *const _, &ffi_spk as *const _) + let txid: Bytes32 = txid.clone().into(); + (self.install_watch_tx_ptr)(&txid as *const _, &ffi_spk as *const _) } fn install_watch_outpoint(&self, outpoint: (Txid, u32), out_script: &Script) { self.util.install_watch_outpoint(outpoint, out_script); - let txid: FFISha256dHash = outpoint.0.into(); + let txid: Bytes32 = outpoint.0.into(); let ffi_outpoint = FFIOutPoint { txid: txid, index: outpoint.1 as u16 }; let out_script_vec = bitcoin_serialize(out_script); let ffi_outscript = FFIScript::from(out_script_vec.as_slice()); @@ -320,7 +318,7 @@ pub enum FFIErrorActionType { #[derive(Debug, Clone)] pub struct FFIErrorMsg { pub channel_id: [u8; 32], - pub data: cstr, + pub data: Cstr, } impl From for ErrorMessage { @@ -370,7 +368,7 @@ impl From for ErrorAction { #[repr(C)] pub struct FFILightningError { /// A human-readable message describing the error - pub err: cstr, + pub err: Cstr, /// The action which should be taken against the offending peer. pub action: FFIErrorAction, } @@ -387,7 +385,7 @@ impl From for LightningError { pub mod routing_msg_descriptor_fn { use super::*; - use crate::adaptors::primitives::PublicKey; + use crate::adaptors::primitives::Bytes33; /// Handle an incoming node_announcement message, returning true if it should be forwarded on, /// false or returning an Err otherwise. @@ -410,9 +408,9 @@ pub mod routing_msg_descriptor_fn { /// immediately higher (as defined by ::cmp) than starting_point. /// If None is provided for starting_point, we start at the first node. /// Return type is binary serialized `Vec` . - pub type GetNextNodeAnnouncements = extern "cdecl" fn (starting_point: Option<*const PublicKey>, batch_amount: u8) -> FFIBytes; + pub type GetNextNodeAnnouncements = extern "cdecl" fn (starting_point: Option<*const Bytes33>, batch_amount: u8) -> FFIBytes; /// Returns whether a full sync should be requested from a peer. - pub type ShouldRequestFullSync = extern "cdecl" fn (node_id: NonNull) -> Bool; + pub type ShouldRequestFullSync = extern "cdecl" fn (node_id: Bytes33) -> Bool; } pub struct FFIRoutingMsgHandler { @@ -439,17 +437,7 @@ impl Writer for VecWriter { impl RoutingMessageHandler for FFIRoutingMsgHandler { fn handle_node_announcement(&self, msg: &NodeAnnouncement) -> Result { - let mut w = VecWriter(Vec::new()); - msg.write(&mut w); - let bytes = FFIBytes::from(w.0.into_boxed_slice()); - let e = std::ptr::null_mut(); - let is_success = (self.handle_node_announcement_ptr)(&bytes as *const _, e); - if e.is_null() { - Ok(is_success.to_bool()) - } else { - let e = unsafe_block!("we know the error is not a null pointer" => (*e).clone()); - Err(e.into()) - } + unimplemented!() } fn handle_channel_announcement(&self, msg: &ChannelAnnouncement) -> Result { diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 079a66d2286..b786dfb9e32 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -1,6 +1,6 @@ use std::{ - convert::{TryFrom, TryInto}, - fmt::{Formatter, Error} + convert::{TryFrom}, + fmt::{Formatter} }; use bitcoin::{ @@ -22,6 +22,139 @@ use crate::{ FFIResult, is_null::IsNull }; +use lightning::ln::channelmanager::PaymentPreimage; + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Bytes32 { + pub bytes: [u8; 32], +} + +impl IsNull for Bytes32 { + fn is_null(&self) -> bool { + false + } +} + +impl From for secp256k1::SecretKey { + fn from(value: Bytes32) -> Self { + secp256k1::SecretKey::from_slice(&value.bytes).unwrap() + } +} + +impl From for Bytes32 { + fn from(value: secp256k1::SecretKey) -> Self { + let mut bytes = [0;32]; + bytes.copy_from_slice(&value[..]); + Self { + bytes + } + } +} + +impl From for PaymentHash { + fn from(ffi_hash: Bytes32) -> PaymentHash { + PaymentHash(ffi_hash.bytes) + } +} + +impl From for Bytes32 { + fn from(hash: Txid) -> Self { + let bytes = hash.as_hash().into_inner(); + Bytes32{ bytes } + } +} + +impl From for Txid { + fn from(hash: Bytes32) -> Self { + Txid::from_slice(&hash.bytes).unwrap() + } +} + +impl From for Bytes32 { + fn from(hash: BlockHash) -> Self { + let bytes = hash.as_hash().into_inner(); + Bytes32{ bytes } + } +} + +impl From for PaymentSecret { + fn from(ffi_secret: Bytes32) -> PaymentSecret { + PaymentSecret(ffi_secret.bytes) + } +} + +impl From for Bytes32 { + fn from(x: PaymentSecret) -> Self { + Self {bytes: x.0} + } +} + +impl From for Bytes32 { + fn from(x: PaymentPreimage) -> Self { + Self { bytes: x.0 } + } +} + +impl From for PaymentPreimage { + fn from(x: Bytes32) -> Self { + PaymentPreimage(x.bytes) + } +} + +#[derive(Clone)] +#[repr(C)] +pub struct Bytes33 { + pub bytes: [u8; 33] +} + +impl IsNull for Bytes33 { + fn is_null(&self) -> bool { + false + } +} + +impl From for secp256k1::PublicKey { + fn from(value: Bytes33) -> Self { + secp256k1::PublicKey::from_slice(&value.bytes).unwrap() + } +} + +impl From for Bytes33 { + fn from(value: secp256k1::PublicKey) -> Self { + Self { + bytes: value.serialize() + } + } +} + + +#[derive(Clone)] +#[repr(C)] +pub struct FFIOutPoint { + pub txid: Bytes32, + pub index: u16, +} + +impl From for OutPoint { + fn from(value: FFIOutPoint) -> Self { + let txid = value.txid.into(); + OutPoint{ txid, index: value.index } + } +} + +impl From for FFIOutPoint { + fn from(value: OutPoint) -> Self { + let txid: Bytes32 = value.txid.into(); + FFIOutPoint { txid, index: value.index } + } +} + +impl IsNull for FFIOutPoint { + fn is_null(&self) -> bool { + false + } +} macro_rules! array_struct{ ( @@ -78,95 +211,6 @@ macro_rules! array_struct{ } } -#[doc="The length must be [the same as a byte number of secp256k1 secret key](secp256k1::constants::SECRET_KEY_SIZE)"] -array_struct!(SecretKey); - -impl TryFrom for secp256k1::SecretKey { - type Error = FFIResult; - - fn try_from(value: SecretKey) -> Result { - let s = value.as_ref(); - secp256k1::SecretKey::from_slice(s).map_err(|e| FFIResult::internal_error().context(e)) - } -} - - -#[doc="The length must be [the same as a byte number of secp256k1 public key] `secp256k1::constants::PUBLIC_KEY_SIZE`"] -array_struct!(PublicKey); - -impl TryFrom for secp256k1::PublicKey { - type Error = FFIResult; - - fn try_from(value: PublicKey) -> Result { - let s = value.as_ref(); - secp256k1::PublicKey::from_slice(s).map_err(|e| FFIResult::internal_error().context(e)) - } -} - -#[doc="256 bit seed to initialize [ChannelManager](lightning::ln::channelmanager::ChannelManager)"] -array_struct!(Seed); - -array_struct!(FFISha256dHash); - -impl TryFrom for PaymentHash { - type Error = FFIResult; - - fn try_from(ffi_hash: FFISha256dHash) -> Result { - let s = unsafe_block!("" => std::slice::from_raw_parts(ffi_hash.ptr, ffi_hash.len)); - let s:[u8; 32] = s.try_into().map_err(|_| FFIResult::invalid_data_length())?; - Ok(PaymentHash(s)) - } -} - -impl From<&Txid> for FFISha256dHash { - fn from(hash: &Txid) -> Self { - let v = hash.encode(); - FFISha256dHash::from(v.into_boxed_slice()) - } -} - -impl From for FFISha256dHash { - fn from(hash: Txid) -> Self { - let v = hash.encode(); - FFISha256dHash::from(v.into_boxed_slice()) - } -} - -impl TryFrom for Txid { - type Error = bitcoin::hashes::Error; - fn try_from(hash: FFISha256dHash) -> Result { - let slice = unsafe_block!("We know it points to valid buffer" => std::slice::from_raw_parts(hash.ptr, hash.len)); - let v = bitcoin::hashes::sha256d::Hash::from_slice(slice)?; - Ok(v.into()) - } -} - -impl From<&BlockHash> for FFISha256dHash { - fn from(hash: &BlockHash) -> Self { - let v = hash.encode(); - FFISha256dHash::from(v.into_boxed_slice()) - } -} - -impl From for FFISha256dHash { - fn from(hash: BlockHash) -> Self { - let v = hash.encode(); - FFISha256dHash::from(v.into_boxed_slice()) - } -} - -array_struct!(FFISecret); - -impl TryFrom for PaymentSecret { - type Error = FFIResult; - - fn try_from(ffi_secret: FFISecret) -> Result { - let s = unsafe_block!("" => std::slice::from_raw_parts(ffi_secret.ptr, ffi_secret.len)); - let s:[u8; 32] = s.try_into().map_err(|_| FFIResult::invalid_data_length())?; - Ok(PaymentSecret(s)) - } -} - array_struct!(FFIScript); impl FFIScript { pub fn to_script(&self) -> Script { @@ -174,33 +218,6 @@ impl FFIScript { } } -#[derive(Clone)] -#[repr(C)] -pub struct FFIOutPoint { - pub txid: FFISha256dHash, - pub index: u16, -} - -impl TryFrom for OutPoint { - type Error = bitcoin::hashes::Error; - fn try_from(value: FFIOutPoint) -> Result { - let txid = value.txid.try_into()?; - Ok(OutPoint{ txid, index: value.index }) - } -} - -impl From for FFIOutPoint { - fn from(value: OutPoint) -> Self { - FFIOutPoint { txid: value.txid.into(), index: value.index } - } -} - -impl IsNull for FFIOutPoint { - fn is_null(&self) -> bool { - false - } -} - #[derive(Clone)] #[repr(C)] pub struct FFITxOut { @@ -244,7 +261,7 @@ array_struct!(FFITransaction); array_struct!(FFIBlock); array_struct!(FFIEvents); -/// General purpose byte array which has to cross ffi-boundary +// General purpose byte array which has to cross ffi-boundary array_struct!(FFIBytes); /// For `ChainWatchInterface::filter_block` diff --git a/bindings/src/block_notirifer.rs b/bindings/src/block_notirifer.rs deleted file mode 100644 index 339e1c5a3d5..00000000000 --- a/bindings/src/block_notirifer.rs +++ /dev/null @@ -1,4 +0,0 @@ - -use crate::handle::HandleShared; - -pub type FFIBlockNotifierHandle = HandleShared<'a, FFIBlockNotifier>; \ No newline at end of file diff --git a/bindings/src/broadcaster.rs b/bindings/src/broadcaster.rs deleted file mode 100644 index bcd67fc8034..00000000000 --- a/bindings/src/broadcaster.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! functions for managing broadcaster. -//! Usually, you don't have to have a handler to the broadcaster in a wrapper side. Just create -//! ChannelMonitor or ChannelManager and hold a reference to it. -//! However, sometimes it is useful for testing. - -use std::sync::Arc; - -use bitcoin::blockdata::transaction::Transaction; -use lightning::chain::chaininterface::BroadcasterInterface; -use crate::adaptors::*; -use crate::handle::{Ref, Out, HandleShared}; -use crate::error::FFIResult; - -pub type FFIBroadCasterHandle<'a> = HandleShared<'a, FFIBroadCaster>; - -#[cfg(debug_assertions)] -#[repr(C)] -pub struct BroadcasterWrapper { - broadcaster: Arc, -} - -#[cfg(debug_assertions)] -impl BroadcasterWrapper { - pub fn broadcast(&self, tx: &Transaction) { - self.broadcaster.as_ref().broadcast_transaction(&tx) - } -} - -#[cfg(debug_assertions)] -type BroadcasterWrapperHandle<'a> = HandleShared<'a, BroadcasterWrapper>; - - -ffi! { - fn create_broadcaster(broadcast_transaction_ptr: Ref, out: Out) -> FFIResult { - let broadcast_transaction = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); - let broadcaster = FFIBroadCaster{ broadcast_transaction_ptr: *broadcast_transaction }; - unsafe_block!("" => out.init(FFIBroadCasterHandle::alloc(broadcaster))); - FFIResult::ok() - } - - fn release_broadcaster(handle: FFIBroadCasterHandle) -> FFIResult { - unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIBroadCasterHandle::dealloc(handle, |mut handle| { - FFIResult::ok() - })) - } -} - -/// Useful for testing low-level interoperability. -#[cfg(debug_assertions)] -ffi! { - fn ffi_test_broadcaster(broadcaster_ptr: FFIBroadCasterHandle) -> FFIResult { - let broadcaster = unsafe_block!("" => broadcaster_ptr.as_ref()); - let tx: Transaction = bitcoin::consensus::deserialize(&hex::decode("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")?)?; - broadcaster.broadcast_transaction(&tx); - FFIResult::ok() - } - - fn create_broadcaster_wrapper(fn_ptr: Ref, out: Out) -> FFIResult { - // Test if passing dependent object by handle will be safe. - let broadcaster_fn = unsafe_block!("" => fn_ptr.as_ref()); - let broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcaster_fn }); - let wrapper_raw = BroadcasterWrapper{ broadcaster: broadcaster }; - unsafe_block!("" => out.init(BroadcasterWrapperHandle::alloc(wrapper_raw))); - FFIResult::ok() - } - - fn test_broadcaster_wrapper(wrapper_handle: BroadcasterWrapperHandle) -> FFIResult { - let wrapper = unsafe_block!("" => wrapper_handle.as_ref()); - let tx: Transaction = bitcoin::consensus::deserialize(&hex::decode("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")?)?; - wrapper.broadcast(&tx); - FFIResult::ok() - } - - fn release_broadcaster_wrapper(handle: BroadcasterWrapperHandle) -> FFIResult { - unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => BroadcasterWrapperHandle::dealloc(handle, |mut handle| { - FFIResult::ok() - })) - } - -} diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 336f8cd09fd..cce4b335ade 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -26,15 +26,15 @@ use crate::{ handle::{Out, Ref, HandleShared}, adaptors::{ primitives::{ - PublicKey, - FFISha256dHash, + Bytes32, + Bytes33, FFIRoute, FFIEvents, - FFISecret, FFIOutPoint }, - *, - } + * + }, + utils::into_fixed_buffer, }; use lightning::ln::channelmanager::{PaymentSecret, PaymentPreimage}; @@ -42,32 +42,39 @@ pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; -fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { +fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let payment_hash: &FFISha256dHash = unsafe_block!("" => payment_hash.as_ref()); + let payment_hash: &Bytes32 = unsafe_block!("" => payment_hash.as_ref()); let payment_hash: PaymentHash = payment_hash.clone().try_into()?; Ok(chan_man.fail_htlc_backwards(&payment_hash, payment_secret)) } -fn create_channel_inner(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Option, handle: FFIArcChannelManagerHandle) -> FFIResult { +fn create_channel_inner(their_network_key: Ref, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Option, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let their_network_key = their_network_key.try_into()?; + let their_network_key = unsafe_block!("We know it points to valid public key buffer" => their_network_key.as_ref()).clone().try_into()?; chan_man.create_channel(their_network_key, channel_value_satoshis, push_msat, user_id, override_config)?; FFIResult::ok() } -fn claim_funds_inner(payment_preimage: Ref<[u8; 32]>, payment_secret: Option, expected_amount: u64, handle: FFIArcChannelManagerHandle) -> bool { +fn claim_funds_inner(payment_preimage: Ref, payment_secret: Option, expected_amount: u64, handle: FFIArcChannelManagerHandle) -> bool { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let payment_preimage: &[u8;32] = unsafe_block!("" => payment_preimage.as_ref()); - let payment_preimage = PaymentPreimage(payment_preimage.clone()); + let payment_preimage: PaymentPreimage = unsafe_block!("" => payment_preimage.as_ref()).clone().into(); chan_man.claim_funds(payment_preimage, &payment_secret, expected_amount) } +fn send_payment_inner(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref, payment_secret: Option) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let route_ffi: &FFIRoute = unsafe_block!("We know it points to valid route data" => route_ref.as_ref()); + let payment_hash_ffi: &Bytes32 = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()); + let payment_hash: PaymentHash = payment_hash_ffi.clone().into(); + let route: Route = route_ffi.clone().try_into()?; + chan_man.send_payment(&route, payment_hash, &payment_secret)?; + FFIResult::ok() +} pub(crate) fn construct_channel_manager( - seed_ptr: Ref, - seed_len: usize, + seed: Ref, ffi_network: FFINetwork, cfg: Ref, @@ -82,11 +89,9 @@ pub(crate) fn construct_channel_manager( cur_block_height: usize, ) -> Result { - let seed_slice = unsafe_block!("The seed lives as long as `create_channel_manager` and the length is within the seed" => seed_ptr.as_bytes(seed_len)); let network = ffi_network.to_network(); let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let mut seed: [u8; 32] = Default::default(); - seed.copy_from_slice(seed_slice); + let mut seed: [u8; 32] = unsafe_block!("it points to valid length buffer" => seed.as_ref()).clone().bytes; let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); @@ -127,8 +132,7 @@ pub(crate) fn construct_channel_manager( ffi! { fn create_channel_manager( - seed_ptr: Ref, - seed_len: usize, + seed: Ref, network_ref: Ref, cfg: Ref, @@ -142,9 +146,7 @@ ffi! { get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, chan_man: Out) -> FFIResult { - if (seed_len != 32) { - return FFIResult::invalid_data_length(); - } + let log_ref = unsafe_block!("" => log_ptr.as_ref()); let network = unsafe_block!("" => *network_ref.as_ref()); let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); @@ -153,8 +155,7 @@ ffi! { let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); let chan_man_raw = construct_channel_manager( - seed_ptr, - seed_len, + seed, network, cfg, install_watch_tx_ref, @@ -171,26 +172,33 @@ ffi! { FFIResult::ok() } - fn create_channel(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, handle: FFIArcChannelManagerHandle) -> FFIResult { + fn list_channels(buf_out: Out, buf_len: usize, actual_channels_len: Out, handle: FFIArcChannelManagerHandle) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let mut channels = chan_man.list_channels(); + into_fixed_buffer(&mut channels, buf, &mut actual_channels_len) + } + + fn create_channel(their_network_key: Ref, channel_value_satoshis: u64, push_msat: u64, user_id: u64, handle: FFIArcChannelManagerHandle) -> FFIResult { create_channel_inner(their_network_key, channel_value_satoshis, push_msat, user_id, None, handle) } - fn create_channel_with_custom_config(their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { + fn create_channel_with_custom_config(their_network_key: Ref, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { let override_config = unsafe_block!("We know it points to valid UserConfig" => override_config.as_ref()); create_channel_inner(their_network_key, channel_value_satoshis, push_msat, user_id, Some(override_config.clone()), handle) } - fn close_channel(channel_id: Ref<[u8; 32]>, handle: FFIArcChannelManagerHandle) -> FFIResult { + fn close_channel(channel_id: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let channel_id: &[u8; 32] = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); - chan_man.close_channel(channel_id)?; + let channel_id: &Bytes32 = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); + chan_man.close_channel(&channel_id.bytes)?; FFIResult::ok() } - fn force_close_channel(channel_id: Ref<[u8; 32]>, handle: FFIArcChannelManagerHandle) -> FFIResult { + fn force_close_channel(channel_id: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let channel_id: &[u8; 32] = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); - chan_man.force_close_channel(channel_id); + let channel_id = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); + chan_man.force_close_channel(&channel_id.bytes); FFIResult::ok() } @@ -200,6 +208,23 @@ ffi! { FFIResult::ok() } + fn send_payment(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref, payment_secret_ref: Ref) -> FFIResult { + let payment_secret: &Bytes32 = unsafe_block!("We know it points to valid payment_secret data or empty 32 bytes" => payment_secret_ref.as_ref()); + let maybe_secret = Some(payment_secret.clone().into()); + send_payment_inner(handle, route_ref, payment_hash_ref, maybe_secret) + } + + fn send_payment_without_secret(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref) -> FFIResult { + send_payment_inner(handle, route_ref, payment_hash_ref, None) + } + + fn funding_transaction_generated(temporary_channel_id: Ref, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to a valid channel_manager" => handle.as_ref()); + let temporary_channel_id: &Bytes32 = unsafe_block!("data lives as long as this function and it points to a valid value" => temporary_channel_id.as_ref()); + let funding_txo: OutPoint = funding_txo.try_into()?; + chan_man.funding_transaction_generated(&temporary_channel_id.bytes, funding_txo); + FFIResult::ok() + } + fn process_pending_htlc_forwards(handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); chan_man.process_pending_htlc_forwards(); @@ -212,42 +237,24 @@ ffi! { FFIResult::ok() } - fn fail_htlc_backwards(payment_hash: Ref, payment_secret: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { - let payment_secret: &FFISecret = unsafe_block!("it points to valid data and lives as long as the function call" => payment_secret.as_ref()); - let payment_secret: Option = Some(payment_secret.clone().try_into()?); + fn fail_htlc_backwards(payment_hash: Ref, payment_secret: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let payment_secret: &Bytes32 = unsafe_block!("it points to valid data and lives as long as the function call" => payment_secret.as_ref()); + let payment_secret: PaymentSecret = payment_secret.clone().into(); + let payment_secret: Option = Some(payment_secret); let r = fail_htlc_backwards_inner(payment_hash, &payment_secret, handle)?; unsafe_block!("We know out parameter is writable" => result.init(r.into())); FFIResult::ok() } - fn fail_htlc_backwards_without_secret(payment_hash: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + fn fail_htlc_backwards_without_secret(payment_hash: Ref, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { let r = fail_htlc_backwards_inner(payment_hash, &None, handle)?; unsafe_block!("We know out parameter is writable" => result.init(r.into())); FFIResult::ok() } - fn send_payment(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref, payment_secret_ref: Ref) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let route_ffi: &FFIRoute = unsafe_block!("We know it points to valid route data" => route_ref.as_ref()); - let payment_hash_ffi: &FFISha256dHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()); - let payment_hash: PaymentHash = payment_hash_ffi.clone().try_into()?; - let payment_secret: &FFISecret = unsafe_block!("We know it points to valid payment_secret data or empty 32 bytes" => payment_secret_ref.as_ref()); - let maybe_secret = if payment_secret.as_ref().is_empty() { None } else { Some(payment_secret.clone().try_into()?) }; - let route: Route = route_ffi.clone().try_into()?; - chan_man.send_payment(&route, payment_hash, &maybe_secret)?; - FFIResult::ok() - } - - fn funding_transaction_generated(temporary_channel_id: Ref<[u8;32]>, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to a valid channel_manager" => handle.as_ref()); - let temporary_channel_id: &[u8; 32] = unsafe_block!("data lives as long as this function and it points to a valid value" => temporary_channel_id.as_ref()); - let funding_txo: OutPoint = funding_txo.try_into()?; - chan_man.funding_transaction_generated(temporary_channel_id, funding_txo); - FFIResult::ok() - } - - fn claim_funds(payment_preimage: Ref<[u8; 32]>, payment_secret: Ref<[u8; 32]>, expected_amount: u64, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { - let payment_secret: &[u8;32] = unsafe_block!("" => payment_secret.as_ref()); - let payment_secret: Option = Some(PaymentSecret(payment_secret.clone())); + fn claim_funds(payment_preimage: Ref, payment_secret: Ref, expected_amount: u64, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let payment_secret: &Bytes32 = unsafe_block!("" => payment_secret.as_ref()); + let payment_secret: Option = Some(payment_secret.clone().into()); let r = claim_funds_inner(payment_preimage, payment_secret, expected_amount, handle); unsafe_block!("We know out parameter is writable" => result.init(r.into())); FFIResult::ok() diff --git a/bindings/src/error.rs b/bindings/src/error.rs index 8ad3689286d..acd16d57fb7 100644 --- a/bindings/src/error.rs +++ b/bindings/src/error.rs @@ -8,7 +8,6 @@ use std::panic::{catch_unwind, UnwindSafe}; use std::any::Any; use std::sync::atomic::{AtomicU32, Ordering}; -use lightning::util::errors::APIError; use crate::utils::option_extensions::OptionMutExt; static LAST_ERR_ID: AtomicU32 = AtomicU32::new(0); diff --git a/bindings/src/fee_estimator.rs b/bindings/src/fee_estimator.rs deleted file mode 100644 index b1a56f5d3a1..00000000000 --- a/bindings/src/fee_estimator.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::adaptors::*; -use crate::handle::{Ref, Out, HandleShared}; -use crate::error::FFIResult; - -pub type FeeEstimatorHandle<'a> = HandleShared<'a, FFIFeeEstimator>; - -ffi! { - fn create_fee_estimator(fn_ref: Ref, out: Out) -> FFIResult { - let func = unsafe_block!("" => *fn_ref.as_ref()); - let fee_estimator = FFIFeeEstimator {get_est_sat_per_1000_weight_ptr: func}; - unsafe_block!("We know fee_estimator handle is not null by wrapper macro. And we know `Out` is writable" => - out.init(FeeEstimatorHandle::alloc(fee_estimator)) - ); - FFIResult::ok() - } - - fn release_fee_estimator(handle: FeeEstimatorHandle) -> FFIResult { - unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FeeEstimatorHandle::dealloc(handle, |mut handle| { - FFIResult::ok() - })) - } -} \ No newline at end of file diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs index 00c70eac5d6..fa4e7d4523f 100644 --- a/bindings/src/ffi_test_utils.rs +++ b/bindings/src/ffi_test_utils.rs @@ -1,8 +1,7 @@ -use std::sync::Arc; use crate::error::FFIResult; -/// These tests should be used for asserting that the wrapper code can see the expected -/// error messages when it fails (or succeeds). +// These tests should be used for asserting that the wrapper code can see the expected +// error messages when it fails (or succeeds). ffi! { fn ffi_test_error() -> FFIResult { use std::io; diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs index 9256dfd0939..bc9e1945240 100644 --- a/bindings/src/lib.rs +++ b/bindings/src/lib.rs @@ -24,10 +24,6 @@ pub(crate) mod is_null; mod adaptors; -mod logger; -mod broadcaster; -mod fee_estimator; - mod channelmanager; mod peermanager; mod error; diff --git a/bindings/src/logger.rs b/bindings/src/logger.rs deleted file mode 100644 index 9cde1187c8c..00000000000 --- a/bindings/src/logger.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::adaptors::*; -use crate::handle::{Ref, Out, HandleShared}; -use crate::error::FFIResult; -use lightning::util::logger::Logger; - -pub type FFILoggerHandle<'a> = HandleShared<'a, FFILogger>; - -ffi! { - fn create_logger(log_ref: Ref, out: Out) -> FFIResult { - let log = unsafe_block!("" => log_ref.as_ref()); - unsafe_block!("We know logger handle is not null by wrapper macro. And we know `Out` is writable" => out.init(FFILoggerHandle::alloc( FFILogger { log_ptr: *log }))); - FFIResult::ok() - } - - fn release_logger(handle: FFILoggerHandle) -> FFIResult { - unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFILoggerHandle::dealloc(handle, |mut handle| { - FFIResult::ok() - })) - } -} - -use lightning::util::logger::{Record, Level}; - -use std::fmt::Arguments; -/// Useful for testing low-level interoperability. -#[cfg(debug_assertions)] -ffi! { - fn test_logger(handle: FFILoggerHandle) -> FFIResult { - let logger: &FFILogger = unsafe_block!("" => handle.as_ref()); - logger.log(&Record::new(Level::Warn, std::format_args!("{}", "warn_msg"), "module_path", "logger.rs", 29)); - FFIResult::ok() - } -} diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index e09177a4620..462a3a9c002 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -1,27 +1,23 @@ use std::{ - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicUsize}, convert::TryInto, sync::Arc, - convert::TryFrom }; -use rand::{thread_rng, RngCore}; - use bitcoin::secp256k1; use lightning::{ ln::peer_handler::{PeerManager, MessageHandler}, routing::network_graph::NetGraphMsgHandler, util::config::UserConfig, - util::ser::Writeable }; use crate::{ - channelmanager::{FFIArcChannelManager, construct_channel_manager}, + channelmanager::{FFIArcChannelManager}, error::FFIResult, handle::{Out, Ref, HandleShared}, adaptors::*, - adaptors::primitives::{SecretKey, FFIBytes, PublicKey}, + adaptors::primitives::{Bytes32, FFIBytes, Bytes33}, utils::into_fixed_buffer }; use crate::channelmanager::FFIArcChannelManagerHandle; @@ -47,8 +43,7 @@ fn construct_socket_desc ( ffi! { fn create_peer_manager( - seed_ptr: Ref, - seed_len: usize, + seed: Ref, network_ref: Ref, cfg: Ref, @@ -59,23 +54,20 @@ ffi! { get_chain_utxo_ptr: Ref, log_ptr: Ref, - our_node_secret_ptr: Ref, - our_node_id_ptr: Ref, + our_node_secret_ptr: Ref, + our_node_id_ptr: Ref, handle: Out ) -> FFIResult { - if (seed_len != 32) { - return FFIResult::invalid_data_length(); - } let network = unsafe_block!("" => *network_ref.as_ref()); let log_ref = unsafe_block!("" => log_ptr.as_ref()); let our_node_secret: secp256k1::SecretKey = { let o = unsafe_block!("" => our_node_secret_ptr.as_ref()); - TryFrom::try_from(o.clone())? + o.clone().into() }; let our_node_id: secp256k1::PublicKey = { let o = unsafe_block!("" => our_node_id_ptr.as_ref()); - TryFrom::try_from(o.clone())? + o.clone().into() }; let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); @@ -96,12 +88,11 @@ ffi! { let chan_man = unsafe_block!("It must point to valid ChannelManager" => chan_man.as_arc()); let msg_handler = MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; - let mut rng = thread_rng(); - let mut ephemeral_bytes = [0; 32]; - rng.fill_bytes(&mut ephemeral_bytes); + let seed = unsafe_block!("It points to valid length buffer" => seed.as_ref()); let peer_man = - FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &ephemeral_bytes, logger_arc); + + FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &seed.bytes, logger_arc); unsafe_block!("" => handle.init(FFIArcPeerManagerHandle::alloc(peer_man))); FFIResult::ok() } @@ -122,18 +113,18 @@ ffi! { index: usize, send_data_ptr: Ref, disconnect_socket_ptr: Ref, - their_node_id: Ref, + their_node_id: Ref, handle: FFIArcPeerManagerHandle, initial_send: Out<[u8; 50]> ) -> FFIResult { let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); - let their_node_id: &PublicKey = unsafe_block!("" => their_node_id.as_ref()); + let their_node_id = unsafe_block!("" => their_node_id.as_ref()); let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; let act_one = peer_man.new_outbound_connection(their_node_id, socket)?; let mut return_value = [0u8; 50]; return_value.copy_from_slice(act_one.as_slice()); - unsafe_block!("We know `initial_send` points to valid FFIBytes to store pointers" => initial_send.init(return_value)); + unsafe_block!("We know `initial_send` points to valid buffer" => initial_send.init(return_value)); FFIResult::ok() } @@ -197,8 +188,10 @@ ffi! { fn release_peer_manager(handle: FFIArcPeerManagerHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcPeerManagerHandle::dealloc(handle, |mut handle| { + // We keep reference to message_handler from wrapper side so that we can call methods + // on it. So disposing it is their job. Not ours. + std::mem::forget(handle.message_handler); FFIResult::ok() })) } - } diff --git a/bindings/src/test_utils.rs b/bindings/src/test_utils.rs index 3e986508f0f..f9c40c6bc56 100644 --- a/bindings/src/test_utils.rs +++ b/bindings/src/test_utils.rs @@ -1,15 +1,3 @@ -macro_rules! assert_match { - ($bind:pat = $bind_from:expr) => { - assert_match!($bind = $bind_from => ()) - }; - ($bind:pat = $bind_from:expr => $with:expr) => { - match $bind_from { - $bind => $with, - _ => panic!("assertion failed: unexpected value `{:?}`", $bind_from), - } - }; -} - pub mod static_assert { use std::panic::UnwindSafe; diff --git a/bindings/src/utils/mod.rs b/bindings/src/utils/mod.rs index 9db7f592e16..b9f59cb66db 100644 --- a/bindings/src/utils/mod.rs +++ b/bindings/src/utils/mod.rs @@ -41,7 +41,7 @@ pub (crate) fn into_fixed_buffer( let data_vec = data.encode(); let actual_len = data_vec.len(); - if (actual_len > buf.len()) + if actual_len > buf.len() { unsafe_block!("The out pointer is valid and not mutably aliased elsewhere" => actual_value_len.init(actual_len)); FFIResult::buffer_too_small() diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index fa270b946ca..cd64561ab5f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -45,7 +45,7 @@ use util::errors::APIError; use std::{cmp, mem}; use std::collections::{HashMap, hash_map, HashSet}; -use std::io::{Cursor, Read}; +use std::io::{Cursor, Read, Error}; use std::sync::{Arc, Mutex, MutexGuard, RwLock}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; @@ -486,6 +486,61 @@ pub struct ChannelDetails { pub is_live: bool, } +impl Writeable for ChannelDetails { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.channel_id.write(writer)?; + self.short_channel_id.write(writer)?; + self.remote_network_id.write(writer)?; + self.counterparty_features.write(writer)?; + self.channel_value_satoshis.write(writer)?; + self.user_id.write(writer)?; + self.outbound_capacity_msat.write(writer)?; + self.inbound_capacity_msat.write(writer)?; + self.is_live.write(writer)?; + Ok(()) + } +} + +impl Writeable for Vec { + fn write(&self, writer: &mut W) -> Result<(), Error> { + (self.len() as u16).write(writer)?; + for i in self.iter() { + i.write(writer)?; + } + Ok(()) + } +} + +impl Readable for ChannelDetails { + fn read(reader: &mut R) -> Result { + let channel_id: [u8; 32] = Readable::read(reader)?; + let short_channel_id: Option = Readable::read(reader)?; + let remote_network_id: PublicKey = Readable::read(reader)?; + let counterparty_features: InitFeatures = Readable::read(reader)?; + let channel_value_satoshis: u64 = Readable::read(reader)?; + let user_id: u64 = Readable::read(reader)?; + let outbound_capacity_msat: u64 = Readable::read(reader)?; + let inbound_capacity_msat: u64 = Readable::read(reader)?; + let is_live: bool = Readable::read(reader)?; + Ok(ChannelDetails { + channel_id, short_channel_id, remote_network_id, counterparty_features, + channel_value_satoshis, user_id, outbound_capacity_msat, inbound_capacity_msat, is_live + }) + } +} + +impl Readable for Vec { + fn read(r: &mut R) -> Result { + let len: u16 = Readable::read(r)?; + let mut ret = Vec::with_capacity(len as usize); + for i in 0..(len as usize) { + let d: ChannelDetails = Readable::read(r)?; + ret[i] = d; + } + Ok(ret) + } +} + /// If a payment fails to send, it can be in one of several states. This enum is returned as the /// Err() type describing which state the payment is in, see the description of individual enum /// states for more. diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs index ca9a31e8cdc..fefc4adf63e 100644 --- a/lightning/src/util/logger.rs +++ b/lightning/src/util/logger.rs @@ -16,7 +16,6 @@ use std::cmp; use std::fmt; -use std::sync::Arc; use std::panic::{UnwindSafe, RefUnwindSafe}; static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; From 29024ca86f7c925b10748f355cb2af0843cf91d0 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 23 May 2020 23:46:17 +0900 Subject: [PATCH 07/45] return variable length buffer as events --- bindings/src/adaptors/primitives.rs | 34 +++++++++++++++++++++-------- bindings/src/channelmanager.rs | 8 +++---- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index b786dfb9e32..3ad8ff8245b 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -23,6 +23,9 @@ use crate::{ is_null::IsNull }; use lightning::ln::channelmanager::PaymentPreimage; +use std::io::{Error, Read}; +use lightning::ln::msgs::DecodeError; +use lightning::util::ser::Writer; #[derive(Debug, Clone)] #[repr(C)] @@ -259,7 +262,6 @@ impl TryFrom for Route { array_struct!(FFITransaction); array_struct!(FFIBlock); -array_struct!(FFIEvents); // General purpose byte array which has to cross ffi-boundary array_struct!(FFIBytes); @@ -273,15 +275,29 @@ impl TryFrom for (Vec<&Transaction>, Vec) { } } -impl From> for FFIEvents { +pub struct FFIEvents { + pub events: Vec, +} - fn from(value: Vec) -> Self { - let len = value.len(); - let mut result_vec: Vec = Vec::with_capacity(len * std::mem::size_of::()); - for e in value { - result_vec.extend(e.encode()); +impl Writeable for FFIEvents { + fn write(&self, writer: &mut W) -> Result<(), Error> { + (self.events.len() as u16).write(writer); + for e in &self.events { + match e { + Event::FundingGenerationReady {ref temporary_channel_id, ref channel_value_satoshis, ref output_script, ref user_channel_id} => { + temporary_channel_id.write(writer)?; + channel_value_satoshis.write(writer)?; + output_script.write(writer)?; + user_channel_id.write(writer)? + } + Event::PendingHTLCsForwardable { ref time_forwardable } => { + let milli = time_forwardable.as_millis() as u64; + milli.write(writer)?; + }, + x => x.write(writer)?, + } } - let r = result_vec.into_boxed_slice(); - r.into() + Ok(()) } } + diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index cce4b335ade..0546e2bb764 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -267,11 +267,11 @@ ffi! { FFIResult::ok() } - fn get_and_clear_pending_events(handle: FFIArcChannelManagerHandle, events: Out) -> FFIResult { + fn get_and_clear_pending_events(handle: FFIArcChannelManagerHandle, buf_out: Out, buf_len: usize, actual_channels_len: Out) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); - let e = chan_man.get_and_clear_pending_events().try_into()?; - unsafe_block!("We know out parameter is writable" => events.init(e)); - FFIResult::ok() + let mut e = FFIEvents{ events: chan_man.get_and_clear_pending_events() }; + into_fixed_buffer(&mut e, buf, &mut actual_channels_len) } fn release_ffi_channel_manager(handle: FFIArcChannelManagerHandle) -> FFIResult { From aecfc6f52e3468b9dc5b28a64d5c166de36edb77 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 20 Jun 2020 19:35:29 +0900 Subject: [PATCH 08/45] support BlockNotifier for ffi --- bindings/src/adaptors/mod.rs | 2 +- bindings/src/adaptors/primitives.rs | 11 ++- bindings/src/blocknotifier.rs | 105 ++++++++++++++++++++++++++ bindings/src/channelmanager.rs | 13 +++- bindings/src/is_null.rs | 1 + bindings/src/lib.rs | 1 + lightning/src/chain/chaininterface.rs | 3 +- 7 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 bindings/src/blocknotifier.rs diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index b4ad090cea3..1a9acb379f4 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -13,7 +13,7 @@ use bitcoin::secp256k1; use lightning::{ chain::chaininterface::{BroadcasterInterface, FeeEstimator, ConfirmationTarget, ChainWatchInterface, ChainError}, util::logger::{Logger, Record, Level}, - util::ser::{Writeable, Writer}, + util::ser::{Writer}, ln::peer_handler::SocketDescriptor, ln::msgs::ErrorAction, chain::chaininterface::ChainWatchInterfaceUtil, diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 3ad8ff8245b..2eed602595c 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -1,6 +1,7 @@ use std::{ convert::{TryFrom}, - fmt::{Formatter} + fmt::{Formatter}, + io::{Error} }; use bitcoin::{ @@ -16,16 +17,14 @@ use lightning::{ routing::router::{Route}, util::ser::{Readable, Writeable}, util::events::Event, - chain::transaction::OutPoint + chain::transaction::OutPoint, + ln::channelmanager::PaymentPreimage, + util::ser::Writer }; use crate::{ FFIResult, is_null::IsNull }; -use lightning::ln::channelmanager::PaymentPreimage; -use std::io::{Error, Read}; -use lightning::ln::msgs::DecodeError; -use lightning::util::ser::Writer; #[derive(Debug, Clone)] #[repr(C)] diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs new file mode 100644 index 00000000000..7e808beae33 --- /dev/null +++ b/bindings/src/blocknotifier.rs @@ -0,0 +1,105 @@ +use std::sync::Arc; +use lightning::chain::chaininterface::{BlockNotifier, ChainListener}; +use crate::{ + HandleShared, + FFIResult, + Ref, + Out, + channelmanager::{FFIArcChannelManagerHandle, FFIArcChannelManager}, + adaptors::{chain_watch_interface_fn, FFIChainWatchInterface} +}; +use crate::adaptors::{FFINetwork, ffilogger_fn, FFILogger}; +use bitcoin::BlockHeader; + +type FFIBlockNotifier = BlockNotifier<'static, Arc, Arc>; +type FFIBlockNotifierHandle<'a> = HandleShared<'a, FFIBlockNotifier>; + +ffi! { + + fn create_block_notifier( + network_ref: FFINetwork, + log_ptr: Ref, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + handle: Out + ) -> FFIResult { + let network = network_ref.to_network(); + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); + + let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let chain_watch_interface_arc = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx_ref, + *install_watch_outpoint_ref, + *watch_all_txn_ref, + *get_chain_utxo_ref, + network, + logger_arc.clone() + )); + let block_notifier = FFIBlockNotifier::new(chain_watch_interface_arc); + unsafe_block!("We know handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(block_notifier))); + FFIResult::ok() + } + + fn register_channel_manager( + channel_manager: FFIArcChannelManagerHandle, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); + let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + block_notifier.register_listener(chan_man); + FFIResult::ok() + } + + fn unregister_channel_manager( + channel_manager: FFIArcChannelManagerHandle, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); + let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + block_notifier.unregister_listener(chan_man); + FFIResult::ok() + } + + fn block_connected( + block_ptr: Ref, + block_len: usize, + height: u32, + handle: FFIBlockNotifierHandle) -> FFIResult { + let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + let block_bytes = unsafe_block!("block_ptr points to valid buffer of block_len length" => block_ptr.as_bytes(block_len)); + let block = bitcoin::consensus::deserialize(block_bytes)?; + block_notifier.block_connected(&block, height); + FFIResult::ok() + } + + fn block_disconnected( + block_header_ptr: Ref, + block_header_len: usize, + height: u32, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + + let block_header_bytes: &[u8] = unsafe_block!("We know it points to valid buffer of specified length" => block_header_ptr.as_bytes(block_header_len)); + let block_header: BlockHeader = bitcoin::consensus::encode::deserialize(block_header_bytes)?; + block_notifier.block_disconnected(&block_header, height); + FFIResult::ok() + } + + fn release_block_notifier(handle: FFIBlockNotifierHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIBlockNotifierHandle::dealloc(handle, |mut handle| { + // We keep reference to listeners from wrapper side (as a `SafeHandle` + // to a `ChannelManager`) so that we can call methods + // on it. So disposing it is their job. Not ours. + std::mem::forget(handle.listeners); + FFIResult::ok() + })) + } +} \ No newline at end of file diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 0546e2bb764..9b7396c39cf 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -4,7 +4,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH} }; -use bitcoin::secp256k1; +use bitcoin::{secp256k1, BlockHeader, Transaction}; use lightning::{ util::{ config::UserConfig, @@ -19,6 +19,8 @@ use lightning::{ transaction::OutPoint }, routing::router::Route, + ln::channelmanager::{PaymentSecret, PaymentPreimage}, + chain::chaininterface::ChainListener }; use crate::{ @@ -36,7 +38,14 @@ use crate::{ }, utils::into_fixed_buffer, }; -use lightning::ln::channelmanager::{PaymentSecret, PaymentPreimage}; + +#[inline] +pub fn slice_to_be32(v: &[u8]) -> u32 { + ((v[0] as u32) << 8*3) | + ((v[1] as u32) << 8*2) | + ((v[2] as u32) << 8*1) | + ((v[3] as u32) << 8*0) +} pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; diff --git a/bindings/src/is_null.rs b/bindings/src/is_null.rs index 48adf3a2f12..a281f71cdb5 100644 --- a/bindings/src/is_null.rs +++ b/bindings/src/is_null.rs @@ -44,3 +44,4 @@ impl IsNull for *mut T { } never_null!(usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); +never_null!(crate::adaptors::FFINetwork); diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs index bc9e1945240..686e91a82c4 100644 --- a/bindings/src/lib.rs +++ b/bindings/src/lib.rs @@ -26,6 +26,7 @@ mod adaptors; mod channelmanager; mod peermanager; +mod blocknotifier; mod error; mod handle; diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 4fc1742f7b9..120c77393cd 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -238,7 +238,8 @@ pub type BlockNotifierRef<'a, C> = BlockNotifier<'a, &'a ChainListener, C>; /// you should default to using a BlockNotifierRef, and use a BlockNotifierArc instead when you /// require ChainListeners with static lifetimes, such as when you're using lightning-net-tokio. pub struct BlockNotifier<'a, CL: Deref + 'a, C: Deref> where C::Target: ChainWatchInterface { - listeners: Mutex>, + /// Listeners to which we notify about information for a new block. + pub listeners: Mutex>, chain_monitor: C, phantom: PhantomData<&'a ()>, } From c4ec642536d6e8fb3618b4030b9702cc3e4ec116 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 21 Jun 2020 17:20:28 +0900 Subject: [PATCH 09/45] do not return Result in ChannelManager constructor * Since it is now pointless. --- bindings/src/channelmanager.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 9b7396c39cf..60d1b680480 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -97,7 +97,7 @@ pub(crate) fn construct_channel_manager( get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, -) -> Result { +) -> FFIArcChannelManager { let network = ffi_network.to_network(); let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let mut seed: [u8; 32] = unsafe_block!("it points to valid length buffer" => seed.as_ref()).clone().bytes; @@ -176,7 +176,7 @@ ffi! { log_ref, get_est_sat_per_1000_weight_ptr, cur_block_height, - )?; + ); unsafe_block!("We know chan_man is not null by wrapper macro. And we know `Out` is writable" => chan_man.init(HandleShared::alloc(chan_man_raw))); FFIResult::ok() } From cbef3f4623baaac02f9a8076e7c928035dc4c235 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 24 Jun 2020 14:13:32 +0900 Subject: [PATCH 10/45] fix bug in Event serialization --- bindings/src/adaptors/primitives.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 2eed602595c..6fbd70b1136 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -284,12 +284,14 @@ impl Writeable for FFIEvents { for e in &self.events { match e { Event::FundingGenerationReady {ref temporary_channel_id, ref channel_value_satoshis, ref output_script, ref user_channel_id} => { + 0u8.write(writer)?; temporary_channel_id.write(writer)?; channel_value_satoshis.write(writer)?; output_script.write(writer)?; user_channel_id.write(writer)? } Event::PendingHTLCsForwardable { ref time_forwardable } => { + 5u8.write(writer)?; let milli = time_forwardable.as_millis() as u64; milli.write(writer)?; }, From 8efa515f9835696d728791b33c9b4f1e81066353 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 24 Jun 2020 19:00:04 +0900 Subject: [PATCH 11/45] add new entrypoint for testing event serialization --- bindings/src/ffi_test_utils.rs | 63 ++++++++++++++++++++++++++++++++-- bindings/src/test_utils.rs | 12 +++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs index fa4e7d4523f..bc2d0c4ae80 100644 --- a/bindings/src/ffi_test_utils.rs +++ b/bindings/src/ffi_test_utils.rs @@ -1,7 +1,18 @@ use crate::error::FFIResult; +use lightning::util::events::Event; +use lightning::chain::transaction::OutPoint; +use bitcoin::hash_types::Txid; +use hex; +use crate::adaptors::primitives::FFIEvents; +use crate::utils::into_fixed_buffer; +use crate::Out; +use lightning::ln::channelmanager::{PaymentHash, PaymentSecret, PaymentPreimage}; +use bitcoin_hashes::core::time::Duration; +use lightning::chain::keysinterface::SpendableOutputDescriptor; +use bitcoin::TxOut; -// These tests should be used for asserting that the wrapper code can see the expected -// error messages when it fails (or succeeds). + +// These tests should be used for asserting that the wrapper can receive expected items from rust. ffi! { fn ffi_test_error() -> FFIResult { use std::io; @@ -12,5 +23,51 @@ ffi! { fn ffi_test_ok() -> FFIResult { FFIResult::ok() } -} + fn test_event_serialization(buf_out: Out, buf_len: usize, actual_len: Out) -> FFIResult { + let mut events = Vec::with_capacity(5); + + let txid = bitcoin::consensus::deserialize(&hex::decode("4141414141414141414141414141414141414141414141414141414141414142").unwrap()).unwrap(); + let funding_txo= OutPoint::new(txid, 1); + let user_channel_id = 1111; + events.push(Event::FundingBroadcastSafe {funding_txo, user_channel_id} ); + + let payment_hash = PaymentHash([2;32]); + let payment_secret = Some(PaymentSecret([3; 32])); + let amt = 50000; + events.push(Event::PaymentReceived {payment_secret, payment_hash, amt}); + + + let payment_preimage = PaymentPreimage([4;32]); + events.push(Event::PaymentSent {payment_preimage}); + + let payment_hash = PaymentHash([5;32]); + let rejected_by_dest = true; + events.push(Event::PaymentFailed {payment_hash, rejected_by_dest}); + + let time_forwardable = Duration::from_millis(100); + events.push(Event::PendingHTLCsForwardable {time_forwardable}); + + // expected txid for this tx is "1e8a6ed582813120a85e1dfed1249f1a32f530ba4b3fbabf4047cfbc1faea28c" + let tx: bitcoin::blockdata::transaction::Transaction = bitcoin::consensus::deserialize(&hex::decode("02000000000101b7ab83b98315c8e44e92aef50e2f43e3e21b1ca3a6299cbe72fa78caed5b49140000000000feffffff026c5f042a0100000016001438ce449f272f685f24c9d741444bc5224a62749ea086010000000000220020debbba2af4c4f581437a66e3e8d839e883f9c2ec8c7a12d002aba7317170284002473044022006f1e5f46202752b2ac41ce524f88c95f51d97adf39f5b120ae2576329b7bb1802202ecdad16893b28deeb5886db76dc52b8ccbfc02a185c6e159b30f4c86bf922a801210333218b9a0778cd13c3bc2d8eb73962cb4f4b4528ef359f120aa961c39a8bdb66d2000000").unwrap()).unwrap(); + let outpoint = bitcoin::blockdata::transaction::OutPoint::new(tx.txid(), 1); + let output = TxOut {value: 255, script_pubkey: bitcoin::blockdata::script::Script::new() }; + let static_output = SpendableOutputDescriptor::StaticOutput {outpoint, output: output.clone()}; + + let key = bitcoin::secp256k1::key::SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()).unwrap(); + let dynamic_output_p2wsh = SpendableOutputDescriptor::DynamicOutputP2WSH { + outpoint, + key, + witness_script: bitcoin::blockdata::script::Script::new(), + to_self_delay: 144, + output + }; + let outputs = vec![static_output, dynamic_output_p2wsh]; + events.push(Event::SpendableOutputs {outputs}); + + let mut e = FFIEvents{ events }; + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + into_fixed_buffer(&mut e, buf, &mut actual_len) + } + +} diff --git a/bindings/src/test_utils.rs b/bindings/src/test_utils.rs index f9c40c6bc56..3e986508f0f 100644 --- a/bindings/src/test_utils.rs +++ b/bindings/src/test_utils.rs @@ -1,3 +1,15 @@ +macro_rules! assert_match { + ($bind:pat = $bind_from:expr) => { + assert_match!($bind = $bind_from => ()) + }; + ($bind:pat = $bind_from:expr => $with:expr) => { + match $bind_from { + $bind => $with, + _ => panic!("assertion failed: unexpected value `{:?}`", $bind_from), + } + }; +} + pub mod static_assert { use std::panic::UnwindSafe; From 173830769ee3af80eb36b93014ee89639f4565d7 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 25 Jun 2020 18:00:57 +0900 Subject: [PATCH 12/45] Update * Add check for genesis_hash in `get_chain_utxo` . This is done automatically by `ChainWatchInterfaceUtil` * Remove now-useless primitive conversion function. * Change the type signature of get_chain_utxo for ffi fucntion. --- bindings/src/adaptors/mod.rs | 25 ++++++++++++++++++------- bindings/src/channelmanager.rs | 8 -------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 1a9acb379f4..b5fde02eec3 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -196,7 +196,7 @@ pub mod chain_watch_interface_fn { pub type InstallWatchTxPtr = extern "cdecl" fn(*const Bytes32, script_pub_key: *const FFIScript); pub type InstallWatchOutpointPtr = extern "cdecl" fn(outpoint: *const FFIOutPoint, out_script: *const FFIScript); pub type WatchAllTxnPtr = extern "cdecl" fn(); - pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const Bytes32, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script: *mut FFITxOut); + pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const Bytes32, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script_ptr: *mut u8, script_len: *mut usize, amount_satoshis: *mut u64); } #[repr(C)] @@ -247,14 +247,25 @@ impl ChainWatchInterface for FFIChainWatchInterface { (self.watch_all_txn_ptr)() } fn get_chain_utxo(&self, genesis_hash: BlockHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { + match self.util.get_chain_utxo(genesis_hash, unspent_tx_output_identifier) { + Err(ChainError::NotWatched) => { + return Err(ChainError::NotWatched); + }, + _ => {}, + } let err = std::ptr::null_mut(); - let tx_out = std::ptr::null_mut(); - (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err, tx_out); + // the length can be anything as long as it is enough to put the scriptPubKey. + // probably this is a bit overkill but who cares. + let mut script = [0u8; 128]; + let script_len = std::ptr::null_mut(); + let amount_satoshis = std::ptr::null_mut(); + (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err, script.as_mut_ptr(), script_len, amount_satoshis); if err.is_null() { - let tx_out: FFITxOut = unsafe_block!("We know the caller has set the value into the tx_out" => (*tx_out).clone()); - Ok((tx_out.script_pubkey.to_script(), tx_out.value)) - } - else { + let script_bytes: &[u8] = unsafe_block!("We know the caller has set the value into the script_ptr, script_len" => &script[..(*script_len)]); + let amount: u64 = unsafe_block!("We know the caller has set the value into the amount_satoshis" => *amount_satoshis); + let s = bitcoin::consensus::deserialize(script_bytes).expect("Failed to parse scriptpubkey"); + Ok((s, amount)) + } else { let e = unsafe_block!("we know the error is not a null pointer" => (*err).clone()); Err(e.into()) } diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 60d1b680480..7a1281da54b 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -39,14 +39,6 @@ use crate::{ utils::into_fixed_buffer, }; -#[inline] -pub fn slice_to_be32(v: &[u8]) -> u32 { - ((v[0] as u32) << 8*3) | - ((v[1] as u32) << 8*2) | - ((v[2] as u32) << 8*1) | - ((v[3] as u32) << 8*0) -} - pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; From d0fa01f65b1aa55b07b96733cbb748f84619b3d9 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Fri, 26 Jun 2020 16:30:41 +0900 Subject: [PATCH 13/45] WIP: refactoring to use Option instead of Ref for fn pointer --- bindings/src/blocknotifier.rs | 10 +++++----- bindings/src/channelmanager.rs | 16 ++++++++-------- bindings/src/is_null.rs | 6 ++++++ bindings/src/peermanager.rs | 34 +++++++++++++++++----------------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index 7e808beae33..89988d6787d 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -18,11 +18,11 @@ ffi! { fn create_block_notifier( network_ref: FFINetwork, - log_ptr: Ref, - install_watch_tx_ptr: Ref, - install_watch_outpoint_ptr: Ref, - watch_all_txn_ptr: Ref, - get_chain_utxo_ptr: Ref, + log_ptr: Option, + install_watch_tx_ptr: Option, + install_watch_outpoint_ptr: Option, + watch_all_txn_ptr: Option, + get_chain_utxo_ptr: Option, handle: Out ) -> FFIResult { let network = network_ref.to_network(); diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 7a1281da54b..7f53ff9c514 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -137,14 +137,14 @@ ffi! { network_ref: Ref, cfg: Ref, - install_watch_tx_ptr: Ref, - install_watch_outpoint_ptr: Ref, - watch_all_txn_ptr: Ref, - get_chain_utxo_ptr: Ref, - - broadcast_transaction_ptr: Ref, - log_ptr: Ref, - get_est_sat_per_1000_weight_ptr: Ref, + install_watch_tx_ptr: Option, + install_watch_outpoint_ptr: Option, + watch_all_txn_ptr: Option, + get_chain_utxo_ptr: Option, + + broadcast_transaction_ptr: Option, + log_ptr: Option, + get_est_sat_per_1000_weight_ptr: Option, cur_block_height: usize, chan_man: Out) -> FFIResult { diff --git a/bindings/src/is_null.rs b/bindings/src/is_null.rs index a281f71cdb5..91728536e81 100644 --- a/bindings/src/is_null.rs +++ b/bindings/src/is_null.rs @@ -43,5 +43,11 @@ impl IsNull for *mut T { } } +impl IsNull for Option { + fn is_null(&self) -> bool { + self.is_none() + } +} + never_null!(usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); never_null!(crate::adaptors::FFINetwork); diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 462a3a9c002..ddfa5dbd730 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -31,8 +31,8 @@ lazy_static! { fn construct_socket_desc ( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Option, + disconnect_socket_ptr: Option, ) -> FFISocketDescriptor { let send_data_ref = unsafe_block!("" => send_data_ptr.as_ref()); let disconnect_socket_ref = unsafe_block!("" => disconnect_socket_ptr.as_ref()); @@ -48,11 +48,11 @@ ffi! { cfg: Ref, chan_man: FFIArcChannelManagerHandle, - install_watch_tx_ptr: Ref, - install_watch_outpoint_ptr: Ref, - watch_all_txn_ptr: Ref, - get_chain_utxo_ptr: Ref, - log_ptr: Ref, + install_watch_tx_ptr: Option, + install_watch_outpoint_ptr: Option, + watch_all_txn_ptr: Option, + get_chain_utxo_ptr: Option, + log_ptr: Option, our_node_secret_ptr: Ref, our_node_id_ptr: Ref, @@ -99,8 +99,8 @@ ffi! { fn new_inbound_connection( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Option, + disconnect_socket_ptr: Option, handle: FFIArcPeerManagerHandle ) -> FFIResult { let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); @@ -111,8 +111,8 @@ ffi! { fn new_outbound_connection( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Option, + disconnect_socket_ptr: Option, their_node_id: Ref, handle: FFIArcPeerManagerHandle, initial_send: Out<[u8; 50]> @@ -136,8 +136,8 @@ ffi! { fn write_buffer_space_avail( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Optionf, + disconnect_socket_ptr: Option, handle: FFIArcPeerManagerHandle ) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); @@ -148,8 +148,8 @@ ffi! { fn read_event( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Option, + disconnect_socket_ptr: Option, data_ref: Ref, should_pause_read: Out, handle: FFIArcPeerManagerHandle @@ -170,8 +170,8 @@ ffi! { fn socket_disconnected( index: usize, - send_data_ptr: Ref, - disconnect_socket_ptr: Ref, + send_data_ptr: Option, + disconnect_socket_ptr: Option, handle: FFIArcPeerManagerHandle) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); From db9ea63f914dd51449d111ab9075b7c57062138d Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Fri, 26 Jun 2020 17:40:48 +0900 Subject: [PATCH 14/45] Revert "WIP: refactoring to use Option instead of Ref for fn pointer" This reverts commit b7f4af81bb15fd436a512ea0716c7206335564bf. --- bindings/src/blocknotifier.rs | 10 +++++----- bindings/src/channelmanager.rs | 16 ++++++++-------- bindings/src/is_null.rs | 6 ------ bindings/src/peermanager.rs | 34 +++++++++++++++++----------------- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index 89988d6787d..7e808beae33 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -18,11 +18,11 @@ ffi! { fn create_block_notifier( network_ref: FFINetwork, - log_ptr: Option, - install_watch_tx_ptr: Option, - install_watch_outpoint_ptr: Option, - watch_all_txn_ptr: Option, - get_chain_utxo_ptr: Option, + log_ptr: Ref, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, handle: Out ) -> FFIResult { let network = network_ref.to_network(); diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 7f53ff9c514..7a1281da54b 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -137,14 +137,14 @@ ffi! { network_ref: Ref, cfg: Ref, - install_watch_tx_ptr: Option, - install_watch_outpoint_ptr: Option, - watch_all_txn_ptr: Option, - get_chain_utxo_ptr: Option, - - broadcast_transaction_ptr: Option, - log_ptr: Option, - get_est_sat_per_1000_weight_ptr: Option, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + + broadcast_transaction_ptr: Ref, + log_ptr: Ref, + get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, chan_man: Out) -> FFIResult { diff --git a/bindings/src/is_null.rs b/bindings/src/is_null.rs index 91728536e81..a281f71cdb5 100644 --- a/bindings/src/is_null.rs +++ b/bindings/src/is_null.rs @@ -43,11 +43,5 @@ impl IsNull for *mut T { } } -impl IsNull for Option { - fn is_null(&self) -> bool { - self.is_none() - } -} - never_null!(usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); never_null!(crate::adaptors::FFINetwork); diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index ddfa5dbd730..462a3a9c002 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -31,8 +31,8 @@ lazy_static! { fn construct_socket_desc ( index: usize, - send_data_ptr: Option, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, ) -> FFISocketDescriptor { let send_data_ref = unsafe_block!("" => send_data_ptr.as_ref()); let disconnect_socket_ref = unsafe_block!("" => disconnect_socket_ptr.as_ref()); @@ -48,11 +48,11 @@ ffi! { cfg: Ref, chan_man: FFIArcChannelManagerHandle, - install_watch_tx_ptr: Option, - install_watch_outpoint_ptr: Option, - watch_all_txn_ptr: Option, - get_chain_utxo_ptr: Option, - log_ptr: Option, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + log_ptr: Ref, our_node_secret_ptr: Ref, our_node_id_ptr: Ref, @@ -99,8 +99,8 @@ ffi! { fn new_inbound_connection( index: usize, - send_data_ptr: Option, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, handle: FFIArcPeerManagerHandle ) -> FFIResult { let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); @@ -111,8 +111,8 @@ ffi! { fn new_outbound_connection( index: usize, - send_data_ptr: Option, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, their_node_id: Ref, handle: FFIArcPeerManagerHandle, initial_send: Out<[u8; 50]> @@ -136,8 +136,8 @@ ffi! { fn write_buffer_space_avail( index: usize, - send_data_ptr: Optionf, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, handle: FFIArcPeerManagerHandle ) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); @@ -148,8 +148,8 @@ ffi! { fn read_event( index: usize, - send_data_ptr: Option, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, data_ref: Ref, should_pause_read: Out, handle: FFIArcPeerManagerHandle @@ -170,8 +170,8 @@ ffi! { fn socket_disconnected( index: usize, - send_data_ptr: Option, - disconnect_socket_ptr: Option, + send_data_ptr: Ref, + disconnect_socket_ptr: Ref, handle: FFIArcPeerManagerHandle) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); From c540783d80e663d723dbfce9e90c3737cba38722 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 28 Jun 2020 13:05:35 +0900 Subject: [PATCH 15/45] stop using util in FFIChainWatchInterface --- bindings/src/adaptors/mod.rs | 42 ++++++++++++++++++++--------- bindings/src/adaptors/primitives.rs | 2 +- bindings/src/blocknotifier.rs | 9 +++++++ bindings/src/channelmanager.rs | 16 +++++++++-- bindings/src/peermanager.rs | 8 ++++++ 5 files changed, 61 insertions(+), 16 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index b5fde02eec3..cd19414cfd8 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -197,6 +197,8 @@ pub mod chain_watch_interface_fn { pub type InstallWatchOutpointPtr = extern "cdecl" fn(outpoint: *const FFIOutPoint, out_script: *const FFIScript); pub type WatchAllTxnPtr = extern "cdecl" fn(); pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const Bytes32, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script_ptr: *mut u8, script_len: *mut usize, amount_satoshis: *mut u64); + pub type FilterBlock = extern "cdecl" fn(block_ptr: *const u8, block_len: usize, matched_index_ptr: *mut u8, matched_inedx_len: *mut usize); + pub type ReEntered = extern "cdecl" fn() -> usize; } #[repr(C)] @@ -205,7 +207,8 @@ pub struct FFIChainWatchInterface { pub install_watch_outpoint_ptr: chain_watch_interface_fn::InstallWatchOutpointPtr, pub watch_all_txn_ptr: chain_watch_interface_fn::WatchAllTxnPtr, pub get_chain_utxo_ptr: chain_watch_interface_fn::GetChainUtxoPtr, - util: ChainWatchInterfaceUtil + pub filter_block_ptr: chain_watch_interface_fn::FilterBlock, + pub reentered_ptr: chain_watch_interface_fn::ReEntered } impl FFIChainWatchInterface { pub fn new( @@ -213,6 +216,8 @@ impl FFIChainWatchInterface { install_watch_outpoint: chain_watch_interface_fn::InstallWatchOutpointPtr, watch_all_txn: chain_watch_interface_fn::WatchAllTxnPtr, get_chain_utxo: chain_watch_interface_fn::GetChainUtxoPtr, + filter_block: chain_watch_interface_fn::FilterBlock, + reentered: chain_watch_interface_fn::ReEntered, network: Network, logger: Arc ) -> FFIChainWatchInterface { @@ -221,21 +226,20 @@ impl FFIChainWatchInterface { install_watch_outpoint_ptr: install_watch_outpoint, watch_all_txn_ptr: watch_all_txn, get_chain_utxo_ptr: get_chain_utxo, - util: ChainWatchInterfaceUtil::new(network) + filter_block_ptr: filter_block, + reentered_ptr:reentered } } } impl ChainWatchInterface for FFIChainWatchInterface { fn install_watch_tx(&self, txid: &Txid, script_pub_key: &Script) { - self.util.install_watch_tx(txid, script_pub_key); let spk_vec = bitcoin_serialize(script_pub_key); let ffi_spk = FFIScript::from(spk_vec.as_slice()); let txid: Bytes32 = txid.clone().into(); (self.install_watch_tx_ptr)(&txid as *const _, &ffi_spk as *const _) } fn install_watch_outpoint(&self, outpoint: (Txid, u32), out_script: &Script) { - self.util.install_watch_outpoint(outpoint, out_script); let txid: Bytes32 = outpoint.0.into(); let ffi_outpoint = FFIOutPoint { txid: txid, index: outpoint.1 as u16 }; let out_script_vec = bitcoin_serialize(out_script); @@ -243,16 +247,9 @@ impl ChainWatchInterface for FFIChainWatchInterface { (self.install_watch_outpoint_ptr)(&ffi_outpoint as *const _, &ffi_outscript as *const _) } fn watch_all_txn(&self) { - self.util.watch_all_txn(); (self.watch_all_txn_ptr)() } fn get_chain_utxo(&self, genesis_hash: BlockHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { - match self.util.get_chain_utxo(genesis_hash, unspent_tx_output_identifier) { - Err(ChainError::NotWatched) => { - return Err(ChainError::NotWatched); - }, - _ => {}, - } let err = std::ptr::null_mut(); // the length can be anything as long as it is enough to put the scriptPubKey. // probably this is a bit overkill but who cares. @@ -270,11 +267,30 @@ impl ChainWatchInterface for FFIChainWatchInterface { Err(e.into()) } } + fn filter_block<'a>(&self, block: &'a Block) -> (Vec<&'a Transaction>, Vec) { - self.util.filter_block(block) + let block_bytes = bitcoin_serialize(block); + // the minimum weight for one tx is 440. So the max number of tx in one block is 9090. + // so the max size of the buffer we have to prepare is. + // `2 + (9090 * 4) = 36362`. + let mut matched_tx_index = [0u8; 36864]; + let mut matched_tx_index_len_ptr: &mut usize = todo!(); + println!("coinbase tx in rl {:?}", block.txdata[0]); + (self.filter_block_ptr)(block_bytes.as_ptr(), block_bytes.len(), matched_tx_index.as_mut_ptr(), matched_tx_index_len_ptr as *mut _); + if (matched_tx_index_len_ptr.clone() == usize::MAX) { + panic!("FFI failure. the caller must set the actual serialized length of the tx-indexes in filter_block"); + } + let mut matched_tx_index: &[u8] = unsafe_block!("We know the caller has set the value for serialized tx index" => &matched_tx_index[..(*matched_tx_index_len_ptr)]); + let matched_tx_indexes: Vec = lightning::util::ser::Readable::read(&mut std::io::Cursor::new(matched_tx_index)).unwrap(); + let mut matched_txs = Vec::with_capacity(matched_tx_indexes.len()); + for i in matched_tx_indexes.iter() { + matched_txs.push(&block.txdata[i.clone() as usize]); + } + (matched_txs, matched_tx_indexes) } + fn reentered(&self) -> usize { - self.util.reentered() + (self.reentered_ptr)() } } diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 6fbd70b1136..6a41b758dcd 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -280,7 +280,7 @@ pub struct FFIEvents { impl Writeable for FFIEvents { fn write(&self, writer: &mut W) -> Result<(), Error> { - (self.events.len() as u16).write(writer); + (self.events.len() as u16).write(writer)?; for e in &self.events { match e { Event::FundingGenerationReady {ref temporary_channel_id, ref channel_value_satoshis, ref output_script, ref user_channel_id} => { diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index 7e808beae33..dfc40308278 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -19,10 +19,14 @@ ffi! { fn create_block_notifier( network_ref: FFINetwork, log_ptr: Ref, + install_watch_tx_ptr: Ref, install_watch_outpoint_ptr: Ref, watch_all_txn_ptr: Ref, get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + handle: Out ) -> FFIResult { let network = network_ref.to_network(); @@ -33,12 +37,17 @@ ffi! { let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let chain_watch_interface_arc = Arc::new(FFIChainWatchInterface::new( *install_watch_tx_ref, *install_watch_outpoint_ref, *watch_all_txn_ref, *get_chain_utxo_ref, + *filter_block_ref, + *reentered_ref, network, logger_arc.clone() )); diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 7a1281da54b..e2d2f972a95 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -83,6 +83,8 @@ pub(crate) fn construct_channel_manager( install_watch_outpoint: &chain_watch_interface_fn::InstallWatchOutpointPtr, watch_all_txn: &chain_watch_interface_fn::WatchAllTxnPtr, get_chain_utxo: &chain_watch_interface_fn::GetChainUtxoPtr, + filter_block: &chain_watch_interface_fn::FilterBlock, + reentered: &chain_watch_interface_fn::ReEntered, broadcast_transaction_ptr: Ref, log_ref: &ffilogger_fn::LogExtern, @@ -98,11 +100,13 @@ pub(crate) fn construct_channel_manager( let chain_watch_interface_arc = Arc::new(FFIChainWatchInterface::new( - *install_watch_tx, + *install_watch_tx, *install_watch_outpoint, *watch_all_txn, *get_chain_utxo, - network, + *filter_block, + *reentered, + network, logger_arc.clone() )); @@ -141,6 +145,8 @@ ffi! { install_watch_outpoint_ptr: Ref, watch_all_txn_ptr: Ref, get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, broadcast_transaction_ptr: Ref, log_ptr: Ref, @@ -154,15 +160,21 @@ ffi! { let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let chan_man_raw = construct_channel_manager( seed, network, cfg, + install_watch_tx_ref, install_watch_outpoint_ref, watch_all_txn_ref, get_chain_utxo_ref, + filter_block_ref, + reentered_ref, broadcast_transaction_ptr, log_ref, diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 462a3a9c002..b808a439bea 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -52,6 +52,9 @@ ffi! { install_watch_outpoint_ptr: Ref, watch_all_txn_ptr: Ref, get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + log_ptr: Ref, our_node_secret_ptr: Ref, @@ -74,6 +77,9 @@ ffi! { let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let logger_arc = Arc::new(FFILogger { log_ptr: *log_ref }); let chain_watch_interface_arc = Arc::new(FFIChainWatchInterface::new( @@ -81,6 +87,8 @@ ffi! { *install_watch_outpoint_ref, *watch_all_txn_ref, *get_chain_utxo_ref, + *filter_block_ref, + *reentered_ref, network.to_network(), logger_arc.clone() )); From 9cbf90abcb489232e4aba0445c21b74d7fc37abc Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 28 Jun 2020 15:53:42 +0900 Subject: [PATCH 16/45] Incorporate upstream changes * change fn signature of `filter_block` * Use u32 instead of u64 for fee_est. * Change fields in `DynamicOutputP2WSH` --- bindings/src/adaptors/mod.rs | 25 ++++++++++--------------- bindings/src/channelmanager.rs | 2 +- bindings/src/ffi_test_utils.rs | 12 +++++++----- bindings/src/peermanager.rs | 2 +- lightning/src/ln/peer_handler.rs | 3 ++- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index cd19414cfd8..f9ea31a5e85 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -84,7 +84,7 @@ impl From for FFIConfirmationTarget { pub mod fee_estimator_fn { use super::{FFIConfirmationTarget}; - pub type GetEstSatPer1000WeightPtr = extern "cdecl" fn (FFIConfirmationTarget) -> u64; + pub type GetEstSatPer1000WeightPtr = extern "cdecl" fn (FFIConfirmationTarget) -> u32; } #[repr(C)] @@ -94,7 +94,7 @@ pub struct FFIFeeEstimator { } impl FeeEstimator for FFIFeeEstimator { - fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u64 { + fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { (self.get_est_sat_per_1000_weight_ptr)(confirmation_target.into()) } } @@ -197,7 +197,7 @@ pub mod chain_watch_interface_fn { pub type InstallWatchOutpointPtr = extern "cdecl" fn(outpoint: *const FFIOutPoint, out_script: *const FFIScript); pub type WatchAllTxnPtr = extern "cdecl" fn(); pub type GetChainUtxoPtr = extern "cdecl" fn(genesis_hash: *const Bytes32, unspent_tx_output_identifier: u64, err: *mut FFIChainError, script_ptr: *mut u8, script_len: *mut usize, amount_satoshis: *mut u64); - pub type FilterBlock = extern "cdecl" fn(block_ptr: *const u8, block_len: usize, matched_index_ptr: *mut u8, matched_inedx_len: *mut usize); + pub type FilterBlock = extern "cdecl" fn(block_ptr: *const u8, block_len: usize, matched_index_ptr: *mut usize, matched_inedx_len: *mut usize); pub type ReEntered = extern "cdecl" fn() -> usize; } @@ -268,25 +268,18 @@ impl ChainWatchInterface for FFIChainWatchInterface { } } - fn filter_block<'a>(&self, block: &'a Block) -> (Vec<&'a Transaction>, Vec) { + fn filter_block<'a>(&self, block: &'a Block) -> Vec { let block_bytes = bitcoin_serialize(block); // the minimum weight for one tx is 440. So the max number of tx in one block is 9090. - // so the max size of the buffer we have to prepare is. - // `2 + (9090 * 4) = 36362`. - let mut matched_tx_index = [0u8; 36864]; - let mut matched_tx_index_len_ptr: &mut usize = todo!(); + let mut matched_tx_index = [0; 9091]; + let mut matched_tx_index_len_ptr: &mut usize = &mut usize::MAX; println!("coinbase tx in rl {:?}", block.txdata[0]); (self.filter_block_ptr)(block_bytes.as_ptr(), block_bytes.len(), matched_tx_index.as_mut_ptr(), matched_tx_index_len_ptr as *mut _); if (matched_tx_index_len_ptr.clone() == usize::MAX) { panic!("FFI failure. the caller must set the actual serialized length of the tx-indexes in filter_block"); } - let mut matched_tx_index: &[u8] = unsafe_block!("We know the caller has set the value for serialized tx index" => &matched_tx_index[..(*matched_tx_index_len_ptr)]); - let matched_tx_indexes: Vec = lightning::util::ser::Readable::read(&mut std::io::Cursor::new(matched_tx_index)).unwrap(); - let mut matched_txs = Vec::with_capacity(matched_tx_indexes.len()); - for i in matched_tx_indexes.iter() { - matched_txs.push(&block.txdata[i.clone() as usize]); - } - (matched_txs, matched_tx_indexes) + let mut matched_tx_indexes: &[usize] = unsafe_block!("We know the caller has set the value for serialized tx index" => &matched_tx_index[..(*matched_tx_index_len_ptr)]); + matched_tx_indexes.to_vec() } fn reentered(&self) -> usize { @@ -410,6 +403,8 @@ impl From for LightningError { } } +// --- routing stuff --- +/// TODO: enable to pass routing handler from outside. pub mod routing_msg_descriptor_fn { use super::*; use crate::adaptors::primitives::Bytes33; diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index e2d2f972a95..5a0be610d64 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -273,7 +273,7 @@ ffi! { FFIResult::ok() } - fn update_fee(channel_id: Ref<[u8; 32]>, feerate_per_kw: u64, handle: FFIArcChannelManagerHandle) -> FFIResult { + fn update_fee(channel_id: Ref<[u8; 32]>, feerate_per_kw: u32, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); let channel_id: &[u8;32] = unsafe_block!("" => channel_id.as_ref()); chan_man.update_fee(channel_id.clone(), feerate_per_kw)?; diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs index bc2d0c4ae80..2df9c468520 100644 --- a/bindings/src/ffi_test_utils.rs +++ b/bindings/src/ffi_test_utils.rs @@ -28,7 +28,7 @@ ffi! { let mut events = Vec::with_capacity(5); let txid = bitcoin::consensus::deserialize(&hex::decode("4141414141414141414141414141414141414141414141414141414141414142").unwrap()).unwrap(); - let funding_txo= OutPoint::new(txid, 1); + let funding_txo = OutPoint{ txid, index: 1}; let user_channel_id = 1111; events.push(Event::FundingBroadcastSafe {funding_txo, user_channel_id} ); @@ -54,13 +54,15 @@ ffi! { let output = TxOut {value: 255, script_pubkey: bitcoin::blockdata::script::Script::new() }; let static_output = SpendableOutputDescriptor::StaticOutput {outpoint, output: output.clone()}; - let key = bitcoin::secp256k1::key::SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()).unwrap(); + let per_commitment_point = bitcoin::secp256k1::key::PublicKey::from_slice(&hex::decode("02aca35d6de21baefaf65db590611fabd42ed4d52683c36caff58761d309314f65").unwrap()).unwrap(); + let remote_revocation_pubkey = bitcoin::secp256k1::key::PublicKey::from_slice(&hex::decode("02812cb18bf5c19374b34419095a09aa0b0d5559a24ce0ef558845230b0a096161").unwrap()).unwrap(); let dynamic_output_p2wsh = SpendableOutputDescriptor::DynamicOutputP2WSH { outpoint, - key, - witness_script: bitcoin::blockdata::script::Script::new(), + per_commitment_point, to_self_delay: 144, - output + key_derivation_params: (3, 4), + output, + remote_revocation_pubkey }; let outputs = vec![static_output, dynamic_output_p2wsh]; events.push(Event::SpendableOutputs {outputs}); diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index b808a439bea..5f56a47b2bb 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -22,7 +22,7 @@ use crate::{ }; use crate::channelmanager::FFIArcChannelManagerHandle; -type FFISimpleArcPeerManager = PeerManager, Arc>; +type FFISimpleArcPeerManager = PeerManager, Arc, Arc>>, Arc>; type FFIArcPeerManagerHandle<'a> = HandleShared<'a, FFISimpleArcPeerManager>; lazy_static! { diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 2902ece7694..6c44de51864 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -196,7 +196,8 @@ pub struct PeerManager, + /// MessageHandler for this PeerManager + pub message_handler: MessageHandler, peers: Mutex>, our_node_secret: SecretKey, ephemeral_key_midstate: Sha256Engine, From 3c28b6462e195fcb51896fe0267b8b545d8bc0dd Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 29 Jun 2020 12:34:43 +0900 Subject: [PATCH 17/45] remove useless println --- bindings/src/adaptors/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index f9ea31a5e85..38d02c849a6 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -273,7 +273,6 @@ impl ChainWatchInterface for FFIChainWatchInterface { // the minimum weight for one tx is 440. So the max number of tx in one block is 9090. let mut matched_tx_index = [0; 9091]; let mut matched_tx_index_len_ptr: &mut usize = &mut usize::MAX; - println!("coinbase tx in rl {:?}", block.txdata[0]); (self.filter_block_ptr)(block_bytes.as_ptr(), block_bytes.len(), matched_tx_index.as_mut_ptr(), matched_tx_index_len_ptr as *mut _); if (matched_tx_index_len_ptr.clone() == usize::MAX) { panic!("FFI failure. the caller must set the actual serialized length of the tx-indexes in filter_block"); From acbca147bad162776addafbea13cf71c7637401a Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 1 Jul 2020 19:09:10 +0900 Subject: [PATCH 18/45] add new entrypoint for testing ChannelDetails serialization --- bindings/src/adaptors/primitives.rs | 1 + bindings/src/ffi_test_utils.rs | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 6a41b758dcd..44a24a98b5e 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -213,6 +213,7 @@ macro_rules! array_struct{ } } +/// Length-prefixed script. array_struct!(FFIScript); impl FFIScript { pub fn to_script(&self) -> Script { diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs index 2df9c468520..ab7a5078aa4 100644 --- a/bindings/src/ffi_test_utils.rs +++ b/bindings/src/ffi_test_utils.rs @@ -6,10 +6,11 @@ use hex; use crate::adaptors::primitives::FFIEvents; use crate::utils::into_fixed_buffer; use crate::Out; -use lightning::ln::channelmanager::{PaymentHash, PaymentSecret, PaymentPreimage}; +use lightning::ln::channelmanager::{PaymentHash, PaymentSecret, PaymentPreimage, ChannelDetails}; use bitcoin_hashes::core::time::Duration; use lightning::chain::keysinterface::SpendableOutputDescriptor; use bitcoin::TxOut; +use lightning::ln::features::InitFeatures; // These tests should be used for asserting that the wrapper can receive expected items from rust. @@ -71,5 +72,27 @@ ffi! { let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); into_fixed_buffer(&mut e, buf, &mut actual_len) } + fn test_channel_details_serialization(buf_out: Out, buf_len: usize, actual_len: Out) -> FFIResult { + let mut ret = Vec::with_capacity(1); + let id_ref= &hex::decode("4141414141414141414141414141414141414141414141414141414141414142").unwrap(); + let mut id = [0u8;32]; + id.copy_from_slice(id_ref); + let remote_network_id = bitcoin::secp256k1::key::PublicKey::from_slice(&hex::decode("02aca35d6de21baefaf65db590611fabd42ed4d52683c36caff58761d309314f65").unwrap()).unwrap(); + let counterparty_features = InitFeatures::known(); + let detail = ChannelDetails{ + channel_id: id, + short_channel_id: Some(3), + remote_network_id, + counterparty_features: counterparty_features, + channel_value_satoshis: 5454, + user_id: 2223, + outbound_capacity_msat: 2828, + inbound_capacity_msat: 9292, + is_live: false + }; + ret.push(detail); + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + into_fixed_buffer(&mut ret, buf, &mut actual_len) + } } From 95266f8090a01bbbe648d930f5cdc0e740ea6ebd Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 4 Jul 2020 20:27:11 +0900 Subject: [PATCH 19/45] fix some compiler warnings in binding --- bindings/src/adaptors/mod.rs | 5 ++--- bindings/src/channelmanager.rs | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 38d02c849a6..e762cc4bb4b 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -16,7 +16,6 @@ use lightning::{ util::ser::{Writer}, ln::peer_handler::SocketDescriptor, ln::msgs::ErrorAction, - chain::chaininterface::ChainWatchInterfaceUtil, ln::msgs::{ErrorMessage, RoutingMessageHandler, HTLCFailChannelUpdate, ChannelAnnouncement, NodeAnnouncement, LightningError, ChannelUpdate} }; @@ -274,7 +273,7 @@ impl ChainWatchInterface for FFIChainWatchInterface { let mut matched_tx_index = [0; 9091]; let mut matched_tx_index_len_ptr: &mut usize = &mut usize::MAX; (self.filter_block_ptr)(block_bytes.as_ptr(), block_bytes.len(), matched_tx_index.as_mut_ptr(), matched_tx_index_len_ptr as *mut _); - if (matched_tx_index_len_ptr.clone() == usize::MAX) { + if matched_tx_index_len_ptr.clone() == usize::MAX { panic!("FFI failure. the caller must set the actual serialized length of the tx-indexes in filter_block"); } let mut matched_tx_indexes: &[usize] = unsafe_block!("We know the caller has set the value for serialized tx index" => &matched_tx_index[..(*matched_tx_index_len_ptr)]); @@ -396,7 +395,7 @@ impl From for LightningError { fn from(v: FFILightningError) -> Self { let err = unsafe_block!("We know error msg is non-null c string" => CStr::from_ptr(v.err.as_ptr()) ); LightningError { - err: err.to_str().unwrap(), + err: err.to_str().unwrap().to_owned(), action: v.action.into() } } diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 5a0be610d64..b7f6bff1f9c 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -4,7 +4,6 @@ use std::{ time::{SystemTime, UNIX_EPOCH} }; -use bitcoin::{secp256k1, BlockHeader, Transaction}; use lightning::{ util::{ config::UserConfig, @@ -20,7 +19,6 @@ use lightning::{ }, routing::router::Route, ln::channelmanager::{PaymentSecret, PaymentPreimage}, - chain::chaininterface::ChainListener }; use crate::{ From 64117759ef0d3c9782814086141325add397cdab Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 5 Jul 2020 16:15:00 +0900 Subject: [PATCH 20/45] add new entrypoint for serilizing ChannelManager --- bindings/src/channelmanager.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index b7f6bff1f9c..950aa554a6f 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -285,6 +285,12 @@ ffi! { into_fixed_buffer(&mut e, buf, &mut actual_channels_len) } + fn serialize_channel_manager(buf_out: Out, buf_len: usize, actual_len: Out, handle: FFIArcChannelManagerHandle) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let mut chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + into_fixed_buffer(&mut chan_man, buf, &mut actual_len) + } + fn release_ffi_channel_manager(handle: FFIArcChannelManagerHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcChannelManagerHandle::dealloc(handle, |mut handle| { FFIResult::ok() From 7b4b5d45995fb16a99f7eaf24148df30074488e5 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Tue, 7 Jul 2020 16:37:01 +0900 Subject: [PATCH 21/45] Remove useless node_id parameter from PeerManager constructor --- bindings/src/peermanager.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 5f56a47b2bb..444576d37e8 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -58,7 +58,6 @@ ffi! { log_ptr: Ref, our_node_secret_ptr: Ref, - our_node_id_ptr: Ref, handle: Out ) -> FFIResult { let network = unsafe_block!("" => *network_ref.as_ref()); @@ -68,11 +67,6 @@ ffi! { let o = unsafe_block!("" => our_node_secret_ptr.as_ref()); o.clone().into() }; - let our_node_id: secp256k1::PublicKey = { - let o = unsafe_block!("" => our_node_id_ptr.as_ref()); - o.clone().into() - }; - let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); @@ -99,7 +93,6 @@ ffi! { let seed = unsafe_block!("It points to valid length buffer" => seed.as_ref()); let peer_man = - FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &seed.bytes, logger_arc); unsafe_block!("" => handle.init(FFIArcPeerManagerHandle::alloc(peer_man))); FFIResult::ok() From 4d8af6f36ae26cf51fd722d9c2ee4b1e20494c0e Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 8 Jul 2020 16:45:07 +0900 Subject: [PATCH 22/45] externalize KeysInterface --- bindings/src/adaptors/mod.rs | 148 ++++++++++++++++++++++++++++++++- bindings/src/channelmanager.rs | 37 +++++++-- 2 files changed, 176 insertions(+), 9 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index e762cc4bb4b..55daae3c901 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -7,8 +7,9 @@ use std::{ }; use bitcoin::hash_types::{BlockHash, Txid}; -use bitcoin::{blockdata::transaction::Transaction, blockdata::script::Script, blockdata::block::Block, consensus::serialize as bitcoin_serialize, Network}; +use bitcoin::{blockdata::transaction::Transaction, blockdata::script::Script, blockdata::block::Block, consensus::serialize as bitcoin_serialize, consensus::deserialize as bitcoin_deserialize, Network}; use bitcoin::secp256k1; +use bitcoin::secp256k1::{Secp256k1, Signing}; use lightning::{ chain::chaininterface::{BroadcasterInterface, FeeEstimator, ConfirmationTarget, ChainWatchInterface, ChainError}, @@ -22,6 +23,12 @@ use lightning::{ pub mod primitives; use primitives::*; use std::sync::Arc; +use lightning::chain::keysinterface::{ChannelKeys, InMemoryChannelKeys, KeysInterface}; +use bitcoin::secp256k1::{SecretKey, PublicKey}; +use bitcoin::secp256k1::Error::InvalidMessage; +use bitcoin::util::key::Error::Secp256k1 as Secp256k1Error; +use lightning::util::ser::Readable; +use lightning::ln::msgs::DecodeError; type Cstr = NonNull; @@ -285,6 +292,145 @@ impl ChainWatchInterface for FFIChainWatchInterface { } } +#[repr(C)] +pub struct FFIChannelKeys { + funding_key: [u8; 32], + revocation_base_key: [u8; 32], + payment_key: [u8; 32], + delayed_payment_base_key: [u8; 32], + htlc_base_key: [u8; 32], + commitment_seed: [u8; 32], + channel_value_satoshis: u64, + key_derivation_params_1: u64, + key_derivation_params_2: u64, +} + +impl FFIChannelKeys { + fn to_in_memory_channel_keys(&self, ctx: &Secp256k1) -> InMemoryChannelKeys { + InMemoryChannelKeys::new( + ctx, + secp256k1::key::SecretKey::from_slice(&self.funding_key).unwrap(), + secp256k1::key::SecretKey::from_slice(&self.revocation_base_key).unwrap(), + secp256k1::key::SecretKey::from_slice(&self.payment_key).unwrap(), + secp256k1::key::SecretKey::from_slice(&self.delayed_payment_base_key).unwrap(), + secp256k1::key::SecretKey::from_slice(&self.htlc_base_key).unwrap(), + self.commitment_seed, + self.channel_value_satoshis, + (self.key_derivation_params_1, self.key_derivation_params_2) + ) + } +} + +impl Readable for FFIChannelKeys { + fn read(reader: &mut R) -> Result { + let funding_key = Readable::read(reader)?; + let revocation_base_key = Readable::read(reader)?; + let payment_key = Readable::read(reader)?; + let delayed_payment_base_key = Readable::read(reader)?; + let htlc_base_key = Readable::read(reader)?; + let commitment_seed = Readable::read(reader)?; + let channel_value_satoshis = Readable::read(reader)?; + let key_derivation_params_1 = Readable::read(reader)?; + let key_derivation_params_2 = Readable::read(reader)?; + Ok (FFIChannelKeys{ + funding_key, + revocation_base_key, + payment_key, + delayed_payment_base_key, + htlc_base_key, + commitment_seed, + channel_value_satoshis, + key_derivation_params_1, + key_derivation_params_2, + }) + } +} + +pub mod keys_interface_fn { + use super::{Bytes32, FFIChannelKeys, Bool}; + pub type GetNodeSecret = extern "cdecl" fn (*mut [u8; 32]); + pub type GetDestinationScript = extern "cdecl" fn (script_ptr: *mut u8, script_len: *mut usize); + pub type GetShutdownPubKey = extern "cdecl" fn (pk_ptr: *mut [u8; 33]); + pub type GetChannelKeys = extern "cdecl" fn (inbound: Bool, satoshis: u64, ffi_channel_keys_ptr: *mut [u8; 216]); + pub type GetOnionRand = extern "cdecl" fn (secret: *mut [u8; 32], prng_seed: *mut [u8; 32]); + pub type GetChannelId = extern "cdecl" fn(channel_id: *mut [u8; 32]); +} + +pub struct FFIKeysInterface { + pub get_node_secret_ptr: keys_interface_fn::GetNodeSecret, + pub get_destination_script_ptr: keys_interface_fn::GetDestinationScript, + pub get_shutdown_pubkey_ptr: keys_interface_fn::GetShutdownPubKey, + pub get_channel_keys_ptr: keys_interface_fn::GetChannelKeys, + pub get_onion_rand_ptr: keys_interface_fn::GetOnionRand, + pub get_channel_id_ptr: keys_interface_fn::GetChannelId, + secp_ctx: Secp256k1, +} + +impl FFIKeysInterface { + pub fn new( + get_node_secret_ptr: keys_interface_fn::GetNodeSecret, + get_destination_script_ptr: keys_interface_fn::GetDestinationScript, + get_shutdown_pubkey_ptr: keys_interface_fn::GetShutdownPubKey, + get_channel_keys_ptr: keys_interface_fn::GetChannelKeys, + get_onion_rand_ptr: keys_interface_fn::GetOnionRand, + get_channel_id_ptr: keys_interface_fn::GetChannelId + ) -> Self { + FFIKeysInterface { + get_node_secret_ptr, + get_destination_script_ptr, + get_shutdown_pubkey_ptr, + get_channel_keys_ptr, + get_onion_rand_ptr, + get_channel_id_ptr, + secp_ctx: Secp256k1::signing_only() + } + } +} + +impl KeysInterface for FFIKeysInterface { + type ChanKeySigner = InMemoryChannelKeys; + + fn get_node_secret(&self) -> SecretKey { + let mut secret = [0u8; 32]; + (self.get_node_secret_ptr)(&mut secret as *mut _); + SecretKey::from_slice(&secret).unwrap() + } + + fn get_destination_script(&self) -> Script { + let mut script_ptr = [0u8; 512]; + let mut script_len: &mut usize = &mut usize::MAX; + (self.get_destination_script_ptr)(script_ptr.as_mut_ptr(), script_len as *mut _); + let s = bitcoin::consensus::Decodable::consensus_decode(&script_ptr[..(*script_len)]).expect("Failed to deserialize script"); + s + } + + fn get_shutdown_pubkey(&self) -> PublicKey { + let mut pk = [0u8; 33]; + (self.get_shutdown_pubkey_ptr)(&mut pk as *mut _); + PublicKey::from_slice(&pk).unwrap() + } + + fn get_channel_keys(&self, inbound: bool, channel_value_satoshis: u64) -> Self::ChanKeySigner { + let mut channel_keys_b = [0u8; 216]; + (self.get_channel_keys_ptr)(if inbound { Bool::True } else { Bool::False }, channel_value_satoshis, &mut channel_keys_b as *mut _); + let ffi_channel_keys: FFIChannelKeys = Readable::read(&mut channel_keys_b.as_ref()).expect("Failed to deserialize channel keys"); + ffi_channel_keys.to_in_memory_channel_keys(&self.secp_ctx) + } + + fn get_onion_rand(&self) -> (SecretKey, [u8; 32]) { + let mut secret = [0; 32]; + let mut prng_seed = [0; 32]; + (self.get_onion_rand_ptr)(&mut secret as *mut _, &mut prng_seed as *mut _); + (SecretKey::from_slice(&secret).unwrap(), prng_seed) + } + + fn get_channel_id(&self) -> [u8; 32] { + let mut channel_id = [0; 32]; + (self.get_channel_id_ptr)(&mut channel_id as *mut _); + channel_id + } +} + pub mod socket_descriptor_fn { use super::FFIBytes; pub type SendData = extern "cdecl" fn (data: FFIBytes, resume_read: u8) -> usize; diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 950aa554a6f..e25fadc9a11 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -14,7 +14,7 @@ use lightning::{ channelmonitor::SimpleManyChannelMonitor }, chain::{ - keysinterface::{KeysManager, InMemoryChannelKeys}, + keysinterface::{InMemoryChannelKeys}, transaction::OutPoint }, routing::router::Route, @@ -38,7 +38,7 @@ use crate::{ }; pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; -pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; +pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { @@ -73,7 +73,6 @@ fn send_payment_inner(handle: FFIArcChannelManagerHandle, route_ref: Ref, ffi_network: FFINetwork, cfg: Ref, @@ -84,6 +83,8 @@ pub(crate) fn construct_channel_manager( filter_block: &chain_watch_interface_fn::FilterBlock, reentered: &chain_watch_interface_fn::ReEntered, + keys_interface: Arc, + broadcast_transaction_ptr: Ref, log_ref: &ffilogger_fn::LogExtern, get_est_sat_per_1000_weight_ptr: Ref, @@ -92,7 +93,6 @@ pub(crate) fn construct_channel_manager( ) -> FFIArcChannelManager { let network = ffi_network.to_network(); let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let mut seed: [u8; 32] = unsafe_block!("it points to valid length buffer" => seed.as_ref()).clone().bytes; let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); @@ -114,7 +114,6 @@ pub(crate) fn construct_channel_manager( let fee_est_fn_ref = unsafe_block!("" => get_est_sat_per_1000_weight_ptr.as_ref()); let fee_est = FFIFeeEstimator{ get_est_sat_per_1000_weight_ptr: *fee_est_fn_ref }; - let keyman = Arc::new(KeysManager::new(&seed, network, now.as_secs(), now.subsec_nanos())); let cfg = unsafe_block!("" => cfg.as_ref()); let monitor = @@ -126,7 +125,7 @@ pub(crate) fn construct_channel_manager( monitor, broadcaster, logger_arc, - keyman, + keys_interface, cfg.clone(), cur_block_height ) @@ -135,7 +134,6 @@ pub(crate) fn construct_channel_manager( ffi! { fn create_channel_manager( - seed: Ref, network_ref: Ref, cfg: Ref, @@ -146,6 +144,13 @@ ffi! { filter_block_ptr: Ref, reentered_ptr: Ref, + get_node_secret_ptr: Ref, + get_destination_script_ptr: Ref, + get_shutdown_key_ptr: Ref, + get_channel_keys_ptr: Ref, + get_onion_rand_ptr: Ref, + get_channel_id_ptr: Ref, + broadcast_transaction_ptr: Ref, log_ptr: Ref, get_est_sat_per_1000_weight_ptr: Ref, @@ -161,9 +166,23 @@ ffi! { let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let get_node_secret_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_node_secret_ptr.as_ref()); + let get_destination_script_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_destination_script_ptr.as_ref()); + let get_shutdown_key_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_shutdown_key_ptr.as_ref()); + let get_channel_keys_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_channel_keys_ptr.as_ref()); + let get_onion_rand_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_onion_rand_ptr.as_ref()); + let get_channel_id_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_channel_id_ptr.as_ref()); + let keys_interface = FFIKeysInterface::new( + *get_node_secret_ref, + *get_destination_script_ref, + *get_shutdown_key_ref, + *get_channel_keys_ref, + *get_onion_rand_ref, + *get_channel_id_ref, + ); + let chan_man_raw = construct_channel_manager( - seed, network, cfg, @@ -174,6 +193,8 @@ ffi! { filter_block_ref, reentered_ref, + Arc::new(keys_interface), + broadcast_transaction_ptr, log_ref, get_est_sat_per_1000_weight_ptr, From 7352003caf8a3d57b2fe957a1a9f0d7a3fc9bb28 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 9 Jul 2020 18:54:42 +0900 Subject: [PATCH 23/45] avoid using null pointer everywhere possible --- bindings/src/adaptors/mod.rs | 14 +++++++------- bindings/src/adaptors/primitives.rs | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 55daae3c901..05aae7c1b38 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -256,21 +256,21 @@ impl ChainWatchInterface for FFIChainWatchInterface { (self.watch_all_txn_ptr)() } fn get_chain_utxo(&self, genesis_hash: BlockHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { - let err = std::ptr::null_mut(); + println!("Querying chain utxo by shortChannelId {}. ", unspent_tx_output_identifier); + let mut err = &mut FFIChainError::UnInitialized; // the length can be anything as long as it is enough to put the scriptPubKey. // probably this is a bit overkill but who cares. let mut script = [0u8; 128]; - let script_len = std::ptr::null_mut(); - let amount_satoshis = std::ptr::null_mut(); - (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err, script.as_mut_ptr(), script_len, amount_satoshis); - if err.is_null() { + let mut script_len = &mut usize::MAX; + let mut amount_satoshis = &mut u64::MAX; + (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err as *mut FFIChainError, script.as_mut_ptr(), script_len as *mut _, amount_satoshis as *mut _); + if *err == FFIChainError::UnInitialized { let script_bytes: &[u8] = unsafe_block!("We know the caller has set the value into the script_ptr, script_len" => &script[..(*script_len)]); let amount: u64 = unsafe_block!("We know the caller has set the value into the amount_satoshis" => *amount_satoshis); let s = bitcoin::consensus::deserialize(script_bytes).expect("Failed to parse scriptpubkey"); Ok((s, amount)) } else { - let e = unsafe_block!("we know the error is not a null pointer" => (*err).clone()); - Err(e.into()) + Err(err.clone().into()) } } diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 44a24a98b5e..22cf0d10b79 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -228,8 +228,8 @@ pub struct FFITxOut { pub script_pubkey: FFIScript, } -#[derive(Clone, Copy, Debug)] -#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u32)] pub enum FFIChainError { /// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash) NotSupported, @@ -237,6 +237,7 @@ pub enum FFIChainError { NotWatched, /// Tx doesn't exist or is unconfirmed UnknownTx, + UnInitialized, } impl From for ChainError { @@ -245,6 +246,7 @@ impl From for ChainError { FFIChainError::NotSupported => ChainError::NotSupported, FFIChainError::NotWatched => ChainError::NotWatched, FFIChainError::UnknownTx => ChainError::UnknownTx, + FFIChainError::UnInitialized => panic!("We should never try to convert uninitialized FFIChainError into ChainError") } } } From 082d03e1105f8cb5b02147d6e329e897b8fb760d Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 11 Jul 2020 11:44:34 +0900 Subject: [PATCH 24/45] add new entrypoint ChannelManager::claim_funds_without_secret --- bindings/src/channelmanager.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index e25fadc9a11..ea2083cc10f 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -292,6 +292,12 @@ ffi! { FFIResult::ok() } + fn claim_funds_without_secret(payment_preimage: Ref, expected_amount: u64, handle: FFIArcChannelManagerHandle, result: Out) -> FFIResult { + let r = claim_funds_inner(payment_preimage, None, expected_amount, handle); + unsafe_block!("We know out parameter is writable" => result.init(r.into())); + FFIResult::ok() + } + fn update_fee(channel_id: Ref<[u8; 32]>, feerate_per_kw: u32, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); let channel_id: &[u8;32] = unsafe_block!("" => channel_id.as_ref()); From 5ca14319b301211fbda1f9514d1d0c66412fe1da Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 15 Jul 2020 10:20:52 +0900 Subject: [PATCH 25/45] impl Read/Write for RouteHint --- lightning/src/ln/msgs.rs | 9 +++++++++ lightning/src/ln/peer_handler.rs | 3 ++- lightning/src/routing/router.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index cdf1078de1f..cf20c34b486 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -468,6 +468,14 @@ pub struct LightningError { pub action: ErrorAction, } +impl fmt::Display for LightningError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.err.as_str()) + } +} + +impl std::error::Error for LightningError {} + /// Struct used to return values from revoke_and_ack messages, containing a bunch of commitment /// transaction updates if they were pending. #[derive(PartialEq, Clone)] @@ -659,6 +667,7 @@ pub use self::fuzzy_internal_msgs::*; #[cfg(not(feature = "fuzztarget"))] pub(crate) use self::fuzzy_internal_msgs::*; use std::panic::{RefUnwindSafe, UnwindSafe}; +use bitcoin::hashes::core::fmt::{Formatter}; #[derive(Clone)] pub(crate) struct OnionPacket { diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 6c44de51864..d1252d2ae47 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -207,7 +207,8 @@ pub struct PeerManager { + fn write(&self, writer: &mut W) -> Result<(), Error> { + (self.len() as u64).write(writer)?; + for hint in self { + hint.write(writer)?; + } + Ok(()) + } +} + +impl Readable for Vec { + fn read(reader: &mut R) -> Result { + let hint_count: u64 = Readable::read(reader)?; + let mut hints = Vec::with_capacity(hint_count as usize); + for _ in 0..hint_count { + hints.push(Readable::read(reader)?); + } + Ok(hints) + } +} + #[derive(Eq, PartialEq)] struct RouteGraphNode { pubkey: PublicKey, From 0c74e216919c42590490d4e8fab74b0beae9f689 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 15 Jul 2020 17:04:55 +0900 Subject: [PATCH 26/45] Add send_payment ffi entrypoint for PeerManager Which is a facade for `ChannelManager::send_payment` Since that PeerManager holds an information about current network graph, we need this entrypoint to not let user caluculate the path by themselves. It also deletes some useless imports and unused function `static_assert` from original `rust-csharp-ffi` --- bindings/src/adaptors/mod.rs | 10 +++------ bindings/src/adaptors/primitives.rs | 2 +- bindings/src/blocknotifier.rs | 2 -- bindings/src/channelmanager.rs | 2 -- bindings/src/ffi_test_utils.rs | 1 - bindings/src/peermanager.rs | 33 ++++++++++++++++++++++++----- bindings/src/test_utils.rs | 12 ----------- 7 files changed, 32 insertions(+), 30 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 05aae7c1b38..b6343d0c2d3 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -7,7 +7,7 @@ use std::{ }; use bitcoin::hash_types::{BlockHash, Txid}; -use bitcoin::{blockdata::transaction::Transaction, blockdata::script::Script, blockdata::block::Block, consensus::serialize as bitcoin_serialize, consensus::deserialize as bitcoin_deserialize, Network}; +use bitcoin::{blockdata::transaction::Transaction, blockdata::script::Script, blockdata::block::Block, consensus::serialize as bitcoin_serialize, Network}; use bitcoin::secp256k1; use bitcoin::secp256k1::{Secp256k1, Signing}; @@ -23,10 +23,8 @@ use lightning::{ pub mod primitives; use primitives::*; use std::sync::Arc; -use lightning::chain::keysinterface::{ChannelKeys, InMemoryChannelKeys, KeysInterface}; +use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface}; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use bitcoin::secp256k1::Error::InvalidMessage; -use bitcoin::util::key::Error::Secp256k1 as Secp256k1Error; use lightning::util::ser::Readable; use lightning::ln::msgs::DecodeError; @@ -224,8 +222,6 @@ impl FFIChainWatchInterface { get_chain_utxo: chain_watch_interface_fn::GetChainUtxoPtr, filter_block: chain_watch_interface_fn::FilterBlock, reentered: chain_watch_interface_fn::ReEntered, - network: Network, - logger: Arc ) -> FFIChainWatchInterface { FFIChainWatchInterface{ install_watch_tx_ptr: install_watch_tx, @@ -347,7 +343,7 @@ impl Readable for FFIChannelKeys { } pub mod keys_interface_fn { - use super::{Bytes32, FFIChannelKeys, Bool}; + use super::Bool; pub type GetNodeSecret = extern "cdecl" fn (*mut [u8; 32]); pub type GetDestinationScript = extern "cdecl" fn (script_ptr: *mut u8, script_len: *mut usize); pub type GetShutdownPubKey = extern "cdecl" fn (pk_ptr: *mut [u8; 33]); diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 22cf0d10b79..ecb40aff44e 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -213,7 +213,7 @@ macro_rules! array_struct{ } } -/// Length-prefixed script. +// Length-prefixed script. array_struct!(FFIScript); impl FFIScript { pub fn to_script(&self) -> Script { diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index dfc40308278..c0f370c1a09 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -48,8 +48,6 @@ ffi! { *get_chain_utxo_ref, *filter_block_ref, *reentered_ref, - network, - logger_arc.clone() )); let block_notifier = FFIBlockNotifier::new(chain_watch_interface_arc); unsafe_block!("We know handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(block_notifier))); diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index ea2083cc10f..41609c81caa 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -104,8 +104,6 @@ pub(crate) fn construct_channel_manager( *get_chain_utxo, *filter_block, *reentered, - network, - logger_arc.clone() )); let broadcast_ref = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); diff --git a/bindings/src/ffi_test_utils.rs b/bindings/src/ffi_test_utils.rs index ab7a5078aa4..be987cc8361 100644 --- a/bindings/src/ffi_test_utils.rs +++ b/bindings/src/ffi_test_utils.rs @@ -1,7 +1,6 @@ use crate::error::FFIResult; use lightning::util::events::Event; use lightning::chain::transaction::OutPoint; -use bitcoin::hash_types::Txid; use hex; use crate::adaptors::primitives::FFIEvents; use crate::utils::into_fixed_buffer; diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 444576d37e8..acbbaca9a51 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -20,7 +20,10 @@ use crate::{ adaptors::primitives::{Bytes32, FFIBytes, Bytes33}, utils::into_fixed_buffer }; -use crate::channelmanager::FFIArcChannelManagerHandle; +use crate::channelmanager::{FFIArcChannelManagerHandle}; +use lightning::routing::router::{get_route, RouteHint}; +use lightning::ln::channelmanager::PaymentHash; +use lightning::util::ser::Readable; type FFISimpleArcPeerManager = PeerManager, Arc, Arc>>, Arc>; type FFIArcPeerManagerHandle<'a> = HandleShared<'a, FFISimpleArcPeerManager>; @@ -40,7 +43,6 @@ fn construct_socket_desc ( socket } - ffi! { fn create_peer_manager( seed: Ref, @@ -82,9 +84,7 @@ ffi! { *watch_all_txn_ref, *get_chain_utxo_ref, *filter_block_ref, - *reentered_ref, - network.to_network(), - logger_arc.clone() + *reentered_ref )); let route_handler = NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()); let chan_man = unsafe_block!("It must point to valid ChannelManager" => chan_man.as_arc()); @@ -187,6 +187,29 @@ ffi! { into_fixed_buffer(&mut node_ids, buf, &mut actual_node_ids_len) } + fn send_payment_with_peer_manager( + their_node_id: Ref, + payment_hash_ref: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + peerman_handle: FFIArcPeerManagerHandle, + chanman_handle: FFIArcChannelManagerHandle + ) -> FFIResult { + let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid ChannelManager" => chanman_handle.as_ref()); + let their_node_id = unsafe_block!("" => their_node_id.as_ref()); + let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know the handle points to valid PeerManager" => peerman_handle.as_ref()); + let graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); + let hops = chan_man.list_usable_channels(); + let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); + let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); + let route = get_route(&chan_man.get_our_node_id(), &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; + let payment_hash: PaymentHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()).clone().into(); + chan_man.send_payment(&route, payment_hash, &None)?; + FFIResult::ok() + } + fn release_peer_manager(handle: FFIArcPeerManagerHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcPeerManagerHandle::dealloc(handle, |mut handle| { // We keep reference to message_handler from wrapper side so that we can call methods diff --git a/bindings/src/test_utils.rs b/bindings/src/test_utils.rs index 3e986508f0f..f9c40c6bc56 100644 --- a/bindings/src/test_utils.rs +++ b/bindings/src/test_utils.rs @@ -1,15 +1,3 @@ -macro_rules! assert_match { - ($bind:pat = $bind_from:expr) => { - assert_match!($bind = $bind_from => ()) - }; - ($bind:pat = $bind_from:expr => $with:expr) => { - match $bind_from { - $bind => $with, - _ => panic!("assertion failed: unexpected value `{:?}`", $bind_from), - } - }; -} - pub mod static_assert { use std::panic::UnwindSafe; From fd028921d16ac731b2ec286e46044b71a38866d8 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 16 Jul 2020 20:24:00 +0900 Subject: [PATCH 27/45] split send_payment into mpp and non-mpp --- bindings/src/error.rs | 8 +++++ bindings/src/peermanager.rs | 61 ++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/bindings/src/error.rs b/bindings/src/error.rs index acd16d57fb7..582666dda85 100644 --- a/bindings/src/error.rs +++ b/bindings/src/error.rs @@ -39,6 +39,7 @@ pub enum Kind { /// When this is the last error, the caller should try to increase the buffer size and call the function again. BufferTooSmall, InternalError, + PaymentSendFailure, } impl From for FFIResult @@ -167,6 +168,12 @@ impl FFIResult { self.kind == Kind::InternalError } + pub (super) fn payment_send_failure() -> Self { + FFIResult { kind: Kind::PaymentSendFailure, id: next_err_id() } + } + + pub fn is_payment_send_failure(&self) -> bool { self.kind == Kind::PaymentSendFailure } + /// Attempt to get a human-readable error message for a result. /// If the result is successful then this method returns `None`. pub fn as_err(&self) -> Option<&'static str> { @@ -177,6 +184,7 @@ impl FFIResult { Kind::DeserializationFailure => Some("Failed to deserialize byte array passed to ffi"), Kind::InternalError => Some("An internal error occured"), Kind::BufferTooSmall => Some("buffer was too small"), + Kind::PaymentSendFailure => Some("send_payment has failed") } } diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index acbbaca9a51..b8495748b08 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -21,8 +21,8 @@ use crate::{ utils::into_fixed_buffer }; use crate::channelmanager::{FFIArcChannelManagerHandle}; -use lightning::routing::router::{get_route, RouteHint}; -use lightning::ln::channelmanager::PaymentHash; +use lightning::routing::router::{get_route, RouteHint, Route}; +use lightning::ln::channelmanager::{PaymentHash, PaymentSecret}; use lightning::util::ser::Readable; type FFISimpleArcPeerManager = PeerManager, Arc, Arc>>, Arc>; @@ -43,6 +43,34 @@ fn construct_socket_desc ( socket } +fn send_payment_inner( + their_node_id: Ref, + payment_hash_ref: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + maybe_payment_secret: &Option, + peerman_handle: FFIArcPeerManagerHandle, + chanman_handle: FFIArcChannelManagerHandle +) -> FFIResult { + let their_node_id = unsafe_block!("" => their_node_id.as_ref()); + let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; + let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know the handle points to valid PeerManager" => peerman_handle.as_ref()); + let chan_man = unsafe_block!("It must point to valid ChannelManager" => chanman_handle.as_arc()); + let graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); + let hops = chan_man.list_usable_channels(); + let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); + let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); + let mut route = get_route(&chan_man.get_our_node_id(), &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; + if (maybe_payment_secret.is_none()) + { + route = Route { paths: route.paths[0..1].to_vec() } + } + let payment_hash: PaymentHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()).clone().into(); + chan_man.send_payment(&route, payment_hash, maybe_payment_secret)?; + FFIResult::ok() +} + ffi! { fn create_peer_manager( seed: Ref, @@ -187,7 +215,7 @@ ffi! { into_fixed_buffer(&mut node_ids, buf, &mut actual_node_ids_len) } - fn send_payment_with_peer_manager( + fn send_non_mpp_payment_with_peer_manager( their_node_id: Ref, payment_hash_ref: Ref, last_hops_ref: Ref, @@ -196,18 +224,21 @@ ffi! { peerman_handle: FFIArcPeerManagerHandle, chanman_handle: FFIArcChannelManagerHandle ) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid ChannelManager" => chanman_handle.as_ref()); - let their_node_id = unsafe_block!("" => their_node_id.as_ref()); - let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know the handle points to valid PeerManager" => peerman_handle.as_ref()); - let graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); - let hops = chan_man.list_usable_channels(); - let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); - let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); - let route = get_route(&chan_man.get_our_node_id(), &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; - let payment_hash: PaymentHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()).clone().into(); - chan_man.send_payment(&route, payment_hash, &None)?; - FFIResult::ok() + send_payment_inner(their_node_id, payment_hash_ref, last_hops_ref, final_value_msat, final_cltv, &None, peerman_handle, chanman_handle) + } + + fn send_mpp_payment_with_peer_manager( + their_node_id: Ref, + payment_hash_ref: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + payment_secret_ref: Ref, + peerman_handle: FFIArcPeerManagerHandle, + chanman_handle: FFIArcChannelManagerHandle + ) -> FFIResult { + let payment_secret: PaymentSecret = unsafe_block!("We know the pointer points to valid payment secret and it lives as long as this function" => payment_secret_ref.as_ref()).clone().into(); + send_payment_inner(their_node_id, payment_hash_ref, last_hops_ref, final_value_msat, final_cltv, &Some(payment_secret), peerman_handle, chanman_handle) } fn release_peer_manager(handle: FFIArcPeerManagerHandle) -> FFIResult { From 106da74eada0a35afec19caabee8d4e913cb78ad Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 20 Jul 2020 17:53:13 +0900 Subject: [PATCH 28/45] fix some compiler warnings --- bindings/src/adaptors/mod.rs | 33 +++++++++++++++-------------- bindings/src/adaptors/primitives.rs | 10 --------- bindings/src/blocknotifier.rs | 15 ++++--------- bindings/src/channelmanager.rs | 33 ++++++++++++++--------------- bindings/src/handle/mod.rs | 6 +++--- bindings/src/peermanager.rs | 26 +++++++++++------------ 6 files changed, 53 insertions(+), 70 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index b6343d0c2d3..12b7eed3482 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -22,7 +22,6 @@ use lightning::{ pub mod primitives; use primitives::*; -use std::sync::Arc; use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface}; use bitcoin::secp256k1::{SecretKey, PublicKey}; use lightning::util::ser::Readable; @@ -253,16 +252,18 @@ impl ChainWatchInterface for FFIChainWatchInterface { } fn get_chain_utxo(&self, genesis_hash: BlockHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { println!("Querying chain utxo by shortChannelId {}. ", unspent_tx_output_identifier); - let mut err = &mut FFIChainError::UnInitialized; + let err = &mut FFIChainError::UnInitialized; // the length can be anything as long as it is enough to put the scriptPubKey. // probably this is a bit overkill but who cares. let mut script = [0u8; 128]; - let mut script_len = &mut usize::MAX; - let mut amount_satoshis = &mut u64::MAX; + let script_len = &mut usize::MAX; + let amount_satoshis = &mut u64::MAX; (self.get_chain_utxo_ptr)(&genesis_hash.into(), unspent_tx_output_identifier, err as *mut FFIChainError, script.as_mut_ptr(), script_len as *mut _, amount_satoshis as *mut _); if *err == FFIChainError::UnInitialized { - let script_bytes: &[u8] = unsafe_block!("We know the caller has set the value into the script_ptr, script_len" => &script[..(*script_len)]); - let amount: u64 = unsafe_block!("We know the caller has set the value into the amount_satoshis" => *amount_satoshis); + assert!(*amount_satoshis != u64::MAX); + assert!(*script_len != usize::MAX); + let script_bytes: &[u8] = &script[..(*script_len)]; + let amount: u64 = *amount_satoshis; let s = bitcoin::consensus::deserialize(script_bytes).expect("Failed to parse scriptpubkey"); Ok((s, amount)) } else { @@ -274,12 +275,12 @@ impl ChainWatchInterface for FFIChainWatchInterface { let block_bytes = bitcoin_serialize(block); // the minimum weight for one tx is 440. So the max number of tx in one block is 9090. let mut matched_tx_index = [0; 9091]; - let mut matched_tx_index_len_ptr: &mut usize = &mut usize::MAX; + let matched_tx_index_len_ptr: &mut usize = &mut usize::MAX; (self.filter_block_ptr)(block_bytes.as_ptr(), block_bytes.len(), matched_tx_index.as_mut_ptr(), matched_tx_index_len_ptr as *mut _); if matched_tx_index_len_ptr.clone() == usize::MAX { panic!("FFI failure. the caller must set the actual serialized length of the tx-indexes in filter_block"); } - let mut matched_tx_indexes: &[usize] = unsafe_block!("We know the caller has set the value for serialized tx index" => &matched_tx_index[..(*matched_tx_index_len_ptr)]); + let matched_tx_indexes: &[usize] = &matched_tx_index[..(*matched_tx_index_len_ptr)]; matched_tx_indexes.to_vec() } @@ -394,7 +395,7 @@ impl KeysInterface for FFIKeysInterface { fn get_destination_script(&self) -> Script { let mut script_ptr = [0u8; 512]; - let mut script_len: &mut usize = &mut usize::MAX; + let script_len: &mut usize = &mut usize::MAX; (self.get_destination_script_ptr)(script_ptr.as_mut_ptr(), script_len as *mut _); let s = bitcoin::consensus::Decodable::consensus_decode(&script_ptr[..(*script_len)]).expect("Failed to deserialize script"); s @@ -598,31 +599,31 @@ impl Writer for VecWriter { } impl RoutingMessageHandler for FFIRoutingMsgHandler { - fn handle_node_announcement(&self, msg: &NodeAnnouncement) -> Result { + fn handle_node_announcement(&self, _msg: &NodeAnnouncement) -> Result { unimplemented!() } - fn handle_channel_announcement(&self, msg: &ChannelAnnouncement) -> Result { + fn handle_channel_announcement(&self, _msg: &ChannelAnnouncement) -> Result { unimplemented!() } - fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result { + fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result { unimplemented!() } - fn handle_htlc_fail_channel_update(&self, update: &HTLCFailChannelUpdate) { + fn handle_htlc_fail_channel_update(&self, _update: &HTLCFailChannelUpdate) { unimplemented!() } - fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { + fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { unimplemented!() } - fn get_next_node_announcements(&self, starting_point: Option<&secp256k1::PublicKey>, batch_amount: u8) -> Vec { + fn get_next_node_announcements(&self, _starting_point: Option<&secp256k1::PublicKey>, _batch_amount: u8) -> Vec { unimplemented!() } - fn should_request_full_sync(&self, node_id: &secp256k1::PublicKey) -> bool { + fn should_request_full_sync(&self, _node_id: &secp256k1::PublicKey) -> bool { unimplemented!() } } diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index ecb40aff44e..14e415a0ae2 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -8,7 +8,6 @@ use bitcoin::{ hash_types::{Txid, BlockHash}, blockdata::script::Script, hashes::Hash, - Transaction, secp256k1 }; use lightning::{ @@ -268,15 +267,6 @@ array_struct!(FFIBlock); // General purpose byte array which has to cross ffi-boundary array_struct!(FFIBytes); -/// For `ChainWatchInterface::filter_block` -impl TryFrom for (Vec<&Transaction>, Vec) { - type Error = FFIResult; - - fn try_from(bytes: FFIBytes) -> Result { - unimplemented!() - } -} - pub struct FFIEvents { pub events: Vec, } diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index c0f370c1a09..999433ebb69 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -8,7 +8,6 @@ use crate::{ channelmanager::{FFIArcChannelManagerHandle, FFIArcChannelManager}, adaptors::{chain_watch_interface_fn, FFIChainWatchInterface} }; -use crate::adaptors::{FFINetwork, ffilogger_fn, FFILogger}; use bitcoin::BlockHeader; type FFIBlockNotifier = BlockNotifier<'static, Arc, Arc>; @@ -17,9 +16,6 @@ type FFIBlockNotifierHandle<'a> = HandleShared<'a, FFIBlockNotifier>; ffi! { fn create_block_notifier( - network_ref: FFINetwork, - log_ptr: Ref, - install_watch_tx_ptr: Ref, install_watch_outpoint_ptr: Ref, watch_all_txn_ptr: Ref, @@ -29,9 +25,6 @@ ffi! { handle: Out ) -> FFIResult { - let network = network_ref.to_network(); - let log_ref = unsafe_block!("" => log_ptr.as_ref()); - let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); @@ -59,7 +52,7 @@ ffi! { handle: FFIBlockNotifierHandle ) -> FFIResult { let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); - let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.register_listener(chan_man); FFIResult::ok() } @@ -69,7 +62,7 @@ ffi! { handle: FFIBlockNotifierHandle ) -> FFIResult { let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); - let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.unregister_listener(chan_man); FFIResult::ok() } @@ -79,7 +72,7 @@ ffi! { block_len: usize, height: u32, handle: FFIBlockNotifierHandle) -> FFIResult { - let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + let block_notifier: &FFIBlockNotifier = handle.as_ref(); let block_bytes = unsafe_block!("block_ptr points to valid buffer of block_len length" => block_ptr.as_bytes(block_len)); let block = bitcoin::consensus::deserialize(block_bytes)?; block_notifier.block_connected(&block, height); @@ -92,7 +85,7 @@ ffi! { height: u32, handle: FFIBlockNotifierHandle ) -> FFIResult { - let block_notifier: &FFIBlockNotifier = unsafe_block!("We know the handle pointers to valid block notifier" => handle.as_ref()); + let block_notifier: &FFIBlockNotifier = handle.as_ref(); let block_header_bytes: &[u8] = unsafe_block!("We know it points to valid buffer of specified length" => block_header_ptr.as_bytes(block_header_len)); let block_header: BlockHeader = bitcoin::consensus::encode::deserialize(block_header_bytes)?; diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 41609c81caa..95d0459240a 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -1,7 +1,6 @@ use std::{ convert::{TryInto}, sync::Arc, - time::{SystemTime, UNIX_EPOCH} }; use lightning::{ @@ -42,28 +41,28 @@ pub type FFIArcChannelManager = ChannelManager = HandleShared<'a, FFIArcChannelManager>; fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let payment_hash: &Bytes32 = unsafe_block!("" => payment_hash.as_ref()); let payment_hash: PaymentHash = payment_hash.clone().try_into()?; Ok(chan_man.fail_htlc_backwards(&payment_hash, payment_secret)) } fn create_channel_inner(their_network_key: Ref, channel_value_satoshis: u64, push_msat: u64, user_id: u64, override_config: Option, handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let their_network_key = unsafe_block!("We know it points to valid public key buffer" => their_network_key.as_ref()).clone().try_into()?; chan_man.create_channel(their_network_key, channel_value_satoshis, push_msat, user_id, override_config)?; FFIResult::ok() } fn claim_funds_inner(payment_preimage: Ref, payment_secret: Option, expected_amount: u64, handle: FFIArcChannelManagerHandle) -> bool { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let payment_preimage: PaymentPreimage = unsafe_block!("" => payment_preimage.as_ref()).clone().into(); chan_man.claim_funds(payment_preimage, &payment_secret, expected_amount) } fn send_payment_inner(handle: FFIArcChannelManagerHandle, route_ref: Ref, payment_hash_ref: Ref, payment_secret: Option) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let route_ffi: &FFIRoute = unsafe_block!("We know it points to valid route data" => route_ref.as_ref()); let payment_hash_ffi: &Bytes32 = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()); let payment_hash: PaymentHash = payment_hash_ffi.clone().into(); @@ -92,7 +91,6 @@ pub(crate) fn construct_channel_manager( ) -> FFIArcChannelManager { let network = ffi_network.to_network(); - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let logger_arc = Arc::new( FFILogger{ log_ptr: *log_ref } ); @@ -204,7 +202,7 @@ ffi! { fn list_channels(buf_out: Out, buf_len: usize, actual_channels_len: Out, handle: FFIArcChannelManagerHandle) -> FFIResult { let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let mut channels = chan_man.list_channels(); into_fixed_buffer(&mut channels, buf, &mut actual_channels_len) } @@ -219,21 +217,21 @@ ffi! { } fn close_channel(channel_id: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let channel_id: &Bytes32 = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); chan_man.close_channel(&channel_id.bytes)?; FFIResult::ok() } fn force_close_channel(channel_id: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let channel_id = unsafe_block!("We know it points to valid data and it lives as long as the function call" => channel_id.as_ref()); chan_man.force_close_channel(&channel_id.bytes); FFIResult::ok() } fn force_close_all_channels(handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); chan_man.force_close_all_channels(); FFIResult::ok() } @@ -248,7 +246,8 @@ ffi! { send_payment_inner(handle, route_ref, payment_hash_ref, None) } - fn funding_transaction_generated(temporary_channel_id: Ref, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to a valid channel_manager" => handle.as_ref()); + fn funding_transaction_generated(temporary_channel_id: Ref, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { + let chan_man: &FFIArcChannelManager = handle.as_ref(); let temporary_channel_id: &Bytes32 = unsafe_block!("data lives as long as this function and it points to a valid value" => temporary_channel_id.as_ref()); let funding_txo: OutPoint = funding_txo.try_into()?; chan_man.funding_transaction_generated(&temporary_channel_id.bytes, funding_txo); @@ -256,13 +255,13 @@ ffi! { } fn process_pending_htlc_forwards(handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); chan_man.process_pending_htlc_forwards(); FFIResult::ok() } fn timer_chan_freshness_every_min(handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); chan_man.timer_chan_freshness_every_min(); FFIResult::ok() } @@ -297,7 +296,7 @@ ffi! { } fn update_fee(channel_id: Ref<[u8; 32]>, feerate_per_kw: u32, handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let channel_id: &[u8;32] = unsafe_block!("" => channel_id.as_ref()); chan_man.update_fee(channel_id.clone(), feerate_per_kw)?; FFIResult::ok() @@ -305,19 +304,19 @@ ffi! { fn get_and_clear_pending_events(handle: FFIArcChannelManagerHandle, buf_out: Out, buf_len: usize, actual_channels_len: Out) -> FFIResult { let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); - let chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let mut e = FFIEvents{ events: chan_man.get_and_clear_pending_events() }; into_fixed_buffer(&mut e, buf, &mut actual_channels_len) } fn serialize_channel_manager(buf_out: Out, buf_len: usize, actual_len: Out, handle: FFIArcChannelManagerHandle) -> FFIResult { let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); - let mut chan_man: &FFIArcChannelManager = unsafe_block!("We know handle points to valid channel_manager" => handle.as_ref()); + let mut chan_man: &FFIArcChannelManager = handle.as_ref(); into_fixed_buffer(&mut chan_man, buf, &mut actual_len) } fn release_ffi_channel_manager(handle: FFIArcChannelManagerHandle) -> FFIResult { - unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcChannelManagerHandle::dealloc(handle, |mut handle| { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcChannelManagerHandle::dealloc(handle, |mut _handle| { FFIResult::ok() })) } diff --git a/bindings/src/handle/mod.rs b/bindings/src/handle/mod.rs index 17ce1af12af..52311272467 100644 --- a/bindings/src/handle/mod.rs +++ b/bindings/src/handle/mod.rs @@ -49,9 +49,9 @@ where } unsafe_fn!("The pointer must be nonnull" => - pub fn as_arc(&self) -> Arc { - Arc::from_raw(self.0) - } + pub fn as_arc(&self) -> Arc { + Arc::from_raw(self.0) + } ); unsafe_fn!("There are no other live references and the handle won't be used again" => diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index b8495748b08..60e7917940b 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -55,14 +55,16 @@ fn send_payment_inner( ) -> FFIResult { let their_node_id = unsafe_block!("" => their_node_id.as_ref()); let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know the handle points to valid PeerManager" => peerman_handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = peerman_handle.as_ref(); let chan_man = unsafe_block!("It must point to valid ChannelManager" => chanman_handle.as_arc()); let graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); let hops = chan_man.list_usable_channels(); let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); - let mut route = get_route(&chan_man.get_our_node_id(), &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; - if (maybe_payment_secret.is_none()) + let our_node_id = chan_man.get_our_node_id(); + println!("parameter to get_rotue is our_node_id: {:?} \n final_value_msat: {:?} \n final_cltv: {:?}", &our_node_id, final_value_msat, final_cltv); + let mut route = get_route(&our_node_id, &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; + if maybe_payment_secret.is_none() { route = Route { paths: route.paths[0..1].to_vec() } } @@ -74,7 +76,6 @@ fn send_payment_inner( ffi! { fn create_peer_manager( seed: Ref, - network_ref: Ref, cfg: Ref, chan_man: FFIArcChannelManagerHandle, @@ -90,7 +91,6 @@ ffi! { our_node_secret_ptr: Ref, handle: Out ) -> FFIResult { - let network = unsafe_block!("" => *network_ref.as_ref()); let log_ref = unsafe_block!("" => log_ptr.as_ref()); let our_node_secret: secp256k1::SecretKey = { @@ -133,7 +133,7 @@ ffi! { handle: FFIArcPeerManagerHandle ) -> FFIResult { let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); peer_man.new_inbound_connection(socket)?; FFIResult::ok() } @@ -147,7 +147,7 @@ ffi! { initial_send: Out<[u8; 50]> ) -> FFIResult { let socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); let their_node_id = unsafe_block!("" => their_node_id.as_ref()); let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; let act_one = peer_man.new_outbound_connection(their_node_id, socket)?; @@ -158,7 +158,7 @@ ffi! { } fn timer_tick_occured(handle: FFIArcPeerManagerHandle) -> FFIResult { - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); peer_man.timer_tick_occured(); FFIResult::ok() } @@ -170,7 +170,7 @@ ffi! { handle: FFIArcPeerManagerHandle ) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); peer_man.write_buffer_space_avail(&mut socket)?; FFIResult::ok() } @@ -184,7 +184,7 @@ ffi! { handle: FFIArcPeerManagerHandle ) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); let data = unsafe_block!("data lives as long as this function and it points to valid value" => data_ref.as_ref()); let should_pause = peer_man.read_event(&mut socket, data.as_ref())?; unsafe_block!("We know it points to valid u8" => should_pause_read.init(if should_pause { Bool::True } else { Bool::False })); @@ -192,7 +192,7 @@ ffi! { } fn process_events(handle: FFIArcPeerManagerHandle) -> FFIResult { - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); peer_man.process_events(); FFIResult::ok() } @@ -203,14 +203,14 @@ ffi! { disconnect_socket_ptr: Ref, handle: FFIArcPeerManagerHandle) -> FFIResult { let mut socket = construct_socket_desc(index, send_data_ptr, disconnect_socket_ptr); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); peer_man.socket_disconnected(&socket); FFIResult::ok() } fn get_peer_node_ids(buf_out: Out, buf_len: usize, actual_node_ids_len: Out, handle: FFIArcPeerManagerHandle) -> FFIResult { let buf = unsafe_block!("The buffer lives as long as `get_peer_node_ids`, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); - let peer_man: &FFISimpleArcPeerManager = unsafe_block!("We know handle points to valid PeerManager" => handle.as_ref()); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); let mut node_ids = peer_man.get_peer_node_ids(); into_fixed_buffer(&mut node_ids, buf, &mut actual_node_ids_len) } From 8ea1b254fd76df2bb387fdfe957eac2290c9ad7f Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 25 Jul 2020 20:24:55 +0900 Subject: [PATCH 29/45] WIP: expose SimpleManyChannelMonitor to C# side --- bindings/src/channelmanager.rs | 90 ++++++++++++++++++++++++++- bindings/src/channelmonitor.rs | 108 +++++++++++++++++++++++++++++++++ bindings/src/lib.rs | 1 + 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 bindings/src/channelmonitor.rs diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 95d0459240a..cb31bcadbc1 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -18,8 +18,12 @@ use lightning::{ }, routing::router::Route, ln::channelmanager::{PaymentSecret, PaymentPreimage}, + util::ser::{Readable, ReadableArgs}, + ln::channelmanager::ChannelManagerReadArgs }; +use bitcoin::hash_types::BlockHash; + use crate::{ error::FFIResult, handle::{Out, Ref, HandleShared}, @@ -34,11 +38,13 @@ use crate::{ * }, utils::into_fixed_buffer, + channelmonitor::FFIManyChannelMonitor }; -pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; +pub type FFIChannelManagerReadArgs<'a> = ChannelManagerReadArgs<'a, Arc, Arc, Arc, Arc, Arc, Arc>; + fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { let chan_man: &FFIArcChannelManager = handle.as_ref(); @@ -314,11 +320,93 @@ ffi! { let mut chan_man: &FFIArcChannelManager = handle.as_ref(); into_fixed_buffer(&mut chan_man, buf, &mut actual_len) } + fn deserialize_channel_manager(buf_ptr: Ref, buf_len: usize, + cfg: Ref, + + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + + get_node_secret_ptr: Ref, + get_destination_script_ptr: Ref, + get_shutdown_key_ptr: Ref, + get_channel_keys_ptr: Ref, + get_onion_rand_ptr: Ref, + get_channel_id_ptr: Ref, + + broadcast_transaction_ptr: Ref, + log_ptr: Ref, + get_est_sat_per_1000_weight_ptr: Ref, + handle: Out) -> FFIResult { + + // TODO: use macro? + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + let install_watch_tx = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + + let get_node_secret_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_node_secret_ptr.as_ref()); + let get_destination_script_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_destination_script_ptr.as_ref()); + let get_shutdown_key_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_shutdown_key_ptr.as_ref()); + let get_channel_keys_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_channel_keys_ptr.as_ref()); + let get_onion_rand_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_onion_rand_ptr.as_ref()); + let get_channel_id_ref = unsafe_block!("function pointer lives as long as KeysInterface and it points to valid data" => get_channel_id_ptr.as_ref()); + let keys_manager = Arc::new(FFIKeysInterface::new( + *get_node_secret_ref, + *get_destination_script_ref, + *get_shutdown_key_ref, + *get_channel_keys_ref, + *get_onion_rand_ref, + *get_channel_id_ref, + )); + + let logger = Arc::new( FFILogger{ log_ptr: *log_ref } ); + + let chain_watch_interface_arc = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx, + *install_watch_outpoint, + *watch_all_txn, + *get_chain_utxo, + *filter_block, + *reentered, + )); + + let broadcast_ref = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); + let tx_broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcast_ref }); + + let fee_est_fn_ref = unsafe_block!("" => get_est_sat_per_1000_weight_ptr.as_ref()); + let fee_estimator = Arc::new(FFIFeeEstimator{ get_est_sat_per_1000_weight_ptr: *fee_est_fn_ref }); + + let default_config = unsafe_block!("" => cfg.as_ref()); + let mut buf = unsafe_block!("The buffer lives as long as this function. And its length is buf_len" => buf_ptr.as_bytes(buf_len)); + let monitor = + Arc::new(FFIManyChannelMonitor::new(chain_watch_interface_arc, tx_broadcaster.clone(), logger.clone(), fee_estimator.clone())); + let readable_args = ChannelManagerReadArgs { + keys_manager, + fee_estimator, + monitor, + tx_broadcaster, + logger, + default_config: default_config.clone(), + channel_monitors: &mut Default::default() + }; + let (_hash, chan_man): (BlockHash, FFIArcChannelManager) = ReadableArgs::read(&mut buf, readable_args).unwrap(); + unsafe_block!("We know chan_man is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(chan_man))); + FFIResult::ok() + } fn release_ffi_channel_manager(handle: FFIArcChannelManagerHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIArcChannelManagerHandle::dealloc(handle, |mut _handle| { FFIResult::ok() })) } + } diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs new file mode 100644 index 00000000000..70922801abf --- /dev/null +++ b/bindings/src/channelmonitor.rs @@ -0,0 +1,108 @@ +use crate::{ + error::FFIResult, + handle::{Out, Ref, HandleShared}, + adaptors::{ + FFIBroadCaster, + FFIFeeEstimator, + FFILogger, + FFIChainWatchInterface, + chain_watch_interface_fn, + ffilogger_fn, + broadcaster_fn, + fee_estimator_fn + } +}; +use lightning::{ + ln::channelmonitor::SimpleManyChannelMonitor, + chain::{ + transaction::OutPoint, + keysinterface::InMemoryChannelKeys + } +}; +use std::sync::Arc; +use crate::adaptors::primitives::{FFIOutPoint, Bytes33}; + +pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; +pub type FFIManyChannelMonitorHandle<'a> = HandleShared<'a, FFIManyChannelMonitor>; + + +fn add_monitor_by_key(outpoint: FFIOutPoint, handle: FFIManyChannelMonitorHandle) -> FFIResult { + unimplemented!() +} + +ffi! { + fn create_many_channel_monitor( + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + + broadcast_transaction_ptr: Ref, + log_ptr: Ref, + get_est_sat_per_1000_weight_ptr: Ref, + handle: Out) -> FFIResult { + let install_watch_tx = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let chain_watch_interface = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx, + *install_watch_outpoint, + *watch_all_txn, + *get_chain_utxo, + *filter_block, + *reentered, + )); + + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + let logger = Arc::new( FFILogger{ log_ptr: *log_ref } ); + + let broadcast_ref = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); + let tx_broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcast_ref }); + + let fee_est_fn_ref = unsafe_block!("" => get_est_sat_per_1000_weight_ptr.as_ref()); + let fee_estimator = Arc::new(FFIFeeEstimator{ get_est_sat_per_1000_weight_ptr: *fee_est_fn_ref }); + + let many_channel_monitor = SimpleManyChannelMonitor::new(chain_watch_interface, tx_broadcaster, logger, fee_estimator); + unsafe_block!("We know the handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(many_channel_monitor))); + FFIResult::ok() + } + + fn block_connected( + block_ptr: Ref, + block_len: usize, + height: u32, + handle: FFIManyChannelMonitorHandle + ) -> FFIResult { + let many_channel_monitor: &FFIManyChannelMonitor = handle.as_ref(); + let block_bytes = unsafe_block!("block_ptr points to valid buffer of block_len length" => block_ptr.as_bytes(block_len)); + let block = bitcoin::consensus::deserialize(block_bytes)?; + many_channel_monitor.block_connected(&block, height); + FFIResult::ok() + } + + fn block_disconnected( + block_header_ptr: Ref, + block_header_len: usize, + height: u32, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let many_channel_monitor: &FFIManyChannelMonitor = handle.as_ref(); + let block_header_bytes: &[u8] = unsafe_block!("We know it points to valid buffer of specified length" => block_header_ptr.as_bytes(block_header_len)); + let block_header: BlockHeader = bitcoin::consensus::encode::deserialize(block_header_bytes)?; + many_channel_monitor.block_disconnected(&block_header, height); + FFIResult::ok() + } + + + fn release_many_channel_monitor(handle: FFIManyChannelMonitorHandle) -> FFIResult { + unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIManyChannelMonitorHandle::dealloc(handle, |mut _handle| { + FFIResult::ok() + })) + } +} \ No newline at end of file diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs index 686e91a82c4..53fd625a73b 100644 --- a/bindings/src/lib.rs +++ b/bindings/src/lib.rs @@ -27,6 +27,7 @@ mod adaptors; mod channelmanager; mod peermanager; mod blocknotifier; +mod channelmonitor; mod error; mod handle; From cb799cd21c0502a3729b3bb014d3de3b1fa6207b Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 26 Jul 2020 16:24:20 +0900 Subject: [PATCH 30/45] implement Readable/Writeable for SimpleManyChannelMonitor --- lightning/src/ln/channelmonitor.rs | 81 ++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 2b177207c29..1edfb6504e0 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -25,7 +25,7 @@ use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash}; use bitcoin::secp256k1::{Secp256k1,Signature}; use bitcoin::secp256k1::key::{SecretKey,PublicKey}; -use bitcoin::secp256k1; +use bitcoin::{secp256k1, Block}; use ln::msgs::DecodeError; use ln::chan_utils; @@ -36,13 +36,14 @@ use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInter use chain::transaction::OutPoint; use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys}; use util::logger::Logger; -use util::ser::{Readable, MaybeReadable, Writer, Writeable, U48}; +use util::ser::{Readable, MaybeReadable, Writer, Writeable, U48, ReadableArgs}; use util::{byte_utils, events}; use std::collections::{HashMap, hash_map}; use std::sync::Mutex; use std::{hash,cmp, mem}; use std::ops::Deref; +use std::io::Error; /// An update generated by the underlying Channel itself which contains some new information the /// ChannelMonitor should be made aware of. @@ -176,7 +177,62 @@ pub struct SimpleManyChannelMonitor +impl Writeable for SimpleManyChannelMonitor + where ChanSigner: ChannelKeys + Writeable, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + C::Target: ChainWatchInterface, +{ + fn write(&self, writer: &mut W) -> Result<(), Error> { + let monitors = self.monitors.lock().unwrap(); + (monitors.len() as u64).write(writer)?; + for (key, m) in monitors.iter() { + key.write(writer)?; + m.write_for_disk(writer)?; + } + Ok(()) + } +} + +/// Arguments for the creation of a SimpleManyChannelMonitor that are not deserialized. +pub struct SimpleManyChannelMonitorReadArgs + where T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + C::Target: ChainWatchInterface, +{ + /// + pub fee_estimator: F, + /// + pub tx_broadcaster: T, + /// + pub logger: L, + /// + pub chain_watch_interface: C +} + +impl ReadableArgs> for (Vec, SimpleManyChannelMonitor) + where T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + C::Target: ChainWatchInterface, +{ + fn read(reader: &mut R, args: SimpleManyChannelMonitorReadArgs) -> Result { + let num: u64 = Readable::read(reader)?; + let result = SimpleManyChannelMonitor::new(args.chain_watch_interface, args.tx_broadcaster, args.logger, args.fee_estimator); + let mut latest_block_hashes = Vec::with_capacity(num as usize); + for _ in 0..num { + let key: Key = Readable::read(reader)?; + let (hash, chan_mon): (BlockHash, ChannelMonitor) = Readable::read(reader)?; + result.add_monitor_by_key(key, chan_mon).expect("failed to add monitor by key"); + latest_block_hashes.push(hash); + } + Ok((latest_block_hashes, result)) + } +} + +impl ChainListener for SimpleManyChannelMonitor where T::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -207,6 +263,25 @@ impl +SimpleManyChannelMonitor + where T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + C::Target: ChainWatchInterface, +{ + /// wrapper for `SimpleManyChannelMonitor::block_connected` so that ffi function can be dumb + /// enough to pass the simple block + pub fn raw_block_connected(&self, block: &Block, height: u32) { + let idxes = (*(&self.chain_monitor)).filter_block(block); + let mut txs: Vec<&Transaction> = Vec::with_capacity(idxes.len()); + for i in &idxes { + txs.push(&block.txdata[i.clone()]); + } + self.block_connected(&block.header, height, txs.as_slice(), idxes.as_slice()) + } +} + impl SimpleManyChannelMonitor where T::Target: BroadcasterInterface, From 9a99587f22f77044351b4dca3b9b8ed788a70d2c Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sun, 26 Jul 2020 17:27:31 +0900 Subject: [PATCH 31/45] add entrypoint for deserializing ChannelMonitor --- bindings/src/blocknotifier.rs | 21 ++++++++ bindings/src/channelmanager.rs | 1 + bindings/src/channelmonitor.rs | 98 ++++++++++++++++++++++------------ 3 files changed, 87 insertions(+), 33 deletions(-) diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index 999433ebb69..fefc3ea33ff 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -6,6 +6,7 @@ use crate::{ Ref, Out, channelmanager::{FFIArcChannelManagerHandle, FFIArcChannelManager}, + channelmonitor::{FFIManyChannelMonitorHandle, FFIManyChannelMonitor}, adaptors::{chain_watch_interface_fn, FFIChainWatchInterface} }; use bitcoin::BlockHeader; @@ -67,6 +68,26 @@ ffi! { FFIResult::ok() } + fn register_many_channel_monitor( + chan_mon_handle: FFIManyChannelMonitorHandle, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let chan_mon: Arc = unsafe_block!("We know the handle points to valid ChannelMonitor" => chan_mon_handle.as_arc()); + let block_notifier = handle.as_ref(); + block_notifier.register_listener(chan_mon); + FFIResult::ok() + } + + fn unregister_many_channel_monitor( + chan_mon_handle: FFIManyChannelMonitorHandle, + handle: FFIBlockNotifierHandle + ) -> FFIResult { + let chan_mon: Arc = unsafe_block!("We know the handle points to valid ChannelMonitor" => chan_mon_handle.as_arc()); + let block_notifier = handle.as_ref(); + block_notifier.unregister_listener(chan_mon); + FFIResult::ok() + } + fn block_connected( block_ptr: Ref, block_len: usize, diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index cb31bcadbc1..e7c88cb10d2 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -320,6 +320,7 @@ ffi! { let mut chan_man: &FFIArcChannelManager = handle.as_ref(); into_fixed_buffer(&mut chan_man, buf, &mut actual_len) } + fn deserialize_channel_manager(buf_ptr: Ref, buf_len: usize, cfg: Ref, diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index 70922801abf..175ef5249ac 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -1,6 +1,20 @@ +use std::sync::Arc; + +use bitcoin::hash_types::{BlockHash}; + +use lightning::{ + ln::channelmonitor::SimpleManyChannelMonitor, + chain::{ + transaction::OutPoint, + keysinterface::InMemoryChannelKeys + }, + ln::channelmonitor::SimpleManyChannelMonitorReadArgs, + util::ser::ReadableArgs +}; + use crate::{ error::FFIResult, - handle::{Out, Ref, HandleShared}, + handle::{Out, Ref, RefMut, HandleShared}, adaptors::{ FFIBroadCaster, FFIFeeEstimator, @@ -10,17 +24,10 @@ use crate::{ ffilogger_fn, broadcaster_fn, fee_estimator_fn - } + }, + utils::into_fixed_buffer, + adaptors::primitives::{FFIOutPoint, Bytes33} }; -use lightning::{ - ln::channelmonitor::SimpleManyChannelMonitor, - chain::{ - transaction::OutPoint, - keysinterface::InMemoryChannelKeys - } -}; -use std::sync::Arc; -use crate::adaptors::primitives::{FFIOutPoint, Bytes33}; pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIManyChannelMonitorHandle<'a> = HandleShared<'a, FFIManyChannelMonitor>; @@ -30,6 +37,7 @@ fn add_monitor_by_key(outpoint: FFIOutPoint, handle: FFIManyChannelMonitorHandle unimplemented!() } + ffi! { fn create_many_channel_monitor( install_watch_tx_ptr: Ref, @@ -73,33 +81,57 @@ ffi! { FFIResult::ok() } - fn block_connected( - block_ptr: Ref, - block_len: usize, - height: u32, - handle: FFIManyChannelMonitorHandle - ) -> FFIResult { - let many_channel_monitor: &FFIManyChannelMonitor = handle.as_ref(); - let block_bytes = unsafe_block!("block_ptr points to valid buffer of block_len length" => block_ptr.as_bytes(block_len)); - let block = bitcoin::consensus::deserialize(block_bytes)?; - many_channel_monitor.block_connected(&block, height); - FFIResult::ok() + fn serialize_many_channel_monitor(buf_out: Out, buf_len: usize, actual_len: Out, handle: FFIManyChannelMonitorHandle) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let mut chan_mon = handle.as_ref(); + into_fixed_buffer(&mut chan_mon, buf, &mut actual_len) } - fn block_disconnected( - block_header_ptr: Ref, - block_header_len: usize, - height: u32, - handle: FFIBlockNotifierHandle - ) -> FFIResult { - let many_channel_monitor: &FFIManyChannelMonitor = handle.as_ref(); - let block_header_bytes: &[u8] = unsafe_block!("We know it points to valid buffer of specified length" => block_header_ptr.as_bytes(block_header_len)); - let block_header: BlockHeader = bitcoin::consensus::encode::deserialize(block_header_bytes)?; - many_channel_monitor.block_disconnected(&block_header, height); + fn deserialize_many_channel_monitor(buf_ptr: RefMut, buf_len: usize, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + + broadcast_transaction_ptr: Ref, + log_ptr: Ref, + get_est_sat_per_1000_weight_ptr: Ref, + handle: Out) -> FFIResult + { + let install_watch_tx = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + let chain_watch_interface = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx, + *install_watch_outpoint, + *watch_all_txn, + *get_chain_utxo, + *filter_block, + *reentered, + )); + + let broadcast_ref = unsafe_block!("" => broadcast_transaction_ptr.as_ref()); + let tx_broadcaster = Arc::new(FFIBroadCaster { broadcast_transaction_ptr: *broadcast_ref }); + + let fee_est_fn_ref = unsafe_block!("" => get_est_sat_per_1000_weight_ptr.as_ref()); + let fee_estimator = Arc::new(FFIFeeEstimator{ get_est_sat_per_1000_weight_ptr: *fee_est_fn_ref }); + + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + let logger = Arc::new( FFILogger{ log_ptr: *log_ref } ); + + let read_args = SimpleManyChannelMonitorReadArgs { fee_estimator, tx_broadcaster, logger, chain_watch_interface }; + let mut buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_ptr.as_bytes_mut(buf_len)); + let (hashes, many_channel_monitor): (Vec, FFIManyChannelMonitor) = ReadableArgs::read(&mut ::std::io::Cursor::new(buf), read_args).unwrap(); + unsafe_block!("We know the handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(many_channel_monitor))); FFIResult::ok() } - fn release_many_channel_monitor(handle: FFIManyChannelMonitorHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIManyChannelMonitorHandle::dealloc(handle, |mut _handle| { FFIResult::ok() From 7722df3469a9f2b9f9fdbf7a6b596d2a5f1cbd7e Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 29 Jul 2020 17:47:00 +0900 Subject: [PATCH 32/45] make ChannelManager.logger public * Because we want to pass to `get_route` function when calculating the route. --- lightning/src/ln/channelmanager.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index cd64561ab5f..a60b80ae330 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -417,7 +417,8 @@ pub struct ChannelManager Date: Wed, 29 Jul 2020 17:47:53 +0900 Subject: [PATCH 33/45] impl Display for MonitorUpdateError * So that we can handle it by `?` operator in ffi functions --- lightning/src/ln/channelmonitor.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 1edfb6504e0..9e1c4e73908 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -44,6 +44,7 @@ use std::sync::Mutex; use std::{hash,cmp, mem}; use std::ops::Deref; use std::io::Error; +use bitcoin::hashes::core::fmt::Formatter; /// An update generated by the underlying Channel itself which contains some new information the /// ChannelMonitor should be made aware of. @@ -140,6 +141,14 @@ pub enum ChannelMonitorUpdateErr { #[derive(Debug)] pub struct MonitorUpdateError(pub &'static str); +impl ::std::fmt::Display for MonitorUpdateError +{ + fn fmt(&self, f: &mut Formatter<'_>) -> ::std::fmt::Result { + f.write_str(self.0) + } +} +impl ::std::error::Error for MonitorUpdateError {} + /// Simple structure send back by ManyChannelMonitor in case of HTLC detected onchain from a /// forward channel and from which info are needed to update HTLC in a backward channel. #[derive(Clone, PartialEq)] From c6cf23477bed15508e36ec349bfcbab188e3eee2 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 29 Jul 2020 17:49:31 +0900 Subject: [PATCH 34/45] Update SimpleManyChannelMonitor deserialization --- lightning/src/ln/channelmonitor.rs | 37 ++++++++++++++++++------------ lightning/src/util/ser.rs | 27 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 9e1c4e73908..62adcde37a2 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -221,7 +221,7 @@ pub struct SimpleManyChannelMonitorReadArgs ReadableArgs> for (Vec, SimpleManyChannelMonitor) +impl ReadableArgs> for (Vec<(Key, BlockHash)>, SimpleManyChannelMonitor) where T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, @@ -230,14 +230,14 @@ impl(reader: &mut R, args: SimpleManyChannelMonitorReadArgs) -> Result { let num: u64 = Readable::read(reader)?; let result = SimpleManyChannelMonitor::new(args.chain_watch_interface, args.tx_broadcaster, args.logger, args.fee_estimator); - let mut latest_block_hashes = Vec::with_capacity(num as usize); + let mut key_and_latest_block_hashes = Vec::with_capacity(num as usize); for _ in 0..num { let key: Key = Readable::read(reader)?; - let (hash, chan_mon): (BlockHash, ChannelMonitor) = Readable::read(reader)?; - result.add_monitor_by_key(key, chan_mon).expect("failed to add monitor by key"); - latest_block_hashes.push(hash); + let (hash, chan_mon): (BlockHash, ChannelMonitor) = Readable::read(reader)?; + result.add_monitor_by_key(key.clone(), chan_mon).expect("failed to add monitor by key"); + key_and_latest_block_hashes.push((key, hash)); } - Ok((latest_block_hashes, result)) + Ok((key_and_latest_block_hashes, result)) } } @@ -279,15 +279,22 @@ SimpleManyChannelMonitor L::Target: Logger, C::Target: ChainWatchInterface, { - /// wrapper for `SimpleManyChannelMonitor::block_connected` so that ffi function can be dumb - /// enough to pass the simple block - pub fn raw_block_connected(&self, block: &Block, height: u32) { - let idxes = (*(&self.chain_monitor)).filter_block(block); - let mut txs: Vec<&Transaction> = Vec::with_capacity(idxes.len()); - for i in &idxes { - txs.push(&block.txdata[i.clone()]); - } - self.block_connected(&block.header, height, txs.as_slice(), idxes.as_slice()) + /// wrapper for `ChannelMonitor::block_connected` so that a ffi function can be dumb + /// enough to pass the simple block after deserialization. + pub fn tell_block_connected_after_resume(&self, block: &Block, height: u32, key: Key) -> Result<(), MonitorUpdateError> { + let mut monitors = self.monitors.lock().unwrap(); + match monitors.get_mut(&key) { + Some(channel_monitor) => { + let mut txn = Vec::with_capacity(block.txdata.len()); + for tx in block.txdata.iter() + { + txn.push(tx); + } + channel_monitor.block_connected(&txn, height, &block.header.bitcoin_hash(), &*self.broadcaster, &*self.fee_estimator, &*self.logger); + Ok(()) + }, + None => Err(MonitorUpdateError("No such monitor registered")), + } } } diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 64d91660978..beed0b29997 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -149,6 +149,33 @@ impl Read for ReadTrackingReader { } } +impl Writeable for Vec<(crate::chain::transaction::OutPoint, BlockHash)> +{ + fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + (self.len() as u16).write(w)?; + for (o, hash) in self.iter() { + o.write(w)?; + hash.write(w)?; + } + Ok(()) + } +} +impl Readable for Vec<(crate::chain::transaction::OutPoint, BlockHash)> +{ + fn read(r: &mut R) -> Result { + let len: u16 = Readable::read(r)?; + let mut ret = Vec::with_capacity(len as usize); + for _ in 0..(len as usize) + { + let o = Readable::read(r)?; + let hash = Readable::read(r)?; + ret.push((o, hash)); + } + Ok(ret) + } +} + + /// A trait that various rust-lightning types implement allowing them to be written out to a Writer pub trait Writeable { /// Writes self out to the given Writer From 7436019fd8b28393901c6c82ca613487fbb4ed12 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 29 Jul 2020 18:59:26 +0900 Subject: [PATCH 35/45] Refactor bindings * Add `get_network_graph` function to `PeerManager` * Add `get_route_and_send_payment` to `ChannelManager` These two changes makes those two managers loosely coupled. Before this sometimes `send_payment` cause crash because the `ChannelManager` has been moved by GC. * Add `tell_block_connected_after_resume` endpoint to `ManyChannelMonitor` ffi. So that after deserialization we can add new blocks. * Return pairs of latest block hashes when deserializing `ManyChannelMonitor` So that the api consumer can query new blocks after deserialization. --- bindings/src/adaptors/mod.rs | 9 +-- bindings/src/blocknotifier.rs | 4 +- bindings/src/channelmanager.rs | 106 ++++++++++++++++++++++++++------- bindings/src/channelmonitor.rs | 38 ++++++++---- bindings/src/peermanager.rs | 76 +++++------------------ bindings/src/utils/mod.rs | 2 +- 6 files changed, 134 insertions(+), 101 deletions(-) diff --git a/bindings/src/adaptors/mod.rs b/bindings/src/adaptors/mod.rs index 12b7eed3482..cb4f108f81f 100644 --- a/bindings/src/adaptors/mod.rs +++ b/bindings/src/adaptors/mod.rs @@ -21,6 +21,7 @@ use lightning::{ }; pub mod primitives; + use primitives::*; use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface}; use bitcoin::secp256k1::{SecretKey, PublicKey}; @@ -113,11 +114,11 @@ pub enum FFINetwork { } impl FFINetwork { - pub fn to_network(&self) -> bitcoin::network::constants::Network { + pub fn to_network(&self) -> Network { match self { - FFINetwork::MainNet => { bitcoin::network::constants::Network::Bitcoin }, - FFINetwork::TestNet => { bitcoin::network::constants::Network::Testnet }, - FFINetwork::RegTest => { bitcoin::network::constants::Network::Regtest }, + FFINetwork::MainNet => { Network::Bitcoin }, + FFINetwork::TestNet => { Network::Testnet }, + FFINetwork::RegTest => { Network::Regtest }, } } } diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index fefc3ea33ff..d46fcc8adef 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -11,8 +11,8 @@ use crate::{ }; use bitcoin::BlockHeader; -type FFIBlockNotifier = BlockNotifier<'static, Arc, Arc>; -type FFIBlockNotifierHandle<'a> = HandleShared<'a, FFIBlockNotifier>; +pub type FFIBlockNotifier = BlockNotifier<'static, Arc, Arc>; +pub type FFIBlockNotifierHandle<'a> = HandleShared<'a, FFIBlockNotifier>; ffi! { diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index e7c88cb10d2..76c582b8d58 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -3,27 +3,11 @@ use std::{ sync::Arc, }; -use lightning::{ - util::{ - config::UserConfig, - events::EventsProvider - }, - ln::{ - channelmanager::{ChannelManager, PaymentHash}, - channelmonitor::SimpleManyChannelMonitor - }, - chain::{ - keysinterface::{InMemoryChannelKeys}, - transaction::OutPoint - }, - routing::router::Route, - ln::channelmanager::{PaymentSecret, PaymentPreimage}, - util::ser::{Readable, ReadableArgs}, - ln::channelmanager::ChannelManagerReadArgs +use bitcoin::{ + hash_types::BlockHash, + secp256k1 }; -use bitcoin::hash_types::BlockHash; - use crate::{ error::FFIResult, handle::{Out, Ref, HandleShared}, @@ -33,13 +17,33 @@ use crate::{ Bytes33, FFIRoute, FFIEvents, - FFIOutPoint + FFIOutPoint, + FFIBytes }, * }, utils::into_fixed_buffer, channelmonitor::FFIManyChannelMonitor }; +use lightning::{ + routing::router::{RouteHint, get_route}, + util::{ + config::UserConfig, + events::EventsProvider + }, + ln::{ + channelmanager::{ChannelManager, PaymentHash}, + }, + chain::{ + keysinterface::{InMemoryChannelKeys}, + transaction::OutPoint + }, + routing::router::Route, + ln::channelmanager::{PaymentSecret, PaymentPreimage}, + util::ser::{Readable, ReadableArgs}, + ln::channelmanager::ChannelManagerReadArgs, + routing::network_graph::NetworkGraph +}; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; @@ -67,6 +71,7 @@ fn claim_funds_inner(payment_preimage: Ref, payment_secret: Option, payment_hash_ref: Ref, payment_secret: Option) -> FFIResult { let chan_man: &FFIArcChannelManager = handle.as_ref(); let route_ffi: &FFIRoute = unsafe_block!("We know it points to valid route data" => route_ref.as_ref()); @@ -77,6 +82,36 @@ fn send_payment_inner(handle: FFIArcChannelManagerHandle, route_ref: Ref, + graph_bytes_len: usize, + their_node_id: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + maybe_payment_secret: &Option, + payment_hash_ref: Ref, + chanman_handle: FFIArcChannelManagerHandle +) -> FFIResult { + let chan_man: &FFIArcChannelManager = chanman_handle.as_ref(); + let their_node_id: secp256k1::PublicKey = unsafe_block!("" => their_node_id.as_ref()).clone().try_into()?; + let hops = chan_man.list_usable_channels(); + let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); + let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); + let our_node_id = chan_man.get_our_node_id(); + let mut graph_bytes = unsafe_block!("" => graph_bytes_ptr.as_bytes(graph_bytes_len)); + let graph: NetworkGraph = Readable::read(&mut graph_bytes).expect("Failed to decode graph"); + let mut route = get_route(&our_node_id, &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, chan_man.logger.clone())?; + if maybe_payment_secret.is_none() + { + route = Route { paths: route.paths[0..1].to_vec() } + } + let payment_hash: PaymentHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()).clone().into(); + chan_man.send_payment(&route, payment_hash, maybe_payment_secret)?; + FFIResult::ok() +} + pub(crate) fn construct_channel_manager( ffi_network: FFINetwork, cfg: Ref, @@ -94,7 +129,6 @@ pub(crate) fn construct_channel_manager( log_ref: &ffilogger_fn::LogExtern, get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, - ) -> FFIArcChannelManager { let network = ffi_network.to_network(); @@ -252,8 +286,36 @@ ffi! { send_payment_inner(handle, route_ref, payment_hash_ref, None) } + fn get_route_and_send_payment( + graph_bytes_ptr: Ref, + graph_bytes_len: usize, + their_node_id: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + payment_secret_ref: Ref, + payment_hash: Ref, + chanman_handle: FFIArcChannelManagerHandle + ) -> FFIResult { + let payment_secret = unsafe_block!("" => payment_secret_ref.as_ref()).clone().into(); + get_route_and_send_payment_inner(graph_bytes_ptr, graph_bytes_len, their_node_id, last_hops_ref, final_value_msat, final_cltv, &Some(payment_secret), payment_hash, chanman_handle) + } + + fn get_route_and_send_payment_without_secret( + graph_bytes_ptr: Ref, + graph_bytes_len: usize, + their_node_id: Ref, + last_hops_ref: Ref, + final_value_msat: u64, + final_cltv: u32, + payment_hash: Ref, + chanman_handle: FFIArcChannelManagerHandle + ) -> FFIResult { + get_route_and_send_payment_inner(graph_bytes_ptr, graph_bytes_len, their_node_id, last_hops_ref, final_value_msat, final_cltv, &None, payment_hash, chanman_handle) + } + fn funding_transaction_generated(temporary_channel_id: Ref, funding_txo: FFIOutPoint, handle: FFIArcChannelManagerHandle) -> FFIResult { - let chan_man: &FFIArcChannelManager = handle.as_ref(); + let chan_man: &FFIArcChannelManager = handle.as_ref(); let temporary_channel_id: &Bytes32 = unsafe_block!("data lives as long as this function and it points to a valid value" => temporary_channel_id.as_ref()); let funding_txo: OutPoint = funding_txo.try_into()?; chan_man.funding_transaction_generated(&temporary_channel_id.bytes, funding_txo); diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index 175ef5249ac..034a00ca542 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -26,18 +26,13 @@ use crate::{ fee_estimator_fn }, utils::into_fixed_buffer, - adaptors::primitives::{FFIOutPoint, Bytes33} + adaptors::primitives::{FFIOutPoint} }; +use bitcoin::Block; pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIManyChannelMonitorHandle<'a> = HandleShared<'a, FFIManyChannelMonitor>; - -fn add_monitor_by_key(outpoint: FFIOutPoint, handle: FFIManyChannelMonitorHandle) -> FFIResult { - unimplemented!() -} - - ffi! { fn create_many_channel_monitor( install_watch_tx_ptr: Ref, @@ -87,7 +82,8 @@ ffi! { into_fixed_buffer(&mut chan_mon, buf, &mut actual_len) } - fn deserialize_many_channel_monitor(buf_ptr: RefMut, buf_len: usize, + fn deserialize_many_channel_monitor(input_buf_ptr: RefMut, + input_buf_len: usize, install_watch_tx_ptr: Ref, install_watch_outpoint_ptr: Ref, watch_all_txn_ptr: Ref, @@ -98,6 +94,11 @@ ffi! { broadcast_transaction_ptr: Ref, log_ptr: Ref, get_est_sat_per_1000_weight_ptr: Ref, + + output_buf_ptr: Out, + output_buf_len: usize, + output_actual_len: Out, + handle: Out) -> FFIResult { let install_watch_tx = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); @@ -126,12 +127,29 @@ ffi! { let logger = Arc::new( FFILogger{ log_ptr: *log_ref } ); let read_args = SimpleManyChannelMonitorReadArgs { fee_estimator, tx_broadcaster, logger, chain_watch_interface }; - let mut buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_ptr.as_bytes_mut(buf_len)); - let (hashes, many_channel_monitor): (Vec, FFIManyChannelMonitor) = ReadableArgs::read(&mut ::std::io::Cursor::new(buf), read_args).unwrap(); + let mut buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => input_buf_ptr.as_bytes_mut(input_buf_len)); + let (hashes, many_channel_monitor): (Vec<(OutPoint, BlockHash)>, FFIManyChannelMonitor) = ReadableArgs::read(&mut ::std::io::Cursor::new(buf), read_args).unwrap(); + + let output_buf = unsafe_block!("" => output_buf_ptr.as_uninit_bytes_mut(output_buf_len)); + into_fixed_buffer(&mut hashes, output_buf, &mut output_actual_len)?; unsafe_block!("We know the handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(many_channel_monitor))); FFIResult::ok() } + fn tell_block_connected_after_resume( + block_ref: Ref, + height: u32, + key_ref: Ref, + handle: FFIManyChannelMonitorHandle + ) -> FFIResult { + let block: &Block = unsafe_block!("" => block_ref.as_ref()); + let key: OutPoint = unsafe_block!("" => key_ref.as_ref()).clone().into(); + let chan_mon = handle.as_ref(); + chan_mon.tell_block_connected_after_resume(block, height, key)?; + FFIResult::ok() + } + + fn release_many_channel_monitor(handle: FFIManyChannelMonitorHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIManyChannelMonitorHandle::dealloc(handle, |mut _handle| { FFIResult::ok() diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 60e7917940b..8377e7b75f6 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -13,17 +13,18 @@ use lightning::{ }; use crate::{ - channelmanager::{FFIArcChannelManager}, error::FFIResult, handle::{Out, Ref, HandleShared}, - adaptors::*, - adaptors::primitives::{Bytes32, FFIBytes, Bytes33}, - utils::into_fixed_buffer + adaptors::{ + *, + primitives::{Bytes32, FFIBytes, Bytes33} + }, + utils::into_fixed_buffer, + channelmanager::{ + FFIArcChannelManagerHandle, + FFIArcChannelManager + } }; -use crate::channelmanager::{FFIArcChannelManagerHandle}; -use lightning::routing::router::{get_route, RouteHint, Route}; -use lightning::ln::channelmanager::{PaymentHash, PaymentSecret}; -use lightning::util::ser::Readable; type FFISimpleArcPeerManager = PeerManager, Arc, Arc>>, Arc>; type FFIArcPeerManagerHandle<'a> = HandleShared<'a, FFISimpleArcPeerManager>; @@ -43,36 +44,6 @@ fn construct_socket_desc ( socket } -fn send_payment_inner( - their_node_id: Ref, - payment_hash_ref: Ref, - last_hops_ref: Ref, - final_value_msat: u64, - final_cltv: u32, - maybe_payment_secret: &Option, - peerman_handle: FFIArcPeerManagerHandle, - chanman_handle: FFIArcChannelManagerHandle -) -> FFIResult { - let their_node_id = unsafe_block!("" => their_node_id.as_ref()); - let their_node_id: secp256k1::PublicKey = their_node_id.clone().try_into()?; - let peer_man: &FFISimpleArcPeerManager = peerman_handle.as_ref(); - let chan_man = unsafe_block!("It must point to valid ChannelManager" => chanman_handle.as_arc()); - let graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); - let hops = chan_man.list_usable_channels(); - let last_hops = unsafe_block!("data lives as long as this function and it points to valid value" => last_hops_ref.as_ref()); - let last_hops: Vec = Readable::read(&mut last_hops.as_ref()).expect("Failed to deserialize last_hops"); - let our_node_id = chan_man.get_our_node_id(); - println!("parameter to get_rotue is our_node_id: {:?} \n final_value_msat: {:?} \n final_cltv: {:?}", &our_node_id, final_value_msat, final_cltv); - let mut route = get_route(&our_node_id, &graph, &their_node_id, Some(&hops), last_hops.as_ref(), final_value_msat, final_cltv, peer_man.logger.clone())?; - if maybe_payment_secret.is_none() - { - route = Route { paths: route.paths[0..1].to_vec() } - } - let payment_hash: PaymentHash = unsafe_block!("We know it points to valid hash data" => payment_hash_ref.as_ref()).clone().into(); - chan_man.send_payment(&route, payment_hash, maybe_payment_secret)?; - FFIResult::ok() -} - ffi! { fn create_peer_manager( seed: Ref, @@ -215,30 +186,11 @@ ffi! { into_fixed_buffer(&mut node_ids, buf, &mut actual_node_ids_len) } - fn send_non_mpp_payment_with_peer_manager( - their_node_id: Ref, - payment_hash_ref: Ref, - last_hops_ref: Ref, - final_value_msat: u64, - final_cltv: u32, - peerman_handle: FFIArcPeerManagerHandle, - chanman_handle: FFIArcChannelManagerHandle - ) -> FFIResult { - send_payment_inner(their_node_id, payment_hash_ref, last_hops_ref, final_value_msat, final_cltv, &None, peerman_handle, chanman_handle) - } - - fn send_mpp_payment_with_peer_manager( - their_node_id: Ref, - payment_hash_ref: Ref, - last_hops_ref: Ref, - final_value_msat: u64, - final_cltv: u32, - payment_secret_ref: Ref, - peerman_handle: FFIArcPeerManagerHandle, - chanman_handle: FFIArcChannelManagerHandle - ) -> FFIResult { - let payment_secret: PaymentSecret = unsafe_block!("We know the pointer points to valid payment secret and it lives as long as this function" => payment_secret_ref.as_ref()).clone().into(); - send_payment_inner(their_node_id, payment_hash_ref, last_hops_ref, final_value_msat, final_cltv, &Some(payment_secret), peerman_handle, chanman_handle) + fn get_network_graph(buf_out: Out, buf_len: usize, actual_len: Out, handle: FFIArcPeerManagerHandle) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let peer_man: &FFISimpleArcPeerManager = handle.as_ref(); + let mut graph = peer_man.message_handler.route_handler.network_graph.read().unwrap(); + into_fixed_buffer(&*graph, buf, &mut actual_len) } fn release_peer_manager(handle: FFIArcPeerManagerHandle) -> FFIResult { diff --git a/bindings/src/utils/mod.rs b/bindings/src/utils/mod.rs index b9f59cb66db..7cc92d85ab8 100644 --- a/bindings/src/utils/mod.rs +++ b/bindings/src/utils/mod.rs @@ -29,7 +29,7 @@ impl std::error::Error for Error { /// So in that case, we will return `FFIResult::BufferTooSmall` with actual length we want to write. /// The wrapper must call the function again with a pointer points to a longer buffer. pub (crate) fn into_fixed_buffer( - data: &mut T, + data: &T, buf: &mut [u8], actual_value_len: &mut Out ) -> FFIResult { From bd48bfb063cede264b3ceabbaa5135696121ebbc Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Wed, 29 Jul 2020 19:18:18 +0900 Subject: [PATCH 36/45] remove unsafe_fn in HandleShared::as_arc * Since it uselessly complicates the ffi function by forcing us to specify `unsafe_block` every time we call it. --- bindings/src/blocknotifier.rs | 12 ++++++------ bindings/src/channelmanager.rs | 1 + bindings/src/channelmonitor.rs | 2 +- bindings/src/handle/mod.rs | 10 ++++------ bindings/src/peermanager.rs | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index d46fcc8adef..5cddff52f25 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -5,8 +5,8 @@ use crate::{ FFIResult, Ref, Out, - channelmanager::{FFIArcChannelManagerHandle, FFIArcChannelManager}, - channelmonitor::{FFIManyChannelMonitorHandle, FFIManyChannelMonitor}, + channelmanager::{FFIArcChannelManagerHandle}, + channelmonitor::{FFIManyChannelMonitorHandle}, adaptors::{chain_watch_interface_fn, FFIChainWatchInterface} }; use bitcoin::BlockHeader; @@ -52,7 +52,7 @@ ffi! { channel_manager: FFIArcChannelManagerHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); + let chan_man = channel_manager.as_arc(); let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.register_listener(chan_man); FFIResult::ok() @@ -62,7 +62,7 @@ ffi! { channel_manager: FFIArcChannelManagerHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_man: Arc = unsafe_block!("We know the handle points to valid channel_manager" => channel_manager.as_arc()); + let chan_man = channel_manager.as_arc(); let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.unregister_listener(chan_man); FFIResult::ok() @@ -72,7 +72,7 @@ ffi! { chan_mon_handle: FFIManyChannelMonitorHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_mon: Arc = unsafe_block!("We know the handle points to valid ChannelMonitor" => chan_mon_handle.as_arc()); + let chan_mon = chan_mon_handle.as_arc(); let block_notifier = handle.as_ref(); block_notifier.register_listener(chan_mon); FFIResult::ok() @@ -82,7 +82,7 @@ ffi! { chan_mon_handle: FFIManyChannelMonitorHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_mon: Arc = unsafe_block!("We know the handle points to valid ChannelMonitor" => chan_mon_handle.as_arc()); + let chan_mon = chan_mon_handle.as_arc(); let block_notifier = handle.as_ref(); block_notifier.unregister_listener(chan_mon); FFIResult::ok() diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 76c582b8d58..4cec7504543 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -152,6 +152,7 @@ pub(crate) fn construct_channel_manager( let cfg = unsafe_block!("" => cfg.as_ref()); + // let monitor = monitor_handle.as_arc(); let monitor = Arc::new(FFIManyChannelMonitor::new(chain_watch_interface_arc, broadcaster.clone(), logger_arc.clone(), Arc::new(fee_est.clone()))); diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index 034a00ca542..a85eebec0f9 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -131,7 +131,7 @@ ffi! { let (hashes, many_channel_monitor): (Vec<(OutPoint, BlockHash)>, FFIManyChannelMonitor) = ReadableArgs::read(&mut ::std::io::Cursor::new(buf), read_args).unwrap(); let output_buf = unsafe_block!("" => output_buf_ptr.as_uninit_bytes_mut(output_buf_len)); - into_fixed_buffer(&mut hashes, output_buf, &mut output_actual_len)?; + into_fixed_buffer(&hashes, output_buf, &mut output_actual_len)?; unsafe_block!("We know the handle is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(many_channel_monitor))); FFIResult::ok() } diff --git a/bindings/src/handle/mod.rs b/bindings/src/handle/mod.rs index 52311272467..4d0682807ea 100644 --- a/bindings/src/handle/mod.rs +++ b/bindings/src/handle/mod.rs @@ -45,14 +45,12 @@ where } pub(super) fn as_ref(&self) -> &T { - unsafe_block!("We own, the interioer value" => { &*self.0 }) + unsafe_block!("We own, the interior value" => { &*self.0 }) } - unsafe_fn!("The pointer must be nonnull" => - pub fn as_arc(&self) -> Arc { - Arc::from_raw(self.0) - } - ); + pub(super) fn as_arc(&self) -> Arc { + unsafe_block!("We own, the interior value" => Arc::from_raw(self.0)) + } unsafe_fn!("There are no other live references and the handle won't be used again" => pub(super) fn dealloc(handle: Self, f: impl FnOnce(T) -> R) -> R { diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 8377e7b75f6..646343e71a1 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -86,7 +86,7 @@ ffi! { *reentered_ref )); let route_handler = NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()); - let chan_man = unsafe_block!("It must point to valid ChannelManager" => chan_man.as_arc()); + let chan_man = chan_man.as_arc(); let msg_handler = MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; From be4b916bf03ae2377d810149b512c088e208a731 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Fri, 31 Jul 2020 18:51:31 +0900 Subject: [PATCH 37/45] stop using as_arc * For object which comes from C# side, if we take a reference as `Arc`, then rust will drop the inner value when the function exits. leaving the pointer dangling. --- bindings/src/blocknotifier.rs | 12 ++++++------ bindings/src/channelmanager.rs | 20 ++++++++++++-------- bindings/src/channelmonitor.rs | 2 +- bindings/src/handle/mod.rs | 10 ++-------- bindings/src/peermanager.rs | 6 +++--- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/bindings/src/blocknotifier.rs b/bindings/src/blocknotifier.rs index 5cddff52f25..3d11d819868 100644 --- a/bindings/src/blocknotifier.rs +++ b/bindings/src/blocknotifier.rs @@ -11,8 +11,8 @@ use crate::{ }; use bitcoin::BlockHeader; -pub type FFIBlockNotifier = BlockNotifier<'static, Arc, Arc>; -pub type FFIBlockNotifierHandle<'a> = HandleShared<'a, FFIBlockNotifier>; +pub type FFIBlockNotifier = BlockNotifier<'static, &'static dyn ChainListener, Arc>; +pub type FFIBlockNotifierHandle = HandleShared<'static, FFIBlockNotifier>; ffi! { @@ -52,7 +52,7 @@ ffi! { channel_manager: FFIArcChannelManagerHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_man = channel_manager.as_arc(); + let chan_man = channel_manager.as_static_ref(); let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.register_listener(chan_man); FFIResult::ok() @@ -62,7 +62,7 @@ ffi! { channel_manager: FFIArcChannelManagerHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_man = channel_manager.as_arc(); + let chan_man = channel_manager.as_static_ref(); let block_notifier: &FFIBlockNotifier = handle.as_ref(); block_notifier.unregister_listener(chan_man); FFIResult::ok() @@ -72,7 +72,7 @@ ffi! { chan_mon_handle: FFIManyChannelMonitorHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_mon = chan_mon_handle.as_arc(); + let chan_mon = chan_mon_handle.as_static_ref(); let block_notifier = handle.as_ref(); block_notifier.register_listener(chan_mon); FFIResult::ok() @@ -82,7 +82,7 @@ ffi! { chan_mon_handle: FFIManyChannelMonitorHandle, handle: FFIBlockNotifierHandle ) -> FFIResult { - let chan_mon = chan_mon_handle.as_arc(); + let chan_mon = chan_mon_handle.as_static_ref(); let block_notifier = handle.as_ref(); block_notifier.unregister_listener(chan_mon); FFIResult::ok() diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 4cec7504543..f7131ccf448 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -44,10 +44,11 @@ use lightning::{ ln::channelmanager::ChannelManagerReadArgs, routing::network_graph::NetworkGraph }; +use crate::channelmonitor::FFIManyChannelMonitorHandle; -pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc, Arc>; -pub type FFIArcChannelManagerHandle<'a> = HandleShared<'a, FFIArcChannelManager>; -pub type FFIChannelManagerReadArgs<'a> = ChannelManagerReadArgs<'a, Arc, Arc, Arc, Arc, Arc, Arc>; +pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc>; +pub type FFIArcChannelManagerHandle = HandleShared<'static, FFIArcChannelManager>; +pub type FFIChannelManagerReadArgs<'a> = ChannelManagerReadArgs<'a, Arc, &'static FFIManyChannelMonitor, Arc, Arc, Arc, Arc>; fn fail_htlc_backwards_inner(payment_hash: Ref, payment_secret: &Option, handle: FFIArcChannelManagerHandle) -> Result { @@ -129,6 +130,7 @@ pub(crate) fn construct_channel_manager( log_ref: &ffilogger_fn::LogExtern, get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, + monitor_handle: FFIManyChannelMonitorHandle ) -> FFIArcChannelManager { let network = ffi_network.to_network(); @@ -153,8 +155,7 @@ pub(crate) fn construct_channel_manager( let cfg = unsafe_block!("" => cfg.as_ref()); // let monitor = monitor_handle.as_arc(); - let monitor = - Arc::new(FFIManyChannelMonitor::new(chain_watch_interface_arc, broadcaster.clone(), logger_arc.clone(), Arc::new(fee_est.clone()))); + let monitor = monitor_handle.as_static_ref(); ChannelManager::new( network, @@ -192,7 +193,9 @@ ffi! { log_ptr: Ref, get_est_sat_per_1000_weight_ptr: Ref, cur_block_height: usize, - chan_man: Out) -> FFIResult { + monitor_handle: FFIManyChannelMonitorHandle, + chan_man: Out + ) -> FFIResult { let log_ref = unsafe_block!("" => log_ptr.as_ref()); let network = unsafe_block!("" => *network_ref.as_ref()); @@ -236,6 +239,7 @@ ffi! { log_ref, get_est_sat_per_1000_weight_ptr, cur_block_height, + monitor_handle ); unsafe_block!("We know chan_man is not null by wrapper macro. And we know `Out` is writable" => chan_man.init(HandleShared::alloc(chan_man_raw))); FFIResult::ok() @@ -404,6 +408,7 @@ ffi! { broadcast_transaction_ptr: Ref, log_ptr: Ref, get_est_sat_per_1000_weight_ptr: Ref, + monitor_handle: FFIManyChannelMonitorHandle, handle: Out) -> FFIResult { // TODO: use macro? @@ -450,8 +455,7 @@ ffi! { let default_config = unsafe_block!("" => cfg.as_ref()); let mut buf = unsafe_block!("The buffer lives as long as this function. And its length is buf_len" => buf_ptr.as_bytes(buf_len)); - let monitor = - Arc::new(FFIManyChannelMonitor::new(chain_watch_interface_arc, tx_broadcaster.clone(), logger.clone(), fee_estimator.clone())); + let monitor = monitor_handle.as_static_ref(); let readable_args = ChannelManagerReadArgs { keys_manager, fee_estimator, diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index a85eebec0f9..65bf5f3c8e5 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -31,7 +31,7 @@ use crate::{ use bitcoin::Block; pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; -pub type FFIManyChannelMonitorHandle<'a> = HandleShared<'a, FFIManyChannelMonitor>; +pub type FFIManyChannelMonitorHandle = HandleShared<'static, FFIManyChannelMonitor>; ffi! { fn create_many_channel_monitor( diff --git a/bindings/src/handle/mod.rs b/bindings/src/handle/mod.rs index 4d0682807ea..91691cb5692 100644 --- a/bindings/src/handle/mod.rs +++ b/bindings/src/handle/mod.rs @@ -48,8 +48,8 @@ where unsafe_block!("We own, the interior value" => { &*self.0 }) } - pub(super) fn as_arc(&self) -> Arc { - unsafe_block!("We own, the interior value" => Arc::from_raw(self.0)) + pub(super) fn as_static_ref(&self) -> &'static T { + unsafe_block!("We own, the interior value" => { &*self.0 }) } unsafe_fn!("There are no other live references and the handle won't be used again" => @@ -125,12 +125,6 @@ impl<'a, T: ?Sized> Ref<'a, T> { &*self.0 } ); - - unsafe_fn!("The pointer must be nonnull" => - pub fn as_arc(&self) -> Arc { - Arc::from_raw(self.0) - } - ); } impl<'a> Ref<'a, u8> { diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index 646343e71a1..f9003d577e9 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -26,8 +26,8 @@ use crate::{ } }; -type FFISimpleArcPeerManager = PeerManager, Arc, Arc>>, Arc>; -type FFIArcPeerManagerHandle<'a> = HandleShared<'a, FFISimpleArcPeerManager>; +type FFISimpleArcPeerManager = PeerManager, Arc>>, Arc>; +type FFIArcPeerManagerHandle = HandleShared<'static, FFISimpleArcPeerManager>; lazy_static! { static ref SOCKET_DESC_INDEX: AtomicUsize = AtomicUsize::new(0); @@ -86,7 +86,7 @@ ffi! { *reentered_ref )); let route_handler = NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()); - let chan_man = chan_man.as_arc(); + let chan_man = chan_man.as_static_ref(); let msg_handler = MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; From 0ff20988d26105fcb343340a4b4b753078d3ef27 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 3 Aug 2020 18:30:33 +0900 Subject: [PATCH 38/45] update tell_block_(dis)connected_after resume endpoint --- bindings/src/adaptors/primitives.rs | 6 ++++++ bindings/src/channelmanager.rs | 1 - bindings/src/channelmonitor.rs | 22 ++++++++++++++++++---- lightning/src/ln/channelmonitor.rs | 12 ++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/bindings/src/adaptors/primitives.rs b/bindings/src/adaptors/primitives.rs index 14e415a0ae2..aedb59b67a7 100644 --- a/bindings/src/adaptors/primitives.rs +++ b/bindings/src/adaptors/primitives.rs @@ -79,6 +79,12 @@ impl From for Bytes32 { } } +impl From for BlockHash { + fn from(this: Bytes32) -> Self { + BlockHash::from_slice(&this.bytes).unwrap() + } +} + impl From for PaymentSecret { fn from(ffi_secret: Bytes32) -> PaymentSecret { PaymentSecret(ffi_secret.bytes) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index f7131ccf448..808fce8c667 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -154,7 +154,6 @@ pub(crate) fn construct_channel_manager( let cfg = unsafe_block!("" => cfg.as_ref()); - // let monitor = monitor_handle.as_arc(); let monitor = monitor_handle.as_static_ref(); ChannelManager::new( diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index 65bf5f3c8e5..e51e5b18fd7 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -26,7 +26,7 @@ use crate::{ fee_estimator_fn }, utils::into_fixed_buffer, - adaptors::primitives::{FFIOutPoint} + adaptors::primitives::{FFIOutPoint, Bytes32} }; use bitcoin::Block; @@ -137,15 +137,29 @@ ffi! { } fn tell_block_connected_after_resume( - block_ref: Ref, + block_ref: Ref, + block_len: usize, height: u32, key_ref: Ref, handle: FFIManyChannelMonitorHandle ) -> FFIResult { - let block: &Block = unsafe_block!("" => block_ref.as_ref()); + let block_bytes = unsafe_block!("" => block_ref.as_bytes(block_len)); + let block = bitcoin::consensus::deserialize(block_bytes)?; let key: OutPoint = unsafe_block!("" => key_ref.as_ref()).clone().into(); let chan_mon = handle.as_ref(); - chan_mon.tell_block_connected_after_resume(block, height, key)?; + chan_mon.tell_block_connected_after_resume(&block, height, key)?; + FFIResult::ok() + } + + fn tell_block_disconnected_after_resume( + block_hash_ref: Ref, + height: u32, + key_ref: Ref, + handle: FFIManyChannelMonitorHandle) -> FFIResult { + let block_hash: BlockHash = unsafe_block!("" => block_hash_ref.as_ref()).clone().into(); + let key: OutPoint = unsafe_block!("" => key_ref.as_ref()).clone().into(); + let chan_mon = handle.as_ref(); + chan_mon.tell_block_disconnected_after_resume(&block_hash, height, key)?; FFIResult::ok() } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 62adcde37a2..853e74efac1 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -296,6 +296,18 @@ SimpleManyChannelMonitor None => Err(MonitorUpdateError("No such monitor registered")), } } + + /// + pub fn tell_block_disconnected_after_resume(&self, block_hash: &BlockHash, height: u32, key: Key) -> Result<(), MonitorUpdateError> { + let mut monitors = self.monitors.lock().unwrap(); + match monitors.get_mut(&key) { + Some(channel_monitor) => { + channel_monitor.block_disconnected(height, &block_hash, &*self.broadcaster, &*self.fee_estimator, &*self.logger); + Ok(()) + }, + None => Err(MonitorUpdateError("No such monitor registered")) + } + } } From c7ef0f14c85d23845d26fb8ea1f5459955c58036 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Tue, 4 Aug 2020 14:29:37 +0900 Subject: [PATCH 39/45] return latest block hash in ChannelManager deserialization --- bindings/src/channelmanager.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 808fce8c667..2c012e2f466 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -408,6 +408,11 @@ ffi! { log_ptr: Ref, get_est_sat_per_1000_weight_ptr: Ref, monitor_handle: FFIManyChannelMonitorHandle, + + output_buf_ptr: Out, + output_buf_len: usize, + output_actual_len: Out, + handle: Out) -> FFIResult { // TODO: use macro? @@ -464,7 +469,11 @@ ffi! { default_config: default_config.clone(), channel_monitors: &mut Default::default() }; - let (_hash, chan_man): (BlockHash, FFIArcChannelManager) = ReadableArgs::read(&mut buf, readable_args).unwrap(); + let (hash, chan_man): (BlockHash, FFIArcChannelManager) = ReadableArgs::read(&mut buf, readable_args).unwrap(); + + let output_buf = unsafe_block!("" => output_buf_ptr.as_uninit_bytes_mut(output_buf_len)); + into_fixed_buffer(&hash, output_buf, &mut output_actual_len)?; + unsafe_block!("We know chan_man is not null by wrapper macro. And we know `Out` is writable" => handle.init(HandleShared::alloc(chan_man))); FFIResult::ok() } From 2a5ae730cf519c8ccdc5bf4faa479964470f2137 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 8 Aug 2020 11:35:54 +0900 Subject: [PATCH 40/45] resume channel_monitor state when deserializing channel maanger --- bindings/src/channelmanager.rs | 18 ++++++++++++------ lightning/src/ln/channelmonitor.rs | 4 +--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 2c012e2f466..1acad7a1728 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -45,6 +45,9 @@ use lightning::{ routing::network_graph::NetworkGraph }; use crate::channelmonitor::FFIManyChannelMonitorHandle; +use std::collections::HashMap; +use lightning::ln::channelmonitor::ChannelMonitor; +use bitcoin_hashes::hex::ToHex; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc>; pub type FFIArcChannelManagerHandle = HandleShared<'static, FFIArcChannelManager>; @@ -68,7 +71,6 @@ fn create_channel_inner(their_network_key: Ref, channel_value_satoshis: fn claim_funds_inner(payment_preimage: Ref, payment_secret: Option, expected_amount: u64, handle: FFIArcChannelManagerHandle) -> bool { let chan_man: &FFIArcChannelManager = handle.as_ref(); let payment_preimage: PaymentPreimage = unsafe_block!("" => payment_preimage.as_ref()).clone().into(); - chan_man.claim_funds(payment_preimage, &payment_secret, expected_amount) } @@ -409,9 +411,9 @@ ffi! { get_est_sat_per_1000_weight_ptr: Ref, monitor_handle: FFIManyChannelMonitorHandle, - output_buf_ptr: Out, - output_buf_len: usize, - output_actual_len: Out, + output_buf_ptr: Out, + output_buf_len: usize, + output_actual_len: Out, handle: Out) -> FFIResult { @@ -460,6 +462,11 @@ ffi! { let default_config = unsafe_block!("" => cfg.as_ref()); let mut buf = unsafe_block!("The buffer lives as long as this function. And its length is buf_len" => buf_ptr.as_bytes(buf_len)); let monitor = monitor_handle.as_static_ref(); + let mut monitors = monitor.monitors.lock().unwrap(); + let mut channel_monitors: &mut HashMap> = &mut Default::default(); + for m in (*monitors).iter_mut() { + channel_monitors.insert(m.0.clone(), m.1); + } let readable_args = ChannelManagerReadArgs { keys_manager, fee_estimator, @@ -467,7 +474,7 @@ ffi! { tx_broadcaster, logger, default_config: default_config.clone(), - channel_monitors: &mut Default::default() + channel_monitors: channel_monitors }; let (hash, chan_man): (BlockHash, FFIArcChannelManager) = ReadableArgs::read(&mut buf, readable_args).unwrap(); @@ -483,6 +490,5 @@ ffi! { FFIResult::ok() })) } - } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 853e74efac1..3096a39065a 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -176,10 +176,8 @@ pub struct SimpleManyChannelMonitor>>, - #[cfg(not(test))] - monitors: Mutex>>, chain_monitor: C, broadcaster: T, logger: L, From f7b893bf85c5f103d98ccd50ba93c34b73ab65a1 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Sat, 8 Aug 2020 17:52:23 +0900 Subject: [PATCH 41/45] add FFI entrypoint for constructing PeerManager from NetworkGraph --- bindings/src/peermanager.rs | 141 ++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 30 deletions(-) diff --git a/bindings/src/peermanager.rs b/bindings/src/peermanager.rs index f9003d577e9..aa0f515741f 100644 --- a/bindings/src/peermanager.rs +++ b/bindings/src/peermanager.rs @@ -10,6 +10,7 @@ use lightning::{ ln::peer_handler::{PeerManager, MessageHandler}, routing::network_graph::NetGraphMsgHandler, util::config::UserConfig, + util::ser::Readable }; use crate::{ @@ -25,6 +26,7 @@ use crate::{ FFIArcChannelManager } }; +use lightning::routing::network_graph::NetworkGraph; type FFISimpleArcPeerManager = PeerManager, Arc>>, Arc>; type FFIArcPeerManagerHandle = HandleShared<'static, FFISimpleArcPeerManager>; @@ -44,6 +46,62 @@ fn construct_socket_desc ( socket } +fn create_peer_manager_inner( + seed: Ref, + cfg: Ref, + chan_man: FFIArcChannelManagerHandle, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + log_ptr: Ref, + our_node_secret_ptr: Ref, + maybe_network_graph: Option, +) -> FFISimpleArcPeerManager { + let log_ref = unsafe_block!("" => log_ptr.as_ref()); + + let our_node_secret: secp256k1::SecretKey = { + let o = unsafe_block!("" => our_node_secret_ptr.as_ref()); + o.clone().into() + }; + let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); + let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); + let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); + let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); + let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); + let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); + + let logger_arc = Arc::new(FFILogger { log_ptr: *log_ref }); + let chain_watch_interface_arc = + Arc::new(FFIChainWatchInterface::new( + *install_watch_tx_ref, + *install_watch_outpoint_ref, + *watch_all_txn_ref, + *get_chain_utxo_ref, + *filter_block_ref, + *reentered_ref + )); + let route_handler = + match maybe_network_graph { + Some(network) => { + NetGraphMsgHandler::from_net_graph(chain_watch_interface_arc, logger_arc.clone(), network) + }, + None => { + NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()) + }, + }; + let chan_man = chan_man.as_static_ref(); + let msg_handler = + MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; + + let seed = unsafe_block!("It points to valid length buffer" => seed.as_ref()); + let peer_man = + FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &seed.bytes, logger_arc); + peer_man +} + ffi! { fn create_peer_manager( seed: Ref, @@ -62,41 +120,64 @@ ffi! { our_node_secret_ptr: Ref, handle: Out ) -> FFIResult { - let log_ref = unsafe_block!("" => log_ptr.as_ref()); - - let our_node_secret: secp256k1::SecretKey = { - let o = unsafe_block!("" => our_node_secret_ptr.as_ref()); - o.clone().into() - }; - let install_watch_tx_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_tx_ptr.as_ref()); - let install_watch_outpoint_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => install_watch_outpoint_ptr.as_ref()); - let watch_all_txn_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => watch_all_txn_ptr.as_ref()); - let get_chain_utxo_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => get_chain_utxo_ptr.as_ref()); - let filter_block_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => filter_block_ptr.as_ref()); - let reentered_ref = unsafe_block!("function pointer lives as long as ChainWatchInterface and it points to valid data" => reentered_ptr.as_ref()); - - let logger_arc = Arc::new(FFILogger { log_ptr: *log_ref }); - let chain_watch_interface_arc = - Arc::new(FFIChainWatchInterface::new( - *install_watch_tx_ref, - *install_watch_outpoint_ref, - *watch_all_txn_ref, - *get_chain_utxo_ref, - *filter_block_ref, - *reentered_ref - )); - let route_handler = NetGraphMsgHandler::new(chain_watch_interface_arc, logger_arc.clone()); - let chan_man = chan_man.as_static_ref(); - let msg_handler = - MessageHandler { chan_handler: chan_man, route_handler: Arc::new(route_handler) }; - - let seed = unsafe_block!("It points to valid length buffer" => seed.as_ref()); let peer_man = - FFISimpleArcPeerManager::new(msg_handler, our_node_secret.clone(), &seed.bytes, logger_arc); + create_peer_manager_inner( + seed, + cfg, + chan_man, + install_watch_tx_ptr, + install_watch_outpoint_ptr, + watch_all_txn_ptr, + get_chain_utxo_ptr, + filter_block_ptr, + reentered_ptr, + log_ptr, + our_node_secret_ptr, + None + ); unsafe_block!("" => handle.init(FFIArcPeerManagerHandle::alloc(peer_man))); FFIResult::ok() } + fn create_peer_manager_from_net_graph( + seed: Ref, + cfg: Ref, + + chan_man: FFIArcChannelManagerHandle, + install_watch_tx_ptr: Ref, + install_watch_outpoint_ptr: Ref, + watch_all_txn_ptr: Ref, + get_chain_utxo_ptr: Ref, + filter_block_ptr: Ref, + reentered_ptr: Ref, + + log_ptr: Ref, + + our_node_secret_ptr: Ref, + network_graph_buf_ptr: Ref, + network_graph_buf_len: usize, + handle: Out + ) -> FFIResult { + let mut net_graph_buf = unsafe_block!("" => network_graph_buf_ptr.as_bytes(network_graph_buf_len)); + let net_graph: NetworkGraph = Readable::read(&mut net_graph_buf).unwrap(); + let peer_man = + create_peer_manager_inner( + seed, + cfg, + chan_man, + install_watch_tx_ptr, + install_watch_outpoint_ptr, + watch_all_txn_ptr, + get_chain_utxo_ptr, + filter_block_ptr, + reentered_ptr, + log_ptr, + our_node_secret_ptr, + Some(net_graph), + ); + unsafe_block!("" => handle.init(FFIArcPeerManagerHandle::alloc(peer_man))); + FFIResult::ok() + } fn new_inbound_connection( index: usize, send_data_ptr: Ref, From 14a1a8e2367729f4c84462fb61c31d15ad5d3832 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 10 Aug 2020 18:58:26 +0900 Subject: [PATCH 42/45] define serialization for Vec --- lightning/src/util/ser.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index beed0b29997..aaab35fc2c1 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -1,7 +1,7 @@ //! A very simple serialization framework which is used to serialize/deserialize messages as well //! as ChannelsManagers and ChannelMonitors. -use std::io::{Read, Write}; +use std::io::{Read, Write, Error}; use std::collections::HashMap; use std::hash::Hash; use std::sync::Mutex; @@ -16,7 +16,7 @@ use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::{Txid, BlockHash}; use std::marker::Sized; -use ln::msgs::DecodeError; +use ln::msgs::{DecodeError, NetAddress}; use ln::channelmanager::{PaymentPreimage, PaymentHash, PaymentSecret}; use util::byte_utils; @@ -175,6 +175,34 @@ impl Readable for Vec<(crate::chain::transaction::OutPoint, BlockHash)> } } +impl Writeable for Vec { + fn write(&self, w: &mut W) -> Result<(), Error> { + (self.len() as u16).write(w)?; + for addr in self.iter() { + addr.write(w)?; + } + Ok(()) + } +} + +impl Readable for Vec { + fn read(r: &mut R) -> Result { + let len: u16 = Readable::read(r)?; + let mut ret = Vec::with_capacity(len as usize); + for _ in 0..(len as usize) + { + let res = Readable::read(r)?; + match res { + Ok(addr) => { + ret.push(addr); + }, + Err(_) => { return Err(DecodeError::InvalidValue); }, + } + } + Ok(ret) + } +} + /// A trait that various rust-lightning types implement allowing them to be written out to a Writer pub trait Writeable { From f21c92977cfc79b67fc2f0bb24a5f1ec58dbf238 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 10 Aug 2020 18:58:45 +0900 Subject: [PATCH 43/45] Define new entrypoint. `ChannelManager::broadcast_node_announcement` --- bindings/src/channelmanager.rs | 43 +++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/bindings/src/channelmanager.rs b/bindings/src/channelmanager.rs index 1acad7a1728..8e57e398ead 100644 --- a/bindings/src/channelmanager.rs +++ b/bindings/src/channelmanager.rs @@ -26,27 +26,38 @@ use crate::{ channelmonitor::FFIManyChannelMonitor }; use lightning::{ - routing::router::{RouteHint, get_route}, + routing::{ + router::{ + RouteHint, + get_route, + Route + }, + network_graph::NetworkGraph + }, util::{ config::UserConfig, - events::EventsProvider - }, - ln::{ - channelmanager::{ChannelManager, PaymentHash}, + events::EventsProvider, + ser::{Readable, ReadableArgs} }, chain::{ keysinterface::{InMemoryChannelKeys}, transaction::OutPoint }, - routing::router::Route, - ln::channelmanager::{PaymentSecret, PaymentPreimage}, - util::ser::{Readable, ReadableArgs}, - ln::channelmanager::ChannelManagerReadArgs, - routing::network_graph::NetworkGraph + ln::{ + channelmanager::{ + ChannelManagerReadArgs, + PaymentSecret, + PaymentPreimage, + ChannelManager, + PaymentHash + }, + channelmonitor::ChannelMonitor, + msgs::NetAddress + }, }; + use crate::channelmonitor::FFIManyChannelMonitorHandle; use std::collections::HashMap; -use lightning::ln::channelmonitor::ChannelMonitor; use bitcoin_hashes::hex::ToHex; pub type FFIArcChannelManager = ChannelManager, Arc, Arc, Arc>; @@ -328,6 +339,16 @@ ffi! { FFIResult::ok() } + fn broadcast_node_announcement(rgb: Ref<[u8; 3]>, alias: Ref<[u8; 32]>, addresses_bytes_ref: Ref, handle: FFIArcChannelManagerHandle) -> FFIResult { + let rgb = unsafe_block!("" => rgb.as_ref()); + let alias = unsafe_block!("" => alias.as_ref()); + let addresses_bytes = unsafe_block!("" => addresses_bytes_ref.as_ref()); + let addresses: Vec = Readable::read(&mut addresses_bytes.as_ref()).unwrap(); + let chan_man = handle.as_ref(); + chan_man.broadcast_node_announcement(rgb.clone(), alias.clone(), addresses); + FFIResult::ok() + } + fn process_pending_htlc_forwards(handle: FFIArcChannelManagerHandle) -> FFIResult { let chan_man: &FFIArcChannelManager = handle.as_ref(); chan_man.process_pending_htlc_forwards(); From 2fd1ed7093b174beba72c1ed854561e1190e3f89 Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Thu, 13 Aug 2020 17:10:24 +0900 Subject: [PATCH 44/45] Fix bug in Channel Before this commit, `fn get_inbound_outbound_available_balance_msat` always returns 0. It is because using `cmp::min` instead of `cmp::max` . --- lightning/src/ln/channel.rs | 4 ++-- lightning/src/ln/functional_tests.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 6823458e247..f1fa3515f48 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1693,8 +1693,8 @@ impl Channel { /// corner case properly. pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) { // Note that we have to handle overflow due to the above case. - (cmp::min(self.channel_value_satoshis as i64 * 1000 - self.value_to_self_msat as i64 - self.get_inbound_pending_htlc_stats().1 as i64, 0) as u64, - cmp::min(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64) + (cmp::max(self.channel_value_satoshis as i64 * 1000 - self.value_to_self_msat as i64 - self.get_inbound_pending_htlc_stats().1 as i64, 0) as u64, + cmp::max(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64) } // Get the fee cost of a commitment tx with a given number of HTLC outputs. diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 346222280d3..54579d660d4 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1883,6 +1883,25 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() { check_added_monitors!(nodes[1], 1); } +#[test] +fn test_inbound_outbound_capacity_is_not_zero() { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::known(), InitFeatures::known()); + let channels0 = node_chanmgrs[0].list_channels(); + let channels1 = node_chanmgrs[1].list_channels(); + assert_eq!(channels0.len(), 1); + assert_eq!(channels1.len(), 1); + + assert_eq!(channels0[0].inbound_capacity_msat, 95000000); + assert_eq!(channels1[0].outbound_capacity_msat, 95000000); + + assert_eq!(channels0[0].outbound_capacity_msat, 100000 * 1000 - 95000000); + assert_eq!(channels1[0].inbound_capacity_msat, 100000 * 1000 - 95000000); +} + fn commit_tx_fee_msat(feerate: u32, num_htlcs: u64) -> u64 { (COMMITMENT_TX_BASE_WEIGHT + num_htlcs * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate as u64 / 1000 * 1000 } From d59176c09fd7959daa6fdde19c3dca4a9a68b2ab Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Fri, 28 Aug 2020 17:08:33 +0900 Subject: [PATCH 45/45] add get_and_clear_pending_events endpoint to ManyChannelMonitor --- bindings/src/channelmonitor.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bindings/src/channelmonitor.rs b/bindings/src/channelmonitor.rs index e51e5b18fd7..cdddbb99231 100644 --- a/bindings/src/channelmonitor.rs +++ b/bindings/src/channelmonitor.rs @@ -1,6 +1,9 @@ use std::sync::Arc; -use bitcoin::hash_types::{BlockHash}; +use bitcoin::{ + hash_types::{BlockHash}, + Block +}; use lightning::{ ln::channelmonitor::SimpleManyChannelMonitor, @@ -9,7 +12,8 @@ use lightning::{ keysinterface::InMemoryChannelKeys }, ln::channelmonitor::SimpleManyChannelMonitorReadArgs, - util::ser::ReadableArgs + util::ser::ReadableArgs, + util::events::EventsProvider }; use crate::{ @@ -26,9 +30,9 @@ use crate::{ fee_estimator_fn }, utils::into_fixed_buffer, - adaptors::primitives::{FFIOutPoint, Bytes32} + adaptors::primitives::{FFIOutPoint, Bytes32}, + adaptors::primitives::FFIEvents }; -use bitcoin::Block; pub type FFIManyChannelMonitor = SimpleManyChannelMonitor, Arc, Arc, Arc>; pub type FFIManyChannelMonitorHandle = HandleShared<'static, FFIManyChannelMonitor>; @@ -163,6 +167,12 @@ ffi! { FFIResult::ok() } + fn many_channel_monitor_get_and_clear_pending_events(handle: FFIManyChannelMonitorHandle, buf_out: Out, buf_len: usize, actual_buffer_len: Out) -> FFIResult { + let buf = unsafe_block!("The buffer lives as long as this function, the length is within the buffer and the buffer won't be read before initialization" => buf_out.as_uninit_bytes_mut(buf_len)); + let chan_mon = handle.as_ref(); + let mut e = FFIEvents{ events: chan_mon.get_and_clear_pending_events() }; + into_fixed_buffer(&mut e, buf, &mut actual_buffer_len) + } fn release_many_channel_monitor(handle: FFIManyChannelMonitorHandle) -> FFIResult { unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIManyChannelMonitorHandle::dealloc(handle, |mut _handle| {