Learn · 04

The MERA protocol,
reference edition.

A complete description of the MLabChain v0.3 consensus rules, monetary rules, and wire format. Every number here is enforced by the C++ core; every formula is integer arithmetic. This is the specification a re-implementation would need to interoperate with the reference node.

Protocol overview

MLabChain v0.3 is a signed, replay-validated ledger for deterministic machine-learning work. The protocol has two independent mechanisms that are deliberately kept apart:

  • Chain sealing. A low-difficulty SHA-256 Proof-of-Work combined with an Ed25519 signature from the block producer. This is the Sybil-resistance signal and the ordering mechanism.
  • Scientific work. A separate transaction type, ML_WORK, that commits a challenge, a model, an architecture, and metrics. This is the monetary mechanism.

Changing the ML verifier does not affect block sealing. Changing the PoW difficulty does not affect ML rewards. This separation is what allows the protocol to make honest claims about each piece.

Protocol version

PROTOCOL_VERSION = 3. A node refuses to open a SQLite database whose schema_version does not match. Migration from v0.2 requires an explicit snapshot ceremony described in Architecture § Migration.

Challenge registry

A challenge is an on-chain object that pins a scientific task. Any account can register a challenge, subject to a bond and a per-proposer limit. Once registered, the challenge's parameters are immutable.

Registered fields

FieldTypeMeaning
challenge_idstringUnique identifier
manifest_hashhex64SHA-256 of the canonical manifest
dataset_hashhex64Commitment to the dataset
verifierstringVerifier class (MLabChain-linear-v4)
metricstringNMSE
split_rulestringfirst_fraction
n_samplesu64Total samples in the dataset
n_featuresu64Feature dimensionality
train_fraction_ppmu64Train fraction in ppm (e.g. 800000 = 0.8)
max_epochsu64Bound on submitted training runs
baseline_scaledu64Baseline NMSE × 1e9 (fixed at 1e9)
budgetu64Finite Mera budget, in atomic units
created_heightu64Block height at registration
activation_heightu64created_height + CHALLENGE_MATURITY
expiry_heightu64created_height + CHALLENGE_LIFETIME

Lifecycle

  1. Registration. A CHALLENGE_REGISTER transaction pays the bond and creates the challenge record. It is not active yet.
  2. Maturity. After CHALLENGE_MATURITY = 8 blocks, the challenge accepts ML_WORK submissions.
  3. Active. Until expiry_height, submissions that improve the frontier are paid from the challenge budget.
  4. Closed. After expiry, no further submissions are accepted. The remaining budget is not reclaimed — it stays reserved for the challenge forever.

Anti-farming constraints

  • Minimum n_samples: 1000
  • Maximum n_samples: 100,000,000
  • Maximum n_features: 4096
  • train_fraction_ppm in [100000, 999999]
  • Maximum max_epochs: 100,000
  • Per-proposer active challenges: at most 8

ML_WORK transaction

An ML_WORK transaction is the sole path to monetary issuance. It commits the training claim and the metadata that every node uses to validate it.

Fields

FieldMeaning
senderSubmitting account address
challenge_idThe registered challenge
manifest_hashMust match the registered challenge
dataset_hashMust match the registered challenge
model_hashSHA-256 of the trained model artifact
arch_hashSHA-256 of the architecture description
opsSymbolic operation count (must match the formula)
n_trainDerived from n_samples × train_fraction_ppm
n_featuresMust match the challenge
epochsMust be ≤ challenge.max_epochs
nmse_scaledModel NMSE × 1e9
baseline_scaledMust match challenge.baseline_scaled
work_rewardClaimed reward in atomic units
wall_msReported training time (audit only)
lswu_microLSWU score × 1e6 (audit only)
nonceAccount nonce
feeWork fee in atomic units
public_keyEd25519 public key of the sender
signatureEd25519 signature over the canonical message

Canonical signing message

ML_WORK-V3|sender|challenge_id|manifest_hash|dataset_hash|
model_hash|arch_hash|ops|n_train|n_features|epochs|
nmse_scaled|baseline_scaled|work_reward|wall_ms|lswu_micro|
nonce|fee|public_key|note

Fields are joined with |. The signature is Ed25519 over the UTF-8 bytes of that string. The transaction ID is SHA256(message + "|" + signature).

Reward arithmetic

Rewards are computed in integer arithmetic in the C++ core. No floating-point value is ever used in a state transition. This is deliberate: a two-node network must agree on the exact issuance, byte for byte, block for block.

Quality

The improvement of a model over the baseline is scaled to a million parts:

# fixed-point NMSE, scaled by 1e9
quality_ppm = clamp(
    (baseline_scaled − nmse_scaled) × 1,000,000 / baseline_scaled,
    0,
    1,000,000
)

Marginal delta

Reward is measured against the challenge's current best accepted quality, not against the original baseline:

delta_q = quality_ppm − challenge.best_quality_ppm

If delta_q ≤ 0, the submission is rejected with "ML result does not improve challenge best". There is no partial credit for matching or falling short.

Reward

reward = min(
    MAX_WORK_REWARD,
    ops × delta_q × ATOMIC_PER_MERA / (OPS_PER_MERA × 1,000,000)
)

With ATOMIC_PER_MERA = 100,000,000, OPS_PER_MERA = 10,000,000, and MAX_WORK_REWARD = 50 × ATOMIC_PER_MERA.

Constraint stack

A valid reward must satisfy all of:

  • reward == expected_reward(ops, delta_q) (exact match)
  • reward ≤ challenge.budget_remaining
  • total_supply + reward ≤ MAX_SUPPLY
  • ops == epochs × n_train × (3 × n_features + 4)
  • The claim (challenge, dataset, model) has not been paid before
What the core does not verify

The C++ core checks that the reward is arithmetically consistent with the submitted metrics. It does not check that the metrics are honest. A malicious submitter could sign an ML_WORK transaction with a fabricated nmse_scaled and pass the core's checks. The only defence is the Python verify-ml audit, which re-executes the training. See Proof-of-Scientific-Work.

Accounts and nonces

Accounts are identified by an address derived from an Ed25519 public key:

# Standard account
payload = SHA256(public_key_bytes)[:40]
checksum = SHA256("MERA-ADDR-V3|MERA1" + payload)[:8]
address = "MERA1" + payload + checksum

# Multisig account
sorted_keys = sort(public_keys)
material = "MSIG|" + threshold + "|" + join(sorted_keys, ",")
payload = SHA256(material)[:40]
checksum = SHA256("MERA-ADDR-V3|MERA2" + payload)[:8]
address = "MERA2" + payload + checksum

Every account has a monotonically increasing nonce. A transaction from an account is valid only if tx.nonce == account.nonce + pending_count + 1, so the mempool can hold multiple pending transactions from the same sender without ambiguity.

Key rotation

A KEY_ROTATE transaction changes the authorized public key without changing the address. After rotation, only the new key can authorise transfers, block proposals, and ML submissions from that account.

Multisig

A MULTISIG_TRANSFER requires m signatures from an n-key set. Keys must be sorted canonically and unique. The threshold and key set are included in the transaction and checked against the derived address.

Blocks and fork choice

A block header commits to the previous block, the Merkle root of its transactions, the producer address, the producer's Ed25519 signature, and the cumulative chain work.

# Block header (canonical form)
MERA-BLOCK-V3|height|timestamp_ms|previous_hash|merkle_root|
difficulty|nonce|producer|producer_pubkey|producer_signature|
total_ops|total_rewards

# Block hash
block_hash = SHA256(header)

Producer signature

The producer signs a message that excludes the nonce and the block hash, so the signature is stable while the nonce is being ground:

MERA-BLOCK-PROPOSAL-V3|height|timestamp_ms|previous_hash|
merkle_root|difficulty|producer|producer_pubkey|
total_ops|total_rewards

Fork choice

Nodes accept the chain with the highest cumulative work. Ties break by lower tip hash. The rule is applied as:

  • Compute chain_work as the sum of 2^(4 × difficulty) over all blocks.
  • A candidate chain is valid only if it replays from genesis without error.
  • If candidate.work > local.work, replace. If equal, lower tip hash wins.
  • On replacement, orphaned transactions from pending are re-added to the mempool.

