Bincode is an alternative serialization format supported by nodes having "RBC" in their api features. The tables given here correspond to the successive fields in the bincode version of the messages.
## Primitive types
u8 : Unsigned 8-bit integer.
u16 : Unsigned 16-bit integer.
u32 : Unsigned 32-bit integer.
u64 : Unsigned 64-bit integer.
i8 : Signed 8-bit integer.
i16 : Signed 16-bit integer.
i32 : Signed 32-bit integer.
i64 : Signed 64-bit integer.
bool : boolean stored on 8 bits (0x00 = false, 0x01 = true, any other value must generate an error).
0 : Corresponds to data that must be filled with bits to zero (for example padding).
## Useful non-primitive types
### (T, U)
(T, U) = Tuples of 2 elements. the 1st type T and the second type U.
A tuple is simply a concatenation of types written one after the other.
A tuple can be of any size (no maximum limit), but its size is necessarily fixed.
The different elements may be of different types, but the order cannot change.
### [T; n]
Array of n elements of type T.
If the size of the array is variable, its size (in number of cells) is stored on 8 bytes.
| data | size in bytes | data type |
| :---------: | ------------: | ------------: |
| cells_count | 8 | u64 |
| content* | ?*cells_count | (T1, T2, ...) |
All cells in the table are written one after the other without a separator, _like_ a tuple of `cells_count` elements of the same type.
**/*\ CAUTION: If the size of the array is static (=a literal constant) then the array is serialized without a size field. **
For example, the content of a public key ed25519 is a 32-byte array, (a [u8; 32] therefore), it is serialized directly without a size field.
### Opt(T)
Indicates that a field is optional. The optional fields are preceded by a 1-byte boolean that indicates whether or not the field is present.
| data | size in bytes | data type |
| :-----: | ------------: | --------: |
| is_some | 1 | u8 |
| content | ? | T |
If `is_some` is equal to `1`, field `content` is present.
If `is_some` is equal to `0`, field `content` is missing.
Any other value of `is_some` is prohibited and will invalidate the entire message.
### String
String formatted in utf8 and [NFKC normalized](https://fr.wikipedia.org/wiki/Normalisation_Unicode#NFKC).
**/!\ WARNING: All strings are systematically prefixed with their size, including for an empty string. The size is stored on 8 bytes as shown in the table below :**
| data | size in bytes | data type |
| :------: | ------------: | -------------: |
| str_size | 8 | u64 |
| content | str_size | [u8; str_size] |
content := String in binary utf8 with [NFKC normalization](https://fr.wikipedia.org/wiki/Normalisation_Unicode#NFKC).
If the string is empty, field `content` is missing and `str_size` is equal to zero.
### Blockstamp
| data | size in bytes | data type |
| :--------: | ------------: | --------: |
| block_id | 4 | u32 |
| block_hash | 32 | [u8; 32] |
### KeysAlgorithm
| algorithm | u32 |
| :-------: | ---: |
| Ed25519 | 0 |
| Schnorr | 1 |
### PubkeyBox
Contains the signatory's public key.
| data | size in bytes | data type |
| :-------: | ------------: | ------------: |
| algorithm | 4 | KeysAlgorithm |
| content | ? | ?* |
_*The type of field `content` depends on the algorithm. In the case of Ed25519, `content` is a 32-byte array containing the public key._
### SigBox
Contains the cryptographic signature of a document.
| data name | size in bytes | data type |
| :-------: | ------------: | ------------: |
| algorithm | 4 | KeysAlgorithm |
| content | ? | ?* |
_*The type of field `content` depends on the algorithm. In the case of Ed25519, `content` is a 64-byte array containing the signature._
## Endianness
All numbers (integers and floats) are encoded in little endian.
*[Getting peer cards from other Duniter nodes](#getting-peer-cards-from-other-duniter-nodes)
*[Getting peer cards from other Duniter nodes](#getting-peer-cards-from-other-duniter-nodes)
*[Priority of WS2P outcoming connections](#priority-of-ws2p-outcoming-connections)
*[Priority of WS2P out-coming connections](#priority-of-ws2p-outcoming-connections)
*[Establishing a WS2P connection](#establishing-a-ws2p-connection)
*[Establishing a WS2P connection](#establishing-a-ws2p-connection)
*[List of accepted v1 messages](#list-of-accepted-v1-messages)
*[List of accepted v1 messages](#list-of-accepted-v1-messages)
*[CONNECT Message](#connect-message)
*[CONNECT Message](#connect-message)
...
@@ -49,7 +33,7 @@ We are also taking advantage of these changes to address minor issues :
...
@@ -49,7 +33,7 @@ We are also taking advantage of these changes to address minor issues :
*[Rules for accepting an incoming connection](#rules-for-accepting-an-incoming-connection)
*[Rules for accepting an incoming connection](#rules-for-accepting-an-incoming-connection)
*[WS2Pv2 Messages](#ws2pv2-messages)
*[WS2Pv2 Messages](#ws2pv2-messages)
*[List of accepted v1 messages](#list-of-accepted-v1-messages)
*[List of accepted v1 messages](#list-of-accepted-v1-messages)
*[Requets](#requests)
*[Requests](#requests)
*[Requests responses](#requests-responses)
*[Requests responses](#requests-responses)
*[HEADs v2](#heads-v2)
*[HEADs v2](#heads-v2)
*[HEADs v3](#heads-v3)
*[HEADs v3](#heads-v3)
...
@@ -63,7 +47,7 @@ WS2P means "**W**eb **S**ocket **To** **P**eer".
...
@@ -63,7 +47,7 @@ WS2P means "**W**eb **S**ocket **To** **P**eer".
WS2P is the inter-node network layer, it is the only guarantor of synchronization between the different nodes of the network, so its role is critical.
WS2P is the inter-node network layer, it is the only guarantor of synchronization between the different nodes of the network, so its role is critical.
WS2P is the network part of the Duniter protocol, so any Duniter implementation must integrate the WS2P network layer in order to work.
WS2P is the network part of the Duniter protocol
WS2P is exclusively based on websocket technology.
WS2P is exclusively based on websocket technology.
...
@@ -87,22 +71,9 @@ In addition, WS2P v1 has provided a new way to uniquely identify nodes (the node
...
@@ -87,22 +71,9 @@ In addition, WS2P v1 has provided a new way to uniquely identify nodes (the node
Finally, shared peer cards have a big drawback: a peer cannot prune obsolete endpoints of other peer sharing the same key because it cannot easily evaluate if these other endpoints are obsolete. In absolute terms, it is possible to bypass problems but it is simpler to directly delete shared peer records, and as we no longer need them: Occam's razor.
Finally, shared peer cards have a big drawback: a peer cannot prune obsolete endpoints of other peer sharing the same key because it cannot easily evaluate if these other endpoints are obsolete. In absolute terms, it is possible to bypass problems but it is simpler to directly delete shared peer records, and as we no longer need them: Occam's razor.
### Why not a total binarization
### What is the binary format used for serialization
I want to limit this RFC to changes that I am able to implement in duniter-ts, and those within a reasonable time, because I want ws2p v2 to be integrated into Duniter-ts 1.7.
That's why I limit the Duniter-ts changes as much as possible: documents and requests will always be sent and received in the same format. Only peer files and heads change.
On the other hand, on the Duniter-Rust side, the binarization will be total right away, because duniter-rust already stores all documents in binary format.
To allow compatibility between different implementations, I introduced in WS2P v2 a powerful concept of features (this concept is detailed later in the RFC).
To summarize, each WS2P Public node will declare in its endpoint the list of features it supports, and the nodes that contact them can adapt their communication format accordingly.
### What consequences for clients softwares
Developers of client software need to study the new format of peer and HEAD records.
To allow easy interfacing between different implementations, WS2P v2 defines network features (detailed later in the RFC) allowing each WS2P Public to declare in its endpoint the list of features it supports. The default format is [CBOR](https://en.wikipedia.org/wiki/CBOR), a widespread binarization format based on JSON. But for byte-efficiency reasons, Dunitrust defined an other serialization format based on the Rust crate *bincode*.
They must be able to parse this new format such that BMA 1.7 will provide them.
Finally, users associating external endpoints to their peer cards will have to redeclare their external endpoints in the new generic enpoint format.
## Conventions
## Conventions
...
@@ -118,204 +89,99 @@ The PEG grammars of raw formats are presented according to the [pest syntax] (a
...
@@ -118,204 +89,99 @@ The PEG grammars of raw formats are presented according to the [pest syntax] (a
bool : boolean stored on 8 bits (0x00 = false, 0x01 = true, any other value must generate an error).
0 : Corresponds to data that must be filled with bits to zero (for example padding).
#### Useful non-primitive types
##### (T, U)
(T, U) = Tuples of 2 elements. the 1st type T and the second type U.
A tuple is simply a concatenation of types written one after the other.
A tuple can be of any size (no maximum limit), but its size is necessarily fixed.
The different elements may be of different types, but the order cannot change.
##### [T; n]
Array of n elements of type T.
If the size of the array is variable, its size (in number of cells) is stored on 8 bytes.
| data | size in bytes | data type |
|:-----------:|--------------:|----------------:|
| cells_count | 8 | u64 |
| content* | ?*cells_count | (T1, T2, ...) |
All cells in the table are written one after the other without a separator, _like_ a tuple of `cells_count` elements of the same type.
**/*\ CAUTION: If the size of the array is static (=a literal constant) then the array is serialized without a size field. **
For example, the content of a public key ed25519 is a 32-byte array, (a [u8; 32] therefore), it is serialized directly without a size field.
##### Opt(T)
Indicates that a field is optional. The optional fields are preceded by a 1-byte boolean that indicates whether or not the field is present.
| data | size in bytes | data type |
|:---------:|--------------:|----------------:|
| is_some | 1 | u8 |
| content | ? | T |
If `is_some` is equal to `1`, field `content` is present.
If `is_some` is equal to `0`, field `content` is missing.
Any other value of `is_some` is prohibited and will invalidate the entire message.
##### String
String formatted in utf8 and [NFKC normalized](https://fr.wikipedia.org/wiki/Normalisation_Unicode#NFKC).
**/!\ WARNING: All strings are systematically prefixed with their size, including for an empty string. The size is stored on 8 bytes as shown in the table below :**
## Message encapsulation
| data | size in bytes | data type |
All WS2P v2 message are encapsuled under [PKSTL v1 protocol](./0011_pkstl_v1.md).
|:---------:|--------------:|----------------:|
| str_size | 8 | u64 |
| content | str_size | [u8; str_size] |
content := String in binary utf8 with [NFKC normalization](https://fr.wikipedia.org/wiki/Normalisation_Unicode#NFKC).
Each WS2Pv2 message is binarized and then placed in the CUSTOM_DATAS field of the PKSTL message that encapsulates it.
If the string is empty, field `content` is missing and `str_size` is equal to zero.
Several binary serialization formats are available: CBOR and Bincode. The message defined below are written in JSON, allowing straightforward serialization to CBOR. For the Bincode serialization, see [Bincode format reference](./0006_annex/bincode.md).
##### Blockstamp
All WS2Pv2 messages are structured as follows (CBOR format) :
| data | size in bytes | data type |
```json
|:----------:|--------------:|----------:|
{
| block_id | 4 | u32 |
"ws2p_version": 2,
| block_hash | 32 | [u8; 32] |
"currency_name": "g1",
"issuer_node_id": 395,
##### KeysAlgorithm
"issuer_pubkey": {
"algo": "Ed25519",
| algorithm | u32 |
"content": [52, 147, .., 93], // Array of 32 bytes
_*The type of field `content` depends on the algorithm. In the case of Ed25519, `content` is a 32-byte array containing the public key._
##### SigBox
Contains the cryptographic signature of a document.
| data name | size in bytes | data type |
`ws2p_version` := This field is placed first so that future versions of WS2P are not constrained on the other fields,
|:---------:|--------------:|----------------:|
the only constraint will be to start the message with the version number stored in u32.
| algorithm | 4 | KeysAlgorithm |
| content | ? | ?* |
_*The type of field `content` depends on the algorithm. In the case of Ed25519, `content` is a 64-byte array containing the signature._
`currency_name` := Empty string is allowed, it allow the user to synchronize his node without having to manually enter the currency on which he synchronizes. The node will then adopt the currency that is specified in the CONNECT message it will receive from the selected reference node.
#### Endianness
_* The type of `payload` is determined by the content of `message_type`._
All numbers (integers and floats) are encoded in little endian.
### payload.type
Exhaustive list of possible `payload.type` values:
| message type | `payload.type` value | Hashed | Signed |
| PENDING_IDENTITIES | "PendingIdentities" | yes | no |
| PENDING_MEMBERSHIPS | "PendingMemberships" | yes | no |
| PENDING_CERTS | "PendingCerts" | yes | no |
| PENDING_REVOCATIONS | "PendingRevocations" | yes | no |
| PENDING_TXS | "PendingTxs" | yes | no |
## Binary messages format
## Endpoints v2
All binary WS2P v2 messages are encapsulated in the following format :
```json
{
"api": "WS2P",
"api_version": 2,
"network_features": [], // array of bytes, defined below
"api_features": [], // array of bytes, defined below
"host": "subdomain.domaine.tld",
"ip_v4": "5.135.188.170",
"ip_v6": "2001:41d0:8:c5aa::1",
"port": 443,
"path": "ws2p",
}
```
| data name | size in bytes | data type |
### utf8 format
| :------------: | ------------: | ----------: |
| ws2p_version | 4 | u32 |
| currency_name | ? | String |
| issuer_node_id | 4 | u32 |
| issuer_pubkey | ? | PubkeyBox |
| message_type | 4 | u32 |
| payload | ? | ?* |
| signature | ? | SigBox |
`ws2p_version` := This field is placed first so that future versions of WS2P are not constrained on the other fields,
The utf8 format is used to display the endpoint in a human-readable format. It is also in this format that the user can manually enter an endpoint.
the only constraint will be to start the message with the version number stored in u32.
`currency_name` := Empty string is allowed, it allow the user to synchronize his node without having to manually enter the currency on which he synchronizes. The node will then adopt the currency that is specified in the CONNECT message it will receive from the selected reference node.
General utf8 endpoint format :
_* The type of `payload` is determined by the content of `message_type`._
API_NAME VERSION NF1 .. NFn AF PORT HOST IP4 [IP6] PATH
The signature is not always present, it's only present for connection negotiation messages.
VERSION := version number prefixed by `V` (example : `V1`)
The signature is generated from the byte vector of the entire message (signature field excluded).
NF := NETWORK_FEATURE
AF := API_FEATURES in hexadecimal prefixed by `0x` (example : `0x01`)
WS2Pv1 used endpoints version v1. So, in WS2Pv2 we improve to endpoints version v2.
WS2P V2 TLS 0x7 443 g1.durs.info ws2p
### Endpoint binary format
### network_features
| data name | size in bytes | data type |
The 8 bits represent booleans to define the presence or absence of 8 network features. WS2Pv2 defines only 4 features, the remaining 4 are undefined and are in anticipation of future Ğfeatures.
The 16 bits represent booleans to define the presence or absence of 16 network features. WS2Pv2 defines only 2 features, the remaining 14 are undefined and are in anticipation of future Ğfeatures.
Network features :
Network features :
...
@@ -323,14 +189,14 @@ Network features :
...
@@ -323,14 +189,14 @@ Network features :
|:---------:|----------|
|:---------:|----------|
| 0000_0001 | HTTP |
| 0000_0001 | HTTP |
| 0000_0010 | WS |
| 0000_0010 | WS |
| 0000_0100 | TLS |
| 0000_0100 | S |
| 0000_1000 | TOR |
| 0000_1000 | TOR |
HTTP := This feature indicates that the endpoint should be contacted with http protocol.
HTTP := This feature indicates that the endpoint should be contacted with http protocol.
WS := This feature indicates that the endpoint should be contacted with websocket protocol.
WS := This feature indicates that the endpoint should be contacted with websocket protocol.
TLS := This feature indicates that the endpoint should be contacted with an SSL/TLS overlay (HTTPS or WSS or any other protocol that support TLS).
S := This feature indicates that the endpoint should be contacted with an SSL/TLS overlay (HTTPS or WSS or any other protocol that support TLS).
TOR := This feature indicates that the endpoint must be contacted via the tor network (hidden service).
TOR := This feature indicates that the endpoint must be contacted via the tor network (hidden service).
...
@@ -338,89 +204,74 @@ Note about the network protocol to choose: Each API must define a default networ
...
@@ -338,89 +204,74 @@ Note about the network protocol to choose: Each API must define a default networ
For example, the default network protocol for WS2P is `ws://`, it's the only one possible for this API, so there is no need to indicate it in the network features.
For example, the default network protocol for WS2P is `ws://`, it's the only one possible for this API, so there is no need to indicate it in the network features.
#### api_features
### api_features
The interpretation of this field depends on the API because it represents API-specific features. Here is the interpretation for the WS2P API :
The interpretation of this field depends on the API because it represents API-specific features. Here is the interpretation for the WS2P API :
WS2PFeatures type definition :
WS2PFeatures type definition :
| bit | feature |
| bit | feature |
|:---------:|-----------|
|:-------: | -------|
| 0000_0001 | DEF |
| 0000_0100 | RBC |
| 0000_0010 | LOW |
| 0000_0010 | LOW |
| 0000_0100 | ABF |
DEF := Supports permessage-deflate extension
RBC := Support Rust BinCode format
LOW := Accept low speed connection requests
LOW := Accept low speed connection requests
ABF := Support all binary formats.
WS2P v2 uses only 2 of 8 features. The 6 free bits can be used for future versions of WS2P.
WS2P v2 uses only 3 of 8 features. The 5 free bits can be used for future versions of WS2P.
### Endpoint utf8 format
The utf8 format is used to display the endpoint in a human-readable format. It is also in this format that the user can manually enter an endpoint.
General utf8 endpoint format :
API_NAME VERSION NF1 .. NFn AF PORT HOST IP4 [IP6] PATH
VERSION := version number prefixed by `V` (example : `V1`)
NF := NETWORK_FEATURE
AF := API_FEATURES in hexadecimal prefixed by `0x` (example : `0x01`)
A node's peer record can also contain signatures of the node's network key. This allows, for example, to sign mirror nodes with known and trusted keys.
The signed key must be signed with the blockstamp corresponding to the date of signature. More precisely, it is the text `PUBKEY:BLOCKSTAMP` that must be signed.
`endpoint` type is defined above (See "Endpoint format").
### CBOR format
The document is represented by an array of bytes containing (peer_card_size + 2) bytes.
```json
to sign the document it is necessary to calculate the hash sha256 of this bytes array then to sign the hash obtained with the issuer's private key.
Initially, Duniter is totally endpoint agnostic and don't know any node.
Initially, nodes are totally endpoint agnostic and don't know any node.
The ws2p module requires that at least one or more peer cards are already stored in the database when launching the node,
The ws2p module requires that at least one or more peer cards are already stored in the database when launching the node,
it's up to the Duniter `sync` command to handle this (see "WS2P synchronisation" part).
it's up to the `sync` command to handle this (see "WS2P synchronisation" part).
When duniter starts up, the WS2P module accesses the peer cards stored in a local database following synchronization.
When node starts up, the WS2P module accesses the peer cards stored in a local database following synchronization.
## Priority of WS2P outcoming connections
## Priority of WS2P outcoming connections
...
@@ -593,69 +435,35 @@ WARNING: independently of all these rules, each implementation must integrate it
...
@@ -593,69 +435,35 @@ WARNING: independently of all these rules, each implementation must integrate it
### CONNECT message
### CONNECT message
| data name | size in bytes | data type |
```json
|:-------------:|---------------|--------------|
{} // TODO
| challenge | 32 | [u8; 32] |
```
| af_size | 1 | u8 |
| api_features | af_size | WS2PFeatures |
| flags_size | 1 | u8 |
| flags_queries | flags_size | WS2PFlags |
| peer_card | ? | Peer card |
| chunkstamp | 37 | Opt(Blockstamp) |
challenge := random hash generated by the sending node of the CONNECT message, the receiving node will then have to sign this challenge and then send this signature in its ACK message to prove that it has the corresponding private key to the public key it indicates.
challenge := random hash generated by the sending node of the CONNECT message, the receiving node will then have to sign this challenge and then send this signature in its ACK message to prove that it has the corresponding private key to the public key it indicates.
api_features := This is exactly the same type as the field of the same name in the endpoints. But private WS2P nodes do not declare endpoints, so they must be able to indicate in the CONNECT message which features they support. Public WS2P nodes also fill this field, so any changes in the configuration of a public node will be applied on the 1st new connection. (If this was not the case, we would have to wait for the update of the peer record).
api_features := This is exactly the same type as the field of the same name in the endpoints. But private WS2P nodes do not declare endpoints, so they must be able to indicate in the CONNECT message which features they support. Public WS2P nodes also fill this field, so any changes in the configuration of a public node will be applied on the 1st new connection. (If this was not the case, we would have to wait for the update of the peer record).
flags_queries := See "WS2PFlags type definition" below.
connect_type := See ConnectType below
peer_card := This field is optional, if it's not present it must be replaced by 2 bytes filled with zeros. Why two bytes ? Because the first field of any peer card, `peer_card_size`, is 2 bytes long.
chunkstamp := Blockstamp of the last block of the chunk. This field is present only if flag `ASK_SYNC_CHUNK` is present in `flags_queries`.
#### WS2PFlags type definition
| bit | flag |
|:---------:|-----------------|
| 0000_0001 | SYNC |
| 0000_0010 | ASK_SYNC_CHUNK |
| 0000_0100 | RES_SYNC_CHUNK |
| 0000_1000 | CLIENT |
SYNC := Boolean indicating whether the connection corresponds to a synchronization request or not. In case of a synchronization request, the connection will necessarily be accepted if it's possible* but temporary. Whereas in the normal case the connection may or may not be accepted according to classic ws2p rules, but it will be permanent.
_*The anti-spam procedures of the implementation may cause the node to still refuse the connection._
peer_card := See PeerCard above
ASK_SYNC_CHUNK := So that the synchronization is not too slow, the nodes to which chunk are requested are required to accept the connection and send at least 1 chunk before closing it. They can of course accept the connection for longer and send several chunks.
#### ConnectType type definition
RES_SYNC_CHUNK := A WS2P Public node that asks to synchronize sends its PeerCard to the network, which can then contact it spontaneously to send chunk, the node in synchronization will accept in priority this type of connections.
TODO
CLIENT := A third-party program wants to connect in client mode.
### ACK message
### ACK message
| data name | size in bytes | data type |
```json
|:-------------:|---------------|-----------|
{} // TODO
| challenge | 32 | [u8;32] |
```
Each node must sign the challenge of the other to prove that it's in possession of the private key corresponding to the public key under which it identifies.
Each node must sign the challenge of the other to prove that it's in possession of the private key corresponding to the public key under which it identifies.
challenge := The challenge given by the other node in their CONNECt message.
challenge := The challenge given by the other node in their CONNECT message.
The message is already signed at the container level, so there is no need to repeat the signature in the ACK message payload. On the other hand, the challenge to be signed must be in the payload.
The message is already signed at the container level, so there is no need to repeat the signature in the ACK message payload. On the other hand, the challenge to be signed must be in the payload.
If its valued, the `member_proof` field must contain a signature of the challenge* send by other node in their CONNECT message.
If its valued, the `member_proof` field must contain a signature of the challenge* send by other node in their CONNECT message.
...
@@ -674,23 +482,18 @@ LOW_FLOW_DEMAND := The sender node of this message indicates that it's behind a
...
@@ -674,23 +482,18 @@ LOW_FLOW_DEMAND := The sender node of this message indicates that it's behind a
In most cases, the OK message is empty (=3 zero-bytes), it simply indicates that the remote node accepts the connection establishment. If the local node has also sent an OK message, then it considers the connection as fully established.
In most cases, the OK message is empty (=3 zero-bytes), it simply indicates that the remote node accepts the connection establishment. If the local node has also sent an OK message, then it considers the connection as fully established.
But sometimes the OK message can also transmit additional informations :
But sometimes the OK message can also transmit additional informations :
prefix := In ws2p v2, member nodes have the option of not publicly communicating their prefix. It will only reveal their prefix to the node of their choice among those with which they establish a connection. For example, a member node may decide to share its prefix only with other member nodes of the same key.
prefix := In ws2p v2, member nodes have the option of not publicly communicating their prefix. It will only reveal their prefix to the node of their choice among those with which they establish a connection. For example, a member node may decide to share its prefix only with other member nodes of the same key.
If this field is zero, it means that the remote node does not want to reveal its prefix (the prefix being necessarily greater than or equal to 1).
If this field is zero, it means that the remote node does not want to reveal its prefix (the prefix being necessarily greater than or equal to 1).
chunk_size := defines the size of the block used in milestones. A 1000-block chunk is a good compromise between efficiency of compression and duration of transfer
chunk_size := defines the size of the block used in milestones. A 1000-block chunk is a good compromise between efficiency of compression and duration of transfer
...
@@ -702,11 +505,11 @@ peer_cards := see [definition of binary peer card](#peer-card-binary-format)
...
@@ -702,11 +505,11 @@ peer_cards := see [definition of binary peer card](#peer-card-binary-format)
### KO message
### KO message
| data name | size in bytes | data type |
```json
|:---------:|---------------|-----------|
{} // TODO
| reason | 2 | u16 |
```
#### reason interpretation
#### reason
| value | significance |
| value | significance |
|:-----:|------------------|
|:-----:|------------------|
...
@@ -972,19 +775,23 @@ HEAD v3 PEG grammar :
...
@@ -972,19 +775,23 @@ HEAD v3 PEG grammar :
This human-readable format will be used by all APIs that wish to provide heads in a human-readable format. For example, Client APIs.
This human-readable format will be used by all APIs that wish to provide heads in a human-readable format. For example, Client APIs.