Protocol documentationSource snapshot: 0.1.0 alphaNetwork: testnet / devnet

Network

P2P v2 wire protocol

TCP message framing, defensive length validation, network handshake, every command and payload, peer discovery, lifecycle, keepalive, and compatibility boundaries.

Protocol version 2 · incompatible with prototype v1Reviewed August 31, 2026
Specification status

This page documents the implemented alpha. Executable source, interoperability vectors, and tests remain authoritative if prose and code ever diverge.

Transport scope

Luracoin P2P v2 is a binary message protocol over TCP. It carries compatibility handshakes, peer discovery, full blocks, complete signed transactions, inventory announcements, data requests, and liveness probes.

It is separate from both HTTP interfaces:

  • private wallet RPC uses authenticated loopback HTTP under /v1;
  • the public explorer uses read-only HTTP under /api/v1;
  • neither HTTP payload format is valid on a P2P socket.

P2P encryption, authenticated peer identity, compression, multiplexing, and transport negotiation are not implemented.

Network namespaces

The first four bytes of every message identify the network:

Network Chain ID P2P magic
mainnet 0 ba77d89f
testnet 1 fbc0b6db
devnet 2 faceb00c

Mainnet values reserve a namespace, but mainnet initialization remains disabled because no audited public genesis exists.

The CLI’s default P2P listen port is 9999 regardless of network. Operators running multiple networks or nodes on one host must assign distinct --port values explicitly; the wire namespace comes from magic and chain ID, not from a port convention.

Network isolation is redundant by design: magic separates message streams, VERSION carries the chain ID, and every transaction carries a one-byte chain ID. A mismatch at any boundary is rejected.

Message header

Every message begins with exactly 24 bytes:

Offset Size Field Encoding
0 4 magic active network’s exact bytes
4 12 command ASCII, right-padded with zero bytes
16 4 payload_length unsigned little-endian
20 4 checksum first four bytes of SHA256(SHA256(payload))
24 declared payload command-specific binary body

The checksum of an empty payload is still calculated from the empty byte string. A receiver validates magic, known command, command-specific length, complete payload read, and checksum.

Allocate only after validation

The receiver parses the fixed header first and rejects impossible lengths before reading or allocating the body. The global maximum is:

MAX_P2P_PAYLOAD = 8,000,004 bytes

The extra four bytes above the maximum block size exist at the global transport layer; a block command itself remains capped at 8,000,000 bytes.

This validation order is security-critical. An implementation must not trust an attacker-supplied uint32 length and allocate gigabytes before checking the command’s exact domain.

Command registry

Only these zero-padded command fields are valid:

Command Payload length Purpose
version 18 compatibility and height advertisement
verack 0 complete handshake phase
getpeers 0 request known IPv4 endpoints
peers 2–1,538 return count plus up to 256 endpoints
getblocks 6 request a consecutive height range
block 118–8,000,000, aligned transmit one complete block
tx 213 transmit one complete signed transaction
inv 33 announce one block or transaction ID
getdata 33 request one announced object
ping 8 liveness challenge
pong 8 matching liveness response

Unknown commands fail closed. A known command with a structurally wrong length also fails before body processing.

For block, (payload_length - 118) % 213 must equal zero. Semantic deserialization later enforces at least one transaction and the height-specific size limit.

VERSION payload

P2P v2 uses exactly 18 little-endian bytes:

Offset Size Field Requirement
0 4 protocol_version exactly 2
4 4 chain_id equals active network
8 4 height remote canonical tip advertisement
12 4 timestamp remote Unix seconds advertisement
16 2 listening_port 1–65,535

The timestamp is informational in the implemented handshake; it does not set local time or validate blocks. The height is untrusted scheduling input, not proof that the peer possesses a valid chain.

The advertised listening port replaces an inbound connection’s ephemeral source port when the endpoint is registered.

Handshake state machine

Outbound connection:

local  -> VERSION -> remote
local  <- VERSION <- remote
local  -> VERACK  -> remote
local  <- VERACK  <- remote

Inbound connection:

local  <- VERSION <- remote
local  -> VERSION -> remote
local  -> VERACK  -> remote
local  <- VERACK  <- remote

Before activation, the remote version, chain ID, and listening port are checked. A different protocol or chain is rejected before VERACK completes.

Once activated, another version or verack is a protocol-state error and the peer is removed. Application messages must not be processed before a successful handshake.

PEERS payload

Peer exchange is IPv4-only on wire:

count_le_u16
|| ipv4_bytes_4 || port_le_u16
|| ... repeated count times

Rules:

  • count <= 256;
  • payload length is exactly 2 + count × 6;
  • every port is nonzero;
  • each address is decoded as an IPv4 literal;
  • invalid, self, duplicate, unspecified, multicast, and reserved addresses are not admitted;
  • the local known-peer capacity still applies.

DNS names may exist in local seed/connection configuration but cannot be serialized into this v2 peers format. IPv6 is not represented.

GETBLOCKS payload

The request is:

start_height_le_u32 || count_le_u16

