Accumulation And Verification
How batches are accumulated and sent for verification
Overview
This document describes the end-to-end process of how batches of computations, represented by batch trees, are aggregated off-chain into a single elliptic curve accumulator and then efficiently verified on-chain. This system ensures the integrity and continuity of off-chain work without posting large amounts of data to the blockchain.
The process is divided into two main phases:
- Off-Chain Accumulation: A stateful service running within a Trusted Execution Environment (TEE) continuously aggregates new batch roots into a global cryptographic accumulator.
- On-Chain Verification: A Solana smart contract verifies that each new submission is a valid and sequential update to the previously verified state using an efficient pairing-based cryptographic check.
Key Data Structures
Several structs are central to this process.
Bn254Accumulator
This is the core cryptographic engine that aggregates members. It represents a accumulation to a set of items as a single elliptic curve point.
// Location: encifher-coprocessor/crates/core/src/accumulator.rs
#[derive(Clone)]
pub struct Bn254Accumulator {
pub g1: G1Projective,
pub g2: G2Projective,
pub acc: G1Projective,
pub members: Vec<Fr>,
}g1,g2: The generator points for the G1 and G2 groups of the BN254 curve.acc: The current accumulator value. It starts as the generator and is updated by multiplying it by the scalar hash of each new member.members: A list of the scalar hashes of all members added.
AccumulatorState
This struct acts as a shared, in-memory container for the Bn254Accumulator, allowing the processor to maintain its state across different accumulation cycles.
// Location: encifher-coprocessor/crates/processor/src/lib.rs
#[derive(Clone)]
pub struct AccumulatorState {
pub acc: Option<Bn254Accumulator>,
}acc: Holds the current state of the global accumulator. It is wrapped in anArc<RwLock<...>>to allow for safe concurrent access.
BatchTreeSubmission
This is the data payload sent from the processor to the submitter service, which then forwards the relevant parts to the on-chain verifier.
// Inferred from encifher-coprocessor/crates/processor/src/update.rs
pub struct BatchTreeSubmission {
pub acc: String, // The new global accumulator root as a hex string
pub signature: Vec<u8>,
pub recovery_id: u8,
pub batch_trees: Vec<BatchTree>, // The raw batch trees included in this submission
pub da_submission_id: Option<String>,
}Phase 1: Off-Chain Accumulation Process
The accumulation is a continuous, stateful process handled by the accumulate_and_send function in update.rs.
The cycle proceeds as follows:
-
Load Previous State: The function first acquires a read lock on the shared
AccumulatorStateto retrieve the result of the last successful cycle. This is the previous global accumulator root, let's call it . -
Process New Batches: It dequeues a set of new
BatchTreesfrom a database queue. For each tree, it extracts its 32-byte batch root (). -
Update Accumulator: The function iteratively adds each batch root to the accumulator instance. The core operation for each addition is:
After processing all new batch roots, the final accumulator value is:
-
Prepare Submission Payload:
- The final G1 point, , is the new global accumulator root.
- This G1 point is serialized and hashed with SHA-256 to create a 32-byte message hash.
- The TEE signs this 32-byte hash to produce a
signatureandrecovery_id. - This data is packaged into a
BatchTreeSubmissionpayload.
-
Update Global State: After the payload is successfully submitted, the function acquires a write lock on
AccumulatorStateand updates it with the newBn254Accumulatorinstance containing . This ensures the next cycle builds upon the latest verified state.
Phase 2: On-Chain Verification Process
The on-chain verification is performed by the update_batch_root function in the Solana smart contract. It ensures that every new root is a valid and sequential extension of the previous one.
The on-chain verification steps are:
-
Authentication: The contract first verifies the TEE's signature. It uses
secp256k1_recoveron the submitted root hash, signature, and recovery ID. The recovered public key must match the public key of the active coprocessor registered in the contract. -
Fetch Previous State: The contract reads the previously verified global accumulator root, , from its on-chain storage (
batch_root_storage). This value serves as the witness to the state transition. -
Perform Cryptographic Check: The core of the verification is a single, efficient pairing equation that validates the state transition. The contract checks if:
The terms are provided as arguments to the instruction:
- is the
witnessfetched from on-chain storage. - is the
batch_rootargument, representing the scalar product of the new batch roots being added in this update
- is the
rootargument, the new global accumulator root being submitted. - is the constant generator of the G2 group.
This equation holds true if and only if the new root was correctly computed from the previous root and the new batch data. This check is performed using Solana's
alt_bn128_multiplicationandalt_bn128_pairingprecompiles. - is the
-
Update On-Chain State: If the pairing check is successful, the contract updates its
batch_root_storageto store the new root, . This completes the state transition, and this new value will serve as thewitnessfor the next update.