use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::hashes::sha256d::Hash as Sha256d;
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
use bitcoin::secp256k1::key::{PublicKey,SecretKey};
use util::config::{UserConfig,ChannelConfig};
use util::scid_utils::scid_from_parts;
-use std;
-use std::{cmp,mem,fmt};
-use std::ops::Deref;
+use prelude::*;
+use core::{cmp,mem,fmt};
+use core::ops::Deref;
#[cfg(any(test, feature = "fuzztarget"))]
use std::sync::Mutex;
use bitcoin::hashes::hex::ToHex;
channel_update_status: ChannelUpdateStatus,
+ /// Our counterparty's channel_announcement signatures provided in announcement_signatures.
+ /// This can be used to rebroadcast the channel_announcement message later.
+ announcement_sigs: Option<(Signature, Signature)>,
+
// We save these values so we can make sure `next_local_commit_tx_fee_msat` and
// `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will
// be, by comparing the cached values to the fee of the tranaction generated by
next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
#[cfg(any(test, feature = "fuzztarget"))]
next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
+
+ /// lnd has a long-standing bug where, upon reconnection, if the channel is not yet confirmed
+ /// they will not send a channel_reestablish until the channel locks in. Then, they will send a
+ /// funding_locked *before* sending the channel_reestablish (which is clearly a violation of
+ /// the BOLT specs). We copy c-lightning's workaround here and simply store the funding_locked
+ /// message until we receive a channel_reestablish.
+ ///
+ /// See-also <https://github.com/lightningnetwork/lnd/issues/4006>
+ pub workaround_lnd_bug_4006: Option<msgs::FundingLocked>,
}
#[cfg(any(test, feature = "fuzztarget"))]
channel_update_status: ChannelUpdateStatus::Enabled,
+ announcement_sigs: None,
+
#[cfg(any(test, feature = "fuzztarget"))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
#[cfg(any(test, feature = "fuzztarget"))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
})
}
channel_update_status: ChannelUpdateStatus::Enabled,
+ announcement_sigs: None,
+
#[cfg(any(test, feature = "fuzztarget"))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
#[cfg(any(test, feature = "fuzztarget"))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
};
Ok(chan)
// on-chain ChannelsMonitors during block rescan. Ideally we'd figure out a way to drop
// these, but for now we just have to treat them as normal.
- let mut pending_idx = std::usize::MAX;
+ let mut pending_idx = core::usize::MAX;
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
if htlc.htlc_id == htlc_id_arg {
assert_eq!(htlc.payment_hash, payment_hash_calc);
break;
}
}
- if pending_idx == std::usize::MAX {
+ if pending_idx == core::usize::MAX {
return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned()));
}
///
/// Note that it is still possible to hit these assertions in case we find a preimage on-chain
/// but then have a reorg which settles on an HTLC-failure on chain.
- pub fn get_update_fail_htlc(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket) -> Result<Option<msgs::UpdateFailHTLC>, ChannelError> {
+ pub fn get_update_fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, logger: &L) -> Result<Option<msgs::UpdateFailHTLC>, ChannelError> where L::Target: Logger {
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
panic!("Was asked to fail an HTLC when channel was not in an operational state");
}
// on-chain ChannelsMonitors during block rescan. Ideally we'd figure out a way to drop
// these, but for now we just have to treat them as normal.
- let mut pending_idx = std::usize::MAX;
+ let mut pending_idx = core::usize::MAX;
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
if htlc.htlc_id == htlc_id_arg {
match htlc.state {
pending_idx = idx;
}
}
- if pending_idx == std::usize::MAX {
+ if pending_idx == core::usize::MAX {
return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned()));
}
_ => {}
}
}
+ log_trace!(logger, "Placing failure for HTLC ID {} in holding cell", htlc_id_arg);
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::FailHTLC {
htlc_id: htlc_id_arg,
err_packet,
return Ok(None);
}
+ log_trace!(logger, "Failing HTLC ID {} back with a update_fail_htlc message", htlc_id_arg);
{
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(err_packet.clone()));
if msg.channel_reserve_satoshis > self.channel_value_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(format!("Bogus channel_reserve ({}) and dust_limit ({})", msg.channel_reserve_satoshis, msg.dust_limit_satoshis)));
- }
if msg.channel_reserve_satoshis < self.holder_dust_limit_satoshis {
return Err(ChannelError::Close(format!("Peer never wants payout outputs? channel_reserve_satoshis was ({}). dust_limit is ({})", msg.channel_reserve_satoshis, self.holder_dust_limit_satoshis)));
}
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".to_owned()));
+ self.workaround_lnd_bug_4006 = Some(msg.clone());
+ return Err(ChannelError::Ignore("Peer sent funding_locked when we needed a channel_reestablish. The peer is likely lnd, see https://github.com/lightningnetwork/lnd/issues/4006".to_owned()));
}
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
}
},
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, ref err_packet } => {
- match self.get_update_fail_htlc(htlc_id, err_packet.clone()) {
+ match self.get_update_fail_htlc(htlc_id, err_packet.clone(), logger) {
Ok(update_fail_msg_option) => update_fail_htlcs.push(update_fail_msg_option.unwrap()),
Err(e) => {
if let ChannelError::Ignore(_) = e {}
}
}
- /// Removes any uncommitted HTLCs, to be used on peer disconnection, including any pending
- /// HTLCs that we intended to add but haven't as we were waiting on a remote revoke.
- /// Returns the set of PendingHTLCStatuses from remote uncommitted HTLCs (which we're
- /// implicitly dropping) and the payment_hashes of HTLCs we tried to add but are dropping.
+ /// Removes any uncommitted inbound HTLCs and resets the state of uncommitted outbound HTLC
+ /// updates, to be used on peer disconnection. After this, update_*_htlc messages need to be
+ /// resent.
/// No further message handling calls may be made until a channel_reestablish dance has
/// completed.
- pub fn remove_uncommitted_htlcs_and_mark_paused<L: Deref>(&mut self, logger: &L) -> Vec<(HTLCSource, PaymentHash)> where L::Target: Logger {
- let mut outbound_drops = Vec::new();
-
+ pub fn remove_uncommitted_htlcs_and_mark_paused<L: Deref>(&mut self, logger: &L) where L::Target: Logger {
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
if self.channel_state < ChannelState::FundingSent as u32 {
self.channel_state = ChannelState::ShutdownComplete as u32;
- return outbound_drops;
+ return;
}
// Upon reconnect we have to start the closing_signed dance over, but shutdown messages
// will be retransmitted.
}
}
- self.holding_cell_htlc_updates.retain(|htlc_update| {
- match htlc_update {
- // Note that currently on channel reestablish we assert that there are
- // no holding cell HTLC update_adds, so if in the future we stop
- // dropping added HTLCs here and failing them backwards, then there will
- // need to be corresponding changes made in the Channel's re-establish
- // logic.
- &HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, .. } => {
- outbound_drops.push((source.clone(), payment_hash.clone()));
- false
- },
- &HTLCUpdateAwaitingACK::ClaimHTLC {..} | &HTLCUpdateAwaitingACK::FailHTLC {..} => true,
- }
- });
self.channel_state |= ChannelState::PeerDisconnected as u32;
- log_debug!(logger, "Peer disconnection resulted in {} remote-announced HTLC drops and {} waiting-to-locally-announced HTLC drops on channel {}", outbound_drops.len(), inbound_drop_count, log_bytes!(self.channel_id()));
- outbound_drops
+ log_debug!(logger, "Peer disconnection resulted in {} remote-announced HTLC drops on channel {}", inbound_drop_count, log_bytes!(self.channel_id()));
}
/// Indicates that a ChannelMonitor update failed to be stored by the client and further
/// May panic if some calls other than message-handling calls (which will all Err immediately)
/// have been called between remove_uncommitted_htlcs_and_mark_paused and this call.
- pub fn channel_reestablish<L: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitorUpdate>, RAACommitmentOrder, Option<msgs::Shutdown>), ChannelError> where L::Target: Logger {
+ pub fn channel_reestablish<L: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitorUpdate>, RAACommitmentOrder, Vec<(HTLCSource, PaymentHash)>, Option<msgs::Shutdown>), ChannelError> where L::Target: Logger {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
// 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
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));
+ return Ok((None, None, None, None, RAACommitmentOrder::CommitmentFirst, Vec::new(), shutdown_msg));
}
// We have OurFundingLocked set!
return Ok((Some(msgs::FundingLocked {
channel_id: self.channel_id(),
next_per_commitment_point,
- }), None, None, None, RAACommitmentOrder::CommitmentFirst, shutdown_msg));
+ }), None, None, None, RAACommitmentOrder::CommitmentFirst, Vec::new(), shutdown_msg));
}
let required_revoke = if msg.next_remote_commitment_number + 1 == INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number {
}
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {
- // Note that if in the future we no longer drop holding cell update_adds on peer
- // disconnect, this logic will need to be updated.
- for htlc_update in self.holding_cell_htlc_updates.iter() {
- if let &HTLCUpdateAwaitingACK::AddHTLC { .. } = htlc_update {
- debug_assert!(false, "There shouldn't be any add-HTLCs in the holding cell now because they should have been dropped on peer disconnect. Panic here because said HTLCs won't be handled correctly.");
- }
- }
-
// We're up-to-date and not waiting on a remote revoke (if we are our
// channel_reestablish should result in them sending a revoke_and_ack), but we may
// have received some updates while we were disconnected. Free the holding cell
Err(ChannelError::Close(msg)) => return Err(ChannelError::Close(msg)),
Err(ChannelError::Ignore(_)) | Err(ChannelError::CloseDelayBroadcast(_)) => panic!("Got non-channel-failing result from free_holding_cell_htlcs"),
Ok((Some((commitment_update, monitor_update)), htlcs_to_fail)) => {
- // If in the future we no longer drop holding cell update_adds on peer
- // disconnect, we may be handed some HTLCs to fail backwards here.
- assert!(htlcs_to_fail.is_empty());
- return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), shutdown_msg));
+ return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));
},
Ok((None, htlcs_to_fail)) => {
- // If in the future we no longer drop holding cell update_adds on peer
- // disconnect, we may be handed some HTLCs to fail backwards here.
- assert!(htlcs_to_fail.is_empty());
- return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), shutdown_msg));
+ return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), htlcs_to_fail, shutdown_msg));
},
}
} else {
- return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), shutdown_msg));
+ return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), Vec::new(), shutdown_msg));
}
} else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 {
if required_revoke.is_some() {
if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) != 0 {
self.monitor_pending_commitment_signed = true;
- return Ok((resend_funding_locked, None, None, None, self.resend_order.clone(), shutdown_msg));
+ return Ok((resend_funding_locked, None, None, None, self.resend_order.clone(), Vec::new(), shutdown_msg));
}
- return Ok((resend_funding_locked, required_revoke, Some(self.get_last_commitment_update(logger)), None, self.resend_order.clone(), shutdown_msg));
+ return Ok((resend_funding_locked, required_revoke, Some(self.get_last_commitment_update(logger)), None, self.resend_order.clone(), Vec::new(), shutdown_msg));
} else {
return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction".to_owned()));
}
/// closing).
/// Note that the "channel must be funded" requirement is stricter than BOLT 7 requires - see
/// https://github.com/lightningnetwork/lightning-rfc/issues/468
+ ///
+ /// This will only return ChannelError::Ignore upon failure.
pub fn get_channel_announcement(&self, 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".to_owned()));
Ok((msg, sig))
}
+ /// Signs the given channel announcement, returning a ChannelError::Ignore if no keys are
+ /// available.
+ fn sign_channel_announcement(&self, our_node_secret: &SecretKey, our_node_id: PublicKey, msghash: secp256k1::Message, announcement: msgs::UnsignedChannelAnnouncement, our_bitcoin_sig: Signature) -> Result<msgs::ChannelAnnouncement, ChannelError> {
+ if let Some((their_node_sig, their_bitcoin_sig)) = self.announcement_sigs {
+ let were_node_one = announcement.node_id_1 == our_node_id;
+
+ let our_node_sig = self.secp_ctx.sign(&msghash, our_node_secret);
+ Ok(msgs::ChannelAnnouncement {
+ node_signature_1: if were_node_one { our_node_sig } else { their_node_sig },
+ node_signature_2: if were_node_one { their_node_sig } else { our_node_sig },
+ bitcoin_signature_1: if were_node_one { our_bitcoin_sig } else { their_bitcoin_sig },
+ bitcoin_signature_2: if were_node_one { their_bitcoin_sig } else { our_bitcoin_sig },
+ contents: announcement,
+ })
+ } else {
+ Err(ChannelError::Ignore("Attempted to sign channel announcement before we'd received announcement_signatures".to_string()))
+ }
+ }
+
+ /// Processes an incoming announcement_signatures message, providing a fully-signed
+ /// channel_announcement message which we can broadcast and storing our counterparty's
+ /// signatures for later reconstruction/rebroadcast of the channel_announcement.
+ pub fn announcement_signatures(&mut self, our_node_secret: &SecretKey, our_node_id: PublicKey, chain_hash: BlockHash, msg: &msgs::AnnouncementSignatures) -> Result<msgs::ChannelAnnouncement, ChannelError> {
+ let (announcement, our_bitcoin_sig) = self.get_channel_announcement(our_node_id.clone(), chain_hash)?;
+
+ let msghash = hash_to_message!(&Sha256d::hash(&announcement.encode()[..])[..]);
+
+ if self.secp_ctx.verify(&msghash, &msg.node_signature, &self.get_counterparty_node_id()).is_err() {
+ return Err(ChannelError::Close(format!(
+ "Bad announcement_signatures. Failed to verify node_signature. UnsignedChannelAnnouncement used for verification is {:?}. their_node_key is {:?}",
+ &announcement, self.get_counterparty_node_id())));
+ }
+ if self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, self.counterparty_funding_pubkey()).is_err() {
+ return Err(ChannelError::Close(format!(
+ "Bad announcement_signatures. Failed to verify bitcoin_signature. UnsignedChannelAnnouncement used for verification is {:?}. their_bitcoin_key is ({:?})",
+ &announcement, self.counterparty_funding_pubkey())));
+ }
+
+ self.announcement_sigs = Some((msg.node_signature, msg.bitcoin_signature));
+
+ self.sign_channel_announcement(our_node_secret, our_node_id, msghash, announcement, our_bitcoin_sig)
+ }
+
+ /// Gets a signed channel_announcement for this channel, if we previously received an
+ /// announcement_signatures from our counterparty.
+ pub fn get_signed_channel_announcement(&self, our_node_secret: &SecretKey, our_node_id: PublicKey, chain_hash: BlockHash) -> Option<msgs::ChannelAnnouncement> {
+ let (announcement, our_bitcoin_sig) = match self.get_channel_announcement(our_node_id.clone(), chain_hash) {
+ Ok(res) => res,
+ Err(_) => return None,
+ };
+ let msghash = hash_to_message!(&Sha256d::hash(&announcement.encode()[..])[..]);
+ match self.sign_channel_announcement(our_node_secret, our_node_id, msghash, announcement, our_bitcoin_sig) {
+ Ok(res) => Some(res),
+ Err(_) => None,
+ }
+ }
+
/// May panic if called on a channel that wasn't immediately-previously
/// self.remove_uncommitted_htlcs_and_mark_paused()'d
pub fn get_channel_reestablish<L: Deref>(&self, logger: &L) -> msgs::ChannelReestablish where L::Target: Logger {
const SERIALIZATION_VERSION: u8 = 1;
const MIN_SERIALIZATION_VERSION: u8 = 1;
-impl Writeable for InboundHTLCRemovalReason {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &InboundHTLCRemovalReason::FailRelay(ref error_packet) => {
- 0u8.write(writer)?;
- error_packet.write(writer)?;
- },
- &InboundHTLCRemovalReason::FailMalformed((ref onion_hash, ref err_code)) => {
- 1u8.write(writer)?;
- onion_hash.write(writer)?;
- err_code.write(writer)?;
- },
- &InboundHTLCRemovalReason::Fulfill(ref payment_preimage) => {
- 2u8.write(writer)?;
- payment_preimage.write(writer)?;
- },
- }
- Ok(())
- }
-}
-
-impl Readable for InboundHTLCRemovalReason {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
- Ok(match <u8 as Readable>::read(reader)? {
- 0 => InboundHTLCRemovalReason::FailRelay(Readable::read(reader)?),
- 1 => InboundHTLCRemovalReason::FailMalformed((Readable::read(reader)?, Readable::read(reader)?)),
- 2 => InboundHTLCRemovalReason::Fulfill(Readable::read(reader)?),
- _ => return Err(DecodeError::InvalidValue),
- })
- }
-}
+impl_writeable_tlv_based_enum!(InboundHTLCRemovalReason,;
+ (0, FailRelay),
+ (1, FailMalformed),
+ (2, Fulfill),
+);
impl Writeable for ChannelUpdateStatus {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
impl<Signer: Sign> Writeable for Channel<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
// Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been
- // called but include holding cell updates (and obviously we don't modify self).
+ // called.
- writer.write_all(&[SERIALIZATION_VERSION; 1])?;
- writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?;
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.user_id.write(writer)?;
self.config.write(writer)?;
let mut key_data = VecWriter(Vec::new());
self.holder_signer.write(&mut key_data)?;
- assert!(key_data.0.len() < std::usize::MAX);
- assert!(key_data.0.len() < std::u32::MAX as usize);
+ assert!(key_data.0.len() < core::usize::MAX);
+ assert!(key_data.0.len() < core::u32::MAX as usize);
(key_data.0.len() as u32).write(writer)?;
writer.write_all(&key_data.0[..])?;
&OutboundHTLCState::Committed => {
1u8.write(writer)?;
},
- &OutboundHTLCState::RemoteRemoved(ref fail_reason) => {
- 2u8.write(writer)?;
- fail_reason.write(writer)?;
+ &OutboundHTLCState::RemoteRemoved(_) => {
+ // Treat this as a Committed because we haven't received the CS - they'll
+ // resend the claim/fail on reconnect as we all (hopefully) the missing CS.
+ 1u8.write(writer)?;
},
&OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref fail_reason) => {
3u8.write(writer)?;
self.commitment_secrets.write(writer)?;
self.channel_update_status.write(writer)?;
+
+ write_tlv_fields!(writer, {(0, self.announcement_sigs, option)});
+
Ok(())
}
}
impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel<Signer>
where K::Target: KeysInterface<Signer = Signer> {
fn read<R : ::std::io::Read>(reader: &mut R, keys_source: &'a K) -> Result<Self, DecodeError> {
- let _ver: u8 = Readable::read(reader)?;
- let min_ver: u8 = Readable::read(reader)?;
- if min_ver > SERIALIZATION_VERSION {
- return Err(DecodeError::UnknownVersion);
- }
+ let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let user_id = Readable::read(reader)?;
let config: ChannelConfig = Readable::read(reader)?;
let channel_update_status = Readable::read(reader)?;
+ let mut announcement_sigs = None;
+ read_tlv_fields!(reader, {(0, announcement_sigs, option)});
+
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_source.get_secure_random_bytes());
channel_update_status,
+ announcement_sigs,
+
#[cfg(any(test, feature = "fuzztarget"))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
#[cfg(any(test, feature = "fuzztarget"))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
+
+ workaround_lnd_bug_4006: None,
})
}
}
use bitcoin::hashes::Hash;
use bitcoin::hash_types::{Txid, WPubkeyHash};
use std::sync::Arc;
+ use prelude::*;
struct TestFeeEstimator {
fee_est: u32