Skip to content

Commit

Permalink
feat(plonky2): Add circuits primitives needed for commitment mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimo99 committed Jun 26, 2023
1 parent e853c0d commit 68040ef
Show file tree
Hide file tree
Showing 20 changed files with 12,414 additions and 175 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
- name: Run circom tests
run: nix develop -c make test-circom-circuits

- name: Run plonky2 circuit tests
run: nix develop -c make test-plonky2-circuits

- name: Run Verifier in Cosmos - Relayer test
run: nix develop -c yarn test './tests/cosmosLightClient/test-verifier-in-cosmos-relay.ts'

Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ test-solidity-beacon-light-client-verifier:
test-circom-circuits:
cd beacon-light-client/circom && \
./test/run_snarkit2_tests.sh

test-plonky2-circuits:
cd beacon-light-client/plonky2/circuits && \
cargo test --release
3 changes: 3 additions & 0 deletions beacon-light-client/circom/circuits/validator_balances.circom
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@ template ValidatorBalances(N) {
signal output commitment;

signal currentEpoch <-- slot \ 32;
// signal currentEpochRemainder <-- slot % 32;
// slot === currentEpoch * 32 + currentEpochRemainder;

signal epochHighestSlot <== currentEpoch * 32;

// Should be LessThanOrEqualBitsCheck(64)([slot, epochHighestSlot + 32])
signal slotLessThan <== LessThanBitsCheck(64)([slot, epochHighestSlot]);

signal slotBits[256] <== SSZNum(64)(slot);
Expand Down
24 changes: 23 additions & 1 deletion beacon-light-client/circom/scripts/validator_balances/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { BeaconApi } from '../../../../relay/implementations/beacon-api';
import { Tree } from '@chainsafe/persistent-merkle-tree';
import { bytesToHex } from '../../../../libs/typescript/ts-utils/bls';
import { sha256 } from 'ethers/lib/utils';
import { verifyMerkleProof } from '../../../../libs/typescript/ts-utils/ssz-utils';
import {
verifyMerkleProof,
hashTreeRoot,
} from '../../../../libs/typescript/ts-utils/ssz-utils';
import { get } from 'node:http';

(async () => {
const { ssz } = await import('@lodestar/types');
Expand All @@ -24,6 +28,22 @@ import { verifyMerkleProof } from '../../../../libs/typescript/ts-utils/ssz-util

const beaconState = ssz.capella.BeaconState.deserialize(beaconStateSZZ);

console.log(
BigInt(
'0x' +
bytesToHex(
ssz.phase0.Validator.fields.exitEpoch.hashTreeRoot(
beaconState.validators[0].exitEpoch,
),
),
)
.toString(2)
.padStart(256, '0')
.split('')
.map(x => `"${x.toString()}"`)
.join(','),
);

console.log(
BigInt(
'0x' +
Expand All @@ -41,6 +61,8 @@ import { verifyMerkleProof } from '../../../../libs/typescript/ts-utils/ssz-util
// const beaconStateView = ssz.capella.BeaconState.toViewDU(beaconState);
// const beaconStateTree = new Tree(beaconStateView.node);

// beaconStateTree.getProof()

// console.log(beaconState.slot);

// console.log(ssz.capella.BeaconState.getPathInfo(['slot']).gindex);
Expand Down
26 changes: 26 additions & 0 deletions beacon-light-client/plonky2/circuits/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions beacon-light-client/plonky2/circuits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ edition = "2021"
plonky2 = { git = "https://github.com/polymerdao/plonky2", rev = "4cb0b48df1d227d5461a4c28ed025aaea64e2e62" }
plonky2_sha256 = { git = "https://github.com/polymerdao/plonky2-sha256", branch = "main" }
sha2 = "0.9"
serde = "1.0.164"
serde_json = "1.0.96"
anyhow = "1.0.71"
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use anyhow::Result;
use circuits::is_valid_merkle_branch::is_valid_merkle_branch;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::field::types::Field;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig};
use plonky2::plonk::config::PoseidonGoldilocksConfig;
use serde::Deserialize;
use std::fs::File;
use std::io::BufReader;
use std::println;

#[derive(Debug, Deserialize)]
struct RawMerkleProof {
root: Vec<String>,
leaf: Vec<String>,
branch: Vec<Vec<String>>,
index: u64,
}

#[derive(Debug)]
struct MerkleProof {
root: Vec<bool>,
leaf: Vec<bool>,
branch: Vec<Vec<bool>>,
index: u64,
}

fn main() -> Result<()> {
let input_file = File::open("is_valid_merkle_branch_input.json")?;
let reader = BufReader::new(input_file);
let raw_merkle_proof: RawMerkleProof = serde_json::from_reader(reader)?;

let merkle_proof = MerkleProof {
root: raw_merkle_proof
.root
.into_iter()
.map(|s| s == "1")
.collect(),
leaf: raw_merkle_proof
.leaf
.into_iter()
.map(|s| s == "1")
.collect(),
branch: raw_merkle_proof
.branch
.into_iter()
.map(|v| v.into_iter().map(|s| s == "1").collect())
.collect(),
index: raw_merkle_proof.index,
};

create_proof(merkle_proof)?;

Ok(())
}

fn create_proof(merkle_proof: MerkleProof) -> std::result::Result<(), anyhow::Error> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = GoldilocksField;

let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);

let hasher = is_valid_merkle_branch(&mut builder, merkle_proof.branch.len());
println!("Building circuit");

let data = builder.build::<C>();

println!("Building proof");

let mut pw = PartialWitness::new();
pw.set_target(hasher.index, F::from_canonical_u64(merkle_proof.index));

for i in 0..256 {
pw.set_bool_target(hasher.root[i], merkle_proof.root[i]);
pw.set_bool_target(hasher.leaf[i], merkle_proof.leaf[i]);
}

for i in 0..merkle_proof.branch.len() {
for j in 0..256 {
pw.set_bool_target(hasher.branch[i][j], merkle_proof.branch[i][j]);
}
}

let proof = data.prove(pw).unwrap();

println!("Verifying proof");

let res = data.verify(proof);

res
}
104 changes: 104 additions & 0 deletions beacon-light-client/plonky2/circuits/examples/merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::{marker::PhantomData, println};

use plonky2::{
field::{goldilocks_field::GoldilocksField, types::Field},
hash::{
hash_types::{RichField},
hashing::{PlonkyPermutation, SPONGE_WIDTH},
merkle_tree::MerkleTree,
},
plonk::config::{GenericHashOut, Hasher},
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AdditionPermutation<F: RichField> {
phantom: PhantomData<F>,
}

impl<F: RichField> PlonkyPermutation<F> for AdditionPermutation<F> {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
let mut output = input;
output.rotate_left(1);
output
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
#[serde(bound = "")]
pub struct AdditionHash<F: RichField>(F);

impl GenericHashOut<GoldilocksField> for AdditionHash<GoldilocksField> {
fn to_bytes(&self) -> Vec<u8> {
let bytes = self.0.0.to_le_bytes().to_vec();
bytes
}

fn from_bytes(bytes: &[u8]) -> Self {
let mut array = [0u8; 8];
let bytes = &bytes[..array.len()]; // panics if not enough input
array.copy_from_slice(bytes);
let num = u64::from_le_bytes(array);
AdditionHash(GoldilocksField::from_canonical_u64(num))
}

fn to_vec(&self) -> Vec<GoldilocksField> {
vec![self.0]
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AdditionHasher {
phantom: PhantomData<GoldilocksField>,
}

impl Hasher<GoldilocksField> for AdditionHasher {
const HASH_SIZE: usize = std::mem::size_of::<GoldilocksField>();

type Hash = AdditionHash<GoldilocksField>;
type Permutation = AdditionPermutation<GoldilocksField>;

fn hash_no_pad(input: &[GoldilocksField]) -> Self::Hash {
AdditionHash(input.iter().fold(GoldilocksField::ZERO, |acc, x| acc + *x))
}

fn hash_public_inputs(input: &[GoldilocksField]) -> Self::Hash {
Self::hash_no_pad(input)
}

fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
AdditionHash(left.0 + right.0)
}
}

fn main() {
type F = GoldilocksField;

let merkle_tree = MerkleTree::<F, AdditionHasher>::new(
vec![
vec![F::from_canonical_u32(1)],
vec![F::from_canonical_u32(2)],
vec![F::from_canonical_u32(3)],
vec![F::from_canonical_u32(4)],
vec![F::from_canonical_u32(5)],
vec![F::from_canonical_u32(6)],
vec![F::from_canonical_u32(7)],
vec![F::from_canonical_u32(8)],
vec![F::from_canonical_u32(9)],
vec![F::from_canonical_u32(10)],
vec![F::from_canonical_u32(11)],
vec![F::from_canonical_u32(12)],
vec![F::from_canonical_u32(13)],
vec![F::from_canonical_u32(14)],
vec![F::from_canonical_u32(15)],
vec![F::from_canonical_u32(16)],
],
0,
);

let proof = merkle_tree.prove(3);

println!("{:?}", proof);

println!("{:?}", merkle_tree.digests);
}
Loading

0 comments on commit 68040ef

Please sign in to comment.