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
| Field | Type | Description |
|---|---|---|
version | Int | Block format version (currently 1) |
chain_id | Int | 0 for test chain, 1 for main chain |
height | Int | Monotonically increasing block number |
timestamp | Int | Unix timestamp in seconds |
previous_block_hash | Bytes (32) | BLAKE3 hash of the previous block |
nonce | Int | Nonce satisfying the difficulty target |
difficulty | Int | Number of required leading zero bits in block hash |
validator_public_key_bytes | Bytes (32) | Ed25519 public key of the block creator |
signature | Bytes (64) | Ed25519 signature over the block body |
body_hash | Bytes (32) | BLAKE3 hash of the block body expression (computed from the block body expression) |
accounts_hash | Bytes (32) | Root hash of the accounts radix trie after applying this block |
transactions_hash | Bytes (32) | Hash of the transactions list expression |
receipts_hash | Bytes (32) | Hash of the receipts list expression |
bloom_hash | Bytes (32) | Root hash of the bloom tree for transaction search indexing |
previous_era_hash | Bytes (32) | Hash of the previous era's bloom tree root (for era-skipping search) |
total_transaction_fee | Int | Sum of transaction fees in this block |
total_storage_fee | Int | Sum of storage fees in this block |
total_mint | Int | Total 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
| Field | Type | Description |
|---|---|---|
version | Int | Transaction format version (currently 1) |
chain_id | Int | Chain this transaction targets (must match block) |
amount | Int | Amount transferred to recipient |
sender | Bytes (32) | Ed25519 public key of the sender |
recipient | Bytes (32) | Ed25519 public key of the recipient (or contract address) |
counter | Int | Sender's nonce — must match the account's current counter for replay protection |
cost_limit | Int | Maximum total meter cost the sender will pay |
code | TransactionCode (IntEnum) | Transaction type (see table below) |
data | Expr | Arbitrary expression payload (code for deploy/call) |
signature | Bytes (64) | Ed25519 signature over the transaction body hash |
body_hash | Bytes (32) | BLAKE3 hash of the serialized body (computed, stored for verification) |
Transaction codes
| Code | Name | Subsystem | Description |
|---|---|---|---|
0x00 | TRANSFER | Core | Move amount from sender to recipient without code execution. Recipient account is created when absent. |
0x10 | CHANNEL_UPDATE | Payment Channel | Update channel state with a signed payload. recipient must equal sender. |
0x11 | CHANNEL_WITHDRAW | Payment Channel | Withdraw party balance from a channel after timeout. Sets amount to 0. |
0x12 | CHANNEL_CLOSE | Payment Channel | Close a channel and settle final balances. recipient must equal sender. |
0x20 | TREASURY_DEPOSIT | Treasury | Deposit stake into the treasury account. Updates the sender's stake record. |
0x21 | TREASURY_BORROW | Treasury | Borrow tokens from the treasury against stake. Sets amount to 0 and handles loan creation. |
0x22 | TREASURY_REPAY | Treasury | Repay tokens to the treasury. recipient must be the treasury address. |
0x23 | TREASURY_CLOSE | Treasury | Close a treasury position and reclaim stake. |
0x30 | STORAGE_CREATE | Storage | Create a storage contract. recipient must be the storage address. amount must be 0. |
0x31 | STORAGE_PAYMENT | Storage | Pay for storage. recipient must be the storage address. May mint tokens for providers. |
0x32 | STORAGE_REMOVE | Storage | Remove a storage contract. Sets amount to 0. |
0x40 | CODE_ACCOUNT_CREATE | Code Account | Store data as the recipient account's code expression. Creates the account if needed. |
0x41 | CODE_ACCOUNT_CALL | Code Account | Execute 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
| Field | Type | Description |
|---|---|---|
version | Int | Receipt format version (currently 1) |
transaction_hash | Bytes (32) | BLAKE3 hash of the transaction that produced this receipt |
transaction_fee | Int | Base transaction fee charged |
storage_fee | Int | Storage fee for on-chain data writes |
data_fee | Int | Fee for the data payload size |
execution_fee | Int | Fee for VM execution metering |
status | Int | 0 = success, 1 = failure |
logs_hash | Bytes (32) | BLAKE3 hash of the transaction's log expression |
mint | Int | Tokens 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
| Field | Type | Description |
|---|---|---|
balance | Int | Current token balance |
counter | Int | Current nonce — incremented with each outgoing transaction |
code_hash | Bytes (32) | BLAKE3 hash of the account's deployed code expression (or zero) |
data_hash | Bytes (32) | Root hash of the account's key-value data store (a radix trie) |
channels_hash | Bytes (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
| Field | Type | Description |
|---|---|---|
head | Bytes (32) | Block hash of this fork's tip |
root | Bytes (32) | Genesis block hash for this chain |
verified_up_to | Bytes (32) | Hash of the earliest verified ancestor block on this fork |
chain_fork_position | Bytes (32) | Block hash where this fork diverged from the main chain |
reached_genesis | Bool | Whether verification traced back to the genesis block |
malicious_block_hash | Bytes (32) | If verification failed, the hash of the first invalid block |
peers | Set | Set 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. - Operations —
getwalks the trie by nibble;putinserts or updates and returns a new root with structural sharing;clonecreates 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
Exprobject. - Hash pointers — link pairs can carry
head_hash/tail_hashinstead of inlined sub-expressions. This enables lazy DAG traversal without loading the full tree. - Lazy resolution —
resolve_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 (
0x00for link,0x01for symbol,0x02for bytes). Composed types (int, str, floats) serialize aslink(bytes(payload), symbol(tag)).
