Skip to content
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

Add minimal Kademlia DHT spec #108

Merged
merged 40 commits into from
Jun 29, 2021
Merged
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5378e61
WIP Initial Kademlia DHT spec.
raulk Nov 7, 2018
ee9734b
format kad-dht spec.
raulk Nov 8, 2018
8b89dc2
Update Kademlia DHT spec
jhiesey Dec 21, 2018
cb971aa
dht: fix distance function
Stebalien Aug 8, 2019
aee103e
Merge branch 'libp2p/master' into kad-dht-spec
mxinden May 7, 2021
4d43958
kad-dht: Add document header
mxinden May 7, 2021
55d32ff
kad-dht: Wrap lines at 80 and fix typos
mxinden May 7, 2021
3de65ae
kad-dht: Document to use one stream per request
mxinden May 7, 2021
6909f8a
kad-dht: Reword validation description
mxinden May 10, 2021
b3693d0
kad-dht: Document FIND_NODE
mxinden May 12, 2021
f05ee26
kad-dht: Restructure by DHT operations
mxinden May 12, 2021
6ae2551
kad-dht: Allow stream reuse and document length prefix
mxinden May 14, 2021
0126af1
kad-dht: Require closer peers even with value
mxinden May 14, 2021
008f1a8
kad-dht: Reword algorithm sections
mxinden May 14, 2021
c21ca3a
kad-dht: Rework references
mxinden May 14, 2021
f240a22
kad-dht: Remove appendix detailing difference of implementations
mxinden May 14, 2021
6c1c224
kad-dht: Document early GET_VALUE termination
mxinden May 14, 2021
defe08d
kad-dht: Do not specify k-bucket split strategy (for now)
mxinden May 14, 2021
e1442fa
kad-dht: Deprecate PING message type
mxinden May 14, 2021
26dd2f3
kad-dht: Document timeReceived formatted with RFC3339
mxinden May 14, 2021
a9ec523
kad-dht: Use peer ID instead of node ID in bootstrap
mxinden May 14, 2021
77168f9
kad-dht: Fix peer routing link
mxinden May 14, 2021
dbe1ff7
README: Add kademlia to protocols index
mxinden May 14, 2021
aa7e8fc
kad-dht: Fix protobuf indentation
mxinden May 14, 2021
d742e2e
kad-dht/README.md: Fix typo
mxinden May 27, 2021
9355a8f
kad-dht/README: Remove requirement on kbucket data structure
mxinden Jun 3, 2021
072360f
kad-dht/README: Restructure and reword DHT operations section
mxinden Jun 3, 2021
c4d4b53
kad-dht/README: Seed with k instead of alpha peers
mxinden Jun 3, 2021
b074091
kad-dht/README: Require algorithms to make progress towards target key
mxinden Jun 9, 2021
a065aac
kad-dht/README: Remove `/pk` special namespace
mxinden Jun 25, 2021
20b3b73
kad-dht/README: Replicate record to closest peers without it
mxinden Jun 25, 2021
3e13846
kad-dht/README: Demote validate purity to `should`
mxinden Jun 25, 2021
6ec65b5
kad-dht/README: Do not require storing provider addresses
mxinden Jun 25, 2021
1dcb218
kad-dht/README: Remove periodic record pruning section
mxinden Jun 25, 2021
c755a41
kad-dht/README: Include bootstrap lookup for oneself
mxinden Jun 25, 2021
e9c18bd
kad-dht/README: Make k value recommended instead of default
mxinden Jun 25, 2021
dab4549
kad-dht/README: Always return k closest peers with FIND_NODE
mxinden Jun 25, 2021
324f915
kad-dht/README: Extend on reasoning for quorums
mxinden Jun 25, 2021
dbd17a2
kad-dht/README: Stress republishing to close nodes once more
mxinden Jun 25, 2021
3e6f8f5
kad-dht/README: Add disclaimer for bootstrap process
mxinden Jun 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 132 additions & 25 deletions kad-dht/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,20 @@ than 3 inflight requests, at any given time.

## Record keys

Records in the DHT are keyed by CID [4]. There are intentions to move to
multihash [5] keys in the near future, as certain CID components like the
multicodec are redundant.
Records in the DHT are keyed by CID [4], roughly speaking. There are intentions
to move to multihash [5] keys in the future, as certain CID components like the
multicodec are redundant. This will be an incompatible change.

