Protocol documentationSource snapshot: 0.1.0 alphaNetwork: testnet / devnet

Core protocol

Blocks & validation

The canonical block encoding, block ID preimage, validation order, transaction state transition, size schedule, and linear-chain consensus boundary.

Consensus-critical encoding · block 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.

Block model

A block is a fixed-format header followed by one or more complete 213-byte transactions. The first transaction is the unique coinbase. Luracoin does not use a Merkle root in this alpha: the canonical block hash commits directly to the transaction count and every serialized transaction.

The wire and disk representation begins with a 118-byte transmitted header:

Offset Size Field Encoding
0 4 version unsigned little-endian; currently 1
4 32 declared block ID raw 32-byte digest
36 32 prev_block_hash raw 32-byte digest
68 4 height unsigned little-endian
72 34 miner canonical address as ASCII
106 4 timestamp Unix seconds, unsigned big-endian
110 4 bits compact target bytes
114 4 nonce unsigned little-endian
118 N × 213 transactions complete transactions, no count on wire

The mixed timestamp endianness is intentional and consensus-critical. Do not normalize it in an independent implementation.

Canonical block ID

The declared block ID stored at bytes 4–35 cannot hash itself. The canonical preimage omits that field and inserts a two-byte transaction count before the transaction bodies:

version_le_u32
|| prev_block_hash_32
|| height_le_u32
|| miner_ascii_34
|| timestamp_be_u32
|| bits_4
|| nonce_le_u32
|| transaction_count_le_u16
|| transaction_0_213
|| ...
|| transaction_n_213

Then:

block_id = SHA256(SHA256(canonical_preimage))

The displayed identifier is lowercase hexadecimal. Deserialization recalculates this digest and rejects a declared-ID mismatch before the block can be accepted.

This design commits to transaction order and signatures. Reordering two otherwise valid transactions changes the ID. Re-signing a transaction also changes its transaction ID and therefore its containing block ID.

Structural parsing rules

A decoder must reject a block when any of these conditions holds:

  • the payload is shorter than the 118-byte header;
  • the trailing byte length is zero or is not divisible by 213;
  • the transaction count exceeds the unsigned 16-bit limit;
  • the version, height, timestamp, or nonce is outside its encoded integer domain;
  • the miner is not exactly 34 ASCII bytes;
  • any embedded transaction fails exact 213-byte deserialization;
  • the computed ID differs from the declared 32-byte ID;
  • bytes remain after the expected transaction sequence.

There is no permissive extension area. A future format must introduce an explicit compatibility boundary rather than appending fields to version 1.

Block size schedule

The maximum serialized block size changes at fixed heights:

Starting height Maximum serialized bytes
0 10,000
28,800 50,000
57,600 75,000
86,400 200,000
172,800 1,000,000
259,200 2,000,000
345,600 3,000,000
432,000 8,000,000

The limit includes the full 118-byte header and every 213-byte transaction. It is not a transaction-body-only allowance. The P2P transport independently caps block payloads at 8,000,000 bytes and all message payloads at 8,000,004 bytes.

Because the transaction count is uint16, a block can contain at most 65,535 transactions regardless of byte capacity. One slot is reserved for coinbase, so a miner selects at most 65,534 normal transactions.

Context-free checks

Before reading mutable account state, the validator establishes:

  1. the block object has a canonical serialization and matching ID;
  2. its serialized size is within the limit for its own height;
  3. its version is supported;
  4. its miner address is valid;
  5. it contains at least one transaction;
  6. transaction IDs are unique within the block;
  7. every transaction belongs to the active chain ID;
  8. exactly one coinbase exists and it is transaction zero.

A block with a second zero-origin transaction is invalid even if its amount is zero. A normal transaction cannot occupy index zero in front of coinbase.

For a non-genesis candidate at local tip height H, the next block must satisfy:

candidate.height          == H + 1
candidate.prev_block_hash == local_tip.id
candidate.bits            == expected_bits(candidate.height)
candidate.timestamp       > local_tip.timestamp
candidate.timestamp       <= local_clock_unix + 500
numeric(candidate.id)     <= target(candidate.bits)

The future-time window is evaluated against the validating node’s clock. Operators therefore need reliable time synchronization. A timestamp equal to the predecessor is invalid; monotonicity is strict.