Timestamp rules

  • Must be strictly greater than the median of the last 11 block timestamps.
  • Must be no more than MAX_FUTURE_MS = 120,000 ms ahead of the node's clock.

Difficulty retarget

Every DIFFICULTY_WINDOW = 16 blocks, the difficulty is adjusted toward a target block time of TARGET_BLOCK_MS = 60,000. The adjustment is bounded to a single step of ±1 and clamped to [1, 8].

target_span = TARGET_BLOCK_MS × DIFFICULTY_WINDOW
elapsed     = block[parent].timestamp_ms − block[start].timestamp_ms

if elapsed < target_span / 2:
    difficulty += 1
elif elapsed > target_span × 2:
    difficulty -= 1

The retarget is deterministic: given the same chain, every node computes the same difficulty for the next block. This is required for consensus and is checked during validation.

Governance timelock

A governance multisig, defined at genesis, can schedule changes to a bounded set of parameters. The change takes effect after a delay of GOVERNANCE_TIMELOCK = 32 blocks.

Modifiable parameters

  • min_fee
  • min_work_fee
  • challenge_bond

Immutable parameters

  • Maximum supply
  • Faucet enablement
  • Reward formula constants
  • Block size and transaction size limits

The immutable set is enforced by the code: there is no GOV_PARAM key that can modify them.

Wire protocol

MLabChain's P2P protocol is a line-based request/response. Each message is a single line terminated by \n. Fields within a message are separated by |, and any field that may contain | is hex-encoded before transport.

MessageRequestResponse
PINGPINGPONG
STATUSSTATUSSTATUS|height|tip|chain_work
GETCHAINGETCHAINSequence of BLOCK|wire, ending with END
TX relayTX|wireOK or ERROR|reason
BLOCK relayBLOCK|wireOK, SYNC_REQUIRED, or ERROR|reason

The wire format is deliberately simple: no framing, no encryption, no version negotiation, no peer authentication. The reference protocol is a testnet implementation. See Security Notes for what a production protocol would need to add.

Wire encoding for transactions

A transaction is serialised as 36 hex-encoded fields separated by |. The full order is:

type|sender|recipient|amount|fee|nonce|challenge_id|
manifest_hash|dataset_hash|model_hash|arch_hash|ops|
n_train|n_features|epochs|nmse_scaled|baseline_scaled|
work_reward|wall_ms|lswu_micro|public_key|signature|note|
train_fraction_ppm|max_epochs|budget|challenge_verifier|
challenge_metric|challenge_split_rule|new_public_key|
multisig_threshold|multisig_keys|multisig_sigs|
gov_key|gov_value|gov_effective_height

Protocol constants

Supply and decimals

ConstantValue
ATOMIC_PER_MERA100,000,000
MAX_SUPPLY100,000,000 MERA
OPS_PER_MERA10,000,000
MAX_WORK_REWARD50 MERA

Fees

ConstantDevnetTestnet / Mainnet
MIN_TX_FEE1,00010,000
MIN_WORK_FEE10,000100,000
CHALLENGE_BOND100,0001,000,000

Limits

ConstantValue
MAX_BLOCK_TX2,048
MAX_MEMPOOL_TX20,000
MAX_PENDING_PER_SENDER32
MAX_TX_BYTES65,536
MAX_BLOCK_BYTES1,048,576
MAX_NOTE_BYTES4,096
MAX_OPS_PER_WORK10^15
MAX_CHALLENGE_BUDGET1,000 MERA
MAX_SIGNERS16

Timing

ConstantValue
TARGET_BLOCK_MS60,000
DIFFICULTY_WINDOW16
CHALLENGE_MATURITY8 blocks
CHALLENGE_LIFETIME1,000 blocks
GOVERNANCE_TIMELOCK32 blocks
MAX_FUTURE_MS120,000

Difficulty bounds

ConstantValue
BASE_DIFFICULTY2
MIN_DIFFICULTY1
MAX_DIFFICULTY8
Reference implementation

The authoritative implementation of every constant and every rule on this page is cpp/mera_core.cpp. When the code and this page disagree, the code is correct and this page is a bug report waiting to happen.