SP1 Vector

Overview

Implementation of zero-knowledge proof circuits for Vector, Avail's Data Attestation Bridge in SP1.

  • /program: The SP1 Vector program.
  • /primitives: Libraries for types and helper functions used in the program.
  • /script: Scripts for getting the contract's genesis parameters and deploying the operator to update the light client.
  • /services: RPC fetcher for the script + the justification indexer.
  • /contracts: The contract's source code and deployment scripts.
  • /query: Contains the logic for querying data root proofs from the contracts. Automatically deploys to https://vectorx-query.succinct.xyz.

Program Architecture

Header Range

The header range program computes the data root and state root commitments for a range of headers. The program does the following:

  1. The first header in the range is the trusted header (matching the latestHeader on the SP1Vector.sol contract when the proof is verified).
  2. There exists a valid justification by an authority set in the SP1Vector.sol contract for the target block with an authority set id >= the authority set id of the trusted header.
  3. The intermediate headers are linked in order by number and parent hash. Note: There is no way to produce a valid linked header range that does not match the real range as long as the justification on the target block is valid and the trusted header is also valid.
  4. The authority set hash of the justification matches the authority set hash of the target block.
graph TD;
    subgraph SP1Vector.sol
        LatestBlock[Latest Block]
        AuthoritySetIdToHash[Authority Set Id to Hash]
        DataRootCommitments[Data Root Commitments]
        StateRootCommitments[State Root Commitments]
    end

    subgraph Program
        LatestBlock -->|latest block| TrustedHeaderHash[Trusted Header Hash]
        TrustedHeaderHash[Trusted Header Hash] -->|matches first header in| HeaderData
        AuthoritySetIdToHash -->|includes| AuthoritySetHash[Authority Set Hash]
        AuthoritySetHash -->|preimage| AuthoritySetPubkeys[Authority Set Pubkeys]
        AuthoritySetPubkeys -->|signs| TargetJustification[Target Justification]
        TargetJustification -->|includes| TargetBlockHash[Target Block Hash]
        TargetBlockHash -->|matches last header in| HeaderData[Header Data]
        HeaderData -->|verify header hashes and numbers are linked| ValidatedHeaderData[Validated Header Data]
        ValidatedHeaderData -->|hash data and state roots of headers| MerkleCommitments[Data Root and State Root Merkle Commitments]
        MerkleCommitments -->|data root commitment| DataRootCommitments[Data Root Commitments]
        MerkleCommitments -->|state root commitment| StateRootCommitments[State Root Commitments]
    end

Rotate

The rotate program computes the next authority set hash from the last header in an epoch signed by the current authority set. The program does the following:

  1. The current authority set signed the justification for the target block.
  2. The preimage of the target block hash which will be used for extracting the next validator set matches the target block hash signed in the justification.
  3. Extract the next validator set from the epoch end header. In this process, validate that this epoch end header is valid.
  4. Compute the next authority set hash from the next validator set.
graph TD;
    subgraph SP1Vector.sol
        CurrentAuthoritySet[Current Authority Set Hash]
        NextAuthoritySet[Next Authority Set Hash]
    end

    subgraph Program
        CurrentAuthoritySet -->|preimage| CurrentAuthoritySetPubkeys[Current Authority Set Pubkeys]
        CurrentAuthoritySetPubkeys[Current Authority Set Pubkeys] -->|signs| Justification[Circuit Justification]
        Justification -->|includes| TargetBlockHash[Target Block Hash]
        TargetBlockHash -->|preimage| EncodedHeaderData[Encoded Header Data]
        EncodedHeaderData -->|contains| NewPubkeys[Next Pubkeys]
        NewPubkeys -->|hash| NextAuthoritySetHash[Next Authority Set Hash]
        NextAuthoritySetHash --> NextAuthoritySet[Next Authority Set Hash]
    end

Components

An SP1 Vector implementation has a few key components:

  • An SP1Vector contract. Contains the logic for verifying SP1 Vector proofs, storing the latest data from the Avail chain, including the headers and data commitments. Matches the interface of the existing VectorX contract so it can be upgraded in-place.
  • An SP1Verifier contract. Verifies arbitrary SP1 programs. Most chains will have canonical deployments upon SP1's mainnet launch. Until then, users can deploy their own SP1Verifier contracts to verify SP1 programs on their chain. The SP1 Vector implementation will use the SP1Verifier contract to verify the proofs of the SP1 Vector programs.
  • The SP1 Vector program. An SP1 program that verifies the transition between two Avail headers and computes the data commitment of the intermediate blocks.
  • The operator. A Rust script that fetches the latest data from a deployed SP1Vector contract and an Avail chain, determines the block to request, requests for/generates a proof, and relays the proof to the SP1Vector contract.

Deployment

Overview

Here's how to deploy an SP1 Vector contract for an Avail chain.

