- self.context.channel_state |= ChannelState::LocalShutdownSent as u32;
- self.context.update_time_counter += 1;
-
- Ok((shutdown, monitor_update, dropped_outbound_htlcs))
- }
-
- fn build_signed_closing_transaction(&self, closing_tx: &ClosingTransaction, counterparty_sig: &Signature, sig: &Signature) -> Transaction {
- let mut tx = closing_tx.trust().built_transaction().clone();
-
- tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
-
- let funding_key = self.context.get_holder_pubkeys().funding_pubkey.serialize();
- let counterparty_funding_key = self.context.counterparty_funding_pubkey().serialize();
- let mut holder_sig = sig.serialize_der().to_vec();
- holder_sig.push(EcdsaSighashType::All as u8);
- let mut cp_sig = counterparty_sig.serialize_der().to_vec();
- cp_sig.push(EcdsaSighashType::All as u8);
- if funding_key[..] < counterparty_funding_key[..] {
- tx.input[0].witness.push(holder_sig);
- tx.input[0].witness.push(cp_sig);
- } else {
- tx.input[0].witness.push(cp_sig);
- tx.input[0].witness.push(holder_sig);
- }
-
- tx.input[0].witness.push(self.context.get_funding_redeemscript().into_bytes());
- tx
- }
-
- pub fn closing_signed<F: Deref>(
- &mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, msg: &msgs::ClosingSigned)
- -> Result<(Option<msgs::ClosingSigned>, Option<Transaction>), ChannelError>
- where F::Target: FeeEstimator
- {
- if self.context.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".to_owned()));
- }
- if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(ChannelError::Close("Peer sent closing_signed when we needed a channel_reestablish".to_owned()));
- }
- if !self.context.pending_inbound_htlcs.is_empty() || !self.context.pending_outbound_htlcs.is_empty() {
- return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned()));
- }
- if msg.fee_satoshis > TOTAL_BITCOIN_SUPPLY_SATOSHIS { // 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".to_owned()));
- }
-
- if self.context.is_outbound() && self.context.last_sent_closing_fee.is_none() {
- return Err(ChannelError::Close("Remote tried to send a closing_signed when we were supposed to propose the first one".to_owned()));
- }
-
- if self.context.channel_state & ChannelState::MonitorUpdateInProgress as u32 != 0 {
- self.context.pending_counterparty_closing_signed = Some(msg.clone());
- return Ok((None, None));
- }
-
- let funding_redeemscript = self.context.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(format!("Remote sent us a closing_signed with a fee other than the value they can claim. Fee in message: {}. Actual closing tx fee: {}", msg.fee_satoshis, used_total_fee)));
- }
- let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.context.channel_value_satoshis);
-
- match self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) {
- Ok(_) => {},
- Err(_e) => {
- // The remote end may have decided to revoke their output due to inconsistent dust
- // limits, so check for that case by re-checking the signature here.
- closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0;
- let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.context.channel_value_satoshis);
- secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, self.context.counterparty_funding_pubkey()), "Invalid closing tx signature from peer".to_owned());
- },
- };
-
- for outp in closing_tx.trust().built_transaction().output.iter() {
- if !outp.script_pubkey.is_witness_program() && outp.value < MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS {
- return Err(ChannelError::Close("Remote sent us a closing_signed with a dust output. Always use segwit closing scripts!".to_owned()));
- }
- }
-
- assert!(self.context.shutdown_scriptpubkey.is_some());
- if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
- if last_fee == msg.fee_satoshis {
- let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
- self.context.channel_state = ChannelState::ShutdownComplete as u32;
- self.context.update_time_counter += 1;
- return Ok((None, Some(tx)));
- }
- }
-
- let (our_min_fee, our_max_fee) = self.calculate_closing_fee_limits(fee_estimator);
-
- macro_rules! propose_fee {
- ($new_fee: expr) => {
- let (closing_tx, used_fee) = if $new_fee == msg.fee_satoshis {
- (closing_tx, $new_fee)
- } else {
- self.build_closing_transaction($new_fee, false)
- };
-
- let sig = self.context.holder_signer
- .sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
-
- let signed_tx = if $new_fee == msg.fee_satoshis {
- self.context.channel_state = ChannelState::ShutdownComplete as u32;
- self.context.update_time_counter += 1;
- let tx = self.build_signed_closing_transaction(&closing_tx, &msg.signature, &sig);
- Some(tx)
- } else { None };
-
- self.context.last_sent_closing_fee = Some((used_fee, sig.clone()));
- return Ok((Some(msgs::ClosingSigned {
- channel_id: self.context.channel_id,
- fee_satoshis: used_fee,
- signature: sig,
- fee_range: Some(msgs::ClosingSignedFeeRange {
- min_fee_satoshis: our_min_fee,
- max_fee_satoshis: our_max_fee,
- }),
- }), signed_tx))
- }
- }
-
- if let Some(msgs::ClosingSignedFeeRange { min_fee_satoshis, max_fee_satoshis }) = msg.fee_range {
- if msg.fee_satoshis < min_fee_satoshis || msg.fee_satoshis > max_fee_satoshis {
- return Err(ChannelError::Close(format!("Peer sent a bogus closing_signed - suggested fee of {} sat was not in their desired range of {} sat - {} sat", msg.fee_satoshis, min_fee_satoshis, max_fee_satoshis)));
- }
- if max_fee_satoshis < our_min_fee {
- return Err(ChannelError::Warn(format!("Unable to come to consensus about closing feerate, remote's max fee ({} sat) was smaller than our min fee ({} sat)", max_fee_satoshis, our_min_fee)));
- }
- if min_fee_satoshis > our_max_fee {
- return Err(ChannelError::Warn(format!("Unable to come to consensus about closing feerate, remote's min fee ({} sat) was greater than our max fee ({} sat)", min_fee_satoshis, our_max_fee)));
- }
-
- if !self.context.is_outbound() {
- // They have to pay, so pick the highest fee in the overlapping range.
- // We should never set an upper bound aside from their full balance
- debug_assert_eq!(our_max_fee, self.context.channel_value_satoshis - (self.context.value_to_self_msat + 999) / 1000);
- propose_fee!(cmp::min(max_fee_satoshis, our_max_fee));
- } else {
- if msg.fee_satoshis < our_min_fee || msg.fee_satoshis > our_max_fee {
- return Err(ChannelError::Close(format!("Peer sent a bogus closing_signed - suggested fee of {} sat was not in our desired range of {} sat - {} sat after we informed them of our range.",
- msg.fee_satoshis, our_min_fee, our_max_fee)));
- }
- // The proposed fee is in our acceptable range, accept it and broadcast!
- propose_fee!(msg.fee_satoshis);
- }
- } else {
- // Old fee style negotiation. We don't bother to enforce whether they are complying
- // with the "making progress" requirements, we just comply and hope for the best.
- if let Some((last_fee, _)) = self.context.last_sent_closing_fee {
- if msg.fee_satoshis > last_fee {
- if msg.fee_satoshis < our_max_fee {
- propose_fee!(msg.fee_satoshis);
- } else if last_fee < our_max_fee {
- propose_fee!(our_max_fee);
- } else {
- return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wants something ({} sat) higher than our max fee ({} sat)", msg.fee_satoshis, our_max_fee)));
- }
- } else {
- if msg.fee_satoshis > our_min_fee {
- propose_fee!(msg.fee_satoshis);
- } else if last_fee > our_min_fee {
- propose_fee!(our_min_fee);
- } else {
- return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wants something ({} sat) lower than our min fee ({} sat)", msg.fee_satoshis, our_min_fee)));
- }
- }
- } else {
- if msg.fee_satoshis < our_min_fee {
- propose_fee!(our_min_fee);
- } else if msg.fee_satoshis > our_max_fee {
- propose_fee!(our_max_fee);
- } else {
- propose_fee!(msg.fee_satoshis);
- }
- }
- }
- }
-
- fn internal_htlc_satisfies_config(
- &self, htlc: &msgs::UpdateAddHTLC, amt_to_forward: u64, outgoing_cltv_value: u32, config: &ChannelConfig,
- ) -> Result<(), (&'static str, u16)> {
- let fee = amt_to_forward.checked_mul(config.forwarding_fee_proportional_millionths as u64)
- .and_then(|prop_fee| (prop_fee / 1000000).checked_add(config.forwarding_fee_base_msat as u64));
- if fee.is_none() || htlc.amount_msat < fee.unwrap() ||
- (htlc.amount_msat - fee.unwrap()) < amt_to_forward {
- return Err((
- "Prior hop has deviated from specified fees parameters or origin node has obsolete ones",
- 0x1000 | 12, // fee_insufficient
- ));
- }
- if (htlc.cltv_expiry as u64) < outgoing_cltv_value as u64 + config.cltv_expiry_delta as u64 {
- return Err((
- "Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta",
- 0x1000 | 13, // incorrect_cltv_expiry
- ));
- }
- Ok(())
- }
-
- /// Determines whether the parameters of an incoming HTLC to be forwarded satisfy the channel's
- /// [`ChannelConfig`]. This first looks at the channel's current [`ChannelConfig`], and if
- /// unsuccessful, falls back to the previous one if one exists.
- pub fn htlc_satisfies_config(
- &self, htlc: &msgs::UpdateAddHTLC, amt_to_forward: u64, outgoing_cltv_value: u32,
- ) -> Result<(), (&'static str, u16)> {
- self.internal_htlc_satisfies_config(&htlc, amt_to_forward, outgoing_cltv_value, &self.context.config())
- .or_else(|err| {
- if let Some(prev_config) = self.context.prev_config() {
- self.internal_htlc_satisfies_config(htlc, amt_to_forward, outgoing_cltv_value, &prev_config)
- } else {
- Err(err)