/ Documentation

Asq Documentation

Asq is a permissioned blockchain whose state machine is a SQLite database. Every validator runs the same binary, applies the same transactions in the same order, and arrives at the same state. The state is directly queryable with standard SQL tools.

Key Concepts

  • Accounts are asq1... Bech32m addresses derived from Ed25519 public keys.
  • Transactions are signed with Ed25519 and carry a sequential nonce.
  • Blocks finalize via BLS-aggregate quorum certificates. No reorgs.
  • State is a SQLite database. Every mutation emits a leaf into a Jellyfish Merkle Tree.
  • Extensions are compiled Rust crates that own their schema and apply logic.
  • Fees are fixed per tx type, paid in the native ASQ token.
  • Subaccounts: (master, subaccount: u32). One key, many isolated accounts.

Consensus: HotStuff-2

Asq uses HotStuff-2, a two-phase responsive BFT protocol. Validators identify with Ed25519 keys (chain identity) and BLS12-381 keys (consensus voting). The validator set is permissioned and known at genesis.

Block lifecycle

  1. The current leader pulls transactions from the mempool.
  2. The leader builds a block, applies it locally, computes the state root.
  3. The leader broadcasts the proposed block to all validators.
  4. Each validator verifies, applies, compares the state root, and votes.
  5. At 2f+1 votes the leader forms a quorum certificate (QC).
  6. QC is broadcast. All nodes finalize. No further reorgs.

Pacemaker

If the leader fails to propose within the timeout, validators send timeout votes. At supermajority the view advances to a new leader. Base timeout: 200ms (production).

State Machine: SQLite

The entire chain state lives in a single SQLite database per node. All tables use STRICT typing and CHECK constraints. SQLite runs in WAL mode: many concurrent readers alongside one writer. HTTP reads never block block-application.

Each block commits as one SQLite transaction. A block either lands in full or rolls back cleanly.

State Commitment: JMT

Every state mutation emits a state_changes row. At block end, all changes drain into a Jellyfish Merkle Tree. The root hash is the block's state root.

Leaf keys are namespaced: bal:{token}:{owner}, tok:{id}, obk:{market_id}, oracle:{feed_id}, cep:{cep_id}. Two nodes that disagree on any leaf produce different roots; the block is rejected.

Extensions

Each extension implements ChainExtension and owns its schema + tx variants. Registration order is fixed:

// dependency-aware order
tokens       // balances, supply, lock/unlock
bridge       // Solana bridge (depends on tokens)
oracle       // fair-value feeds (no deps)
orderbook    // Bid exchange (depends on tokens, reads oracle)
cep          // automation (depends on tokens, reads oracle)

New extensions land via UpgradeBinary. No arbitrary smart-contract deployment.

Transaction Lifecycle

1. Signing

Client constructs a Transaction, pairs with Ed25519 key + nonce, signs the canonical BCS encoding (includes chain_id).

{
  "transaction": { "type": "Transfer", "token_id": "ASQ", "to": "asq1...", "amount": 1000 },
  "signer": "a1b2...hex-pubkey",
  "nonce": 42,
  "signature": "d4e5...hex-sig"
}

2. Submission and Ingress

Client sends POST /tx to any node. The node:

  1. Validates the Ed25519 signature.
  2. Canonicalizes addresses (hex pubkey to asq1...).
  3. If validator: admits to local mempool.
  4. Forwards to all peers.

Response is immediate: "accepted" (queued) or "rejected" (with reason). Acceptance means queued, not confirmed.

3. Mempool and Admission

  • Stale nonce (< expected): rejected.
  • Current nonce (== expected): full validation (simulated apply, rolled back). Failures surface at submit time.
  • Future nonce (within 1024-tx window): accepted without full validation. The leader validates at packing time. This is the MM burst path.
  • Out-of-window: rejected.
Per-signer cap: 1024 pending entries per account. Bounds one-account mempool flooding.

4. Block Packing

The leader's select_executable_candidates:

  1. Priority-sorts by tx type (stable, preserves per-signer nonce order):
    • Class 0: OracleUpdate
    • Class 1: Cancel / Modify
    • Class 2: PlaceLimit { PostOnly }
    • Class 3: everything else
  2. Each candidate wrapped in a SAVEPOINT. Success: release + include. Failure: rollback + reject/defer.
  3. Stops at 128 accepted transactions.
