BLOCKCHAIN NETWORK
Network Layer
The P2P networking layer handles peer discovery, encrypted communication, message routing, and proof-of-work spam prevention. It operates over UDP with X25519 key exchange and ChaCha20-Poly1305 encryption.
Transport
- UDP — all messages are sent over UDP datagrams. The node binds to
0.0.0.0(or::with IPv6 enabled) on the configured port (default52780). - IPv6 — optional dual-stack support via the
use_ipv6config flag. When enabled, the socket binds to::withIPV6_V6ONLY=0. - Socket timeout — the UDP socket uses a 0.5 s timeout to allow the populate thread to yield between reads.
Handshake Protocol
- A handshake message is a single UDP datagram with the format:
0x01(handshake marker) + 32-byte sender Ed25519 public key (storage identity) + 32-byte X25519 public key (relay key). - The recipient and sender each derive a 32-byte shared key via X25519 Diffie-Hellman exchange between their own relay secret key and the counterparty's relay public key. All subsequent messages between the pair are encrypted with this key via ChaCha20-Poly1305.
- Both parties store the shared key and all subsequent messages between them are encrypted.
- Bootstrap nodes receive a handshake immediately on startup. The default bootstrap seed is
bootstrap.astreum.org:52780and additional seeds can be configured.
Message Encryption
- Non-handshake messages are encrypted with ChaCha20-Poly1305 using the shared key derived during the handshake.
- Wire format:
0x00(non-handshake marker) + 32-byte sender public key +nonce || ciphertext || tag. - Nonce is 12 random bytes per message. The Poly1305 authentication tag is appended automatically.
- Plaintext format:
topic_byte || content_bytes.
Message Types
| Topic | Value | Description |
|---|---|---|
PING | 0 | Keep-alive and endpoint verification. Contains Ed25519-signed timestamp for peer validation. |
STORAGE_REQUEST | 1 | Request an expression by its BLAKE3 hash. Includes offered payment. |
STORAGE_RESPONSE | 2 | Return a requested expression to the requester. |
ROUTE_REQUEST | 3 | Ask a peer for its routing table entries close to a target ID. |
ROUTE_RESPONSE | 4 | Return routing table entries matching a route request. |
TRANSACTION | 5 | Relay a signed transaction through the network. |
Routing
- Kademlia-inspired — the routing table is organized by XOR distance from the node's own Ed25519 storage public key. Each peer's ID is the Ed25519 key they advertise.
- Route buckets — peers are grouped into buckets by XOR distance prefix length. When a bucket is full, the least-recently-seen peer is evicted.
- Route requests — nodes periodically send
ROUTE_REQUESTmessages asking peers for entries near a target ID. Responses populate the routing table. - Peer timeout — peers are evicted after 15 minutes (configurable via
peer_timeout) of inactivity. A background thread checks every 10 s (configurable viapeer_timeout_interval).
Identity and Keys
- Storage key — an Ed25519 keypair that identifies the node on the network. Used to sign storage advertisements and to route messages. Always required.
- Relay key — an X25519 keypair used for DH key exchange during handshakes. Used to encrypt all P2P traffic after the handshake completes.
- Validation key — an optional Ed25519 keypair. When present, the node is eligible to create blocks as a validator. The public key is included in blocks it creates.
- Peer identity — each peer is identified by a
Routeobject containing its Ed25519 public key bytes. The XOR distance between two peer IDs determines their bucket placement.
Message Proof-of-Work
- Outgoing messages carry a difficulty parameter. The sender must find a nonce such that the BLAKE3 hash of
nonce || message_bodyhas at leastdifficultyleading zero bits. - Bootstrap handshakes use difficulty 1. Other messages use a queue-pressure-based difficulty that scales with the incoming queue load:
| Queue pressure | Difficulty |
|---|---|
| < 70% | 1 |
| < 75% | 3 |
| < 80% | 5 |
| < 85% | 8 |
| < 90% | 12 |
| < 93% | 16 |
| < 95% | 19 |
| < 97% | 22 |
| < 98% | 24 |
| ≥ 98% | 26 |
- Pressure is calculated as
incoming_queue_size / incoming_queue_size_limit. When the incoming queue is mostly empty, difficulty stays at 1. As the queue fills, difficulty ramps up to make it proportionally harder for peers to send new messages, providing automatic back-pressure. - Incoming messages are verified against their advertised difficulty before being processed. Messages that fail PoW are dropped.
Message Queues
- Incoming queue — a thread-safe
Queuefed by a dedicated populate thread that reads from the UDP socket. A separate process thread dequeues and dispatches messages. Size-limited to 64 MiB (configurable viaincoming_queue_size_limit). - Outgoing queue — a thread-safe
Queueconsumed by a dedicated send thread. Messages are serialized, encrypted, and sent via UDP. - Fair use — the node tracks bytes uploaded vs. downloaded per peer. If the upload/download ratio exceeds
fair_use_ratio(default 0.5) within thefair_use_limitwindow (default 1 MiB), new outgoing messages to that peer are throttled.
Bootstrapping
- On startup, the node resolves the default seed (
bootstrap.astreum.org:52780) via DNS and sends a handshake to each resolved IP. - Additional seeds can be specified in the
additional_seedsconfig list. Each seed is resolved and pinged. - If no peers are connected, the node retries every 30 s (configurable via
bootstrap_retry_interval). - The node's own relay IP is discovered by connecting to the default seed and reading
getsockname().
