Network
Synchronization & relay
Readiness semantics, sequential full-block synchronization, peer selection, cooldowns, inventories, transaction propagation, bounded replay, and convergence limits.
This page documents the implemented alpha. Executable source, interoperability vectors, and tests remain authoritative if prose and code ever diverge.
Synchronization model
The alpha synchronizer downloads and applies complete blocks in strict height order. It does not synchronize headers, compare competing work, or download ranges from several peers concurrently.
Its invariant is simple:
received_block.height == local_tip + 1
The block must then pass complete local validation and persistence. Any missing, duplicate, out-of-order, malformed, contextually invalid, or non-extending block fails that step.
Height advertisements are hints
Each activated peer advertises a uint32 height in VERSION. The node computes:
best_peer_height = max(local_tip, heights advertised by connected peers)
This selects work but is not trusted consensus data. A peer can lie about height. The peer still has to return consecutive, fully valid blocks before local state changes.
The node updates a peer’s observed height upward after accepting a block from it. There is no authenticated chain summary or cumulative-work commitment in the handshake.
Readiness definition
The full node is ready only when:
genesis/state is initialized
AND no synchronization task is active
AND local_tip >= best advertised connected-peer height
This is an operational gate, not a statement of global consensus or network health.
A node with no connected peers may become ready at its local genesis or tip because it has no higher advertised height. That does not mean it is synchronized with an external network.
The private RPC refuses transaction submission while not ready. Read-only status remains available so clients can explain the condition.
Peer selection
Only one synchronization task is active at a time because chain application is sequential. Eligible peers must be:
- connected and activated;
- backed by a synchronization queue;
- outside the host’s cooldown window;
- advertising a height above the local tip.
Among eligible peers, the node chooses the highest advertised height. It does not yet rank by latency, validated chain work, historical reliability, geographic diversity, or bandwidth.
When one attempt ends, the scheduler can choose the next best eligible peer.
Batch request flow
The default batch size is 50 blocks.
For local tip H:
node -> GETBLOCKS(start_height = H + 1, count = 50)
peer -> BLOCK(H + 1)
peer -> BLOCK(H + 2)
...
The expected count for a cycle is limited to the difference between the peer’s advertised height and the current tip. Each block arrives through the connection’s single reader/dispatcher and places an acceptance result into that peer’s bounded sync queue.
The sync task waits up to 30 seconds for each expected result. A false result, queue-window violation, timeout, zero-progress batch, stream failure, or exception marks the attempt failed.
The serving peer caps any requested count at 50 and stops at its local tip if the requested range extends beyond it.
Why the reader is separate
P2P messages can interleave: a peer may send ping, inv, or tx while a block range is in flight. Exactly one dispatcher owns reads from the TCP stream. It routes block acceptance results into the sync queue while continuing to handle other commands.
Creating a second “sync reader” would race for frame boundaries and corrupt message ownership. Independent implementations should preserve the single-reader principle even if their task architecture differs.
Applying each block
For each received block payload, the reference path:
- deserializes exact bytes and verifies the declared ID;
- snapshots any local Redis candidates that compete with confirmed sender/nonce slots;
- requires the next expected height;
- performs complete block, transaction, PoW, timestamp, difficulty, and state validation;
- saves the canonical block and state;
- prunes included and conflicting pending candidates conditionally;
- updates the sending peer’s known height;
- remembers the block inventory;
- relays an
invto other peers.
Mempool cleanup failure does not roll back an already accepted canonical block because Redis is disposable policy state. Later admission/cleanup must treat obsolete pending entries as invalid.
Failure cooldown
After a failed synchronization attempt, the peer is removed and its normalized host identity enters an in-memory exponential cooldown.
Defaults:
first delay 30 seconds
maximum delay 900 seconds
failure histories 4,096 hosts
With defaults, repeated delays are 30, 60, 120, 240, 480, then 900 seconds and remain capped there.
The key is the normalized host or compressed IP, not the advertised port. Reconnecting the same host on a different port does not bypass the history. Failure counts are capped internally and the oldest histories are evicted once the configured map limit is exceeded.
A successful synchronization clears both failure count and cooldown for that host. State is process-local and disappears on restart.
Environment controls are:
LURACOIN_SYNC_COOLDOWN_BASE
LURACOIN_SYNC_COOLDOWN_MAX
LURACOIN_SYNC_COOLDOWN_LIMIT
Values are operational defenses, not wire-consensus parameters.
New block propagation
After mining or accepting a block, the node announces:
INV(type = 0x01, hash = block_id)
A peer that has not seen the inventory requests it with matching GETDATA. The serving node resolves the block ID to a canonical height, reads the block, rechecks its ID, and sends a complete BLOCK payload.
Known-inventory caches prevent repeated requests and loops. They are bounded to 50,000 hashes with insertion-order eviction.
The block synchronizer’s inventory helper currently requests advertised blocks broadly because it lacks a complete fast negative/branch-aware block-hash model. Duplicate or non-extending blocks are ultimately rejected by validation.
Transaction admission from peers
A received TX is:
- deserialized as exactly 213 bytes;
- validated and atomically admitted to the Redis mempool;
- added to the bounded process-local relay backlog;
- remembered in inventory caches;
- relayed directly to all other healthy peers.
If admission returns false—for invalid signature, wrong chain, nonce conflict, insufficient balance, missing contiguous predecessor, coinbase, or Redis failure—it is not relayed.
The immediate relay uses the full transaction rather than only inv. The inventory request path remains available when a peer announces a transaction by ID.
Local RPC broadcast receipt
When private RPC admits a transaction, the node broadcasts it and returns:
{
"accepted": true,
"broadcast": true,
"relay_count": 3
}
relay_count is the number of peer writes that completed in that attempt. It is not an acknowledgement of mempool admission, mining, or onward propagation by those peers.
An accepted transaction with no connected peers remains locally pending:
{
"accepted": true,
"broadcast": false,
"relay_count": 0
}
It is queued for bounded replay when healthy peers become available.
Pending relay backlog
The node keeps canonical pending payloads in an insertion-ordered, process-local map keyed by raw transaction hash. Its effective limit is the lesser of the known-inventory cap and configured pending limit.
Defaults:
pending items retained 1,000
items per peer per cycle 100
time budget per peer/cycle 5 seconds
send timeout 5 seconds
Controls:
LURACOIN_PENDING_RELAY_LIMIT
LURACOIN_REPLAY_BATCH_SIZE
LURACOIN_REPLAY_BUDGET_SECONDS
Each peer has a cursor so a later maintenance cycle can continue after the previous slice. Reaching the end clears the cursor; still-pending transactions may be re-announced from the beginning on a later cycle.
The backlog is populated for locally submitted and peer-admitted transactions. It is pruned when the same transaction or its sender/nonce slot confirms.
Concurrency and slow peers
Broadcast sends to peers concurrently. Each send has a timeout, and per-peer stream locks preserve frame order. A failed relay removes the unhealthy peer; successful peers are not forced to wait serially behind it.
Pending replay returns both a partial success count and a health flag. An unexpected failure retains already counted sends and causes peer removal. Cancellation remains distinguishable from failure during shutdown.
These bounds protect a small alpha node from indefinite stalls. They are not a comprehensive bandwidth scheduler.
Keepalive
Every 60 seconds by default, the node sends each peer a random 64-bit ping. A matching pong must arrive within 10 seconds. Failed peers are disconnected and forgotten from active discovery state.
Ping operations use futures keyed by peer identity and nonce while the main dispatcher continues reading. A late or unrelated pong cannot satisfy a different request.
Maintenance loop
Approximately every 30 seconds while running, the node:
- connects to eligible known addresses until capacity;
- skips hosts still in synchronization cooldown;
- replays bounded pending transactions when the node is ready;
- lets normal peer activation trigger discovery and best-peer sync scheduling.
There are no bundled public seeds in this alpha, so topology must be bootstrapped explicitly.
What can and cannot converge
Nodes converge when one has a strictly longer valid extension of another’s current tip and supplies every intervening block.
They cannot automatically converge when:
- histories fork at the same height;
- a peer offers a branch that does not extend the local tip;
- the locally accepted branch has less cumulative work;
- rollback would be necessary;
- a block was missed but later inventory lacks height/locator context;
- all peers advertise misleading heights.
The implementation stores no side branches and has no reorganization path. Synchronization is therefore catch-up for a single linear history, not a full decentralized fork-resolution algorithm.
Observability checklist
Operators should monitor at minimum:
- local height and tip ID;
- best advertised connected-peer height;
syncingandreadyseparately;- connected/inbound peer counts and addresses;
- repeated sync failures and cooldowns;
- block acceptance/rejection logs;
- Redis availability and pending backlog pressure;
- transaction relay counts and peer-removal spikes;
- disk growth and explorer-index lag.
Compare tip IDs as well as heights. Equal heights do not imply equal histories.
Future protocol work
A public-grade synchronizer requires, at minimum, branch-aware header download, validated cumulative work, deterministic fork choice, reorganization, state rollback/replay, orphan management, peer quality scoring, durable discovery, resource accounting, and adversarial eclipse/Sybil testing.
Until then, run controlled testnet topologies and treat readiness as a local service condition only.
Source anchors
Primary implementation files used for this chapter:
