X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannel.rs;h=16a8829468ec7a0323461369727142eb4bdb5a22;hb=e44713190a090156e4bffac518dad98574e411be;hp=1fc0d8b3cd6bb263b859886e1463c294495e194f;hpb=1521633ca235449130d5fdea1382627c26c5544b;p=rust-lightning diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 1fc0d8b3..16a88294 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -37,9 +37,9 @@ use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter}; use util::logger::Logger; use util::errors::APIError; use util::config::{UserConfig,ChannelConfig}; +use util::scid_utils::scid_from_parts; use std; -use std::default::Default; use std::{cmp,mem,fmt}; use std::ops::Deref; #[cfg(any(test, feature = "fuzztarget"))] @@ -96,6 +96,7 @@ enum InboundHTLCState { /// is used to derive commitment keys, which are used to construct the /// signatures in a commitment_signed message. /// Implies AwaitingRemoteRevoke. + /// /// [BOLT #2]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus), /// Included in a received commitment_signed message (implying we've revoke_and_ack'd it). @@ -282,6 +283,19 @@ impl HTLCCandidate { } } +/// Information needed for constructing an invoice route hint for this channel. +#[derive(Clone)] +pub struct CounterpartyForwardingInfo { + /// Base routing fee in millisatoshis. + pub fee_base_msat: u32, + /// Amount in millionths of a satoshi the channel will charge per transferred satoshi. + pub fee_proportional_millionths: u32, + /// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart, + /// such that the outgoing HTLC is forwardable to this counterparty. See `msgs::ChannelUpdate`'s + /// `cltv_expiry_delta` for more details. + pub cltv_expiry_delta: u16, +} + // TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking // has been completed, and then turn into a Channel to get compiler-time enforcement of things like // calling channel_id() before we're set up or things like get_outbound_funding_signed on an @@ -368,9 +382,6 @@ pub(super) struct Channel { /// could miss the funding_tx_confirmed_in block as well, but it serves as a useful fallback. funding_tx_confirmed_in: Option, short_channel_id: Option, - /// Used to deduplicate block_connected callbacks, also used to verify consistency during - /// ChannelManager deserialization (hence pub(super)) - pub(super) last_block_connected: BlockHash, funding_tx_confirmations: u64, counterparty_dust_limit_satoshis: u64, @@ -395,6 +406,8 @@ pub(super) struct Channel { //implied by OUR_MAX_HTLCS: max_accepted_htlcs: u16, minimum_depth: u32, + counterparty_forwarding_info: Option, + pub(crate) channel_transaction_parameters: ChannelTransactionParameters, counterparty_cur_commitment_point: Option, @@ -569,7 +582,6 @@ impl Channel { funding_tx_confirmed_in: None, short_channel_id: None, - last_block_connected: Default::default(), funding_tx_confirmations: 0, feerate_per_kw: feerate, @@ -582,6 +594,8 @@ impl Channel { counterparty_max_accepted_htlcs: 0, minimum_depth: 0, // Filled in in accept_channel + counterparty_forwarding_info: None, + channel_transaction_parameters: ChannelTransactionParameters { holder_pubkeys: pubkeys, holder_selected_contest_delay: config.own_channel_config.our_to_self_delay, @@ -805,7 +819,6 @@ impl Channel { funding_tx_confirmed_in: None, short_channel_id: None, - last_block_connected: Default::default(), funding_tx_confirmations: 0, feerate_per_kw: msg.feerate_per_kw, @@ -819,6 +832,8 @@ impl Channel { counterparty_max_accepted_htlcs: msg.max_accepted_htlcs, minimum_depth: config.own_channel_config.minimum_depth, + counterparty_forwarding_info: None, + channel_transaction_parameters: ChannelTransactionParameters { holder_pubkeys: pubkeys, holder_selected_contest_delay: config.own_channel_config.our_to_self_delay, @@ -1522,7 +1537,7 @@ impl Channel { &self.get_counterparty_pubkeys().funding_pubkey } - pub fn funding_created(&mut self, msg: &msgs::FundingCreated, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> where L::Target: Logger { + pub fn funding_created(&mut self, msg: &msgs::FundingCreated, last_block_hash: BlockHash, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> where L::Target: Logger { if self.is_outbound() { return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned())); } @@ -1570,13 +1585,13 @@ impl Channel { let funding_redeemscript = self.get_funding_redeemscript(); let funding_txo_script = funding_redeemscript.to_v0_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound()); - let mut channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), - &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), - &self.destination_script, (funding_txo, funding_txo_script.clone()), - &self.channel_transaction_parameters, - funding_redeemscript.clone(), self.channel_value_satoshis, - obscure_factor, - holder_commitment_tx); + let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), + &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), + &self.destination_script, (funding_txo, funding_txo_script.clone()), + &self.channel_transaction_parameters, + funding_redeemscript.clone(), self.channel_value_satoshis, + obscure_factor, + holder_commitment_tx, last_block_hash); channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger); @@ -1593,7 +1608,7 @@ impl Channel { /// Handles a funding_signed message from the remote end. /// 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 { + pub fn funding_signed(&mut self, msg: &msgs::FundingSigned, last_block_hash: BlockHash, logger: &L) -> Result, ChannelError> where L::Target: Logger { if !self.is_outbound() { return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned())); } @@ -1640,13 +1655,13 @@ impl Channel { let funding_txo = self.get_funding_txo().unwrap(); let funding_txo_script = funding_redeemscript.to_v0_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound()); - let mut channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), - &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), - &self.destination_script, (funding_txo, funding_txo_script), - &self.channel_transaction_parameters, - funding_redeemscript.clone(), self.channel_value_satoshis, - obscure_factor, - holder_commitment_tx); + let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), + &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), + &self.destination_script, (funding_txo, funding_txo_script), + &self.channel_transaction_parameters, + funding_redeemscript.clone(), self.channel_value_satoshis, + obscure_factor, + holder_commitment_tx, last_block_hash); channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger); @@ -3325,7 +3340,7 @@ impl Channel { // Upper bound by capacity. We make it a bit less than full capacity to prevent attempts // to use full capacity. This is an effort to reduce routing failures, because in many cases // channel might have been used to route very small values (either by honest users or as DoS). - self.channel_value_satoshis * 9 / 10, + self.channel_value_satoshis * 1000 * 9 / 10, Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis) ); @@ -3517,12 +3532,12 @@ impl Channel { _ => true } }); - let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); - if header.block_hash() != self.last_block_connected { - if self.funding_tx_confirmations > 0 { - self.funding_tx_confirmations += 1; - } + + if self.funding_tx_confirmations > 0 { + self.funding_tx_confirmations += 1; } + + let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { for &(index_in_block, tx) in txdata.iter() { let funding_txo = self.get_funding_txo().unwrap(); @@ -3556,58 +3571,53 @@ impl Channel { } } } - if height > 0xff_ff_ff || (index_in_block) > 0xff_ff_ff { - panic!("Block was bogus - either height 16 million or had > 16 million transactions"); - } - assert!(txo_idx <= 0xffff); // txo_idx is a (u16 as usize), so this is just listed here for completeness self.funding_tx_confirmations = 1; - self.short_channel_id = Some(((height as u64) << (5*8)) | - ((index_in_block as u64) << (2*8)) | - ((txo_idx as u64) << (0*8))); + self.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) { + Ok(scid) => Some(scid), + Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), + } } } } } - if header.block_hash() != self.last_block_connected { - self.last_block_connected = header.block_hash(); - self.update_time_counter = cmp::max(self.update_time_counter, header.time); - if self.funding_tx_confirmations > 0 { - if self.funding_tx_confirmations == self.minimum_depth as u64 { - let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 { - self.channel_state |= ChannelState::OurFundingLocked as u32; - true - } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) { - self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS); - self.update_time_counter += 1; - true - } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) { - // We got a reorg but not enough to trigger a force close, just update - // funding_tx_confirmed_in and return. - false - } else if self.channel_state < ChannelState::ChannelFunded as u32 { - panic!("Started confirming a channel in a state pre-FundingSent?: {}", self.channel_state); + + self.update_time_counter = cmp::max(self.update_time_counter, header.time); + if self.funding_tx_confirmations > 0 { + if self.funding_tx_confirmations == self.minimum_depth as u64 { + let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 { + self.channel_state |= ChannelState::OurFundingLocked as u32; + true + } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) { + self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS); + self.update_time_counter += 1; + true + } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) { + // We got a reorg but not enough to trigger a force close, just update + // funding_tx_confirmed_in and return. + false + } else if self.channel_state < ChannelState::ChannelFunded as u32 { + panic!("Started confirming a channel in a state pre-FundingSent?: {}", self.channel_state); + } else { + // We got a reorg but not enough to trigger a force close, just update + // funding_tx_confirmed_in and return. + false + }; + self.funding_tx_confirmed_in = Some(header.block_hash()); + + //TODO: Note that this must be a duplicate of the previous commitment point they sent us, + //as otherwise we will have a commitment transaction that they can't revoke (well, kinda, + //they can by sending two revoke_and_acks back-to-back, but not really). This appears to be + //a protocol oversight, but I assume I'm just missing something. + if need_commitment_update { + if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 { + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + return Ok((Some(msgs::FundingLocked { + channel_id: self.channel_id, + next_per_commitment_point, + }), timed_out_htlcs)); } else { - // We got a reorg but not enough to trigger a force close, just update - // funding_tx_confirmed_in and return. - false - }; - self.funding_tx_confirmed_in = Some(self.last_block_connected); - - //TODO: Note that this must be a duplicate of the previous commitment point they sent us, - //as otherwise we will have a commitment transaction that they can't revoke (well, kinda, - //they can by sending two revoke_and_acks back-to-back, but not really). This appears to be - //a protocol oversight, but I assume I'm just missing something. - if need_commitment_update { - if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 { - let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); - return Ok((Some(msgs::FundingLocked { - channel_id: self.channel_id, - next_per_commitment_point, - }), timed_out_htlcs)); - } else { - self.monitor_pending_funding_locked = true; - return Ok((None, timed_out_htlcs)); - } + self.monitor_pending_funding_locked = true; + return Ok((None, timed_out_htlcs)); } } } @@ -3625,8 +3635,7 @@ impl Channel { return true; } } - self.last_block_connected = header.block_hash(); - if Some(self.last_block_connected) == self.funding_tx_confirmed_in { + if Some(header.block_hash()) == self.funding_tx_confirmed_in { self.funding_tx_confirmations = self.minimum_depth as u64 - 1; } false @@ -4087,7 +4096,8 @@ impl Channel { signature = res.0; htlc_signatures = res.1; - log_trace!(logger, "Signed remote commitment tx {} with redeemscript {} -> {}", + log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {}", + encode::serialize_hex(&counterparty_commitment_tx.0.trust().built_transaction().transaction), &counterparty_commitment_txid, encode::serialize_hex(&self.get_funding_redeemscript()), log_bytes!(signature.serialize_compact()[..])); @@ -4122,6 +4132,25 @@ impl Channel { } } + /// Get forwarding information for the counterparty. + pub fn counterparty_forwarding_info(&self) -> Option { + self.counterparty_forwarding_info.clone() + } + + pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<(), ChannelError> { + let usable_channel_value_msat = (self.channel_value_satoshis - self.counterparty_selected_channel_reserve_satoshis) * 1000; + if msg.contents.htlc_minimum_msat >= usable_channel_value_msat { + return Err(ChannelError::Close("Minimum htlc value is greater than channel value".to_string())); + } + self.counterparty_forwarding_info = Some(CounterpartyForwardingInfo { + fee_base_msat: msg.contents.fee_base_msat, + fee_proportional_millionths: msg.contents.fee_proportional_millionths, + cltv_expiry_delta: msg.contents.cltv_expiry_delta + }); + + Ok(()) + } + /// Begins the shutdown process, getting a message for the remote peer and returning all /// holding cell HTLCs for payment failure. pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, PaymentHash)>), APIError> { @@ -4178,7 +4207,11 @@ impl Channel { /// those explicitly stated to be allowed after shutdown completes, eg some simple getters). /// Also returns the list of payment_hashes for channels which we can safely fail backwards /// immediately (others we will have to allow to time out). - pub fn force_shutdown(&mut self, should_broadcast: bool) -> (Option, ChannelMonitorUpdate, Vec<(HTLCSource, PaymentHash)>) { + pub fn force_shutdown(&mut self, should_broadcast: bool) -> (Option<(OutPoint, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>) { + // Note that we MUST only generate a monitor update that indicates force-closure - we're + // called during initialization prior to the chain_monitor in the encompassing ChannelManager + // being fully configured in some cases. Thus, its likely any monitor events we generate will + // be delayed in being processed! See the docs for `ChannelManagerReadArgs` for more. assert!(self.channel_state != ChannelState::ShutdownComplete as u32); // We go ahead and "free" any holding cell HTLCs or HTLCs we haven't yet committed to and @@ -4192,7 +4225,7 @@ impl Channel { _ => {} } } - let funding_txo = if let Some(funding_txo) = self.get_funding_txo() { + let monitor_update = if let Some(funding_txo) = self.get_funding_txo() { // If we haven't yet exchanged funding signatures (ie channel_state < FundingSent), // returning a channel monitor update here would imply a channel monitor update before // we even registered the channel monitor to begin with, which is invalid. @@ -4201,17 +4234,17 @@ impl Channel { // monitor update to the user, even if we return one). // See test_duplicate_chan_id and test_pre_lockin_no_chan_closed_update for more. if self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::ChannelFunded as u32 | ChannelState::ShutdownComplete as u32) != 0 { - Some(funding_txo.clone()) + self.latest_monitor_update_id += 1; + Some((funding_txo, ChannelMonitorUpdate { + update_id: self.latest_monitor_update_id, + updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast }], + })) } else { None } } else { None }; self.channel_state = ChannelState::ShutdownComplete as u32; self.update_time_counter += 1; - self.latest_monitor_update_id += 1; - (funding_txo, ChannelMonitorUpdate { - update_id: self.latest_monitor_update_id, - updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast }], - }, dropped_outbound_htlcs) + (monitor_update, dropped_outbound_htlcs) } } @@ -4430,8 +4463,6 @@ impl Writeable for Channel { self.funding_tx_confirmed_in.write(writer)?; self.short_channel_id.write(writer)?; - - self.last_block_connected.write(writer)?; self.funding_tx_confirmations.write(writer)?; self.counterparty_dust_limit_satoshis.write(writer)?; @@ -4443,6 +4474,16 @@ impl Writeable for Channel { self.counterparty_max_accepted_htlcs.write(writer)?; self.minimum_depth.write(writer)?; + match &self.counterparty_forwarding_info { + Some(info) => { + 1u8.write(writer)?; + info.fee_base_msat.write(writer)?; + info.fee_proportional_millionths.write(writer)?; + info.cltv_expiry_delta.write(writer)?; + }, + None => 0u8.write(writer)? + } + self.channel_transaction_parameters.write(writer)?; self.counterparty_cur_commitment_point.write(writer)?; @@ -4592,8 +4633,6 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel let funding_tx_confirmed_in = Readable::read(reader)?; let short_channel_id = Readable::read(reader)?; - - let last_block_connected = Readable::read(reader)?; let funding_tx_confirmations = Readable::read(reader)?; let counterparty_dust_limit_satoshis = Readable::read(reader)?; @@ -4605,6 +4644,16 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel let counterparty_max_accepted_htlcs = Readable::read(reader)?; let minimum_depth = Readable::read(reader)?; + let counterparty_forwarding_info = match ::read(reader)? { + 0 => None, + 1 => Some(CounterpartyForwardingInfo { + fee_base_msat: Readable::read(reader)?, + fee_proportional_millionths: Readable::read(reader)?, + cltv_expiry_delta: Readable::read(reader)?, + }), + _ => return Err(DecodeError::InvalidValue), + }; + let channel_parameters = Readable::read(reader)?; let counterparty_cur_commitment_point = Readable::read(reader)?; @@ -4664,7 +4713,6 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel funding_tx_confirmed_in, short_channel_id, - last_block_connected, funding_tx_confirmations, counterparty_dust_limit_satoshis, @@ -4676,6 +4724,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel counterparty_max_accepted_htlcs, minimum_depth, + counterparty_forwarding_info, + channel_transaction_parameters: channel_parameters, counterparty_cur_commitment_point, @@ -4711,7 +4761,7 @@ mod tests { use ln::channel::{Channel,Sign,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys}; use ln::channel::MAX_FUNDING_SATOSHIS; use ln::features::InitFeatures; - use ln::msgs::{OptionalField, DataLossProtect, DecodeError}; + use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate}; use ln::chan_utils; use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT}; use chain::chaininterface::{FeeEstimator,ConfirmationTarget}; @@ -4722,6 +4772,7 @@ mod tests { use util::test_utils; use util::logger::Logger; use bitcoin::secp256k1::{Secp256k1, Message, Signature, All}; + use bitcoin::secp256k1::ffi::Signature as FFISignature; use bitcoin::secp256k1::key::{SecretKey,PublicKey}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; @@ -4919,6 +4970,8 @@ mod tests { let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; + let chain_hash = genesis_block(network).header.block_hash(); + let last_block_hash = chain_hash; let keys_provider = test_utils::TestKeysInterface::new(&seed, network); // Go through the flow of opening a channel between two nodes. @@ -4929,7 +4982,7 @@ mod tests { let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); // Create Node B's channel by receiving Node A's open_channel message - let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash()); + let open_channel_msg = node_a_chan.get_open_channel(chain_hash); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap(); @@ -4944,10 +4997,10 @@ mod tests { }]}; let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 }; let funding_created_msg = node_a_chan.get_outbound_funding_created(funding_outpoint, &&logger).unwrap(); - let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, &&logger).unwrap(); + let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, last_block_hash, &&logger).unwrap(); // Node B --> Node A: funding signed - let _ = node_a_chan.funding_signed(&funding_signed_msg, &&logger); + let _ = node_a_chan.funding_signed(&funding_signed_msg, last_block_hash, &&logger); // Now disconnect the two nodes and check that the commitment point in // Node B's channel_reestablish message is sane. @@ -4976,6 +5029,54 @@ mod tests { } } + #[test] + fn channel_update() { + let feeest = TestFeeEstimator{fee_est: 15000}; + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let chain_hash = genesis_block(network).header.block_hash(); + let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + + // Create a channel. + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let config = UserConfig::default(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); + assert!(node_a_chan.counterparty_forwarding_info.is_none()); + assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); // the default + assert!(node_a_chan.counterparty_forwarding_info().is_none()); + + // Make sure that receiving a channel update will update the Channel as expected. + let update = ChannelUpdate { + contents: UnsignedChannelUpdate { + chain_hash, + short_channel_id: 0, + timestamp: 0, + flags: 0, + cltv_expiry_delta: 100, + htlc_minimum_msat: 5, + htlc_maximum_msat: OptionalField::Absent, + fee_base_msat: 110, + fee_proportional_millionths: 11, + excess_data: Vec::new(), + }, + signature: Signature::from(unsafe { FFISignature::new() }) + }; + node_a_chan.channel_update(&update).unwrap(); + + // The counterparty can send an update with a higher minimum HTLC, but that shouldn't + // change our official htlc_minimum_msat. + assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); + match node_a_chan.counterparty_forwarding_info() { + Some(info) => { + assert_eq!(info.cltv_expiry_delta, 100); + assert_eq!(info.fee_base_msat, 110); + assert_eq!(info.fee_proportional_millionths, 11); + }, + None => panic!("expected counterparty forwarding info to be Some") + } + } + #[test] fn outbound_commitment_test() { // Test vectors from BOLT 3 Appendix C: