Version: 1.0 Last Updated: 2026-01-12
Unicity is a headers-only blockchain that implements Bitcoin-compatible P2P networking with RandomX proof-of-work and ASERT difficulty adjustment. The architecture emphasizes simplicity through separation of concerns, thread safety through single-threaded design, and correctness through careful resource management.
- Headers-Only: No transactions, no UTXO set, no mempool
- 100-byte Headers: Extended from Bitcoin's 80 bytes for RandomX PoW
- RandomX PoW: ASIC-resistant proof-of-work
- ASERT Difficulty: Per-block difficulty adjustment
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application β
β β’ RPC Server β
β β’ Mining (optional) β
β β’ Periodic Tasks β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β NetworkManager β βChainstate β β Utilities β
β β βManager β β β
ββββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β β
βΌ βΌ βΌ
P2P Network Blockchain Time, Logging
Protocol Validation Thread Pools
The chain layer validates block headers and maintains the blockchain state:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ChainstateManager β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Public Interface β β
β β β’ AcceptBlockHeader() β’ ActivateBestChain() β β
β β β’ ProcessNewBlockHeader() β’ InvalidateBlock() β β
β β β’ GetTip() β’ IsInitialBlockDownload() β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βΌ β
β ββββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββββββ β
β β BlockManager β β ActiveTipCandidatesβ β Orphan Pool β β
β β β β β β β β
β β β’ Block storage β β β’ Candidate tips β β β’ DoS limits β β
β β β’ Active chain β β β’ Best chain β β β β
β β β’ Persistence β β β’ Pruning β β β β
β ββββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββββββ β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Validation Layer β β
β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββββββ β β
β β β CheckBlock β β Contextual β β GetNextWork β β β
β β β Header β β Check β β Required (ASERT) β β β
β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Consensus Layer (PoW) β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β CheckProofOfWork() β β β
β β β βββββββββββββββββββββ βββββββββββββββββββββββββ β β β
β β β β COMMITMENT_ONLY β β FULL (RandomX) β β β β
β β β β (~1ms, DoS) β β (~50ms, cache) β β β β
β β β βββββββββββββββββββββ βββββββββββββββββββββββββ β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The fundamental unit of the blockchain:
| Field | Size | Description |
|---|---|---|
| nVersion | 4 bytes | Block version |
| hashPrevBlock | 32 bytes | Previous block hash |
| payloadRoot | 32 bytes | Payload hash (Hash of reward token id hash and UTB hash) |
| nTime | 4 bytes | Unix timestamp |
| nBits | 4 bytes | Difficulty target (compact format) |
| nNonce | 4 bytes | PoW nonce |
| hashRandomX | 32 bytes | RandomX hash (PoW commitment) |
In-memory metadata for each known block:
- Validation State: Flags indicating validation status
- Chain Position: Height and cumulative proof-of-work
- Parent Pointer: Links to parent block
- Header Fields: Stored inline for quick access
Linear view from genesis to current tip:
- Stored as vector of block pointers
- O(1) lookup by height
- Rebuilt on reorganizations
Three-layer validation ensures security while maintaining performance:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAYER 1: Pre-Filtering β
β (Fast DoS Protection - ~1ms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β CheckProofOfWork(COMMITMENT_ONLY) β
β β’ Validates: hashRandomX commitment meets nBits β
β β’ Purpose: Reject invalid headers before expensive PoW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PASSED
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAYER 2: Context-Free Validation β
β (Full PoW Verification - ~50ms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β CheckBlockHeader() β
β β’ Validates: Full RandomX hash matches hashRandomX β
β β’ Purpose: Cryptographic PoW verification β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PASSED
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAYER 3: Contextual Validation β
β (Consensus Rules - ~5ms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ContextualCheckBlockHeader() β
β β’ Validates: β
β - nBits matches ASERT difficulty β
β - Timestamp > previous block (mainnet/testnet) β
β - Timestamp < now + 10 minutes β
β - Version >= 1 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PASSED
β Block Accepted
When a competing fork has more accumulated proof-of-work:
Before Reorg:
Genesis β A1 β A2 β A3 β A4* (active tip)
β B2 β B3 β B4 β B5 (candidate, more work)
Fork Point: A1
Disconnect: [A4, A3, A2]
Connect: [B2, B3, B4, B5]
After Reorg:
Genesis β A1 β B2 β B3 β B4 β B5* (active tip)
β A2 β A3 β A4 (orphaned fork)
Process:
- Find common ancestor (fork point)
- Disconnect blocks from old chain back to fork point
- Connect blocks from new chain forward from fork point
- Update active chain tip
- Emit notifications to application
- Memory-hard: Requires ~2GB dataset per epoch
- Epoch-based: Dataset changes periodically
- Two-phase verification: Fast commitment check, then full RandomX
Unicity integrates with the BFT layer to establish periodic checkpoints.
- Verification: The node verifies the hardcoded genesis UTB against the epoch 1 trust base fetched from the BFT network.
- Requirement: BFT integration is required for testnet and mainnet. The node will fail to start if it cannot connect to the BFT node at the configured
bftaddr. - Disabling: Integration can be disabled by setting
bftaddrto an empty string. This is the default for regtest. - Epoch 1: When disabled, the node assumes the hardcoded genesis UTB is valid without network verification.
Formula: target_new = target_ref * 2^((time_diff - ideal_time) / half_life)
- Per-block adjustment: Difficulty updates every block
- Exponential response: Based on actual vs. ideal block times
- Anchor system: References a fixed anchor block for calculations
- Parameters:
- Target spacing: 2.4 hours (8640 seconds)
- Half-life: 48 hours (difficulty doubles/halves over this period)
Prevents timestamp manipulation:
- Mainnet/Testnet: Strictly increasing (timestamp > previous block)
- Regtest: Median Time Past (timestamp > median of last 11 blocks)
- Future limit: timestamp < now + 10 minutes
- Ensures monotonic time progression
The network layer manages P2P connections and blockchain synchronization:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NetworkManager β
β (Coordinator, io_context owner, message routing) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
βConnection β β AddrRelay β β HeaderSync β β Helpers β
β Manager β β Manager β β Manager β β (Ban, Evict, β
β β β β β β β Misbehavior) β
ββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
β β β β
βΌ βΌ βΌ βΌ
ββββββββββββ ββββββββββββββββ ββββββββββββββββ βββββββββββββ
βPeer Stateβ βAddressManagerβ βSync State β βBan Tables β
β β βAnchorManager β β β β β
ββββββββββββ ββββββββββββββββ ββββββββββββββββ βββββββββββββ
Responsibilities:
- Owns the reactor (boost::asio::io_context)
- Routes messages to appropriate managers
- Coordinates lifecycle (start/stop)
- Manages periodic tasks (connection attempts, maintenance, feelers)
- Prevents self-connections
Threading Model:
- Single-threaded reactor pattern
- All handlers serialized by event loop
- No locks needed for network state
- Application layer (validation, mining) runs on separate threads
Responsibilities:
- Connection state machine management
- Inbound connection acceptance
- Outbound connection initiation
- VERSION/VERACK handshake
- Connection limits and eviction
- Ban/discourage enforcement
Connection States:
DISCONNECTED β CONNECTING β CONNECTED β VERSION_SENT β READY
β β β β
[Timeout] [Rejected] [Invalid] [Running]
β β β β
DISCONNECTED DISCONNECTED DISCONNECTED READY
Responsibilities:
- Address discovery and storage
- Peer address selection
- ADDR/GETADDR message handling
- Anchor connections (eclipse attack resistance)
Components:
- AddressManager: Bitcoin Core's tried/new table algorithm
- AnchorManager: Persists 2 block-relay anchors across restarts
Address Selection:
- Tried table: Addresses we've successfully connected to
- New table: Unverified addresses from peers
- Collision-resistant selection algorithm
- Exponential backoff on failures
Responsibilities:
- Initial Block Download (IBD) coordination
- Header synchronization via GETHEADERS/HEADERS
- Block announcement via direct HEADERS (no INV/GETDATA needed for headers-only)
- Sync peer selection and rotation
Components:
- HeaderSyncManager: GETHEADERS/HEADERS protocol
Sync Strategy:
- Single sync peer during IBD (prevents resource exhaustion)
- Parallel header requests after IBD
- Stall detection with 120-second timeout
- Automatic sync peer rotation on stall
Network Socket
β
βΌ
Transport Layer
β
βΌ
Peer::on_message_received()
β
βΌ
NetworkManager::handle_message()
β
βββΊ Check running flag
βββΊ VERSION: Check nonce collision
β
βΌ
MessageDispatcher
β
βββΊ Route to appropriate manager:
βββΊ VERACK β ConnectionManager
βββΊ ADDR β AddrRelayManager
βββΊ GETADDR β AddrRelayManager
βββΊ HEADERS β HeaderSyncManager
βββΊ PING/PONG β ConnectionManager
Manager
β
βΌ
Peer::send_message()
β
βΌ
Transport Layer
β
βΌ
Network Socket
- Select address from AddressManager
- Check: not banned, not already connected, slots available
- Initiate TCP connection
- Send VERSION message
- Receive VERSION from peer
- Send VERACK
- Receive VERACK
- Connection ready - begin protocol operations
- Accept TCP connection
- Check: not banned, slots available, rate limits
- Wait for VERSION from peer
- Send VERSION
- Receive VERACK
- Send VERACK
- Connection ready - begin protocol operations
Single-threaded event loop processes:
- Socket Events: Readable, writable, connected, disconnected
- Timers: Connection attempts, maintenance, feelers, message batching
- Application Requests: From validation, mining, RPC threads
Node A Node B
| |
|------ VERSION -------->|
|<----- VERSION ---------|
|------ VERACK --------->|
|<----- VERACK ----------|
| |
[Connection Ready]
Syncing Node Synced Node
| |
|---- GETHEADERS ------->|
|<---- HEADERS ----------|
| |
|---- GETHEADERS ------->|
|<---- HEADERS ----------|
| |
[Repeat until synced]
Mining Node Peer Node
| |
|<----- HEADERS ---------|
| |
Node A Node B
| |
|------ GETADDR -------->|
|<----- ADDR ------------|
| |
Time β
Block Arrives:
AcceptBlockHeader() β Add to BlockIndex
β
TryAddCandidate() β Add to candidate set
β
ActivateBestChain() β Compare accumulated work
β
[If new chain has more work]
β
FindForkPoint() β Locate common ancestor
β
DisconnectBlocks() β Remove old tip blocks
β
ConnectBlocks() β Add new chain blocks
β
UpdateTip() β Set new active chain
β
NotifyChainTip() β Inform application
Chain Layer:
- BlockManager: Owns all block data
- ActiveTipCandidates: Tracks candidate chain tips
- ChainstateManager: Coordinates validation
Network Layer:
- ConnectionManager: Connection management & lifecycle
- AddrRelayManager: Address discovery & storage
- HeaderSyncManager: Synchronization logic
Chain Layer: Single coarse-grained mutex
- Simple locking model
- Prevents data races
- May become bottleneck at high throughput
Network Layer: Single-threaded reactor
- No locks needed for network state
- Handlers serialized by event loop
- Application threads interact via thread-safe interfaces
Fast Rejection:
- Commitment-only PoW check (~1ms) before expensive validation
- Message size limits (8 MB maximum)
- Orphan header limits (1000 total, 50 per peer)
Resource Limits:
- Connection limits (10 outbound, 125 inbound)
- Ban/discourage for misbehaving peers
- Stall detection with timeouts
Address Diversity:
- Tried/new table separation
- Feeler connections test random addresses
- Exponential backoff on failures
Anchor Connections:
- 2 block-relay-only peers saved across restarts
- Prevents attacker from monopolizing connections
- Reconnect to anchors on startup
By eliminating transactions:
- No UTXO set management
- No mempool
- No transaction validation
- No block size limits
- 99% reduction in complexity vs. Bitcoin
Headers carry all consensus-critical information:
- Proof-of-work (RandomX hash)
- Chain linkage (previous block hash)
- Timing (timestamp, difficulty)
- Reward recipient (miner address)