Conversation
753acf6 to
b44be37
Compare
NishantBansal2003
left a comment
There was a problem hiding this comment.
I think this needs to be rebased on top of #162 to consume the affine type SentShutdown
morehouse
left a comment
There was a problem hiding this comment.
I think we want the affine type wired in for sure.
But that probably isn't enough -- we can't expect the target to send us a shutdown message until all HTLCs have been resolved as well. There's also probably a case where we could send two shutdown messages and the target may ignore any of them after the first one.
I think for the HTLCs we can't really implement that part until #111 is implemented, so we can just add a TODO for that.
For the duplicate shutdown case we could probably add a flag to the channel state that indicates whether the peer has already responded to the first shutdown, and if they have then any subsequent RecvShutdown message becomes a no-op (similar to RecvChannelReady).
b44be37 to
7c5f9fc
Compare
bc867ba to
77cf4f8
Compare
Added TODOs in 77cf4f8. I thought it would be useful to group all HTLC TODOs together with
We now check in 77cf4f8 if we expect a shutdown on any channel. Only then we wait for a shutdown response. We don't know on which channel we will receive a shutdown, but I could change TODO:
Mhh, I wonder if there's a conflict between this (#163 (comment)):
and this (#163 (review)):
In 77cf4f8, |
2a50e89 to
d083969
Compare
I forgot tests for |
f399f63 to
74b38b0
Compare
|
This is ready for review now. I'm working on a new PR with a |
|
This PR has grown a bit. @ekzyis would it be possible to split this into smaller PRs? Perhaps something like this:
|
74b38b0 to
fd0bbbb
Compare
|
fd0bbbb: rebased on master (dc2ef28) and only includes the new FYI, this question in #163 (comment) is still open for me:
But I think it's more relevant when we start consuming the output of |
RecvShutdown consumes the SentShutdown of a previous SendShutdown, which now carries the `shutdown` we sent, and waits for the target's `shutdown` in reply. It returns the target's scriptpubkey, or empty bytes if no `shutdown` was received. If our own scriptpubkey isn't standard, BOLT 2 says the target should send a warning instead of replying, so we accept a warning for the channel as a reply. A `shutdown` for another channel may answer one we sent there earlier, which we can't check against the `shutdown` we sent here, so it ends the program as an unexpected message. RecvShutdown is a no-op if we don't track the channel or the target already replied. Before the target sent `channel_ready`, it may choose not to reply, but we still expect one: LDK always replies, and a target that doesn't only costs us a receive timeout, which isn't reported as a violation.
fd0bbbb to
8d65b49
Compare
|
8d65b49: add |
| /// `shutdown` has been sent, so the counterparty's `shutdown` may now be | ||
| /// received. | ||
| SentShutdown, | ||
| SentShutdown(Shutdown), |
There was a problem hiding this comment.
This is the first affine variable to contain actual data, and I don't think we really need to do this.
We could make RecvShutdown like RecvChannelReady and record our shutdown script in the channel state when we send shutdown. Then no matter which shutdown we get a response for, we can look it's channel up in the channel state to get our script.
| let reply = if is_shutdown_expected(&self.channel_states, sent.channel_id) { | ||
| log::debug!("[{:?}] RecvShutdown: waiting", start.elapsed()); | ||
| let reply = recv_shutdown_reply( | ||
| &mut self.conn, | ||
| &sent, | ||
| &self.context.negotiated_features, | ||
| )?; | ||
| log::debug!("[{:?}] RecvShutdown: received", start.elapsed()); | ||
| reply | ||
| } else { | ||
| None | ||
| }; | ||
| match reply { | ||
| Some(sd) => { | ||
| self.channel_states | ||
| .get_mut(&sent.channel_id) | ||
| .expect("is_shutdown_expected guarantees a tracked channel") | ||
| .counterparty_shutdown_received = true; | ||
| Some(Variable::Bytes(sd.scriptpubkey)) | ||
| } | ||
| None => Some(Variable::Bytes(Vec::new())), | ||
| } |
There was a problem hiding this comment.
Nit: this code is over-complicated. If recv_shutdown returns the spk instead, we get:
| let reply = if is_shutdown_expected(&self.channel_states, sent.channel_id) { | |
| log::debug!("[{:?}] RecvShutdown: waiting", start.elapsed()); | |
| let reply = recv_shutdown_reply( | |
| &mut self.conn, | |
| &sent, | |
| &self.context.negotiated_features, | |
| )?; | |
| log::debug!("[{:?}] RecvShutdown: received", start.elapsed()); | |
| reply | |
| } else { | |
| None | |
| }; | |
| match reply { | |
| Some(sd) => { | |
| self.channel_states | |
| .get_mut(&sent.channel_id) | |
| .expect("is_shutdown_expected guarantees a tracked channel") | |
| .counterparty_shutdown_received = true; | |
| Some(Variable::Bytes(sd.scriptpubkey)) | |
| } | |
| None => Some(Variable::Bytes(Vec::new())), | |
| } | |
| if is_shutdown_expected(&self.channel_states, sent.channel_id) { | |
| log::debug!("[{:?}] RecvShutdown: waiting", start.elapsed()); | |
| let spk = recv_shutdown( | |
| &mut self.conn, | |
| &sent, | |
| &self.context.negotiated_features, | |
| )?; | |
| log::debug!("[{:?}] RecvShutdown: received", start.elapsed()); | |
| Some(Variable::Bytes(spk)) | |
| } else { | |
| Some(Variable::Bytes(Vec::new())) | |
| }; |
| log::debug!( | ||
| "received shutdown on {} while waiting on {}", | ||
| sd.channel_id, | ||
| sent.channel_id | ||
| ); |
There was a problem hiding this comment.
I don't think it makes sense to flag this as an error -- we can handle any shutdown message by keeping the necessary info in channel state and looking it up by channel_id.
| Message::Warning(w) | ||
| if may_warn && (w.channel_id == sent.channel_id || w.channel_id == ChannelId::ALL) => | ||
| { | ||
| Ok(None) | ||
| } |
There was a problem hiding this comment.
I don't think it makes sense to handle this warning. It looks like every impl except LDK will disconnect us after sending warning anyway, and LDK acts as if the shutdown never came.
We could just stop execution like we usually do on warnings.
| // TODO: we don't know for sure if the target will reply because if a target didn't reply with | ||
| // `channel_ready` yet, it MAY reply with `shutdown` (but doesn't have to) |
There was a problem hiding this comment.
Should we also check for this case? We could just check whether the PCP exists in the channel state.
We would avoid some 1s timeouts on targets that don't reply, but lose some edge case coverage on targets that do reply. WDYT?
This implements the
RecvShutdownoperation for #98.RecvShutdownconsumes theSentShutdownof a previousSendShutdown, which now carries theshutdownwe sent, and waits for the target'sshutdownin reply. It returns the target's scriptpubkey, or empty bytes if noshutdownwas received.The reply is checked by a new
ShutdownOracle. It flags the target if:upfront_shutdown_scriptwe committed to andoption_upfront_shutdown_scriptwas negotiated, so the target must fail the connection instead of replyingupfront_shutdown_scriptit committed to, or isn't a standard form for the negotiated featuresIf our own scriptpubkey isn't standard, BOLT 2 says the target should send a warning instead of replying. If it breaks our
upfront_shutdown_script, the target may send a warning before failing the connection. In both cases we accept a warning for the channel as a reply.A
shutdownfor another tracked channel may answer one we sent there earlier, which we can't check against theshutdownwe sent here, so it ends the program as an unexpected message.RecvShutdownis a no-op if we don't track the channel or the target already replied. Before the target sentchannel_ready, it may choose not to reply, but we still expect one: LDK always replies, and a target doesn't only costs us a receive timeout, which isn't reported as a violation.