Pyde Technical Design
Version 0.1
This is the comprehensive technical design document for Pyde. For high-level pitch, see WHITEPAPER.md. For operational specs, see the individual documents linked below.
Table of Contents
- Layered Architecture
- Consensus: Mysticeti DAG
- Cryptography
- Execution Layer
- State Layer
- Account Model
- Transaction Lifecycle
- Encryption & MEV Resistance
- Network Protocol
- Performance Targets
- Implementation Status
Layered Architecture
Pyde is a monolithic blockchain (consensus + execution + state in single binary) with these layers:
┌─────────────────────────────────────────────┐
│ Application │
│ WASM smart contracts, dApps, wallets, RPC │
├─────────────────────────────────────────────┤
│ Execution │
│ WebAssembly (wasmtime + Cranelift AOT), Block-STM, │
│ hybrid access-list scheduler │
├─────────────────────────────────────────────┤
│ State │
│ Jellyfish Merkle Tree (JMT) │
│ Hybrid: Blake3 native + Poseidon2 for ZK │
├─────────────────────────────────────────────┤
│ Consensus │
│ Mysticeti DAG, anchor selection, finality │
├─────────────────────────────────────────────┤
│ Cryptography │
│ FALCON-512, Kyber-768 threshold, DKG │
├─────────────────────────────────────────────┤
│ Network │
│ libp2p + QUIC, Gossipsub, worker/primary │
└─────────────────────────────────────────────┘
Consensus: Mysticeti DAG
Algorithm Choice
Pyde uses Mysticeti-style DAG consensus (Mysten Labs' production protocol on Sui). Chosen over Bullshark for faster commit latency (~390ms vs ~1s) and better liveness under validator failures.
Why DAG over HotStuff:
- No single-proposer bottleneck — every committee member contributes vertices continuously
- No view changes — eliminates the bug class that caused Pyde's pre-pivot wedges
- Censorship resistance — 127 honest members can include any transaction; censorship requires near-unanimous collusion
- Throughput scales with committee size, not constrained by one proposer's bandwidth
- Threshold-decryption integrates naturally at the order-commit boundary
Worker / Primary Split (Narwhal Pattern)
Each validator runs:
- Workers (N per validator): handle tx ingress, build batches, gossip batches peer-to-peer
- Primary (one per validator): handles consensus — produces vertices, gathers parents, signs state roots
Transactions travel the network exactly once (via worker gossip). Consensus vertices stay tiny (carry only batch hashes by reference).
Vertex Structure
#![allow(unused)] fn main() { struct Vertex { round: u64, member_id: u32, // validator address as u32 internally batch_refs: Vec<BatchHash>, // hashes of batches I have parent_vertex_refs: Vec<VertexHash>, // ≥85 round-(N-1) vertex hashes state_root_sigs: Vec<StateRootSig>, // attestations on recent commits prev_anchor_attestation: VertexHash, // attests prior round's anchor decryption_shares: Vec<DecryptionShare>, // piggybacked partials falcon_sig: FalconSig, // sig over the vertex } }
Each vertex is dual-role: header (declaring what data I have) AND attestation (acknowledging prior-round vertices via parent_vertex_refs). Parent references are implicit "votes" — no separate vote messages.
Rounds & Anchors
A round is a layer in the DAG, advancing when a member collects ≥85 parent vertices.
Each round has a deterministically-selected anchor:
anchor_member_id = Hash(beacon, round, recent_state_root) mod 128
The recent_state_root lookback (N=3 rounds) limits anchor predictability to ~450ms (down from a full epoch).
A commit fires when the anchor vertex has sufficient support (Mysticeti 3-stage support). Multiple commits can be in flight; ~95% of rounds commit successfully.
Commit
1. Anchor selected by deterministic rule
2. Anchor's subdag walked via parent_vertex_refs (transitive)
3. Subdag sorted: (round, member_id, list_order)
4. Batches dereferenced from each vertex
5. Threshold decryption ceremony runs (pipelined — partials pre-broadcast)
6. ≥85 partials combine per batch → plaintexts revealed
7. wasmtime executes in canonical order
8. State root computed (Blake3 + Poseidon2 dual)
9. ≥85 committee FALCON-sign state root (piggybacked on next vertices)
10. Finality declared
End-to-end latency: ~500ms median for plaintext, ~700ms for encrypted (decryption ceremony adds ~200ms within wave budget).
Committee
- Size: 128 validators per epoch
- Selection: uniform random from all validators with stake ≥
MIN_VALIDATOR_STAKE(10,000 PYDE). Single tier — every staked validator meeting the floor is in the same pool - Anti-Sybil: operator identity binding, max 3 validators per operator
- Equal power: all 128 have equal voting weight, vertex production rate, anchor probability
- Stake influence: only on eligibility + flat 30% pool yield share. Activity rewards within committee are contribution-weighted, not stake-weighted.
- Epoch length: ~3 hours (measured in wall-clock, not in round count, so it's stable across consensus-cadence changes)
- DKG ceremony: runs in background during prior epoch's last minutes
Safety & Liveness
- Safety: Mysticeti BFT — holds under any network with at most
f = 42Byzantine members (BFT tolerance⌊(n-1)/3⌋for n = 128) - Liveness: holds under partial synchrony
- Recovery: explicit halt detection + investigation + recovery (see CHAIN_HALT.md)
- Rollback: bounded to 1 epoch (3 hours) via governance multisig; beyond that, only hard fork
Cryptography
Signatures: FALCON-512
NIST FIPS 206 standard. Used for:
- User transaction authorization
- Validator vertex production
- Committee state-root attestations
- Decryption share authentication
Properties:
- Public key: 897 bytes
- Signature: 666 bytes
- Verification: ~80μs commodity CPU
- Post-quantum secure (lattice-based)
Threshold Encryption: Kyber-768
NIST FIPS 203 standard, with threshold variant.
- Public key: 1184 bytes (one per epoch, shared across all encrypters)
- Ciphertext overhead: ~1088 bytes + plaintext size
- Decryption: requires ≥85 of 128 partial decryptions combined via Lagrange interpolation
Critical invariant: commit-before-reveal. Consensus orders encrypted transactions before any decryption shares are released. Decryption happens after ordering is final.
v1 risk: production-grade threshold variants of lattice schemes (Kyber threshold) are research-stage. Pyde v1 may ship with classical-crypto threshold (ElGamal-style) as transitional measure, migrating to threshold Kyber when audited implementations mature. This is the single largest cryptographic engineering risk in the design.
Hash Functions: Hybrid Layered Strategy
| Use case | Hash | Why |
|---|---|---|
| JMT internal nodes | Blake3 | ~30× faster than Poseidon2 on CPU |
| State root (published) | Both | Blake3 native verification + Poseidon2 for ZK |
| Transaction hashes | Blake3 (ciphertext), Poseidon2 (plaintext canonical) | Per use |
| Address derivation | Poseidon2 | Used in sig-verify ZK circuits |
| FALCON sig hashing | Poseidon2 | Inside ZK aggregation circuit |
| Vertex hashes | Blake3 | Small volume, no ZK |
Random Beacon
Each epoch's beacon is produced by the previous epoch's committee:
- All 128 members sign a known message
"epoch_N_beacon"with threshold-share keys - ≥85 shares combine into deterministic aggregated signature
beacon_N = Hash(aggregated_signature)→ 32 bytes randomness- Published in last wave of epoch N
Properties: deterministic given shares, unpredictable until reveal, bias-resistant.
DKG (Distributed Key Generation)
Pedersen DKG, multi-round protocol (~30-60s runtime):
Round 1: Each member generates random secret polynomial f_i(x), degree 84
Round 2: Each member broadcasts public commitments to coefficients
Round 3: Member i sends f_i(j) to each other member j (encrypted)
Round 4: Member j verifies received values, sums s_j = Σ f_i(j) = f(j)
where f(x) = Σ f_i(x) is the combined polynomial
Result: Each member j holds s_j = f(j) (private share)
SK = f(0) is never computed; PK derivable from public commitments
Threshold = 85
Mathematical foundation: any 85 points on a degree-84 polynomial uniquely determine it (Lagrange interpolation), enabling 85+ members to perform partial decryptions that combine without anyone reconstructing SK.
Partial Decryption Math
Given ciphertext (c1, c2) where c1 = g^r, c2 = m · PK^r:
Each member i: partial_i = c1^(s_i)
Combine via Lagrange (any subset S of 85):
combined = Π_{i in S} partial_i^(λ_i)
= c1^(SK)
= PK^r
Decrypt: m = c2 / combined
SK is never assembled. Each member's s_i is reusable across many ciphertexts within the epoch.
Execution Layer
WASM Execution Layer (wasmtime)
WebAssembly via wasmtime, with Cranelift ahead-of-time compilation:
- WebAssembly Core Specification as the instruction set (industry-standard, externally maintained)
- Deterministic feature subset enforced (NaN canonicalization on; threads, non-deterministic SIMD, reference types, GC, multi-memory, memory64, WASI all disabled)
- Fuel-based gas metering (wasmtime's built-in mechanism)
- Per-contract module compilation cache (Cranelift AOT artifacts persisted)
- Deploy-time validator rejects modules with forbidden imports or non-deterministic features
- Host-function ABI is the only chain-side surface contracts can reach
Determinism is load-bearing. Same input transactions must produce byte-identical state transitions across all validators (consensus safety) and feasible ZK circuits (future validity proofs). wasmtime's determinism config + deploy-time validator together provide this guarantee.
Smart Contract Authoring
Contracts are authored in any wasm32-target language (Rust, AssemblyScript, Go via TinyGo, C/C++). The otigen developer toolchain handles the lifecycle: project scaffolding (otigen init), build with state binding generation (otigen build), deploy (otigen deploy), upgrade governance, wallet management.
Pyde safety attributes (preserved from Otigen-language era):
- Reentrancy off by default (opt-out via
reentrantattribute) - Checked arithmetic (wrapping ops require explicit opt-in)
- Typed storage via
[state]schema inotigen.toml - No
tx.origin(host function ABI exposes onlycaller) - View / payable / reentrant / sponsored / constructor attributes
- Compile-time static access list inference (from declared state schema)
- 4-byte function selectors
Build output: .wasm artifact + JSON ABI + deploy bundle.
Hybrid Parallel Scheduler
Combines two parallel-execution paradigms:
Static access lists (Solana-style): for functions where access can be inferred at compile time, the scheduler partitions transactions into parallel groups by their declared access sets. Deterministic, no speculation overhead.
Block-STM (Aptos-style): for functions with dynamic access patterns, transactions execute optimistically with read/write set tracking; conflicts trigger re-execution in canonical order.
The hybrid: Build-time state binding generator emits both declared_access_set (static) and dynamic_access_regions (runtime). Runtime scheduler uses static info for partition planning, falls back to Block-STM for dynamic regions.
Pyde-specific opportunity: controls compiler, runtime, language, and protocol — enabling this hybrid where most chains commit to one approach.
Preflight Execution
Users request access list + gas estimate via RPC before signing:
Client → pyde_estimateAccess(tx)
→ RPC runs a wasmtime preflight (dry-run against current state)
→ Returns: { gas_estimate, access_list }
Client attaches access_list to tx, signs
State staleness handled by treating access list as a hint — scheduler verifies at runtime, falls back to Block-STM on mismatch.
State Layer
Jellyfish Merkle Tree (JMT)
Radix-16, path-compressed Merkle tree (Diem/Aptos lineage):
- ~5–10 nodes per state operation (vs SMT's ~256)
- Substantial I/O savings at high TPS
- Standard authentication properties (commitment, inclusion/exclusion proofs)
State Root Commitment
Dual-rooted:
- Blake3 root: fast native verification (used by validators)
- Poseidon2 root: ZK-circuit-friendly (future light clients, validity proofs)
Both computed at each commit, both signed by committee.
State Pruning
| Node type | State retention |
|---|---|
| Archive node | All historical state |
| Full node (default) | Last 90 days |
| Committee validator | Last 30 days |
| Light client | Headers + cared-about accounts |
See STATE_SYNC.md for sync protocol details.
Account Model
Account State
#![allow(unused)] fn main() { struct Account { nonce: u64, balance: u128, gas_tank: u128, // pre-deposited for encrypted tx submission auth_keys: AuthKeys, // Single | Multisig | Programmable code_hash: Hash, // for contract accounts storage_root: Hash, // for contract storage (JMT subtree) key_nonce: u32, // FALCON key rotation counter } enum AuthKeys { Single(FalconPubkey), Multisig(M, Vec<FalconPubkey>), // M-of-N, max 16 Programmable, // reserved for v2 } }
Nonce Window
Pyde uses a 16-slot sliding nonce window instead of strict sequential nonces:
#![allow(unused)] fn main() { struct NonceState { base: u64, // lowest unused nonce used: u16, // 16-bit bitmap of consumed slots in [base, base+15] } }
Allows up to 16 concurrent in-flight transactions per account, out-of-order within the window. Standard EVM-style nonces force head-of-line blocking; Pyde's window decouples submission ordering from execution ordering.
Native Multisig (v1)
AuthKeys::Multisig(M, [pubkey_1, ..., pubkey_N]) requires M valid FALCON signatures over the tx hash. Max 16 signers. Used for treasuries, DAOs, exchange custody.
Significantly safer than contract-based multisig (Gnosis Safe model on Ethereum), which reimplements the same logic across projects with subtle bugs.
Programmable Accounts (v2)
Reserved enum variant at v1. When v2 ships:
- Account has signing keys AND attached WASM policy module
- Policy runs on every authorization, can implement: spend limits, time locks, allow-listed recipients, social recovery, tiered authorization, AI agent delegation
- Same fields as contracts (
code_hash+storage_root) - WASM "policy mode" — restricted state access during validation
Session Keys (v2)
Scoped, bounded, revocable delegation. The user authorizes a session key once; the dApp (or agent) signs many transactions on the user's behalf within the declared scope.
Type:
#![allow(unused)] fn main() { struct SessionKey { pubkey: FalconPubkey, scope: SessionScope, expires_at: WaveId, revoked: bool, } struct SessionScope { contracts: Vec<Address>, methods: Vec<Selector>, // optional; empty = all methods on allowed contracts max_spend: u128, spent_so_far: u128, // mutable, updated at tx commit } }
Registry. Session keys are stored under the account's programmable-policy state subtree. The slot_hash clusters with the account under PIP-2 so lookups during authorization are local. New keys are added by RegisterSessionKey txs signed under the main auth_keys; existing keys are revoked by RevokeSessionKey txs.
Authorization-time check (pseudocode):
fn authorize_session_tx(tx) -> Result<(), AuthError> {
let sk = lookup_session_key(tx.session_key_id)?;
// 1. Signature
verify_falcon(sk.pubkey, tx.hash, tx.session_sig)?;
// 2. Liveness
require(current_wave < sk.expires_at, KeyExpired);
require(!sk.revoked, KeyRevoked);
// 3. Scope
require(sk.scope.contracts.contains(&tx.to), OutsideContractScope);
if !sk.scope.methods.is_empty() {
require(sk.scope.methods.contains(&tx.selector), OutsideMethodScope);
}
// 4. Spend cap
let new_spent = sk.scope.spent_so_far + tx.value;
require(new_spent <= sk.scope.max_spend, ExceedsSpendCap);
// On commit:
// sk.scope.spent_so_far = new_spent;
Ok(())
}
Use cases:
- Gaming — sign once, play many actions.
- AI agents — bounded delegation (e.g., "trade at most 100 PYDE/day on this DEX until next Friday").
- Consumer apps — subscriptions, micro-transactions.
- Embedded wallets — passkey-style flows where the main key never leaves a secure enclave.
Limits.
- Maximum 32 active session keys per account (anti-squat).
max_spendis monotonic — increasing it requires a new key, not a mutation.expires_atcannot exceedcurrent_wave + MAX_SESSION_WAVES(default: ~30 days at 500ms/wave = ~5.18M waves).
v1 reservations: AuthKeys::Programmable enum tag 0x03, account code_hash + storage_root fields, WASM policy-mode execution flag, multisig signature pipeline. All present at genesis; only the policy engine and session-key registry need to be added at v2.
Threat-model entries for session keys live in companion/THREAT_MODEL.md §Authorization Layer (added v0.2).
Transaction Lifecycle
Plaintext Transaction
User wallet:
1. Construct unsigned tx (sender, recipient, amount, nonce, gas, payload, deadline)
2. RPC pyde_estimateAccess(tx) → returns gas_estimate + access_list
3. Attach access_list to tx
4. FALCON-sign tx hash
5. Submit: pyde_sendRawTransaction(signed_tx)
RPC node:
6. Verify wire format, size, chain_id
7. Forward to nearest validator worker
Worker:
8. Verify FALCON sig at ingress
9. Verify nonce within window, balance, gas
10. Batch with other txs
11. Gossip batch to peer workers
Primary (every ~150ms):
12. Produce vertex referencing batches + parents
13. Gossip vertex
14. Peer primaries cert via next-round parent refs
Commit (per round, ~390ms median):
15. Anchor selected; subdag walked; canonical order emitted
16. wasmtime executes in canonical order:
- Nonce window check (state may have changed)
- Balance check
- Access list verification (vs runtime)
- Hybrid scheduler partitions txs into parallel groups
- Execute, apply state diffs
17. JMT updated, state root computed (Blake3 + Poseidon2)
18. Committee FALCON-signs state root, ≥85 collected
19. Finality declared
Encrypted Transaction
Same as above, with:
- Step 4.5: After FALCON-sign, Kyber-encrypt signed_tx with epoch PK
- Step 5: pyde_sendRawEncryptedTransaction(encrypted_blob)
- Worker step 8: cannot verify sig (encrypted) — only verify wire format
- Commit step 15.5: threshold decryption ceremony — ≥85 partials combine per encrypted tx (batches contain a mix of plaintext + encrypted txs) → plaintexts revealed. Share-combine is batched across the wave for amortised cost.
- Then wasmtime step 16 includes first sig verification
Encryption & MEV Resistance
Three structural defenses, layered:
Layer 1: Threshold Encryption
Users encrypt time-sensitive transactions (DEX swaps, NFT mints, liquidations) before submission. Encrypted blob is opaque — even committee members cannot decrypt alone.
Layer 2: Commit-Before-Reveal
Consensus orders encrypted transactions before decryption shares are released. By the time content is revealed, ordering is fixed and irreversible.
Layer 3: No Proposer
Pyde's DAG consensus has no single party empowered to choose which transactions enter a commit or in what order. The canonical order emerges deterministically from the DAG; no member can selectively reorder, exclude, or front-run.
Combined effect: sandwich attacks, front-running, proposer extraction are structurally impossible — not policed, not auctioned, not made more efficient, but eliminated.
Encryption is Optional
Per-tx choice via envelope:
pyde_sendRawTransaction— unencrypted, fast path, no MEV protectionpyde_sendRawEncryptedTransaction— encrypted, MEV-resistant, costs more gas
Wallets default to "auto" — encrypt time-sensitive, skip for simple transfers.
Encryption bandwidth cost: ~70% reduction if 80% of txs are unencrypted simple transfers (typical mix).
Network Protocol Summary
See NETWORK_PROTOCOL.md for full details.
Key choices:
- Transport: QUIC over UDP (no HOL blocking, built-in TLS 1.3)
- Library: libp2p (Rust) — mature, audited
- Peer discovery: layered (hardcoded → DNS → on-chain registry → PEX → cache); no DHT
- Gossip: Gossipsub with per-topic meshes
- DoS: 4-layer (connection/message/peer-scoring/application)
- Committee defense: sentry node pattern (Cosmos-style)
Performance Targets
Honest Targets
Validated by multi-region production-realistic harness (see PERFORMANCE_HARNESS.md):
| Metric | Realistic v1 | Stretch v2 | Aspirational |
|---|---|---|---|
| Plaintext TPS (commodity) | 10K-30K | 50K-100K | 500K |
| Encrypted TPS (commodity CPU) | 500-2K | 5K-10K | 50K+ (GPU) |
| Median finality | ~500ms | ~400ms | ~300ms |
| Committee NIC requirement (at TPS) | 500 Mbps | 1 Gbps | 10 Gbps |
"Claim 1/3 of Bench" Rule
- Harness measures: X TPS sustained
- Public claim: X/3 TPS
- Aspirational: X with "production validation pending"
No external TPS claim without harness evidence.
Hardware Tiers
| Role | Hardware |
|---|---|
| Light client | Mobile / browser |
| Full node / RPC | 8c / 16GB / 500GB / 100 Mbps |
| Non-committee validator | 8c / 16GB / 500GB / 100-250 Mbps |
| Committee at 30K TPS (v1 realistic) | 8-16c / 32GB / 1TB SSD / 500 Mbps |
| Committee at 100K TPS (v2 stretch) | 16c / 32GB / 2TB SSD / 1 Gbps |
| Committee at 500K TPS (aspirational, GPU-class) | 32c / 64GB / 4TB SSD / 10 Gbps |
Modest hardware applies to any validator awaiting committee selection at all levels. Active-committee hardware scales with throughput target. The 500K row is aspirational and tied to GPU-acceleration / batch-decryption research advances per the honest performance targets above.
Implementation Status
This documentation reflects designed architecture, not shipped implementation:
| Component | Status |
|---|---|
| Architecture design | ✅ Complete |
| WASM execution layer (wasmtime + Cranelift AOT) | 🟡 Foundation in place; integration in progress; programmable-accounts hooks + hybrid scheduler integration pending |
| State layer (JMT) | 🟡 In place, needs hybrid hashing |
| Consensus (Mysticeti DAG) | 🔴 Not yet — rebuild post-pivot |
| Threshold cryptography | 🔴 Research-grade (PQ threshold is bleeding-edge) |
| Network protocol (libp2p) | 🟡 Existing in archive, needs migration |
| Performance harness | 🔴 Not yet built |
| Slashing + lifecycle | 🟡 Partial in archive |
| State sync | 🟡 Partial design |
| Documentation | 🟡 This is the current state |
Mainnet ships when the work above is complete and the external audit passes. No public schedule.
Highest-risk piece: post-quantum threshold cryptography. Research-stage, may require classical-crypto transitional v1 with migration to PQ threshold in v2 as standards mature.
Cross-References
| Topic | Document |
|---|---|
| Threats & adversaries | THREAT_MODEL.md |
| Operational failures | FAILURE_SCENARIOS.md |
| Halt + recovery procedures | CHAIN_HALT.md |
| Slashing rules | SLASHING.md |
| Validator state machine | VALIDATOR_LIFECYCLE.md |
| State sync protocol | STATE_SYNC.md |
| Network protocol | NETWORK_PROTOCOL.md |
| Performance harness | PERFORMANCE_HARNESS.md |
| Token economics | TOKENOMICS.md |
Document version: 0.1
License: See repository root