
Case Study
Tessera: Python JAM Client
Python client implementation for the JAM protocol.
JAM Graypaper v0.6.4+
Target Specification
Python 3.10+
Implementation Language
Full/Light Node (TBD)
Client Type Goal
Network Sync & Validation
Performance Goal
Web3 Foundation
web3.foundationPythonAsyncIOQUICJamPackPyring+5 more

Project Overview
About This Project
Tessera is a Python client for the JAM (Join-Accumulate Machine) protocol. It turns the Graypaper into node software with block processing, consensus verification, PVM execution, state storage, cryptographic primitives, and peer-to-peer networking.
Client
Web3 Foundation
web3.foundationTimeline
Start
April 2025
Completed
December 2026
Duration
Ongoing Development
Phase 1: Spec Analysis and Core Architecture
Phase 2: Networking and Consensus Layer
Phase 3: State Management and PVM Integration
Phase 4: Services, Testing, and Audits
Technologies
PythonAsyncIOQUICJamPackPyringPyCryptHashlibRISC-V / Wasm (for PVM)RocksDBDocker
What Tessera Is
Tessera is a Python client for the JAM protocol. The job is to turn the Graypaper into a working, spec-aligned node that developers and researchers can read, run, and use as reference implementation material.
Python is not the fastest choice for a blockchain client. That is acceptable here. The point is readability, testability, and clear protocol behavior for people building around JAM.
Turning the Graypaper into Python
Core JAM Concepts in Tessera
The first challenge was translating the Graypaper into Python data structures and execution logic without hiding protocol details behind abstractions that make the client hard to audit.
- Hybrid Architecture: JAM splits computation between on-chain (Accumulation) and in-core (Refinement). We modeled both paths and how they interact.
- Service-Oriented Model: Built out the data structures for service accounts: storage, preimages, refine/accumulate entry points, balances, and gas limits.
- Coretime Handling: Core assignment, authorization checks, and coretime accounting from the client's perspective. The client needs to verify all of this even if it's not producing blocks.
- Block and State Structures: Python classes for blocks, headers, extrinsics (tickets, preimages, reports), and the full partitioned state tuple.
- State Transition Function: The big one. We wrote the block-level state transition logic that takes prior state plus block contents and produces deterministic output.
Talking to the JAM Network
P2P Networking Layer
Tessera needs to find peers, sync chain data, and propagate blocks, transactions, and consensus votes. We built a networking layer on top of QUIC with the right message types and protocols for JAM.
- Peer Discovery: We use Discv5/Kademlia DHT to find other JAM nodes on the network.
- Message Serialization: All network messages go through a JAM-specific serialization format. Every message type has defined encode/decode logic.
- Gossip Protocols: New blocks and consensus messages get propagated via gossip. The implementation keeps bandwidth reasonable while ensuring fast propagation.
- Request/Response for Sync: When syncing, the client requests specific blocks, state fragments, or historical data from peers. We handle retries and peer scoring.
- Transport: QUIC as the transport layer gives us multiplexed streams, built-in encryption, and better connection management than raw TCP.
Verifying Safrole and Grandpa
Consensus Verification
Tessera isn't producing blocks (yet), but it still needs to verify everything. That means understanding Safrole for block production and fork choice, and Grandpa for finality. We process and validate all the consensus messages that come in over the network.
- Safrole Verification: We validate block author eligibility using sealing keys derived from tickets and VRFs. The fork-choice rule follows Safrole's criteria: highest valid ticket score, chain length.
- Grandpa Verification: Process prevotes and precommits from the gossip layer. Verify signatures against the known validator set and identify finalized blocks when 2/3+ supermajority is reached.
- Validator Set Tracking: Keep track of current and upcoming validator sets based on state transitions and epoch changes.
- Epoch Processing: Handle epoch transitions cleanly: validator set rotation, Safrole state updates, new roots and sealers.
Running the JAM State Machine
Block Processing and State Transitions
This is the heart of the client. When a new block comes in, Tessera validates it, processes every extrinsic, runs or verifies PVM code, and updates local state. Every step has to match the Graypaper exactly or the node falls out of sync.
- Block Validation: Check header fields (parent hash, state root, extrinsic root), verify the seal signature, VRF output, and consensus markers.
- Extrinsic Processing: Each extrinsic type (Tickets, Preimages, Reports, Assurances, Disputes) has its own validation rules and state effects. We handle them all.
- State Transition: Update every state component based on prior state plus the processed block. This follows the Graypaper's state transition function precisely.
- Accumulation Verification: Run or verify the Accumulate entry point for service accounts affected by work reports in the block.
- State Root Calculation: After applying all changes, calculate the posterior state root using the specified Merkle Trie structure and verify it matches what the block header claims.
Running RISC-V Code from Python
PVM Integration
JAM uses a RISC-V based PVM for executing service logic. Getting that to work from Python is one of the trickier parts of Tessera. We can either embed a Python RISC-V interpreter or call out to an external PVM via FFI. Either way, we need to handle gas metering, host function calls, and state interactions correctly.
- Execution Environment: We integrated a RISC-V (RV64EM) interpreter. For performance-critical paths, there's an FFI bridge to a Rust implementation.
- Gas Metering: Instruction-level gas counting per PVM spec. Every opcode has a cost, and we enforce limits strictly.
- Host Functions: All the host functions that PVM code can call (state access, historical lookups, crypto ops, logging) are wired up on the Python side.
- State Interaction: The PVM can read relevant JAM state and propose modifications during Accumulate. We sandbox this access carefully.
- Refinement Verification: For in-core refinement computation, we can re-execute or verify proofs of work done by guarantors.
- Accumulation Execution: Run the Accumulate entry point for services based on work reports, respecting gas limits.
Persisting the JAM State Efficiently
State Management and Storage
JAM state is a big, complex structure committed via Merkle Trie. We store it in RocksDB, apply updates atomically per block, and calculate trie roots efficiently. State pruning keeps disk usage from growing forever.
- RocksDB Backend: RocksDB handles the heavy lifting for key-value storage. It's fast enough for our needs and handles large datasets well.
- Merkle Trie: We wrote a binary Patricia Merkle Trie following the Graypaper's Appendix D spec, with data structures chosen around Python's strengths.
- Atomic Updates: All state changes from a block are applied as a single atomic batch. Either everything commits or nothing does.
- State Root Calculation: Root calculation happens after every block. We cache intermediate nodes to avoid recomputing the entire trie each time.
- Pruning: Old state data beyond finality plus a safety margin gets pruned to keep storage growth manageable.
- Preimage and Lookup Handling: Service account preimage data and the historical lookup logic are stored and indexed separately for fast access.
The Crypto Layer
Cryptographic Primitives
JAM uses a bunch of different crypto primitives and they all need to work correctly. Here's what Tessera handles.
- Hashing: Blake2b-256 and Keccak-256 for commitments and identifiers. Standard stuff, well-supported in Python.
- Ed25519 Signatures: Used for assurances and validator messages. Key generation, signing, verification.
- BLS12-381 Signatures: Used for Grandpa finality votes and BEEFY commitments. Includes aggregation support.
- Bandersnatch Signatures: Used for block seals. This required custom curve operations since Python library support is limited.
- Bandersnatch RingVRF: Used for ticket generation and Safrole leader selection. Proof generation, verification, and output derivation.
- Serialization and Merklization: The specific encoding (Appendix C) and Merklization rules (Appendix D/E) from the Graypaper, all in Python.
Metrics
Statistics
1M+
TPS
6s
Block Time
~682MB/s
Data Availability Throughput
~12s
Finality Time
Python-Specific Challenges
Let's be honest about the hard parts. Python is great for readability and developer velocity, but building a blockchain client in it comes with real trade-offs.
- The GIL Problem: Python's Global Interpreter Lock limits CPU-bound parallelism. Crypto operations and PVM execution are noticeably slower than in Rust or C++. We work around this with multiprocessing for the heaviest paths.
- Memory Pressure: Large state tries and network buffers need careful handling. Python's garbage collector can cause pauses if we're not deliberate about allocation patterns.
- Cryptographic Library Gaps: Mature Python libraries for Bandersnatch curve operations are limited, so we wrote bindings to Rust implementations through FFI.
- AsyncIO Discipline: The networking layer runs on asyncio, which works well for I/O-bound tasks but requires careful design to avoid accidentally blocking the event loop.
- How We Mitigate: Use native libraries where they exist, FFI to Rust/C for critical crypto paths, caching, efficient data structures, and multiprocessing for CPU-bound work.
Where Tessera Is Heading
Tessera won't be the fastest JAM client. It will be the most readable one. That matters for a protocol this complex. Developers can step through the code and understand exactly what's happening at each stage of block processing, consensus verification, or PVM execution.
For the performance-critical pieces, we lean on FFI to compiled code. For everything else, clean Python with good test coverage. The result is a client that's genuinely useful for development, testing, and understanding the JAM protocol from the inside.