Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plonky2): Add circuits primitives needed for commitment mapper #168

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

6 changes: 6 additions & 0 deletions beacon-light-client/plonky2/circuits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[net]
git-fetch-with-cli = true

[package]
name = "circuits"
version = "0.1.0"
Expand All @@ -9,3 +12,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
Loading