The serving node returns each available block as an individual block message, in ascending consecutive order. Synchronization requests at most 50 per batch even though the field can encode a larger integer.

This is a full-block protocol. There is no locator list, header message, compact block, Merkle proof, or streaming multi-block envelope.

BLOCK and TX payloads

block carries the canonical serialized block exactly as defined in Blocks & validation. It has no extra network wrapper inside the P2P payload.

tx carries the canonical complete 213 bytes described in Transactions. Unsigned 85-byte bodies and JSON objects are invalid.

Transport checksum success proves only that bytes arrived intact. The receiver still performs full deserialization, ID, signature, chain, nonce, balance, block-link, PoW, and state validation as appropriate.

Inventory messages

Both inv and getdata use:

type_u8 || object_hash_32

Valid types are:

Type Value Hash meaning
block 0x01 canonical block ID
transaction 0x02 canonical transaction ID

An unknown type is malformed. Hashes are raw 32 bytes on wire, not 64-byte hexadecimal ASCII.

inv is an announcement. A node that lacks the object may answer with getdata; the peer then sends the complete block or tx if it can resolve it. The protocol has no explicit “not found” response.

PING and PONG

Both carry one unsigned 64-bit little-endian nonce:

ping: nonce_le_u64
pong: same_nonce_le_u64

The node dispatcher owns the only read loop for an activated connection and correlates pong nonces with pending liveness checks. Concurrent components must not call read() independently on the same stream.

The default periodic ping interval is 60 seconds. A response with a different nonce does not satisfy the outstanding challenge.

Peer admission and capacity

Defaults are:

maximum total peers   = 8
maximum inbound peers = 8

Pending inbound and outbound attempts count toward admission decisions so simultaneous handshakes cannot trivially oversubscribe the cap. Activated endpoints are deduplicated.

The same numerical default for total and inbound peers means outbound connections can reduce remaining inbound capacity. Operators may override limits, but this does not add Sybil resistance.

Send and receive behavior

Per-peer sends are serialized with an async lock so message bytes cannot interleave on one TCP stream. The default drain timeout is five seconds. A timeout or socket failure marks the peer disconnected.

Receives are likewise single-owner and bounded by a timeout. Short headers, short bodies, malformed framing, invalid payload structures, and checksum failures terminate useful processing for that connection.

Relay to multiple peers runs concurrently and deduplicates peer objects. One slow peer should not serially block every healthy peer.

Discovery lifecycle

After handshake, a node:

  1. requests peer addresses with getpeers;
  2. schedules synchronization if the remote height is ahead;
  3. replays a bounded slice of pending transaction backlog when appropriate.

The current code ships with no public seed nodes. A fresh node therefore needs explicit peer configuration or a locally arranged topology before it can discover further addresses.

Known peers and cooldown histories are process-local. There is no durable address manager, reputation database, ban list, autonomous DNS-seed refresh policy, or authenticated node identity.

Protocol violations

Disconnect-worthy cases include:

  • wrong network magic;
  • unknown or noncanonical command field;
  • oversized or command-incompatible payload length;
  • checksum mismatch;
  • malformed structured payload;
  • wrong handshake order;
  • protocol-version or chain-ID mismatch;
  • post-handshake version/verack replay;
  • invalid listening port;
  • failed socket read/write or timeout.

Application-level invalid blocks or transactions are rejected too. The alpha does not maintain a persistent misbehavior score or ban duration.

Compatibility boundary

P2P v1 used a different, 14-byte VERSION shape without the chain ID. P2P v2 is deliberately incompatible. A v2 implementation must never infer a missing chain ID or accept an old handshake by length fallback.

Likewise, adding optional bytes to a current command changes its structural validity. Protocol evolution should define a new protocol version and explicit transition rules.

Security limits

Implemented defenses include bounded allocations, exact payload schemas, checksum validation, peer caps, send timeouts, single-reader ownership, address filtering, bounded caches, and cooldowns after synchronization failure.

They do not provide:

  • encrypted or private transport;
  • peer authentication;
  • message signatures independent of block/transaction signatures;
  • eclipse or Sybil resistance;
  • per-IP connection quotas;
  • persistent scoring or bans;
  • bandwidth accounting and rate limiting;
  • NAT traversal;
  • IPv6 discovery;
  • chain-proof negotiation.

Do not expose an alpha node as critical infrastructure without a network firewall, OS resource limits, observability, and an explicit threat assessment.

Wire conformance checklist

A second implementation should capture byte fixtures for:

  • every exact command field and zero padding;
  • empty-payload checksums;
  • maximum and off-by-one payload lengths;
  • v2 VERSION on all three chain IDs;
  • malformed handshake ordering;
  • peer count/payload mismatches and zero ports;
  • inventory type and raw hash order;
  • ping/pong nonce correlation;
  • block alignment and the 8,000,000-byte boundary;
  • partial reads and concurrent sends;
  • mismatched magic, checksum, chain, and protocol version.

Interoperability means matching both valid bytes and rejection behavior.

Source anchors

Primary implementation files used for this chapter: