Home | About John | Resume/CV | References | Writing | Research |
---|
Succinct1 is building Telepathy2 a zkSNARK circuit that verifies Ethereum validator signatures, allowing for a gas-efficient light client to run as a smart contract on any EVM chain.
The on-chain light client recreates the light client spec in Solidity (code here). In particular, we implement the process_light_client_finality_update function inside the step function in our smart contract. Then, inside step, where we would typically verify an aggregate BLS signature, we instead replace it with verification of a single Groth16 zkSNARK to reduce gas costs.
Recall that the validator set of the sync committee rotates every 27 hours. On chain, we keep track of a commitment to the set of validators in the mapping syncCommitteeRootByPeriod. To update this mapping for the next period, we verify the merkle inclusion proof that the current validator set signs for the commitment for the next validator set. This computation happens inside the updateSyncCommittee function.
Unfortunately, the commitment the validators sign is an SSZ commitment (simple serialization, Eth PoS serialization format) that is quite SNARK unfriendly, as it uses the SHA-256 hash function. It takes ~70 million constraints in a Groth16 circuit to compute the serialization of 512 validator BLS public keys to its corresponding SSZ commitment. Because we don’t want to do this for every single header verification proof (which happens every 6 minutes, i.e. once per epoch), we use an additional SNARK (the commitmentMappingProof argument) to provably map an SSZ commitment to a SNARK-friendly Poseidon commitment, that is stored in the mapping sszToPoseidon. For each BLS signature verification, we pass in the poseidon commitment of the sync committee validators as public input to ensure that the BLS signature we are verifying is from the correct public keys. Overall this approach (using 2 SNARKs) saves us 70M constraints on the BLS signature verification SNARK, which we must run for every update we wish to submit to the light client. The commitment mapping SNARK must only be run every sync committee period (roughly once every 27 hours).
Toolchain We use the Circom programming language and the Groth16 proving system to generate our zkSNARKs. While a newer proof system (like PLONK arithmetization + KZG or FRI) would improve proving time, we believe Circom is the most production-ready zkSNARK stack today. In particular, Tornado Cash’s circuits are built on top of Circom and have been used for several years. Additionally, the on-chain verification cost of a Groth16 zkSNARK is the cheapest of all proving systems available today.
For such large circuits, you need a machine with an Intel processor, lots of RAM and a large hard drive with swap enabled. For example, the zkPairing project used an AWS r5.8xlarge instance with 32-core 3.1GHz, 256G RAM machine with 1T hard drive and 400G swap.
Compilation: for circuits with >20M constraints, one should not compile to WebAssembly because witness generation will exceed the memory cap of WebAssembly. For this reason, one must compile with the C++ flag and remove the wasm flag.
Machine: AWS r5.8xlarge instance with 32-core 3.1GHz, 256G RAM machine with 1T hard drive and 400G swap. $2.016 per hour
Download powersOfTau28_hez_final_27.ptau: 144 GB file containing the encrypted evaluation of the Lagrange polynomials at tau for tau, alphatau and betatau. It takes the beacon ptau file we generated in the previous step, and outputs a final ptau file which will be used to generate the circuit proving and verification keys.
#!/bin/bash
PHASE1=/home/ubuntu/powersOfTau28_hez_final_27.ptau
BUILD_DIR=../build
CIRCUIT_NAME=test_aggregate_bls_verify_512
TEST_DIR=../test
OUTPUT_DIR="$BUILD_DIR"/"$CIRCUIT_NAME"_cpp
run() {
if [ ! -d "$BUILD_DIR" ]; then
echo "No build directory found. Creating build directory..."
mkdir -p "$BUILD_DIR"
fi
# echo "****COMPILING CIRCUIT****"
# start=`date +%s`
# circom "$TEST_DIR"/circuits/"$CIRCUIT_NAME".circom --O1 --r1cs --sym --c --output "$BUILD_DIR"
# end=`date +%s`
# echo "DONE ($((end-start))s)"
# echo "****Running make to make witness generation binary****"
# start=`date +%s`
# make -C "$OUTPUT_DIR"
# end=`date +%s`
# echo "DONE ($((end-start))s)"
echo "****Executing witness generation****"
start=`date +%s`
./"$OUTPUT_DIR"/"$CIRCUIT_NAME" "$TEST_DIR"/input_aggregate_bls_verify_512.json witness.wtns
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****Converting witness to json****"
start=`date +%s`
npx snarkjs wej "$OUTPUT_DIR"/witness.wtns "$OUTPUT_DIR"/witness.json
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****GENERATING ZKEY 0****"
start=`date +%s`
npx --trace-gc --trace-gc-ignore-scavenger --max-old-space-size=2048000 --initial-old-space-size=2048000 --no-global-gc-scheduling --no-incremental-marking --max-semi-space-size=1024 --initial-heap-size=2048000 --expose-gc snarkjs zkey new "$BUILD_DIR"/"$CIRCUIT_NAME".r1cs "$PHASE1" "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p1.zkey
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****CONTRIBUTE TO PHASE 2 CEREMONY****"
start=`date +%s`
npx snarkjs zkey contribute "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p1.zkey "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p2.zkey -n="First phase2 contribution" -e="some random text for entropy"
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****VERIFYING FINAL ZKEY****"
start=`date +%s`
npx --trace-gc --trace-gc-ignore-scavenger --max-old-space-size=2048000 --initial-old-space-size=2048000 --no-global-gc-scheduling --no-incremental-marking --max-semi-space-size=1024 --initial-heap-size=2048000 --expose-gc npx snarkjs zkey verify "$BUILD_DIR"/"$CIRCUIT_NAME".r1cs "$PHASE1" "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p2.zkey
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****EXPORTING VKEY****"
start=`date +%s`
npx snarkjs zkey export verificationkey "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p2.zkey "$OUTPUT_DIR"/"$CIRCUIT_NAME"_vkey.json
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****GENERATING PROOF FOR SAMPLE INPUT****"
start=`date +%s`
~/rapidsnark/build/prover "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p2.zkey "$OUTPUT_DIR"/witness.wtns "$OUTPUT_DIR"/"$CIRCUIT_NAME"_proof.json "$OUTPUT_DIR"/"$CIRCUIT_NAME"_public.json
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****VERIFYING PROOF FOR SAMPLE INPUT****"
start=`date +%s`
npx snarkjs groth16 verify "$OUTPUT_DIR"/"$CIRCUIT_NAME"_vkey.json "$OUTPUT_DIR"/"$CIRCUIT_NAME"_public.json "$OUTPUT_DIR"/"$CIRCUIT_NAME"_proof.json
end=`date +%s`
echo "DONE ($((end-start))s)"
echo "****EXPORTING SOLIDITY SMART CONTRACT****"
start=`date +%s`
npx snarkjs zkey export solidityverifier "$OUTPUT_DIR"/"$CIRCUIT_NAME"_p2.zkey verifier.sol
end=`date +%s`
echo "DONE ($((end-start))s)"
}
mkdir -p logs
run 2>&1 | tee logs/"$CIRCUIT_NAME"_$(date '+%Y-%m-%d-%H-%M').log
include "../circom-pairing/circuits/bls_signature.circom";
include "../circom-pairing/circuits/curve.circom";
include "../circom-pairing/circuits/bls12_381_func.circom";
include "./sha256_bytes.circom";
Built using foundry(github) and forge. Verifiers (Light Client Contracts) can be generated from circuits using snarkjs
Mechanics The oracle works by generating and verifying Merkle Patricia proofs of the following Ethereum state:
Curve stETH/ETH pool contract account and the following slots from its storage trie:
admin_balances[0] admin_balances[1] stETH contract account and the following slots from its storage trie:
shares[0xDC24316b9AE028F1497c275EB9192a3Ea0f67022] keccak256(“lido.StETH.totalShares”) keccak256(“lido.Lido.beaconBalance”) keccak256(“lido.Lido.bufferedEther”) keccak256(“lido.Lido.depositedValidators”) keccak256(“lido.Lido.beaconValidators”)
Note: no public repository for relay functionality was found in succinctlabs github.
TODO: This section should give an overview of
Additional Information can be found in DeepDiveOnNearBridge: Relayers
On Gnosis Chain, after the Ethereum block in which the deposit transaction was included is finalized (generally 2 epochs, ~12 minutes) and the light client has been updated with a block of height greater than or equal to this block, our relayer automatically submits an executeMessage transaction to the Gnosis AMB.