The PoW comparison treats the 32-byte digest as a big-endian integer. <= is valid at the target boundary.

Coinbase contract

At height greater than zero, the first transaction must have:

Field Required value
chain active chain ID
nonce candidate block height
fee 0
value subsidy at height + sum of all normal fees
from_address 34 ASCII zero characters
to_address exactly the block header’s miner address
unlocking field 128 zero bytes

The fee sum is computed with bounded integer arithmetic. A coinbase that underpays or overpays is invalid; there is no permitted donation by omitting part of the subsidy or fees.

Genesis is created from its network manifest and uses the manifest allocation instead of the recurring subsidy rule. Testnet and devnet genesis material is educational and public. Mainnet has no manifest and initialization fails closed.

Ordered state transition

The validator copies the accounts touched by the candidate into a temporary state. It then processes normal transactions in their block order:

require sender exists
require tx.nonce == sender.nonce + 1
require sender.balance >= tx.value + tx.fee
require signature, sender key, address, and chain are valid

sender.balance -= tx.value + tx.fee
sender.nonce    = tx.nonce
receiver.balance += tx.value

Each later transaction observes the earlier temporary changes. This has important consequences:

  • multiple outgoing transactions from one account must use contiguous nonces;
  • an account may spend value received earlier in the same block;
  • a self-transfer consumes only the fee but still increments the nonce;
  • order can determine whether a set of individually well-formed transactions is valid;
  • any uint64 receiver-credit overflow invalidates the whole candidate.

After all normal transactions validate, the coinbase value is credited to the miner. Coinbase funds therefore cannot be spent inside the block that creates them.

No partial state is published if any transaction fails. Validation is an all-or-nothing transition.

Acceptance sequence

The implemented acceptance path is serialized by a state-transition lock. Conceptually it performs:

  1. validate the candidate against the current tip and a temporary account view;
  2. append the exact block record to the active blkNNNNNN.dat file;
  3. publish height and transaction-location indexes;
  4. commit updated accounts and chain metadata;
  5. remove confirmed and conflicting pending transactions from Redis;
  6. notify relay and optional explorer-index components.

Disk append and indexing include narrow crash-recovery logic described in State, mempool & storage. This does not turn the overall alpha into a transactional database across RocksDB, block files, Redis, and SQLite.

Genesis validation

Genesis is not received and accepted as an arbitrary next block. The node selects a built-in manifest for the requested network, constructs the canonical block, verifies its expected ID and proof, and initializes an empty data directory.

Known alpha IDs are:

testnet  0000009e2bcc8e56cee8b5e0fbdac9edc04513542af5c5791ea6165179c468bf
devnet   00000033e4111007fd85423c7a527595e8efefe158a1250db62a32870857ef91

These identify the current educational chains. They are not promises that pre-release balances will survive later resets.

Linear-chain consensus boundary

The alpha validates only the next block extending its current local tip. It does not implement:

  • side-branch storage;
  • cumulative-chain-work comparison;
  • a fork-choice rule;
  • reorganizations or state rollback;
  • orphan-block pools;
  • header-first synchronization;
  • probabilistic or economic finality.

Consequently, “confirmations” means depth in one node’s currently stored linear history. It must not be interpreted as public-chain finality. Two nodes that accept different valid children of the same height have no implemented mechanism to converge automatically.

This is the most important limitation in the current protocol. Production-network work must define branch data structures, cumulative work, deterministic fork choice, reorg-safe state and indexes, mempool resurrection, and adversarial tests before claiming decentralized consensus.

Independent implementation checklist

An implementation is not block-compatible until it can prove all of the following:

  • exact 118-byte transmitted header;
  • ID preimage excludes the declared ID and includes tx_count_le_u16;
  • timestamp uses big-endian while other listed integers retain their specified order;
  • block size is calculated from canonical serialized bytes;
  • transaction order is preserved during hashing and validation;
  • duplicate transaction IDs and misplaced coinbase are rejected;
  • expected bits, PoW boundary, predecessor, height, and timestamps are contextual;
  • normal transfers are applied before the coinbase credit;
  • all monetary arithmetic rejects overflow;
  • unknown versions and trailing data fail closed.

Use the Python tests as executable consensus fixtures, especially regression cases at size, timestamp, overflow, target, duplicate, and coinbase boundaries.

Source anchors

Primary implementation files used for this chapter: