-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lazy negotiation is unsound #20
Comments
Note, lazy negotiation is absolutely necessary for performance. Having two round trips (no lazy select) would kill performance. |
So, there is a sort-of OK solution.
Proposed ProtocolThis is a variant on an addition to the multistream protocol that @whyrusleeping proposed earlier for stream reuse. Assumptions:
MessagesThere are 5 messages in this protocol:
SELECTThe
The select message should always be sent by the initiator and the initiator must wait for a response from the other side before continuing. The order in which the protocols are listed is the order of preference. USEThe
The other side must send We can immediately follow up with data without waiting for a response. STOPA Example:
(we send back the protocol name for completeness). RECOVERA
After sending a recover, the sender should wait until it receives the appropriate RESUMEAfter receiving a
The probability of sending this message by accident is negligible. We XOR TICKET with a bit pattern to avoid accidentally sending it back (just to be paranoid). USE-SELECT(the 6th message...) Example:
So, we may want to consider a 6th use-select message. However, I'm not sure if it's really necessary. Basically, you'd send In theory, this is tempting. It can shave off a round-trip when recovering a stream. However, I'm not sure this will be that common of an issue. Thoughts? |
@Stebalien the way that this is built in go, the API for optimistic selection only allows us select a single protocol at a time: Line 18 in 8d87044
So if either I too thought this was an issue at the protocol level, but the multistream-select specification defines it as an interactive protocol. go-libp2p leverages length-prefixing and TCP ordering in a hack to eagerly select two protocols in a row, but this is an implementation-level optimisation that the spec does not condone, yet it shouldn't cause interoperability issues. |
Note: go-libp2p never actually negotiates two protocols in a row. The example in the original issue is just an example. This issue is about the theoretical unsoundness of lazy negotiation. Specifically specifying the protocol we want to speak and then speaking the protocol without waiting for a response. Unrecorded history: This was supposed to be a feature in the spec. |
I meant the
😆 |
Sending The issue here is sending:
Where |
Setup:
protocolA
.protocolB
.Using the lazy handshake protocol, peer A can send:
Then, as the first message (without waiting for a response), it can send:
Unfortunately, if peer B supports
protocolB
but notprotocolA
, it will respond with:And then think it's speaking
protocolB
. It will then intrpretother data
in the context ofprotocolB
.Unfortunately, there are perfectly valid uses for multistream where this can happen. For example, switching. Let's say that peer A wants to talk to a sub-identity C hosted on peer B. One might reasonably support this using the multistream muxer by registering peer IDs with the muxer (treating them as "services"). One could then end up with the following conversation:
If peer B doesn't have a peerC registered, it would respond as follows:
At this point, peer A will be talking the protocol with peer B instead of with the sub identity C.
Unfortunately, I don't know of a good fix that won't break everything (without upping the version and adding a mandatory round trip).
The text was updated successfully, but these errors were encountered: