BEDROCK-GENESIS-BLOCK
| Field | Value |
|---|---|
| Name | Bedrock Genesis Block |
| Slug | 90 |
| Status | raw |
| Category | Standards Track |
| Editor | David Rusu [email protected] |
| Contributors | Hong-Sheng Zhou, Thomas Lavaur [email protected], Marcin Pawlowski [email protected], Mehmet Gonen [email protected], Álvaro Castro-Castilla [email protected], Daniel Sanchez Quiros [email protected], Filip Dimitrijevic [email protected] |
Timeline
- 2026-09-11 —
b7301a6— [RFC] Mantle: Enable basic EmPoWering machinery (#400) - 2026-08-31 —
3cac48f— docs(blockchain): rename locked notes to service notes (#423) - 2026-08-26 —
eb5ce96— docs(blockchain): specify genesis block validation rule (#419) - 2026-08-19 —
a2a85cb— RFC Bedrock: Cryptarchia with uncle references (#385) - 2026-08-06 —
5ead1e7— docs(blockchain): encode genesis_time as u32 unix timestamp (#370) - 2026-07-03 —
709cf7f— Bedrock-RFC: Remove Concept of a Session (#365) - 2026-05-28 —
d45eed2— Chore: mirror blochain specs into github/mdbook (#347) - 2026-05-18 —
58b5698— chore(blockchain): migrate contributor emails to @logos.co (#338) - 2026-01-19 —
f24e567— Chore/updates mdbook (#262) - 2026-01-16 —
89f2ea8— Chore/mdbook updates (#258)
Revisions History
| Version | Changes | Date |
|---|---|---|
| 1.0.0 | Initial revision. | 2026-02-12 |
| 1.1.0 | [RFC] Make Ledger Transaction an Operation Renamed Nomos to Logos Blockchain Remove notions of DA Minor fix in gas price | 2026-03-27 |
| 1.1.1 | [RFC] Simplify Mantle Transaction and Refactor Ledger Operations | 2026-05-06 |
| 1.1.2 | Encode genesis_time as a u32 unix timestamp instead of an ISO 8601 datetime. Encode the chain_id length prefix as a u8 instead of a u64. | 2026-07-06 |
| 1.1.3 | Replaced the block_root header field with body_root, taken over an empty uncle header list and the initial transaction, due to updated Block Construction, Validation and Execution. | 2026-08-06 |
| 1.1.4 | Stated which validations apply when the Genesis Mantle Transaction is processed: the ordinary Mantle rules apply to every Operation, minus a closed list of exemptions that the absence of any state before Genesis makes impossible to satisfy. | 2026-08-25 |
| 1.1.5 | Renamed locked notes into service notes: the Blend declarations of the Genesis Mantle Transaction name a service_note_id | 2026-08-27 |
| 1.2.0 | Seed the pow reward pool at genesis | 2026-09-08 |
Introduction
The Genesis Block defines the starting state for the Bedrock chain, including the initial bedrock service providers, LGO token distribution and protocol parameters. Its design draws from best practices in the Ouroboros family of protocols (notably Praos and Genesis), as well as privacy and resilience advances from Cryptarchia and related research. The Genesis Block is the root of trust for all subsequent protocol operations and must be constructed in a way that is deterministic, verifiable, and robust against long-range or bootstrap attacks.
Overview
The Genesis Block establishes the initializing values for the various protocols and services. This includes the initial token distribution, initial nodes participating in Blend Network and the result of running the epoch nonce ceremony.
The block body is a single Mantle Transaction (see Mantle) containing a Transfer Operation distributing the notes to initial token holders. The bedrock services are initialized through SDP_DECLARE Operations embedded in the Mantle Transaction’s Operations list and protocol initializing constants are encoded through a CHANNEL_INSCRIBE Operation also embedded in the Operations list.
Not all protocol constants are encoded in the Genesis block. The principle we use to decide whether a value should be in the Genesis block or not is whether it is a value that is derived from blockchain activity or whether it is updated through a protocol update (hard / soft fork). For example, the epoch nonce is updated through normal blockchain Operations and therefore it should be specified in the Genesis block. Gas constants are only changed through protocol updates and hard forks and therefore they will be hardcoded in the node implementation.
Genesis Block Data Structure
The Genesis Block is composed of the Genesis Block Header and the Genesis Mantle Transaction (there is a single transaction in the genesis block). The Mantle Transaction contains all information necessary for initializing Bedrock Services and Cryptarchia state, as well as distributing the initial tokens to stakeholders.
Initial Token Distribution
Initial tokens will be distributed through a Transfer Operation containing zero inputs and one output note for each initial stakeholder. Note that since the Ledger is transparent, the initial stake allocation is visible to everyone. Those wishing to hide their initial stake may opt to subdivide their note into a few different notes of equal value.
In order to participate in the Cryptarchia lottery, stakeholders must generate their note keys in accordance with the Proof of Leadership protocol specified at Protocol.
The initial state of the Ledger will be derived through normal execution of this Transfer Operation, that is, each output’s note ID will be added to the unspent notes set.
Example
STAKE_DISTRIBUTION = Transfer(
inputs=[],
outputs=[
Note(value=1000, public_key=STAKE_HOLDER_0_PK),
Note(value=2000, public_key=STAKE_HOLDER_1_PK),
Note(value=1500, public_key=STAKE_HOLDER_2_PK),
# ...
]
)
Initial Proof of Work Reward Pool
The pow reward pool is seeded once, at genesis, with a fixed quantity of tokens:
POW_REWARD_POOL_GENESIS: TokenValue # Initial balance of the pow reward pool
# = 5/1000 of the maximum supply
The seed is five thousandths of the maximum supply , as specified in Reward Pool.
The pool holds a balance rather than notes, so unlike the stakeholder allocations above it does not appear as an output of the initial Transfer Operation. It is consensus state maintained as specified in Reward Pool, and after genesis it changes only through claims.
The seed is not part of the Cryptarchia parameter inscription, because it is not a Cryptarchia parameter. It is established during Mantle Ledger Initialization.
Initial Service Declarations
Blend Network MUST initialize its set of providers. This is done through a set of SDP_DECLARE Operations in the Genesis Mantle Transaction.
Blend enforces a minimal network size for the service to be active. Thus, in order to have an active Blend service at Genesis, we MUST have at least as many declarations in the Genesis block to meet Blend service’s minimal network size Minimal Network Size.
Example
BLEND_DECLARATIONS = [
Declaration(
msg=DeclarationMessage(
ServiceType.BLEND, ["ip://1.1.1.1:3000"], PROVIDER_ID_0, ZK_ID_0
),
service_note_id=STAKE_DISTRIBUTION_TX.output_note_id(0)
),
# ... 32 total declarations
]
SERVICE_DECLARATIONS = BLEND_DECLARATIONS
Cryptarchia Parameters
Cryptarchia is initialized with the following parameters:
-
genesis_time: u32 unix timestamp (seconds since the Unix epoch). A unix timestamp is conventionally ani64;genesis_timeis restricted to theu32range (0 to 2^32 - 1), which covers all plausible genesis dates (through February 2106). Cryptarchia uses slots as a measure of time offset from some start time. This timestamp must be agreed upon by all nodes in order to have a common clock. -
chain_id: UTF-8 string, encoded with a u8 length prefix (at most 255 bytes). It is useful to differentiate testnets from mainnet. To avoid confusion, we place the chain ID in the Genesis block to guarantee that the networks are disjoint. -
genesis_epoch_nonce: 32 bytes, hex encoded. The initial source of randomness for the Cryptarchia lottery. The process for selecting this value is described in detail at Epoch Nonce Ceremony.
These parameters are encoded in the Genesis block as an inscription sent to the null channel, signed by the null key. The null channel is the channel whose ChannelId is 32 zero bytes and the null key is the Ed25519 public key made of 32 zero bytes, written Ed25519PublicKey_ZERO in the examples below. No one holds the secret key behind it, so the null channel receives the Genesis inscription and nothing else ever after.
Example
CHAIN_ID = "logos-blockchain-mainnet"
GENESIS_TIME = 1767640835 # 2026-01-05T19:20:35Z
GENESIS_EPOCH_NONCE = "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
chain_id_enc = CHAIN_ID.encode("utf-8")
chain_id_len = len(chain_id_enc).to_bytes(1, "little")
genesis_time = GENESIS_TIME.to_bytes(4, "little")
genesis_epoch_nonce = bytes.fromhex(GENESIS_EPOCH_NONCE)
inscription = chain_id_len + chain_id_enc + genesis_time + genesis_epoch_nonce
# >>> inscription.hex()
# '186c6f676f732d626c6f636b636861696e2d6d61696e6e6574030f5c69abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'
CRYPTARCHIA_INSCRIPTION = Inscribe(
channel=bytes(32),
inscription=inscription,
parent=bytes(32),
signer=Ed25519PublicKey_ZERO,
)
Epoch Nonce Ceremony
The initial epoch nonce value governs the Cryptarchia lottery randomness for the first epoch. It must be revealed AFTER the initial stake distribution has been frozen. This is done to prevent any stakeholders from gaining an unfair advantage from prior knowledge of the lottery randomness.
The protocol for generating the initial randomness nonce can be found below.
- Schedule Epoch Nonce Ceremony Event:
We must fix well in advance when this epoch nonce ceremony will take place, let
tdenote the time of the Epoch Nonce Ceremony, broadcasttwidely.
The STAKE_DISTRIBUTION must be finalized before t to ensure a fair Cryptarchia slot lottery.
- Randomness Collection: We collect the entropy from multiple randomness sources:
| Entropy Source | Details |
|---|---|
Bitcoin block hash immediately after time t, denoted as . | Block hash can be found on blockchain.com ’s bitcoin block explorer, e.g. https://www.blockchain.com/explorer/blocks/btc/905030 |
Ethereum block hash immediately after time t, denoted as . | Block hash can be found in the more details section of when viewing a block on etherscan, e.g. https://etherscan.io/block/22894116 |
DRAND beacon value for the round immediately after t, denoted as . | Use the default beacon, and find the round number corresponding to t. https://api.drand.sh/v2/beacons/default/rounds/1234 |
- Randomness Derivation: Once all above entropy contributions, i.e., are collected, then we can compute the initial epoch randomness as:
where is a collision-resistant zkhash function.
Genesis Mantle Transaction
The initial stake distribution, service declarations and Cryptarchia inscription are components of the Genesis Mantle Transaction. This is the single transaction that forms the body of the Genesis block.
GENESIS_MANTLE_TX = MantleTx(
ops=[STAKE_DISTRIBUTION, CRYPTARCHIA_INSCRIPTION] + SERVICE_DECLARATIONS,
)
Block Header Fields
The Genesis Block header fields are set to the following values:
bedrock_version: Protocol version (e.g., 1).parent_block: 0 (as this is the first block).slot: 0 (the Genesis slot).body_root: the body commitment over an emptyuncle_headerslist (as the Genesis Block references no uncle, it encodes as a zero element count) and the Merkle root over the (single) initial transaction.proof_of_leadership: Stubbed leadership proof.leader_voucher: 0 (as there is no leader block reward for the initial block).entropy_contribution: 0 (no entropy is provided through the initial PoL).proof: Null Groth16Proof, all values are set to zero.leader_key: Null PublicKey.
Example
GENESIS_HEADER = Header(
bedrock_version=1,
parent_block=0,
slot=0,
body_root=body_root([], [GENESIS_MANTLE_TX]),
proof_of_leadership=ProofOfLeadership(
leader_voucher=bytes(32),
entropy_contribution=bytes(32),
proof=Groth16Proof(G1_ZERO, G2_ZERO, G1_ZERO),
leader_key=Ed25519PublicKey_ZERO,
)
)
# distribute NMO to all stakeholders
STAKE_DISTRIBUTION = Transfer(
inputs=[],
outputs=[
Note(value=1000, public_key=STAKE_HOLDER_0_PK),
Note(value=2000, public_key=STAKE_HOLDER_1_PK),
Note(value=1500, public_key=STAKE_HOLDER_2_PK),
# ...
]
)
# set Cryptarchia parameters
CHAIN_ID = "logos-blockchain-mainnet"
GENESIS_TIME = 1767640835 # 2026-01-05T19:20:35Z
GENESIS_EPOCH_NONCE = "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
chain_id_enc = CHAIN_ID.encode("utf-8")
inscription = (
len(chain_id_enc).to_bytes(1, "little")
+ chain_id_enc
+ GENESIS_TIME.to_bytes(4, "little")
+ bytes.fromhex(GENESIS_EPOCH_NONCE)
)
CRYPTARCHIA_INSCRIPTION = Inscribe(
channel=bytes(32),
inscription=inscription,
parent=bytes(32),
signer=Ed25519PublicKey_ZERO,
)
# service declarations
BLEND_DECLARATIONS = [
Declaration(
msg=DeclarationMessage(ServiceType.BLEND, ["ip://1.1.1.1:3000"], PROVIDER_ID_0, ZK_ID_0),
service_note_id=STAKE_DISTRIBUTION.output_note_id(0)
),
# ... more declarations
]
SERVICE_DECLARATIONS = BLEND_DECLARATIONS
# build the genesis Mantle Transaction
GENESIS_MANTLE_TX = MantleTx(
ops=[STAKE_DISTRIBUTION, CRYPTARCHIA_INSCRIPTION] + SERVICE_DECLARATIONS,
)
GENESIS_HEADER = Header(
bedrock_version=1,
parent_block=bytes(32),
slot=0,
body_root=body_root([], [GENESIS_MANTLE_TX]),
proof_of_leadership=ProofOfLeadership(
leader_voucher=bytes(32),
entropy_contribution=bytes(32),
proof=Groth16Proof(G1.ZERO, G2.ZERO, G1.ZERO),
leader_key=Ed25519PublicKey_ZERO,
)
)
GENESIS_BLOCK = (GENESIS_HEADER, [GENESIS_MANTLE_TX])
Sample Genesis Block
Initializing Bedrock
Bedrock is initialized by validating and executing the Genesis Mantle Transaction under the ordinary Mantle rules, Validation and Execution, with the exemptions listed in Genesis Validation Exemptions and no others. Its Operations are validated and executed one after the other in the order they appear, each against the state the preceding ones left, which is what makes the SDP_DECLARE Operations able to lock notes the Transfer Operation before them created.
The Genesis block is not a block proposal and is not validated as one: none of the checks of Block Proposal Validation apply, and no validation or execution is done for the Genesis block header, in particular processing of proof_of_leadership is skipped.
Validating the Genesis Mantle Transaction is not what makes the Genesis block trustworthy. The block is agreed upon out of band, every node starts from the same one, and a node that rejected it would have no chain to join. The checks are kept for two reasons: a malformed or inconsistent Genesis block is then reported when a node is set up rather than surfacing later as unexplained runtime behaviour, and Genesis stays on the ordinary Operation processing path instead of needing an unvalidated path of its own. This is why the exemptions below are a closed list rather than a general licence to skip validation.
The Genesis Mantle Transaction holds, in this order, the Transfer Operation distributing the initial tokens, the CHANNEL_INSCRIBE Operation carrying the Cryptarchia parameters, and one SDP_DECLARE Operation per initial service provider. A Genesis Mantle Transaction whose Operations do not follow that shape, or that holds an Operation of any other opcode, is invalid.
Genesis Validation Exemptions
The checks below, and only these, are skipped when the Genesis Mantle Transaction is processed. Each one is skipped because the Genesis block, having no state before it and no signer the chain knows about, cannot satisfy it.
-
Every proof and signature. No Operation proof is verified at Genesis: neither the
ZkSignatureof the Transfer Operation, nor theEd25519Signatureof the inscription, nor theDeclarationProofof theSDP_DECLAREOperations. The Transfer Operation consumes no note and therefore has no public key to verify against, the inscription is signed by the null key whose secret key nobody holds, and the keys a declaration would prove ownership of are already fixed by the out of band agreement on the Genesis block, so verifying them would establish nothing a node does not already have to trust. Theop_proofslist still holds one entry per Operation, of the type that Operation requires, and those entries are placeholders. -
The transaction balance covering the mandatory fees. The whole initial token supply is created out of nothing by the Transfer Operation, so the balance of the Genesis Mantle Transaction is negative and no fee can be paid from it. Step 3 of Validation is skipped, no mandatory fee is charged and no
tx_priority_tipis derived. The Genesis Mantle Transaction is accounted as costing no gas. -
The Transfer Operation inputs. The Genesis Transfer Operation has no inputs, no note existing before it, so the requirement that inputs be non-empty (Input Notes Spendability Validation) does not apply and there is no spendability to check. It is the only Transfer Operation of the chain allowed to consume nothing.
Everything else is validated as it would be in any other block, against the state the Operations preceding it left, the transaction level check that there is one op_proofs entry per Operation included.
Mantle Ledger Initialization
The Transfer Operation distributing the initial tokens is validated and executed as any other Transfer Operation, minus the two exemptions covering its inputs and the transaction balance. Its outputs are validated as Output Notes Validation requires. The result of normal transfer execution adds all outputs to the Ledger, their NoteId derived from the Operation as usual.
The pow reward pool is initialized at the same time:
pow_reward_poolis set toPOW_REWARD_POOL_GENESIS, as described in Initial Proof of Work Reward Pool.epoch_pow_rewardis derived from it by the computation given in Reward Pool, so that claiming is productive from the first epoch rather than waiting for the first refill.difficulty_blendis set toBLEND_DIFFICULTY_BASEfor epochs 0 and 1, as given in Blend Difficulty: the value for an epoch is fixed at the preceding epoch's nonce snapshot from the load of the epoch before that, and no complete input epoch exists before epoch 2. The schedule begins with epoch 2's value, computed during epoch 1 from epoch 0's load.difficulty_rewardis set to the genesis value given in Reward Difficulty, which is set hard so that the controller's first correction loosens rather than tightens, andpow_nullifiersis empty.
Cryptarchia Initialization
The Mantle Transaction contains an inscription sent to the null channel containing the parameters for initializing Cryptarchia. It is validated as an ordinary CHANNEL_INSCRIBE Operation minus its signature: the null channel does not exist yet, so the inscription must carry a parent of ZERO, and its execution creates that channel with the null key as its only accredited key.
Two conditions are specific to Genesis. The inscription must be addressed to the null channel and signed by the null key, an inscription anywhere else not being a set of Cryptarchia parameters. It must also decode to exactly the three parameters, encoded as Cryptarchia Parameters specifies and with no trailing bytes. A node that cannot decode them has no clock, no chain identifier and no lottery randomness, and must reject the Genesis block.
The Cryptarchia slot clock is initialized to genesis_time, LIB is set to the Genesis block and the epoch state is then initialized:
Initial Epoch State
Cryptarchia progresses in epochs where the variables governing the lottery are fixed for the duration of an epoch and the activity during that epoch is used to derive the values of those variables for the next epoch. These variables taken together are called the Epoch State. (see Epoch State).
To initialize the Epoch State, we derive the epoch variables from the genesis block.
- : the epoch nonce is taken directly from the
genesis_epoch_nonce. - : Eligible leader commitment is set to the the Ledger Root over all notes from the initial token distribution. The derivation of this root is specified in Ledger Root.
- : The initial estimate of total stake will be the total tokens distributed at genesis.
Bedrock Services Initialization
Blend network is initialized through normal Mantle Transaction execution. The SDP_DECLARE Operations in the Genesis Mantle Transaction will create the initial set of providers in each service.
Beyond their proofs, the declarations carry no exemption: each is validated as SDP_DECLARE requires, against the state the Operations preceding it left, and executed with created set to epoch 0, the epoch the Genesis block belongs to. The service note a declaration names is an output of the Transfer Operation that precedes it, which is why the Operation order of the Genesis Mantle Transaction is normative, and the minimum stake that note is measured against is the one the node implementation starts with, the Genesis block encoding no service parameter.
The number of declarations is a property of the Genesis block rather than of any single Operation, and is the one Initial Service Declarations requires.
During normal operations, Blend services would wait until a block is deep enough to be finalized, but for the Genesis block, we consider it finalized by definition and so Blend will immediately use the provider set without the usual finalization delay.
References
- Ouroboros Praos: https://eprint.iacr.org/2017/573.pdf
- Ouroboros Genesis: https://eprint.iacr.org/2018/378.pdf
- Ouroboros Crypsinous: https://eprint.iacr.org/2018/1132.pdf
- Cardano Shelley Genesis File Format: https://cardano-course.gitbook.io/cardano-course/handbook/protocol-parameters-and-configuration-files/shelley-genesis-file
- Cardano CIP-16 Key Serialisation: https://cips.cardano.org/cip/CIP-16