Astreum

BLOCKCHAIN DATA MODEL

Data Model

Every chain object in Astreum is a content-addressed expression. Blocks, transactions, receipts, and accounts all serialize to the same Expr tree, using BLAKE3 hashing for content identity and link pairs for structure.

Block

A block is a signed header linking to a body of transactions and receipts. It serializes as (body sig 'block) where the body is a 16-field link chain.

Block fields

FieldTypeDescription
versionIntBlock format version (currently 1)
chain_idInt0 for test chain, 1 for main chain
heightIntMonotonically increasing block number
timestampIntUnix timestamp in seconds
previous_block_hashBytes (32)BLAKE3 hash of the previous block
nonceIntNonce satisfying the difficulty target
difficultyIntNumber of required leading zero bits in block hash
validator_public_key_bytesBytes (32)Ed25519 public key of the block creator
signatureBytes (64)Ed25519 signature over the block body
body_hashBytes (32)BLAKE3 hash of the block body expression (computed from the block body expression)
accounts_hashBytes (32)Root hash of the accounts radix trie after applying this block
transactions_hashBytes (32)Hash of the transactions list expression
receipts_hashBytes (32)Hash of the receipts list expression
bloom_hashBytes (32)Root hash of the bloom tree for transaction search indexing
previous_era_hashBytes (32)Hash of the previous era's bloom tree root (for era-skipping search)
total_transaction_feeIntSum of transaction fees in this block
total_storage_feeIntSum of storage fees in this block
total_mintIntTotal newly minted tokens in this block
statistics[(Int, Int)] or [(Int, Int, Int, Int)]Era-level fee and stake statistics. First era is a 2-tuple (total_fee, stake); subsequent eras are 4-tuples (prev_fee, prev_stake, cum_fee, cum_stake).

Block serialization

The block body is a 16-element link chain in this order: (version accounts_hash bloom_hash chain_id difficulty height nonce previous_block_hash previous_era_hash receipts_hash timestamp total_storage_fee total_transaction_fee transactions_hash validator_pk statistics). The full block wraps this as (body signature 'block).

The block's identity (expr_id) is the BLAKE3 hash of the entire expression. The body_hash is the hash of the body link chain alone (before signing).

Transaction

A signed instruction to transfer value, execute code, or store data. Each transaction is an expression tagged transaction.

Transaction fields

FieldTypeDescription
versionIntTransaction format version (currently 1)
chain_idIntChain this transaction targets (must match block)
amountIntAmount transferred to recipient
senderBytes (32)Ed25519 public key of the sender
recipientBytes (32)Ed25519 public key of the recipient (or contract address)
counterIntSender's nonce — must match the account's current counter for replay protection
cost_limitIntMaximum total meter cost the sender will pay
codeTransactionCode (IntEnum)Transaction type (see table below)
dataExprArbitrary expression payload (code for deploy/call)
signatureBytes (64)Ed25519 signature over the transaction body hash
body_hashBytes (32)BLAKE3 hash of the serialized body (computed, stored for verification)

Transaction codes

CodeNameSubsystemDescription
0x00TRANSFERCoreMove amount from sender to recipient without code execution. Recipient account is created when absent.
0x10CHANNEL_UPDATEPayment ChannelUpdate channel state with a signed payload. recipient must equal sender.
0x11CHANNEL_WITHDRAWPayment ChannelWithdraw party balance from a channel after timeout. Sets amount to 0.
0x12CHANNEL_CLOSEPayment ChannelClose a channel and settle final balances. recipient must equal sender.
0x20TREASURY_DEPOSITTreasuryDeposit stake into the treasury account. Updates the sender's stake record.
0x21TREASURY_BORROWTreasuryBorrow tokens from the treasury against stake. Sets amount to 0 and handles loan creation.
0x22TREASURY_REPAYTreasuryRepay tokens to the treasury. recipient must be the treasury address.
0x23TREASURY_CLOSETreasuryClose a treasury position and reclaim stake.
0x30STORAGE_CREATEStorageCreate a storage contract. recipient must be the storage address. amount must be 0.
0x31STORAGE_PAYMENTStoragePay for storage. recipient must be the storage address. May mint tokens for providers.
0x32STORAGE_REMOVEStorageRemove a storage contract. Sets amount to 0.
0x40CODE_ACCOUNT_CREATECode AccountStore data as the recipient account's code expression. Creates the account if needed.
0x41CODE_ACCOUNT_CALLCode AccountExecute data against the recipient account's VM. The recipient account must exist.

Signing

To sign, the sender serializes the body fields in order: (version amount chain_id code cost_limit counter data recipient sender) — and signs the BLAKE3 hash of this expression with their Ed25519 private key. The signature is 64 bytes.

Receipt

After a transaction executes, a receipt records the outcome. Receipts are stored in a radix trie keyed by transaction hash.

Receipt fields

FieldTypeDescription
versionIntReceipt format version (currently 1)
transaction_hashBytes (32)BLAKE3 hash of the transaction that produced this receipt
transaction_feeIntBase transaction fee charged
storage_feeIntStorage fee for on-chain data writes
data_feeIntFee for the data payload size
execution_feeIntFee for VM execution metering
statusInt0 = success, 1 = failure
logs_hashBytes (32)BLAKE3 hash of the transaction's log expression
mintIntTokens minted by this transaction

A receipt's total fee is the sum of all four fee components. The receipt serializes as (body 'receipt) where the body is a 9-field link chain: (version mint data_fee execution_fee logs_hash status storage_fee transaction_fee transaction_hash).

Account

Every account is an expression with five fields. Accounts are stored in a persistent radix trie keyed by the 32-byte Ed25519 public key (address).

Account fields

FieldTypeDescription
balanceIntCurrent token balance
counterIntCurrent nonce — incremented with each outgoing transaction
code_hashBytes (32)BLAKE3 hash of the account's deployed code expression (or zero)
data_hashBytes (32)Root hash of the account's key-value data store (a radix trie)
channels_hashBytes (32)Root hash of the account's payment channels (a radix trie)

The account serializes as a 5-element link chain: (data_hash counter code_hash channels_hash balance).

Special addresses

  • 0x0101...01 (32 bytes of 0x01) — Treasury address. The built-in treasury account.
  • 0x0000...00 (32 bytes of 0x00) — Storage address. The account managing storage contracts and advertisements.

Fork

A fork is a candidate chain tip tracked by the verification worker. The node maintains a map of known fork heads and verifies them against each other and against stored blocks.

Fork fields

FieldTypeDescription
headBytes (32)Block hash of this fork's tip
rootBytes (32)Genesis block hash for this chain
verified_up_toBytes (32)Hash of the earliest verified ancestor block on this fork
chain_fork_positionBytes (32)Block hash where this fork diverged from the main chain
reached_genesisBoolWhether verification traced back to the genesis block
malicious_block_hashBytes (32)If verification failed, the hash of the first invalid block
peersSetSet of peer IDs following this fork head

Radix Trie

The radix (Patricia) trie is the persistent key-value store backing accounts, account data, payment channels, and receipt lookup.

  • Keys — arbitrary byte strings. Account addresses are 32-byte Ed25519 public keys. Data store keys are arbitrary bytes passed to acc.put.
  • Nodes — each trie node is an expression with a prefix (shared key nibbles), an optional value, and up to 16 child branches. Empty branches are omitted.
  • Root hash — the BLAKE3 hash of the root node's expression. Block headers store the accounts trie root hash as accounts_hash.
  • Operationsget walks the trie by nibble; put inserts or updates and returns a new root with structural sharing; clone creates a shallow copy for snapshot semantics.
  • Persistence — trie nodes are written to content-addressed storage alongside the block. The root hash in the block header is sufficient to reconstruct the full trie.

Content Addressing

  • All expressions are hashed with BLAKE3 (32-byte output). The hash is cached on the Expr object.
  • Hash pointers — link pairs can carry head_hash / tail_hash instead of inlined sub-expressions. This enables lazy DAG traversal without loading the full tree.
  • Lazy resolutionresolve_list_exprs(node, expr) follows hash pointers to materialize a link chain. Missing hashes are collected and returned.
  • Wire encoding — serialization uses a compact binary format with tag bytes (0x00 for link, 0x01 for symbol, 0x02 for bytes). Composed types (int, str, floats) serialize as link(bytes(payload), symbol(tag)).