Introduction
A P2P Encrypted Network is a network of nodes that communicate with each other using a secure, encrypted protocol.
The network is designed to be decentralized, with no central authority or single point of failure.
It is also designed to be lightweight for low end devices, but also resistant to censorship and surveillance, with strong privacy and security guarantees.
Seed nodes are used to help nodes find each other and are connected by default when no peerlist available.
XELIS uses ChaCha20-Poly1305 AEAD cipher for symmetric encryption with key rotation happening for each 1 GB of data transferred.
All transferred data uses a custom serializer / deserializer made by hand to transform a struct representation in raw bytes directly.
This serialization is done using the fixed position of each field and their corresponding bit size.
Before sending a packet to the peer, the local node encrypts it completely to prevent network traffic analysis and to authenticate each transferred data.
All data is transferred using this encrypted protocol which allows for efficient reading, transferring, and serializing of the data.
Protocol
The protocol has a simple design with a packet system that allows transferring data between peers.
To create a connection between with a potential peer, the party that initiated the connection must send its symmetric encryption key first.
The other party will wait on it and will send its own key after receiving the first one.
Once both parties have received the other's symmetric encryption key, they can start the handshake and send / receive data encrypted.
The client that initiated the connection will send a handshake packet to the other party to be upgraded as a Peer, and the other party will respond with a handshake packet to confirm the upgrade.
In the handshake packet, a peer can inform others peers if it accepts that its IP is shared to extend peerlist of others and / or to be returned in RPC API.
To reduce overall bandwidth of network, nodes shouldn't:
- send twice a transaction during propagation time to the same peer.
- send twice a block during propagation time to the same peer.
For block and transaction propagation, the local node keeps a cache of the last N elements sent or received from a peer to not send the same data twice during propagation.
Implementation
As a note, the connection for a new peer (took from the queue or a new incoming connections) is executed through a unique tokio task with the same allocated buffer for handshake. This prevents any DoS attack on creating multiple task and verifying connection.
When the peer is verified and valid, two task are created for it. One reads incoming packets and the other writes packets to the peer. Separating both directions into two tasks prevents incoming packets from blocking outgoing packets.
Chain Sync
The local node randomly select a peer in the peer list which has a greater height than us and send him a chain request.
The chain request includes last CHAIN_SYNC_REQUEST_MAX_BLOCKS
blocks hashes of the local node's chain with their topoheight spaced exponentially.
This data is used by the selected peer to try to find a common point with the local node's chain and his own (block hash must be at same topoheight as other peer).
If the selected peer finds a common point, it adds up to CHAIN_SYNC_RESPONSE_MAX_BLOCKS
blocks hashes ordered by block height.
Through the "ask and await" request object system, the local node asks for the complete block (block header with transactions included) and add it to the chain directly.
Chain sync is requested with a minimum interval of CHAIN_SYNC_DELAY
seconds.
Packets
This part explains all packets used in the XELIS network to communicate over P2p.
Key Exchange
Key Exchange is the first real packet to be sent when creating a new connection. This allow exchanging symmetric encryption keys between peer to establish an encrypted communication channel over TCP.
Currently, the ChaCha20-Poly1305 algorithm is used to encrypt / decrypt all packets.
This packet can be sent later to rotate the key of a peer. This is currently done every 1 GB of data sent.
We're using two different symmetric keys for encryption per Peer. One key is from the local node, to encrypt its packets, and the other key is to decrypt peer's packets.
Handshake
The handshake packet must be the first packet sent with the blockchain state inside to upgrade a connection to a peer. If valid, the peer will send the same packet with is own blockchain state.
This packet should only ever be sent at the beginning of a connection.
Transaction Propagation
The transaction propagation packet contains the hash only to prevent sending the TX. Its also backed by a cache per peer to know if the transaction had already been sent/received.
Block Propagation
The block propagation packet contains the block header only. Its sent to all peers who have their height minus our height less than STABLE_LIMIT
.
To build the block, transactions are retrieved from the mempool.
If a transaction is not found in the mempool, the local node requests it from the same peer in order to build it.
Chain Request
TODO
Chain Response
TODO
Ping
The ping packet is sent at an regular interval and informs peers of the local node's blockchain state.
Every 15 minutes, the packet can contain up to MAX_LEN
sockets addresses (IPv4 or IPv6) to help others nodes to extends their peer lists.
Object Request
TODO
Object Response
TODO
Notify Inventory Request
TODO
Notify Inventory Response
TODO
Bootstrap Chain Request
TODO
Bootstrap Chain Response
TODO
Disconnect
The disconnect packet is sent when a peer is disconnected from the local node and we have it in common with another peer.
This is used to keep synced the "propagation map predicate".