P2P Transport (V2)
Tidecoin supports an encrypted v2 peer-to-peer transport that replaces Bitcoin’s elliptic-curve key exchange with ML-KEM-512. The goal is peer privacy and censorship resistance without leaving the transport handshake dependent on a quantum-vulnerable ECDH primitive.
This page describes the transport protocol. It is not the P2P message reference and it is not a node hardening guide. Operational network settings belong in Node & Operations.
Activation and configuration
| Item | Value |
|---|---|
| Default option | -v2transport=1 |
| Service bit | NODE_P2P_V2 when v2 transport is enabled |
| Manual connection RPC support | addnode and addconnection accept v2transport |
| Backward compatibility | Inbound responders can fall back to v1 when the peer begins with a v1 message header |
When v2 transport is enabled, the node advertises NODE_P2P_V2. Outbound
automatic connections use v2 when both sides signal support. Manual
connections can explicitly request v2 transport.
ML-KEM-512 parameters
| Parameter | Size |
|---|---|
| Encapsulation public key | 800 bytes |
| Decapsulation private key | 1,632 bytes |
| Ciphertext | 768 bytes |
| Shared secret | 32 bytes |
ML-KEM-512 is the NIST FIPS 203 key-encapsulation mechanism. Tidecoin uses it to establish the transport shared secret, then derives packet encryption keys from that shared secret.
Handshake overview
The v2 handshake is asymmetric:
- The initiator generates an ML-KEM-512 keypair.
- The initiator sends the 800-byte ML-KEM public key, followed by random garbage bytes.
- The responder receives the public key, encapsulates to it, and obtains a 768-byte ciphertext plus a 32-byte shared secret.
- The responder sends the ciphertext, followed by random garbage bytes.
- The initiator decapsulates the ciphertext with its private key and obtains the same 32-byte shared secret.
- Both sides derive packet keys, garbage terminators, and a session ID from the shared secret.
- Each side sends a garbage terminator and an encrypted empty version packet.
- After version packet processing, application messages use the encrypted packet channel.
The version packet content is currently empty and ignored. It is reserved for future transport extensions.
Receive state machine
The implementation’s receive path moves through these states:
| State | Applies to | Meaning |
|---|---|---|
KEY_MAYBE_V1 | Responder | Initial detection: incoming bytes may be a v1 header or a v2 ML-KEM public key. |
KEY | Responder | Receive the initiator’s ML-KEM public key. |
CT | Initiator | Receive the responder’s ML-KEM ciphertext. |
GARB_GARBTERM | Both | Receive random garbage until the derived 16-byte garbage terminator appears. |
VERSION | Both | Decrypt the first non-decoy version packet. |
APP | Both | Decrypt application packets. |
APP_READY | Both | A decrypted application message is ready for the net layer. |
V1 | Responder fallback | Use legacy v1 transport. |
The maximum random garbage length is 4,095 bytes. Including the 16-byte garbage terminator, a peer must find the terminator within 4,111 bytes or the connection aborts.
Packet encryption
BIP324PQCipher derives its packet ciphers from the 32-byte ML-KEM
shared secret using HKDF-SHA256. The salt is:
"tidecoin_v2_kem" || network_message_startThe cipher derives separate initiator and responder keys for length encryption and payload authentication/encryption, plus a session ID and garbage terminators.
| Field | Size |
|---|---|
| Session ID | 32 bytes |
| Garbage terminator | 16 bytes |
| Encrypted length field | 3 bytes |
| Packet header | 1 byte |
| Packet expansion | 20 bytes total: 3-byte encrypted length, 1-byte encrypted header, and AEAD tag |
| Rekey interval | 224 packets |
Payload packets use ChaCha20-based length encryption and FSChaCha20Poly1305 for authenticated encryption. The packet header contains an ignore bit used for decoy packets.
Why transport encryption matters
P2P encryption does not hide the blockchain. Confirmed transactions, blocks, and public keys remain public by design. The transport layer instead protects the relay path. It makes passive network observation less useful for:
- Classifying Tidecoin traffic by plaintext message patterns.
- Observing unconfirmed transaction relay content without participating as a peer.
- Correlating transaction propagation with transport-level metadata.
- Selectively censoring or degrading specific message types based on plaintext inspection.
ML-KEM matters because a quantum adversary should not be able to record today’s transport handshakes and later recover the transport secrets with Shor’s algorithm against an elliptic-curve key exchange.
Backward compatibility
Responders begin in KEY_MAYBE_V1. If the incoming bytes match the v1
message prefix for a version message, the transport falls back to v1. If
the bytes do not match that v1 prefix, the connection is treated as v2
and the responder expects an ML-KEM public key.
This preserves compatibility with peers that do not use v2 transport, while allowing v2-capable peers to use the post-quantum encrypted path.
Failure cases
Common transport-level failures include:
| Failure | Result |
|---|---|
| Invalid ML-KEM public key or ciphertext | Handshake fails. |
| Garbage terminator not found within the limit | Connection aborts. |
| Version packet authentication failure | Connection aborts. |
| Application packet authentication failure | Connection aborts. |
| Manual v2 connection requested while v2 is disabled | RPC rejects the request. |
Source of truth
| Topic | Source |
|---|---|
| ML-KEM constants and wrapper API | ../tidecoin/src/pq/pq_api.h |
| Packet cipher, HKDF salt, session ID, packet expansion | ../tidecoin/src/bip324_pq.h, ../tidecoin/src/bip324_pq.cpp |
| V2 transport state machine and V1 fallback | ../tidecoin/src/net.h, ../tidecoin/src/net.cpp |
-v2transport option and service bit | ../tidecoin/src/init.cpp |
| Cipher and state-machine tests | ../tidecoin/src/test/bip324_pq_tests.cpp, ../tidecoin/src/test/net_tests.cpp |
See also: Security Analysis, Node Configuration, Network Upgrades.