Steps

  1. To deploy an SP1 Vector contract for an Avail chain do the following.

    Get the genesis parameters for the SP1Vector contract.

    cd script
    
    # Example with Avail Turing Testnet.
    AVAIL_URL=wss://turing-rpc.avail.so/ws cargo run --bin genesis --release
    
  2. Add the genesis parameters to /contracts/.env mirroring contracts/.env.example.

    | Parameter | Description | |-----------|-------------| | GENESIS_HEIGHT | The block height of the genesis block for the Avail chain | | GENESIS_HEADER | The header of the genesis block for the Avail chain | | GENESIS_AUTHORITY_SET_ID | The ID of the initial authority set for the Avail chain | | GENESIS_AUTHORITY_SET_HASH | The hash of the initial authority set for the Avail chain | | SP1_VECTOR_PROGRAM_VKEY | The verification key for the SP1 Vector program | | HEADER_RANGE_COMMITMENT_TREE_SIZE | The size of the Merkle tree used for header range commitments (Default 1024) |

  3. Deploy the SP1Vector contract with genesis parameters.

    cd ../contracts
    
    forge install
    
    SP1_PROVER={mock, network} CHAINS=sepolia forge script script/Deploy.s.sol --private-key $PRIVATE_KEY --multi --broadcast --verify
    

    If you see the following error, add --legacy to the command.

    Error: Failed to get EIP-1559 fees    
    
  4. Your deployed contract address will be printed to the terminal.

    == Return ==
    0: address <SP1_VECTOR_ADDRESS>
    

    This will be used when you run the operator in step 5.

  5. Export your SP1 Prover Network configuration

    # Export the PRIVATE_KEY you will use to relay proofs.
    export PRIVATE_KEY=<PRIVATE_KEY>
    
    # Optional
    # If you're generating proofs on the Succinct Network, set NETWORK_PRIVATE_KEY to the private key of the account you want to use.
    export NETWORK_PRIVATE_KEY=<NETWORK_PRIVATE_KEY>
    # If you're using a custom endpoint, set NETWORK_RPC_URL to the URL of the endpoint you want to use.
    export NETWORK_RPC_URL=<NETWORK_RPC_URL>
    # If you're generating proofs in mock mode, set SP1_PROVER to "mock".
    export SP1_PROVER={mock}
    
  6. Run the SP1 Vector operator to update the LC continuously.

    cd ../script
    
    AVAIL_URL=wss://turing-rpc.avail.so/ws AVAIL_CHAIN_ID=turing CHAIN_ID=11155111 RPC_URL=https://ethereum-sepolia.publicnode.com/ CONTRACT_ADDRESS=<SP1_VECTOR_ADDRESS> VECTORX_QUERY_URL=https://vectorx-query.succinct.xyz
    cargo run --bin operator --release
    

Demo Contract

An example contract using SP1 Vector can be found on Sepolia here.

Query Data Root Proofs

Overview

Whenever a new data root commitment is stored on-chain, the merkle proofs need to be made available for end-users to prove the data root's of blocks within those data commitments. This service listens for data root commitment events on-chain and stores the merkle proofs for each data root in the range, which is then exposed via a separate endpoint.

The indexed contracts are configured in deployments.json.

RPC Queries

Query for dataRoot Proof Data

Querying with a block number.

https://vectorx-query.succinct.xyz/api?chainName=hex&contractChainId=11155111&contractAddress=0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75&blockNumber=247230

Example response:

{
  "data": {
    "blockNumber": 247230,
    "rangeHash": "0xafad54e98bdaebacc1f220dd919dda48b84ed0689906c288a4d93dae1ae9d7c5",
    ...
  }
}

Querying with a block hash.

https://vectorx-query.succinct.xyz/api?chainName=hex&contractChainId=11155111&contractAddress=0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75&blockHash=0xad664ed32323c70e9c19333f6d7d6f855719f439bc0cb4cd92d89138c252d560

Example response:

{
  "data": {
    "rangeHash": "0xafad54e98bdaebacc1f220dd919dda48b84ed0689906c288a4d93dae1ae9d7c5",
    "dataCommitment": "0x7b0f5743191b390b3ba21cdda41b3940b37566a9f336b9e37cf0ad94c937242a",
    ...
  }
}

Health of the VectorX contract

Querying for the health of the VectorX contract deployed on Sepolia (chain ID: 11155111) at address 0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75.

https://vectorx-query.succinct.xyz/api/health?chainName=hex&contractChainId=11155111&contractAddress=0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75

Example response:

{"data":{"logEmitted":true,"ethBlocksSinceLastLog":35,"lastLogTimestamp":1717707768,"blocksBehindHead":50}}

Note: If logEmitted is false, the contract has not emitted a log in at least the last ethBlocksSinceLastLog blocks.

Range of the VectorX contract

Querying for the range of the VectorX contract deployed on Sepolia (chain ID: 11155111) at address 0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75.

https://vectorx-query.succinct.xyz/api/range?contractChainId=11155111&contractAddress=0xbc281367e1F2dB1c3e92255AA2F040B1c642ec75

Example response:

{"data":{"start":63091,"end":304710}}

Launch the Query Service

Update query/.env with the corresponding variables from .env.example. Then launch the service with:

npm run dev

Deployed Contracts

You can find a list of actively deployed contracts in this deployments.json.

Reproducible Builds

Overview

When deploying SP1 Vector in production, it's important to ensure that the program used when generating proofs is reproducible.

Prerequisites

You first need to install the cargo prove toolchain.

Ensure that you have the latest version of the toolchain by running:

sp1up

Confirm that you have the toolchain installed by running:

cargo prove --version

Verify the SP1 Vector binary

To build the SP1 Vector binary, first ensure that Docker is running.

docker ps

Then build the binaries:

cd program

# Builds the SP1 Vector binary using the corresponding Docker tag and ELF name.
cargo prove build --docker --tag v4.1.3 --elf-name vector-elf

Now, verify the binaries by confirming the output of vkey matches the vkeys on the contract. The vkey program outputs the verification key based on the ELF in /elf.

cargo run --bin vkey --release