sk and pk are pair of keys, and made when DhtNode is created.
kbucket : close peers list which contains PackedNode objects close to this DhtNode pk
getn_timeout : timeout queue for NodesRequest, when a node send a NodesRequest packet to a peer, it wait for response(NodesResponse) for 122 seconds.
This timeout queue will be replaced by future timer event.
If the response(NodesResponse) doesn't arrive for 122 seconds(timeout), the node consider the peer as offline and remove it from kbucket.
precomputed_cache : An hashmap for precomputed keys.
Precomputed keys are used in various place, so redundunt computing should be avoided.
When precomputed key is needed, first search this cache,
if the key exists in cache, the found key is used,
if not, new precomputed key is computed and stored in cache for later use.
peers : An hashmap for tx part of MPSC channel,
this channel is used for communication to peer via udp socket
ping_cache : An hashmap for ping_ids, a ping_id per a peer
nodes_cache : An hashmap for NodesRequest.ids, an id per a peer
nat_ping_cache : An hashmap for NatPingRequest.ping_ids, a ping_id per a peer
DHT Operation
When startup, DHT node sends NodesRequest packet to it's known friends list.
If the known friend is online, it responds with NodesResponse packet.
DHT node listen on udp socket address
For each incoming udp packet
register peer : register peer address and tx part of MPSC channel.
handle packet : process packet and respond if needed.
register peer : create MPSC channel. And then register tx part of channel to peers cache.
handle packet : process packet on it's packet kind and respond if needed. Packet kind is
PingRequest
PingResponse
NodesRequest
NodesResponse
DhtRequest
NatPingRequest
NatPingResponse
Ping Request
PingRequest : Every 60 seconds, DHT node send PingRequest packet to peers to check whether it is alive.
When peer receives PingRequest, create_ping_resp(received PingRequest) is called.
create_ping_resp() does:
decrypt received PingRequest packet's encrypted payload
by calling get_payload(SK) decryption is done.
if decryption == success, make PingResponsePayload which has same ping_id
get precomputed symmetric key using get_symmetric_key() which cache precomputed key.
for caching precomputed key, DHT node uses HashMap named precomputed_cache
call PingResponse::new(PingResponsePayload) to make encrypted PingResponse packet
PingResponse packet is sent to peer by calling send_to_peer(packet)
get_payload() does:
decrypt packet's payload using SK and PK pair
decrypted payload is bytes slice
to get PingRequestPayload object call from_bytes(decrypted bytes slice)
if from_bytes() == success return the object
get_symmetric_key() does:
search hashmap using key as (SK, PK) to find values which is precomputed key
if search == success, return the precomputed key
if search != success, create new precomputed key for (SK, PK)
insert new precomputed key into hashmap for later use
return new precomputed key.
PingResponse::new() does:
call to_bytes() to get bytes slice from PingResponsePayload object
encrypt bytes slice using precomputed key
return new PingResponse object
send_to_peer() does:
search hashmap to find tx part of MPSC channel for peer using hashmap key as peer's udp socket address
if search == success, write packet on tx
if search fails, the peer is not registered, so error return
DHT node stores 8 nodes closest to each of the public keys in its DHT friends list.
PingRequest has a ping-id which provide a method to check the response(PingResponse) is correct.
Ping Response
PingResponse : When a node receives a PingRequest packet, it responds with this packet.
When a node sent PingRequest receives PingResponse, future timeout completes and add or update peer's Socket address in kbucket and known friends list.
If PingResponse doesn't arrive for 122 seconds, future timeout results in error, then the peer removed from kbucket and marked as offline if the peer is known friend.
When a peer receives PingResponse, it calls handle_ping_resp()
handle_ping_resp() does:
decrypt received packet's payload by calling get_payload()
DhtNode struct has a hashmap to hold a ping_id per a peer
search the hashmap to find ping_id last sent to peer
check if hashmap.ping_id == PinResponse.ping_id
if the check is true, then the timeout future completes
if check is not true, then the PingResponse is dropped as if it received nothing
Nodes Request
NodesRequest : Every 20 seconds, DHT node sends NodesRequest packet to a random node in kbucket and its known friends list.
DHT node runs future interval timer to send periodical packets. One of this interval timers is NodesRequest timer.
When NodesRequest timer expires:
select random peer in kbucket and known friends list
call create_nodes_req()
send packet to the peer
create_nodes_req() does:
create NodesRequestPayload struct object
call get_symmetric_key() to get precomputed key
call NodesRequest::new(NodesRequestPayload) to create new object
return NodesRequest object created
NodesRequest::new() does:
call to_bytes() to get bytes slice from NodesRequestPayload struct object
encrypt bytes slice using precomputed key
create and return new NodesRequest objcet
Nodes Response
When receiving NodesRequest:
call create_nodes_res()
if create_ndoes_res() result in success, then call send_to_peer()
create_nodes_res() does:
call get_payload() to get decrypted NodesRequestPaylaod object
call response(kbucket) to get NodesResponsePayload object
call NodesResponse::new() to get new object and return the object
response() does:
call get_closest(PK) to get up to 4 packed nodes closest to PK
call with_nodes(packed nodes vector)
return the result of with_nodes() which is NodesResponsePayload struct
get_closest() does:
create new bucket object which has capacity of 4
iterate all nodes in kbucket calling try_add(PK, node)
return bucket object which has up to 4 entry
try_add() does:
parameters are base_PK and new_node
if new_node is closer than current bucket entry, it will be added or replaced
with_nodes() does:
check if packed nodes vector is empty or more than 4 entry
if true, then return None
if not true, then create and return NodesResponsePayload object
NodesResponse::new() does:
call to_bytes() to get bytes slice of NodesResponsePayload
encrypt the bytes slice
create and return new NodesResponse objcet
When a node sent NodesRequest receives NodesResponse packet,
the timeout future completes and add or update peer's Socket address in kbucket or known friends list.
If NodesResponse doesn't arrive for 122 seconds,
the future timer result in error, then the peer removed from kbucket or marked as offline if the peer is known friend.
When receiving NodesResponse: handle_nodes_resp() is called.
handle_nodes_resp() does:
call get_payload() to get decrypted payload
timeout future completes
call try_add() to add up to 4 nodes contained in payload of NodesResponse packet
try_add() for kbucket does:
calculate kbucket index to get proper bucket to add new node
on calculated bucket call try_add() for bucket
*note : kbucket is a vector of bucket, bucket is a vector of PackedNode.
DhtRequest
When hole-punching starts, every 3 seconds NatPingRequest packet is sent to known friend which is not connected directly.
If there is no response for 6 seconds, hole-punching will stop.
When a peer receives NatPingRequest from a friend, peer checks the Receiver's PK with own DHT PK.
If the two PKs are same, peer responds with NatPingResponse.
If not, peer search its kbucket and known friends list to find the PK which is same with Receiver's PK of NatPingRequest.
If peer found the PK in kbucket or friends list, it resends NatPingRequest to that peer.
NatPingRequest
When future timer inverval for NatPingRequest expires, DHT node calls create_nat_ping_req() and calls sent_to_peer().
create_nat_ping_req() does:
create NatPingRequestPayload object with random ping_id
call NatPingRequest::new() to encrypt payload and to get NatPingRequest object
call send_to_peer() to send packet to peer via udp socket
When a peer receives NatPingRequest, it calls handle_nat_ping_req().
handle_nat_ping_req() does:
call check_pk() to check whether received packet's PK and own node's PK is same
if the two PKs is same, then call create create_nat_ping_resp() to make response packet and send_to_peer() is called
if not, then call search_matching_peer() to find peer which has same PK with received packet's PK
if search_matching_peer() is success, then resend NatPingRequest packet to that peer by calling send_to_peer()
if not, node drops NatPingRequest packet, and the effect of receiving NatPingRequest is nothing
create_nat_ping_resp() does:
call get_payload(SK) to decrypt packet's payload
if decryption == success, make NatPingResponsePayload which has same ping_id
call get_symmetric_key() to get precomputed key
call NatPingResponse::new(NatPingResponsePayload) to make encrypted NatPingResponse packet
NatPingResponse packet is sent to peer by calling send_to_peer(packet)
search_matching_peer() does:
iterate friends list to check whether received PK and friend PK is same
if the two PKs are same return the friend's socket address
if not, iterate kbuck to check whether received PK and node's PK in kbucket is same
if the two PKs are same return the node's socket address
if not, return None
NatPingResponse
When a node sent NatPingRequest receives NatPingResponse calls handle_nat_ping_resp().
handle_nat_ping_resp() does:
call get_payload() to get returned ping_id
check whether sent ping_id and returned ping_id is same
if same, future timer completes and start hole-punching
if not, drop the received packet, the effect of receiving packet is nothing
Description for dht_node
sk : Secret Key of this DHT node
pk : Public Key of this DHT node
kbucket : close peers list which contains PackedNode objects close to this DhtNode pk
getn_timeout : timeout queue for NodesRequest, when a node send a NodesRequest packet to a peer, it wait for response(NodesResponse) for 122 seconds.
This timeout queue will be replaced by future timer event.
If the response(NodesResponse) doesn't arrive for 122 seconds(timeout), the node consider the peer as offline and remove it from kbucket.
precomputed_cache : An hashmap for precomputed keys.
Precomputed keys are used in various place, so redundunt computing should be avoided.
When precomputed key is needed, first search this cache,
if the key exists in cache, the found key is used,
if not, new precomputed key is computed and stored in cache for later use.
peers : An hashmap for tx part of MPSC channel,
this channel is used for communication to peer via udp socket
ping_cache : An hashmap for ping_ids, a ping_id per a peer
nodes_cache : An hashmap for NodesRequest.ids, an id per a peer
nat_ping_cache : An hashmap for NatPingRequest.ping_ids, a ping_id per a peer
DHT Operation
When startup, DHT node sends NodesRequest packet to it's known friends list.
If the known friend is online, it responds with NodesResponse packet.
register peer : create MPSC channel. And then register tx part of channel to peers cache.
handle packet : process packet on it's packet kind and respond if needed. Packet kind is
Ping Request
PingRequest : Every 60 seconds, DHT node send PingRequest packet to peers to check whether it is alive.
When peer receives PingRequest, create_ping_resp(received PingRequest) is called.
create_ping_resp() does:
get_payload() does:
get_symmetric_key() does:
PingResponse::new() does:
send_to_peer() does:
DHT node stores 8 nodes closest to each of the public keys in its DHT friends list.
PingRequest has a ping-id which provide a method to check the response(PingResponse) is correct.
Ping Response
PingResponse : When a node receives a PingRequest packet, it responds with this packet.
When a node sent PingRequest receives PingResponse, future timeout completes and add or update peer's Socket address in kbucket and known friends list.
If PingResponse doesn't arrive for 122 seconds, future timeout results in error, then the peer removed from kbucket and marked as offline if the peer is known friend.
When a peer receives PingResponse, it calls handle_ping_resp()
handle_ping_resp() does:
Nodes Request
NodesRequest : Every 20 seconds, DHT node sends NodesRequest packet to a random node in kbucket and its known friends list.
DHT node runs future interval timer to send periodical packets. One of this interval timers is NodesRequest timer.
When NodesRequest timer expires:
create_nodes_req() does:
NodesRequest::new() does:
Nodes Response
When receiving NodesRequest:
create_nodes_res() does:
response() does:
get_closest() does:
try_add() does:
with_nodes() does:
NodesResponse::new() does:
When a node sent NodesRequest receives NodesResponse packet,
the timeout future completes and add or update peer's Socket address in kbucket or known friends list.
If NodesResponse doesn't arrive for 122 seconds,
the future timer result in error, then the peer removed from kbucket or marked as offline if the peer is known friend.
When receiving NodesResponse: handle_nodes_resp() is called.
handle_nodes_resp() does:
try_add() for kbucket does:
*note : kbucket is a vector of bucket, bucket is a vector of PackedNode.
DhtRequest
When hole-punching starts, every 3 seconds NatPingRequest packet is sent to known friend which is not connected directly.
If there is no response for 6 seconds, hole-punching will stop.
When a peer receives NatPingRequest from a friend, peer checks the Receiver's PK with own DHT PK.
If the two PKs are same, peer responds with NatPingResponse.
If not, peer search its kbucket and known friends list to find the PK which is same with Receiver's PK of NatPingRequest.
If peer found the PK in kbucket or friends list, it resends NatPingRequest to that peer.
NatPingRequest
When future timer inverval for NatPingRequest expires, DHT node calls create_nat_ping_req() and calls sent_to_peer().
create_nat_ping_req() does:
When a peer receives NatPingRequest, it calls handle_nat_ping_req().
handle_nat_ping_req() does:
create_nat_ping_resp() does:
search_matching_peer() does:
NatPingResponse
When a node sent NatPingRequest receives NatPingResponse calls handle_nat_ping_resp().
handle_nat_ping_resp() does: