|
| 1 | +# Multistream Select V2 |
| 2 | + |
| 3 | +| Lifecycle Stage | Maturity | Status | Latest Revision | |
| 4 | +| --------------- | ------------- | ------ | --------------- | |
| 5 | +| 1A | Working Draft | Active | r1, 2025-11-13 | |
| 6 | + |
| 7 | +Authors: [@raulk], [@marcopolo] |
| 8 | + |
| 9 | +Interest Group: [@ppopth] |
| 10 | + |
| 11 | +[@marcopolo]: https://github.com/marcopolo |
| 12 | +[@ppopth]: https://github.com/ppopth |
| 13 | +[@raulk]: https://github.com/raulk |
| 14 | + |
| 15 | +## Terminology |
| 16 | + |
| 17 | +Server: The endpoint advertising its supported protocol strings. |
| 18 | + |
| 19 | +Client: The endpoint receiving the advertisement and using protocol string |
| 20 | +abbreviations when opening streams. |
| 21 | + |
| 22 | +Abbreviation Tree: The tree data structure that determines the abbreviation to |
| 23 | +use for a given protocol string. |
| 24 | + |
| 25 | +Note, A peer can behave as both a client and server. For the purpose of defining |
| 26 | +this protocol, it's useful to focus on the client/server interaction. |
| 27 | + |
| 28 | +## Abbreviation Tree |
| 29 | + |
| 30 | +A list of protocol strings are abbreviated by creating a minimal hash prefix |
| 31 | +tree. |
| 32 | + |
| 33 | +### Construction |
| 34 | + |
| 35 | +Each protocol string is hashed and inserted into the tree as shallowly as |
| 36 | +possible. Each node in the tree has 256 leaf branches representing a byte of the |
| 37 | +hash (2^8). If multiple protocol strings share a common byte prefix, they are |
| 38 | +distinguished the next level down the tree. |
| 39 | + |
| 40 | +Each protocol string in the tree has a tombstone bit associated with it. This is |
| 41 | +set to true if the protocol is currently not supported (but was previously). |
| 42 | + |
| 43 | +A node in the tree may contain a value as well as children. This only happens |
| 44 | +when introducing a new supported protocol introduces a conflict. This does not |
| 45 | +happen on initial construction. |
| 46 | + |
| 47 | +### Example |
| 48 | + |
| 49 | +```txt |
| 50 | +(root) |
| 51 | + | |
| 52 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 53 | + | |
| 54 | + +-- 0xbb -> "/some-protocol/b/v1" |
| 55 | + | |
| 56 | + +-- 0xcc |
| 57 | + | | |
| 58 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 59 | + | | |
| 60 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 61 | + | |
| 62 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 63 | +``` |
| 64 | + |
| 65 | +### Inserting a New Protocol String |
| 66 | + |
| 67 | +The protocol string is inserted into the tree as shallowly as possible. If there |
| 68 | +is a conflict, the protocol string should be inserted one level down and |
| 69 | +duplicate the conflicting protocol string to the new level if it is not |
| 70 | +tombstoned. The conflicting protocol string MUST not removed from its original |
| 71 | +position as it may still be referenced. |
| 72 | + |
| 73 | +#### Example |
| 74 | + |
| 75 | +Using the initial example as the initial state and we add three new protocol |
| 76 | +strings: |
| 77 | + |
| 78 | +1. `"/new-protocol/foo"` that hashes to `0xaa02`. This example highlights a |
| 79 | + conflict with an existing protocol string. |
| 80 | +2. `"/new-protocol/bar"` that hashes to `0xee...`. This example highlights no |
| 81 | + conflicts and inserting shallowly. |
| 82 | +3. `"/new-protocol/d"` that hashes to `0xdd02`. This example highlights a |
| 83 | + conflict with a tombstoned protocol string. |
| 84 | + |
| 85 | +```txt |
| 86 | +(root) |
| 87 | + | |
| 88 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 89 | + | | |
| 90 | + | +-- 0x01 -> "/some-protocol/a/v1" |
| 91 | + | | |
| 92 | + | +-- 0x02 -> "/new-protocol/foo" |
| 93 | + | |
| 94 | + +-- 0xbb -> "/some-protocol/b/v1" |
| 95 | + | |
| 96 | + +-- 0xcc |
| 97 | + | | |
| 98 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 99 | + | | |
| 100 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 101 | + | |
| 102 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 103 | + | | |
| 104 | + | +-- 0xdd01 -> "/new-protocol/d" |
| 105 | + | |
| 106 | + +-- 0xee -> "/new-protocol/bar" |
| 107 | +``` |
| 108 | + |
| 109 | +### Removing a Protocol String |
| 110 | + |
| 111 | +For each instance of the protocol string in the tree, the tombstone bit is set |
| 112 | +to true. The protocol string MUST not be removed from the tree as that could |
| 113 | +lead to inconsistencies if a new protocol string is introduced with the same |
| 114 | +prefix hash. |
| 115 | + |
| 116 | +#### Example |
| 117 | + |
| 118 | +Using the initial example as the initial state and we remove protocol |
| 119 | +string `"/some-protocol/b/v1"`. |
| 120 | + |
| 121 | +```txt |
| 122 | +(root) |
| 123 | + | |
| 124 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 125 | + | |
| 126 | + +-- 0xbb -> "/some-protocol/b/v1" tombstone=true |
| 127 | + | |
| 128 | + +-- 0xcc |
| 129 | + | | |
| 130 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 131 | + | | |
| 132 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 133 | + | |
| 134 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 135 | +``` |
| 136 | + |
| 137 | +### Reintroducing a previously removed protocol string |
| 138 | + |
| 139 | +For each instance of the protocol string in the tree, the tombstone bit should |
| 140 | +be set to false. The protocol string should then be inserted into the tree as if |
| 141 | +inserting a new protocol string. If the protocol string is already present as a |
| 142 | +leaf node, no changes are required. |
| 143 | + |
| 144 | +#### Example |
| 145 | + |
| 146 | +Using the example for removing a protocol string as the initial state. We |
| 147 | +reintroduce protocol string `"/some-protocol/b/v1"`. |
| 148 | + |
| 149 | +```txt |
| 150 | +(root) |
| 151 | + | |
| 152 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 153 | + | |
| 154 | + +-- 0xbb -> "/some-protocol/b/v1" |
| 155 | + | |
| 156 | + +-- 0xcc |
| 157 | + | | |
| 158 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 159 | + | | |
| 160 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 161 | + | |
| 162 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 163 | +``` |
| 164 | + |
| 165 | +#### Example with a new leaf node |
| 166 | + |
| 167 | +If the tombstoned protocol string is no longer at a leaf position we need to |
| 168 | +insert a new protocol string, as well as untombstoning the existing protocol |
| 169 | +string. |
| 170 | + |
| 171 | +Initial State: |
| 172 | + |
| 173 | +```txt |
| 174 | +(root) |
| 175 | + | |
| 176 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 177 | + | |
| 178 | + +-- 0xbb -> "/some-protocol/b/v1" tombstone=true |
| 179 | + | | |
| 180 | + | +-- 0x02 -> "/some-protocol/b'/v1" |
| 181 | + | |
| 182 | + +-- 0xcc |
| 183 | + | | |
| 184 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 185 | + | | |
| 186 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 187 | + | |
| 188 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 189 | +``` |
| 190 | + |
| 191 | +After reintroducing `"/some-protocol/b/v1"`: |
| 192 | + |
| 193 | +```txt |
| 194 | +(root) |
| 195 | + | |
| 196 | + +-- 0xaa -> "/some-protocol/a/v1" |
| 197 | + | |
| 198 | + +-- 0xbb -> "/some-protocol/b/v1" |
| 199 | + | | |
| 200 | + | +-- 0x01 -> "/some-protocol/b/v1" |
| 201 | + | | |
| 202 | + | +-- 0x02 -> "/some-protocol/b'/v1" |
| 203 | + | |
| 204 | + +-- 0xcc |
| 205 | + | | |
| 206 | + | +-- 0x01 -> "/some-protocol/c/v1" |
| 207 | + | | |
| 208 | + | +-- 0x02 -> "/some-protocol/c'/v1" |
| 209 | + | |
| 210 | + +-- 0xdd -> "/some-protocol/d/v1" tombstone=true |
| 211 | +``` |
| 212 | + |
| 213 | +## Limits |
| 214 | + |
| 215 | +Implementations SHOULD limit the number of protocol strings they track |
| 216 | +(including tombstoned protocol strings). |
| 217 | + |
| 218 | +## Mapping Algorithm |
| 219 | + |
| 220 | +A mapping algorithm maps a protocol string to byte array. This byte array is |
| 221 | +used for the abbreviation tree. |
| 222 | + |
| 223 | +SHA256 MUST be used for the mapping algorithm. |
| 224 | + |
| 225 | +TODO: Do we want to consider faster keyed hashes? Risks complicating |
| 226 | +implementations. |
| 227 | + |
| 228 | +TODO: Do we need a hash function at all? Would an index into the list of a |
| 229 | +server's protocols work instead? |
| 230 | + |
| 231 | +## Initial Server Client Exchange |
| 232 | + |
| 233 | +When a client connects to a server, the server shares all the state necessary to |
| 234 | +create an abbreviation tree it can use to communicate with the server. Namely: |
| 235 | +the list of supported protocol strings, and the list of previously supported |
| 236 | +protocol strings (tombstoned protocol strings). |
| 237 | + |
| 238 | +The client's abbreviation tree will differ from the server's abbreviation tree |
| 239 | +only in that protocol strings will only exist at leaf nodes in the client's |
| 240 | +abbreviation tree. This is because non-leaf protocol strings only occur when |
| 241 | +adding a protocol string to an existing tree. |
| 242 | + |
| 243 | +# Multistream Select V2 |
| 244 | + |
| 245 | +## Wire Format |
| 246 | + |
| 247 | +TODO define the wire format of multistream select v2. |
| 248 | + |
| 249 | +## Client opening a new stream |
| 250 | + |
| 251 | +A client identifies the protocol string it wishes to use on a stream by the |
| 252 | +minimal hash prefix as determined by the abbreviate stream. |
| 253 | + |
| 254 | +## Server accepting a new stream |
| 255 | + |
| 256 | +The server identifies the protocol string by looking up the minimal hash prefix |
| 257 | +in the tree. |
| 258 | + |
| 259 | +## Version Negotiation |
| 260 | + |
| 261 | +Multistream Select V2 does not support negotiating different protocols on a |
| 262 | +single stream like Multistream Select V1 does. A client specifies what protocol |
| 263 | +they would like to use for a stream and MAY start sending protocol immediately. |
| 264 | +If the server does not support this protocol, it MUST close the stream with |
| 265 | +error code (TODO specify this). |
0 commit comments