The format of `key` varies depending on message type; however, in all cases
`dXOR(sha256(key1), sha256(key2))` see [Distance function](#distance-function-dxor)
is used as the distance between two keys.

* For `GET_VALUE` and `PUT_VALUE`, `key` is an unstructured array of bytes, except
if it is being used to look up a public key for a `PeerId`, in which case it is
the ASCII string '/pk/' concatenated with the binary `PeerId`.
* For `ADD_PROVIDER` and `GET_PROVIDERS`, `key` is interpreted and validated as
a CID.
* For `FIND_NODE`, `key` is a binary `PeerId`

## Interfaces

Expand Down Expand Up @@ -212,7 +223,17 @@ Nodes must keep track of which nodes advertise that they provide a given key
These records are managed through the `ADD_PROVIDER` and `GET_PROVIDERS`
messages.

_WIP (jhiesey): explain what actually happens when `Provide()` is called._
When `Provide(key)` is called, the DHT finds the closest peers to `key` using
the `FIND_NODE` RPC, and then sends a `ADD_PROVIDER` RPC with its own
`PeerInfo` to each of these peers.

Each peer that receives the `ADD_PROVIDER` RPC should validate that the
received `PeerInfo` matches the sender's `peerID`, and if it does, that peer
must store a record in its datastore the received `PeerInfo` record.

When a node receives a `GET_PROVIDERS` RPC, it must look up the requested
key in its datastore, and respond with any corresponding records in its
datastore, plus a list of closer peers in its routing table.

For performance reasons, a node may prune expired advertisements only
periodically, e.g. every hour.
Expand All @@ -236,33 +257,119 @@ This process is repeated as many times per run as configuration parameter

## RPC messages

_WIP (jheisey): consider just dumping a nicely formatted and simplified
protobuf._

See [protobuf
definition](https://github.com/libp2p/go-libp2p-kad-dht/blob/master/pb/dht.proto)

On any error, the entire stream is reset. This is probably not the behavior we
want.

* `FIND_NODE(key bytes) -> (nodes PeerInfo[])` Finds the `ncp` (default:6) nodes
closest to `key` from the routing table and returns an array of `PeerInfo`s. If
a node with id equal to `key` is found, returns only the `PeerInfo` for that
node.
* `GET_VALUE(key bytes) -> (record Record, closerPeers PeerInfo[])` If `key` is
a public key (begins with `/pk/`) and the key is known, returns a `Record`
containing that key. Otherwise, returns the `Record` for the given key (if in
the datastore) and an array of `PeerInfo`s for closer peers.
* `PUT_VALUE(key bytes, value Record) -> ()` Validates `value` and, if it is
valid, stores it in the datastore.
* `GET_PROVIDERS(key bytes) -> (providerPeers PeerInfo[], closerPeers
PeerInfo[])` Verifies `key` is a valid CID. Returns `providerPeers` if in the
providers cache, and an array of closer peers.
* `ADD_PROVIDER(key, providerPeers PeerInfo[]) -> ()` Verifies `key` is a valid
CID. For each provider `PeerInfo` that matches the sender's id and contains one
or more multiaddrs, that provider info is added to the peerbook and the peer is
added as a provider for the CID.
* `PING() -> ()` Tests connectivity to destination node. Currently never sent.
All RPC messages conform to the following protobuf:
```protobuf
// Record represents a dht record that contains a value
// for a key value pair
message Record {
// The key that references this record
bytes key = 1;

// The actual value this record is storing
bytes value = 2;

// Note: These fields were removed from the Record message
// hash of the authors public key
mxinden marked this conversation as resolved.
Show resolved Hide resolved
//optional string author = 3;
// A PKI signature for the key+value+author
//optional bytes signature = 4;

// Time the record was received, set by receiver
string timeReceived = 5;
};

message Message {
enum MessageType {
PUT_VALUE = 0;
GET_VALUE = 1;
ADD_PROVIDER = 2;
GET_PROVIDERS = 3;
FIND_NODE = 4;
PING = 5;
}

enum ConnectionType {
// sender does not have a connection to peer, and no extra information (default)
NOT_CONNECTED = 0;

// sender has a live connection to peer
CONNECTED = 1;

// sender recently connected to peer
CAN_CONNECT = 2;

// sender recently tried to connect to peer repeatedly but failed to connect
// ("try" here is loose, but this should signal "made strong effort, failed")
CANNOT_CONNECT = 3;
}

message Peer {
// ID of a given peer.
bytes id = 1;

// multiaddrs for a given peer
repeated bytes addrs = 2;

// used to signal the sender's connection capabilities to the peer
ConnectionType connection = 3;
}

// defines what type of message it is.
MessageType type = 1;

// defines what coral cluster level this query/response belongs to.
// in case we want to implement coral's cluster rings in the future.
int32 clusterLevelRaw = 10; // NOT USED

// Used to specify the key associated with this message.
// PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS
bytes key = 2;

// Used to return a value
// PUT_VALUE, GET_VALUE
Record record = 3;

// Used to return peers closer to a key in a query
// GET_VALUE, GET_PROVIDERS, FIND_NODE
repeated Peer closerPeers = 8;

// Used to return Providers
// GET_VALUE, ADD_PROVIDER, GET_PROVIDERS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// GET_VALUE, ADD_PROVIDER, GET_PROVIDERS
// ADD_PROVIDER, GET_PROVIDERS

This seems to be a copy-paste error from the above closerPeers field. Am I missing something?

repeated Peer providerPeers = 9;
}
```

Any time a relevant `Peer` record is encountered, the associated multiaddrs
are stored in the node's peerbook.

These are the requirements for each `MessageType`:
* `FIND_NODE`: `key` must be set in the request. `closerPeers` is set in the
response; for an exact match exactly one `Peer` is returned; otherwise `ncp`
mxinden marked this conversation as resolved.
Show resolved Hide resolved
(default: 6) closest `Peer`s are returned.

* `GET_VALUE`: `key` must be set in the request. If `key` is a public key
(begins with `/pk/`) and the key is known, the response has `record` set to
that key. Otherwise, `record` is set to the value for the given key (if found
in the datastore) and `closerPeers` is set to indicate closer peers.

* `PUT_VALUE`: `key` and `record` must be set in the request. The target
node validates `record`, and if it is valid, it stores it in the datastore.

* `GET_PROVIDERS`: `key` must be set in the request. The target node returns
the closest known `providerPeers` (if any) and the closest known `closerPeers`.

* `ADD_PROVIDER`: `key` and `providerPeers` must be set in the request. The
target node verifies `key` is a valid CID, all `providerPeers` that
match the RPC sender's PeerID are recorded as providers.

* `PING`: Target node responds with `PING`. Nodes should respond to this
message but it is currently never sent.

# Appendix A: differences in implementations

Expand Down