dilation-protocol.md: update for new PLEASE+PLEASE approach
This commit is contained in:
parent
5dca0542eb
commit
72c9683cdf
|
@ -13,19 +13,20 @@ messages are used to open/use/close the application-visible subchannels.
|
||||||
|
|
||||||
## Leaders and Followers
|
## Leaders and Followers
|
||||||
|
|
||||||
Each side of a Wormhole has a randomly-generated "side" string. When the
|
Each side of a Wormhole has a randomly-generated dilation "side" string (this
|
||||||
wormhole is dilated, the side with the lexicographically-higher "side" value
|
is independent of the Wormhole's mailbox "side"). When the wormhole is
|
||||||
is named the "Leader", and the other side is named the "Follower". The general
|
dilated, the side with the lexicographically-higher "side" value is named the
|
||||||
wormhole protocol treats both sides identically, but the distinction matters
|
"Leader", and the other side is named the "Follower". The general wormhole
|
||||||
for the dilation protocol.
|
protocol treats both sides identically, but the distinction matters for the
|
||||||
|
dilation protocol.
|
||||||
|
|
||||||
Either side can trigger dilation, but the Follower does so by asking the
|
Both sides send a `please-dilate` as soon as dilation is triggered. Each side
|
||||||
Leader to start the process, whereas the Leader just starts the process
|
discovers whether it is the Leader or the Follower when the peer's
|
||||||
unilaterally. The Leader has exclusive control over whether a given
|
"please-dilate" arrives. The Leader has exclusive control over whether a
|
||||||
connection is considered established or not: if there are multiple potential
|
given connection is considered established or not: if there are multiple
|
||||||
connections to use, the Leader decides which one to use, and the Leader gets
|
potential connections to use, the Leader decides which one to use, and the
|
||||||
to decide when the connection is no longer viable (and triggers the
|
Leader gets to decide when the connection is no longer viable (and triggers
|
||||||
establishment of a new one).
|
the establishment of a new one).
|
||||||
|
|
||||||
## Connection Layers
|
## Connection Layers
|
||||||
|
|
||||||
|
@ -110,51 +111,60 @@ Each `DILATE-n` message is a JSON-encoded dictionary with a `type` field that
|
||||||
has a string value. The dictionary will have other keys that depend upon the
|
has a string value. The dictionary will have other keys that depend upon the
|
||||||
type.
|
type.
|
||||||
|
|
||||||
`w.dilate()` triggers a `please-dilate` record with a set of versions that
|
`w.dilate()` triggers transmission of a `please-dilate` record with a set of
|
||||||
can be accepted. Both Leader and Follower emit this record, although the
|
versions that can be accepted. Versions use strings, rather than integers, to
|
||||||
Leader is responsible for version decisions. Versions use strings, rather
|
support experimental protocols, however there is still a total ordering of
|
||||||
than integers, to support experimental protocols, however there is still a
|
preferability.
|
||||||
total ordering of preferability.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{ "type": "please-dilate",
|
{ "type": "please-dilate",
|
||||||
|
"side": "abcdef",
|
||||||
"accepted-versions": ["1"]
|
"accepted-versions": ["1"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The Leader then sends a `start-dilation` message with a `version` field (the
|
If one side receives a `please-dilate` before `w.dilate()` has been called
|
||||||
"best" mutually-supported value) and the new "L2 generation" number in the
|
locally, the contents are stored in case `w.dilate()` is called in the
|
||||||
`generation` field. Generation numbers are integers, monotonically increasing
|
future. Once both `w.dilate()` has been called and the peer's `please-dilate`
|
||||||
by 1 each time.
|
has been received, the side determines whether it is the Leader or the
|
||||||
|
Follower. Both sides also compare `accepted-versions` fields to choose the
|
||||||
|
best mutually-compatible version to use: they should always pick the same
|
||||||
|
one.
|
||||||
|
|
||||||
```
|
Then both sides begin the connection process for generation 1 by opening
|
||||||
{ "type": start-dilation,
|
listening sockets and sending `connection-hint` records for each one. After a
|
||||||
"version": "1",
|
slight delay they will also open connections to the Transit Relay of their
|
||||||
"generation": 1,
|
choice and produce hints for it too. The receipt of inbound hints (on both
|
||||||
}
|
sides) will trigger outbound connection attempts.
|
||||||
```
|
|
||||||
|
|
||||||
The Follower responds with a `ok-dilation` message with matching `version`
|
Some number of these connections may succeed, and the Leader decides which to
|
||||||
and `generation` fields.
|
use (via an in-band signal on the established connection). The others are
|
||||||
|
dropped.
|
||||||
|
|
||||||
The Leader decides when a new dilation connection is necessary, both for the
|
If something goes wrong with the established connection and the Leader
|
||||||
initial connection and any subsequent reconnects. Therefore the Leader has
|
decides a new one is necessary, the Leader will send a `reconnect` message.
|
||||||
the exclusive right to send the `start-dilation` record. It won't send this
|
This might happen while connections are still being established, or while the
|
||||||
until after it has sent its own `please-dilate`, and after it has received
|
Follower thinks it still has a viable connection (the Leader might observer
|
||||||
the Follower's `please-dilate`. As a result, local preparations may begin as
|
problems that the Follower does not), or after the Follower thinks the
|
||||||
soon as `w.dilate()` is called, but L2 connections do not begin until the
|
connection has been lost. In all cases, the Leader is the only side which
|
||||||
Leader declares the start of a new L2 generation with the `start-dilation`
|
should send `reconnect`. The state machine code looks the same on both sides,
|
||||||
message.
|
for simplicity, but one path on each side is never used.
|
||||||
|
|
||||||
|
Upon receiving a `reconnect`, the Follower should stop any pending connection
|
||||||
|
attempts and terminate any existing connections (even if they appear viable).
|
||||||
|
Listening sockets may be retained, but any previous connection made through
|
||||||
|
them must be dropped.
|
||||||
|
|
||||||
|
Once all connections have stopped, the Follower should send an `ok` message,
|
||||||
|
then start the connection process for the next generation, which will send
|
||||||
|
new `connection-hint` messages for all listening sockets).
|
||||||
|
|
||||||
Generations are non-overlapping. The Leader will drop all connections from
|
Generations are non-overlapping. The Leader will drop all connections from
|
||||||
generation 1 before sending the `start-dilation` for generation 2, and will
|
generation 1 before sending the `reconnect` for generation 2, and will not
|
||||||
not initiate any gen-2 connections until it receives the matching
|
initiate any gen-2 connections until it receives the matching `ok` from the
|
||||||
`ok-dilation` from the Follower. The Follower must drop all gen-1 connections
|
Follower. The Follower must drop all gen-1 connections before it sends the
|
||||||
before it sends the `ok-dilation` response (even if it thinks they are still
|
`ok` response (even if it thinks they are still functioning: if the Leader
|
||||||
functioning: if the Leader thought the gen-1 connection still worked, it
|
thought the gen-1 connection still worked, it wouldn't have started gen-2).
|
||||||
wouldn't have started gen-2). Listening sockets can be retained, but any
|
|
||||||
previous connection made through them must be dropped. This should avoid a
|
|
||||||
race.
|
|
||||||
|
|
||||||
(TODO: what about a follower->leader connection that was started before
|
(TODO: what about a follower->leader connection that was started before
|
||||||
start-dilation is received, and gets established on the Leader side after
|
start-dilation is received, and gets established on the Leader side after
|
||||||
|
@ -166,6 +176,16 @@ derived key)
|
||||||
|
|
||||||
(TODO: reduce the number of round-trip stalls here, I've added too many)
|
(TODO: reduce the number of round-trip stalls here, I've added too many)
|
||||||
|
|
||||||
|
Each side is in the "connecting" state (which encompasses both making
|
||||||
|
connection attempts and having an established connection) starting with the
|
||||||
|
receipt of a `please-dilate` message and a local `w.dilate()` call. The
|
||||||
|
Leader remains in that state until it abandons the connection and sends a
|
||||||
|
`reconnect` message, at which point it remains in the "flushing" state until
|
||||||
|
the Follower's `ok` message is received. The Follower remains in "connecting"
|
||||||
|
until it receives `reconnect`, then it stays in "dropping" until it finishes
|
||||||
|
halting all outstanding connections, after which it sends `ok` and switches
|
||||||
|
back to "connecting".
|
||||||
|
|
||||||
"Connection hints" are type/address/port records that tell the other side of
|
"Connection hints" are type/address/port records that tell the other side of
|
||||||
likely targets for L2 connections. Both sides will try to determine their
|
likely targets for L2 connections. Both sides will try to determine their
|
||||||
external IP addresses, listen on a TCP port, and advertise `(tcp,
|
external IP addresses, listen on a TCP port, and advertise `(tcp,
|
||||||
|
@ -225,7 +245,7 @@ everything until the first newline.
|
||||||
|
|
||||||
Everything beyond that point is a Noise protocol message, which consists of a
|
Everything beyond that point is a Noise protocol message, which consists of a
|
||||||
4-byte big-endian length field, followed by the indicated number of bytes.
|
4-byte big-endian length field, followed by the indicated number of bytes.
|
||||||
This ises the `NNpsk0` pattern with the Leader as the first party ("-> psk,
|
This uses the `NNpsk0` pattern with the Leader as the first party ("-> psk,
|
||||||
e" in the Noise spec), and the Follower as the second ("<- e, ee"). The
|
e" in the Noise spec), and the Follower as the second ("<- e, ee"). The
|
||||||
pre-shared-key is the "dilation key", which is statically derived from the
|
pre-shared-key is the "dilation key", which is statically derived from the
|
||||||
master PAKE key using HKDF. Each L2 connection uses the same dilation key,
|
master PAKE key using HKDF. Each L2 connection uses the same dilation key,
|
||||||
|
@ -241,8 +261,8 @@ protocol object to the L3 manager, which will decide which connection to
|
||||||
select. When the L2 connection is selected to be the new L3, it will send an
|
select. When the L2 connection is selected to be the new L3, it will send an
|
||||||
empty KCM of its own, to let the Follower know the connection being selected.
|
empty KCM of its own, to let the Follower know the connection being selected.
|
||||||
All other L2 connections (either viable or still in handshake) are dropped,
|
All other L2 connections (either viable or still in handshake) are dropped,
|
||||||
all other connection attempts are cancelled, and all listening sockets are
|
all other connection attempts are cancelled. All listening sockets may or may
|
||||||
shut down.
|
not be shut down (TODO: think about it).
|
||||||
|
|
||||||
The Follower will wait for either an empty KCM (at which point the L2
|
The Follower will wait for either an empty KCM (at which point the L2
|
||||||
connection is delivered to the Dilation manager as the new L3), a
|
connection is delivered to the Dilation manager as the new L3), a
|
||||||
|
|
Loading…
Reference in New Issue
Block a user