N2R (node-to-relay) protocol
N2R allows communication between any two nodes, as long as one of them is a relay.
Packet format
The packet format is based on the InnerPacket
Rust enum:
An InnerPacket
is stuffed into the 8192-byte payload by this process:
First, we encode the packet using bincode, getting
box_plaintext
.We then box-encrypt using an ephemeral keypair, whose public half is
box_epk
, gettingbox_ciphertext
.We sign
box_epk
with our identity secret keyidentity_sk
, whose public half isidentity_pk
, producingidentity_signature
We then package everything into a tuple
(identity_pk, identity_signature, box_ciphertext)
, bincode it, and pad it to 8192 bytes.
This picture roughly illustrates the structure of a fully encoded InnerPacket
that stores a normal Message
.
Socket abstraction
The typical interface exposed by N2R is not raw functions for sending and receiving packets. Instead, we use a socket abstraction inspired by UDP. Each socket represents an Endpoint
, a local fingerprint:dock pair, that can receive and send messages. More specifically:
The user constructs a socket by binding to an identity and a dock number.
This identity can either be the long-term identity of the node, or a temporary anonymous identity.
The socket has a method to send a message to a fingerprint and dock number.
This formats a message with:
source identity public key and source dock number taken from the identity and dock number of the socket
destination identity looked up by fingerprint
If the socket is bound to a temporary identity, reply blocks must be sent to the destination fingerprint as well so that they can talk back to us.
There's also a receive method, which returns a message and a source endpoint, which consists of a fingerprint and a dock number.
This blocks until there is an incoming message addressed to the identity and dock number that the socket is bound to.
In the implementation, there must be some sort of demultiplexing done to separate incoming messages addressed to different sockets.
Last updated