Why priority sort matters: MM cancel+repost lands before taker aggression in the same block. The taker fills against new quotes, not stale ones.

5. Block Application

Both leader (at propose) and followers (at verify) use the same path:

BEGIN SQLite transaction │ ├─ dispatch_begin_block // extension setup hooks │ ├─ apply_transactions // iterate txs in block order │ ├─ nonce check + fee debit │ ├─ dispatch to claiming extension │ └─ emit state_changes │ ├─ credit leader fees │ ├─ dispatch_end_block // extension end-of-block work │ ├─ orderbook: native-MM seed refresh │ ├─ orderbook: stop-order trigger evaluation │ ├─ orderbook: book cache flush │ └─ cep: predicate evaluation + action firing │ ├─ drain_state_changes into JMT // compute state root │ └─ store block row COMMIT SQLite transaction

If a follower's state root differs from the leader's, the block is rejected.

6. Voting and Finality

  1. Leader signs the block (state root in the hash).
  2. Broadcasts proposal to all validators.
  3. Each validator applies independently, verifies root, sends BLS vote.
  4. At 2f+1 votes: quorum certificate (QC) formed.
  5. QC broadcast. Every node finalizes.

Finality is instant. Once a block has a QC it is permanent. No probabilistic finality, no confirmation count, no reorgs.

Tokens and NFTs

First-class chain objects. CreateToken registers with supply, decimals, optional mint authority, max supply, collection. Transfer, Mint, Burn operate on balances. Every balance has (liquid, locked): liquid is free, locked is reserved by orders/stops/CEPs.

Bid Exchange

On-chain CLOB. CreateMarket sets base/quote tokens, tick/lot size, fees.

Order types

  • PlaceLimit: GTC, IOC, FOK, PostOnly.
  • Cancel / CancelAll / CancelByCloid / Modify
  • PlaceStop: conditional limit triggered by oracle feed.

Native market making

ConstantProduct (x*y=k) or OracleSpread (tracks feed with staleness-aware widening). Both post PostOnly and take inventory risk. Refresh at end_block.

Caps: 200 resting orders per account per market. 200 stops per account.

Oracle Feeds

RegisterOracleFeed binds a feed to a publisher. OracleUpdate publishes nonce-ordered values. Fee: 0 ASQ. Consumed by orderbook PropAMM, stops, CEPs, and future perp/prediction-market resolution.

CEPs (Conditional Execution Programs)

Register a predicate + action. Chain evaluates every block, fires when true.

Predicates: OracleValue, BlockHeight, BlockTimestamp, AccountBalance, And/Or/Not (max 8 leaves, depth 4).

Actions: Transfer (v1). PlaceLimit / Cancel (v2).

Caps: 100/account, 10k chain-wide. Lock budget per max_fires.

Stop Orders

Conditional limit orders triggered at end_block when oracle value crosses threshold. Comparators: >= and <=. Inventory locked at placement. Fires through the same apply_place_limit path users use.

Session Signing

SessionOpen delegates a session key for scoped orderbook ops (place, cancel, modify). One verify per session, not per order. Expires at a block height; revocable with SessionRevoke.

Solana Bridge

Validator-majority registration (RegisterBridgedToken), deposit attestation (MintBridged), user withdrawal (BurnBridged). Replay protection via solana_signature. Per-token deposit caps.

API: Chain Status

GET/status

Height, validators, mempool, health, consensus view.

GET/health

"ok" if producing/following blocks.

GET/state

Hex JMT root at tip.

GET/validators

Active validator set.

API: Transactions

POST/tx

Submit signed tx. Returns accepted/rejected.

POST/tx/batch

Submit multiple. Per-tx status.

GET/tx/:hash

Lookup by hash.

GET/nonce/:pubkey

Next expected nonce.

GET/block/:height

Block with full tx list.

API: Accounts

GET/balance/:token/:owner

Liquid balance.

GET/account/:pubkey/holdings

All balances.

GET/tokens

All tokens.

API: Orderbook

GET/orderbook/markets

All markets.

GET/orderbook/:market_id/book?depth=20

Top N levels per side.

GET/orderbook/:market_id/shares/:owner

LP shares.

API: Oracle

GET/oracle/:feed_id

Feed snapshot: value, nonce, freshness, publisher.

API: Bridge

GET/bridge/tokens

All bridged tokens.

GET/bridge/withdrawals

Pending/completed withdrawals.