- /// Implies we have (or are prepared to) send our open_channel/accept_channel message
- OurInitSent = 1 << 0,
- /// Implies we have received their `open_channel`/`accept_channel` message
- TheirInitSent = 1 << 1,
- /// We have sent `funding_created` and are awaiting a `funding_signed` to advance to `FundingSent`.
- /// Note that this is nonsense for an inbound channel as we immediately generate `funding_signed`
- /// upon receipt of `funding_created`, so simply skip this state.
- FundingCreated = 4,
- /// Set when we have received/sent `funding_created` and `funding_signed` and are thus now waiting
- /// on the funding transaction to confirm. The `ChannelReady` flags are set to indicate when we
- /// and our counterparty consider the funding transaction confirmed.
- FundingSent = 8,
- /// Flag which can be set on `FundingSent` to indicate they sent us a `channel_ready` message.
- /// Once both `TheirChannelReady` and `OurChannelReady` are set, state moves on to `ChannelReady`.
- TheirChannelReady = 1 << 4,
- /// Flag which can be set on `FundingSent` to indicate we sent them a `channel_ready` message.
- /// Once both `TheirChannelReady` and `OurChannelReady` are set, state moves on to `ChannelReady`.
- OurChannelReady = 1 << 5,
- ChannelReady = 64,
- /// Flag which is set on `ChannelReady` and `FundingSent` indicating remote side is considered
- /// "disconnected" and no updates are allowed until after we've done a `channel_reestablish`
- /// dance.
- PeerDisconnected = 1 << 7,
- /// Flag which is set on `ChannelReady`, FundingCreated, and `FundingSent` indicating the user has
- /// told us a `ChannelMonitor` update is pending async persistence somewhere and we should pause
- /// sending any outbound messages until they've managed to finish.
- MonitorUpdateInProgress = 1 << 8,
- /// Flag which implies that we have sent a commitment_signed but are awaiting the responding
- /// revoke_and_ack message. During this time period, we can't generate new commitment_signed
- /// messages as then we will be unable to determine which HTLCs they included in their
- /// revoke_and_ack implicit ACK, so instead we have to hold them away temporarily to be sent
- /// later.
- /// Flag is set on `ChannelReady`.
- AwaitingRemoteRevoke = 1 << 9,
- /// Flag which is set on `ChannelReady` or `FundingSent` after receiving a shutdown message from
- /// the remote end. If set, they may not add any new HTLCs to the channel, and we are expected
- /// to respond with our own shutdown message when possible.
- RemoteShutdownSent = 1 << 10,
- /// Flag which is set on `ChannelReady` or `FundingSent` after sending a shutdown message. At this
- /// point, we may not add any new HTLCs to the channel.
- LocalShutdownSent = 1 << 11,
- /// We've successfully negotiated a closing_signed dance. At this point ChannelManager is about
- /// to drop us, but we store this anyway.
- ShutdownComplete = 4096,
- /// Flag which is set on `FundingSent` to indicate this channel is funded in a batch and the
- /// broadcasting of the funding transaction is being held until all channels in the batch
- /// have received funding_signed and have their monitors persisted.
- WaitingForBatch = 1 << 13,
+ /// We are negotiating the parameters required for the channel prior to funding it.
+ NegotiatingFunding(NegotiatingFundingFlags),
+ /// We have sent `funding_created` and are awaiting a `funding_signed` to advance to
+ /// `AwaitingChannelReady`. Note that this is nonsense for an inbound channel as we immediately generate
+ /// `funding_signed` upon receipt of `funding_created`, so simply skip this state.
+ FundingNegotiated,
+ /// We've received/sent `funding_created` and `funding_signed` and are thus now waiting on the
+ /// funding transaction to confirm.
+ AwaitingChannelReady(AwaitingChannelReadyFlags),
+ /// Both we and our counterparty consider the funding transaction confirmed and the channel is
+ /// now operational.
+ ChannelReady(ChannelReadyFlags),
+ /// We've successfully negotiated a `closing_signed` dance. At this point, the `ChannelManager`
+ /// is about to drop us, but we store this anyway.
+ ShutdownComplete,
+}
+
+macro_rules! impl_state_flag {
+ ($get: ident, $set: ident, $clear: ident, $state_flag: expr, [$($state: ident),+]) => {
+ #[allow(unused)]
+ fn $get(&self) -> bool {
+ match self {
+ $(
+ ChannelState::$state(flags) => flags.is_set($state_flag.into()),
+ )*
+ _ => false,
+ }
+ }
+ #[allow(unused)]
+ fn $set(&mut self) {
+ match self {
+ $(
+ ChannelState::$state(flags) => *flags |= $state_flag,
+ )*
+ _ => debug_assert!(false, "Attempted to set flag on unexpected ChannelState"),
+ }
+ }
+ #[allow(unused)]
+ fn $clear(&mut self) {
+ match self {
+ $(
+ ChannelState::$state(flags) => *flags &= !($state_flag),
+ )*
+ _ => debug_assert!(false, "Attempted to clear flag on unexpected ChannelState"),
+ }
+ }
+ };
+ ($get: ident, $set: ident, $clear: ident, $state_flag: expr, FUNDED_STATES) => {
+ impl_state_flag!($get, $set, $clear, $state_flag, [AwaitingChannelReady, ChannelReady]);
+ };
+ ($get: ident, $set: ident, $clear: ident, $state_flag: expr, $state: ident) => {
+ impl_state_flag!($get, $set, $clear, $state_flag, [$state]);
+ };
+}
+
+impl ChannelState {
+ fn from_u32(state: u32) -> Result<Self, ()> {
+ match state {
+ state_flags::FUNDING_NEGOTIATED => Ok(ChannelState::FundingNegotiated),
+ state_flags::SHUTDOWN_COMPLETE => Ok(ChannelState::ShutdownComplete),
+ val => {
+ if val & state_flags::AWAITING_CHANNEL_READY == state_flags::AWAITING_CHANNEL_READY {
+ AwaitingChannelReadyFlags::from_u32(val & !state_flags::AWAITING_CHANNEL_READY)
+ .map(|flags| ChannelState::AwaitingChannelReady(flags))
+ } else if val & state_flags::CHANNEL_READY == state_flags::CHANNEL_READY {
+ ChannelReadyFlags::from_u32(val & !state_flags::CHANNEL_READY)
+ .map(|flags| ChannelState::ChannelReady(flags))
+ } else if let Ok(flags) = NegotiatingFundingFlags::from_u32(val) {
+ Ok(ChannelState::NegotiatingFunding(flags))
+ } else {
+ Err(())
+ }
+ },
+ }
+ }
+
+ fn to_u32(&self) -> u32 {
+ match self {
+ ChannelState::NegotiatingFunding(flags) => flags.0,
+ ChannelState::FundingNegotiated => state_flags::FUNDING_NEGOTIATED,
+ ChannelState::AwaitingChannelReady(flags) => state_flags::AWAITING_CHANNEL_READY | flags.0,
+ ChannelState::ChannelReady(flags) => state_flags::CHANNEL_READY | flags.0,
+ ChannelState::ShutdownComplete => state_flags::SHUTDOWN_COMPLETE,
+ }
+ }
+
+ fn is_pre_funded_state(&self) -> bool {
+ matches!(self, ChannelState::NegotiatingFunding(_)|ChannelState::FundingNegotiated)
+ }
+
+ fn is_both_sides_shutdown(&self) -> bool {
+ self.is_local_shutdown_sent() && self.is_remote_shutdown_sent()
+ }
+
+ fn with_funded_state_flags_mask(&self) -> FundedStateFlags {
+ match self {
+ ChannelState::AwaitingChannelReady(flags) => FundedStateFlags((*flags & FundedStateFlags::ALL).0),
+ ChannelState::ChannelReady(flags) => FundedStateFlags((*flags & FundedStateFlags::ALL).0),
+ _ => FundedStateFlags::new(),
+ }
+ }
+
+ fn should_force_holding_cell(&self) -> bool {
+ match self {
+ ChannelState::ChannelReady(flags) =>
+ flags.is_set(ChannelReadyFlags::AWAITING_REMOTE_REVOKE) ||
+ flags.is_set(FundedStateFlags::MONITOR_UPDATE_IN_PROGRESS.into()) ||
+ flags.is_set(FundedStateFlags::PEER_DISCONNECTED.into()),
+ _ => {
+ debug_assert!(false, "The holding cell is only valid within ChannelReady");
+ false
+ },
+ }
+ }
+
+ impl_state_flag!(is_peer_disconnected, set_peer_disconnected, clear_peer_disconnected,
+ FundedStateFlags::PEER_DISCONNECTED, FUNDED_STATES);
+ impl_state_flag!(is_monitor_update_in_progress, set_monitor_update_in_progress, clear_monitor_update_in_progress,
+ FundedStateFlags::MONITOR_UPDATE_IN_PROGRESS, FUNDED_STATES);
+ impl_state_flag!(is_local_shutdown_sent, set_local_shutdown_sent, clear_local_shutdown_sent,
+ FundedStateFlags::LOCAL_SHUTDOWN_SENT, FUNDED_STATES);
+ impl_state_flag!(is_remote_shutdown_sent, set_remote_shutdown_sent, clear_remote_shutdown_sent,
+ FundedStateFlags::REMOTE_SHUTDOWN_SENT, FUNDED_STATES);
+ impl_state_flag!(is_our_channel_ready, set_our_channel_ready, clear_our_channel_ready,
+ AwaitingChannelReadyFlags::OUR_CHANNEL_READY, AwaitingChannelReady);
+ impl_state_flag!(is_their_channel_ready, set_their_channel_ready, clear_their_channel_ready,
+ AwaitingChannelReadyFlags::THEIR_CHANNEL_READY, AwaitingChannelReady);
+ impl_state_flag!(is_waiting_for_batch, set_waiting_for_batch, clear_waiting_for_batch,
+ AwaitingChannelReadyFlags::WAITING_FOR_BATCH, AwaitingChannelReady);
+ impl_state_flag!(is_awaiting_remote_revoke, set_awaiting_remote_revoke, clear_awaiting_remote_revoke,
+ ChannelReadyFlags::AWAITING_REMOTE_REVOKE, ChannelReady);