Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into merkle_tree_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kroist committed Feb 21, 2024
2 parents b3aff02 + 13ae225 commit 9c87b9c
Show file tree
Hide file tree
Showing 12 changed files with 1,336 additions and 0 deletions.
870 changes: 870 additions & 0 deletions relations/Cargo.lock

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions relations/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "liminal-halo2-relations"
version = "0.0.0"
edition = "2021"
authors = ["Cardinal"]
documentation = "https://docs.rs/?"
homepage = "https://alephzero.org"
license = "Apache-2.0"
categories = ["cryptography"]
keywords = ["cryptography", "snark", "zero-knowledge", "liminal", "shielder"]
repository = "https://github.com/Cardinal-Cryptography/zk-apps"
description = "A collection of halo2-based relations for use in liminal."

[dependencies]
rand = "=0.8"
serde = { version = "=1.0", default-features = false, features = ["derive"] }
serde_json = "=1.0"

halo2-base = { package = "halo2-base", git = "https://github.com/Cardinal-Cryptography/halo2-lib", branch = "aleph" }


#[dev-dependencies]
# Fork with halo2curves = 0.6 to match the version of halo2-base
poseidon = { git = "https://github.com/zemse/pse-poseidon" }
11 changes: 11 additions & 0 deletions relations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#Notes and Accounts

#Relations

#Tests

```
cargo test
```

#Benchmarks
2 changes: 2 additions & 0 deletions relations/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly-2023-12-21"
21 changes: 21 additions & 0 deletions relations/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use halo2_base::{gates::GateChip, utils::BigPrimeField, AssignedValue, Context};

use crate::{
operation::{CircuitOperation, Operation},
CloneToVec,
};

pub trait Account<F: BigPrimeField>: CloneToVec<F> {
type CircuitAccount: CircuitAccount<F>;
type Op: Operation<F>;

fn update(&self, op: &Self::Op) -> Self;

fn load(&self, ctx: &mut Context<F>) -> Self::CircuitAccount;
}

pub trait CircuitAccount<F: BigPrimeField>: CloneToVec<AssignedValue<F>> {
type Op: CircuitOperation<F>;

fn update(&self, op: Self::Op, ctx: &mut Context<F>, gate: &GateChip<F>) -> Self;
}
26 changes: 26 additions & 0 deletions relations/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
pub mod account;
pub mod merkle_proof;
pub mod note;
pub mod operation;
pub mod relations;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Token {
AZERO,
USDT,
}

pub trait CloneToVec<T> {
fn clone_to_vec(&self) -> Vec<T>;
}

pub mod poseidon_consts {
/// Has to be greater than 1 and equal to RATE + 1, due to the outer Poseidon implementation.
pub const T_WIDTH: usize = RATE + 1;

pub const RATE: usize = 4;

pub const R_F: usize = 8;

pub const R_P: usize = 56;
}
62 changes: 62 additions & 0 deletions relations/src/merkle_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use halo2_base::{
gates::{GateChip, GateInstructions},
poseidon::hasher::PoseidonHasher,
utils::BigPrimeField,
AssignedValue, Context,
};

use crate::poseidon_consts::{RATE, T_WIDTH};

#[derive(Clone, Debug)]
pub struct MerkleProof<F: BigPrimeField, const TREE_HEIGHT: usize> {
pub path_shape: [bool; TREE_HEIGHT],
pub path: [F; TREE_HEIGHT],
}

#[derive(Clone, Debug)]
pub struct CircuitMerkleProof<F: BigPrimeField, const TREE_HEIGHT: usize> {
pub path_shape: [AssignedValue<F>; TREE_HEIGHT],
pub path: [AssignedValue<F>; TREE_HEIGHT],
}

impl<F: BigPrimeField, const TREE_HEIGHT: usize> MerkleProof<F, TREE_HEIGHT> {
pub fn new(path_shape: [bool; TREE_HEIGHT], path: [F; TREE_HEIGHT]) -> Self {
Self { path_shape, path }
}

pub fn load(&self, ctx: &mut Context<F>) -> CircuitMerkleProof<F, TREE_HEIGHT> {
let path_shape = self
.path_shape
.map(|x| ctx.load_witness(F::from_u128(x as u128)));
let path = self.path.map(|x| ctx.load_witness(x));

CircuitMerkleProof { path_shape, path }
}
}

impl<F: BigPrimeField, const TREE_HEIGHT: usize> CircuitMerkleProof<F, TREE_HEIGHT> {
pub fn verify(
&self,
ctx: &mut Context<F>,
gate: &GateChip<F>,
poseidon: &mut PoseidonHasher<F, T_WIDTH, RATE>,
root: AssignedValue<F>,
leaf: AssignedValue<F>,
) {
let mut current_node = leaf;

// TREE_HIGHT is definied in a way that path[TREE_HIGHT] would be the root
for i in 0..TREE_HEIGHT {
let sibling = self.path[i];
let shape = self.path_shape[i];

let selector = gate.is_zero(ctx, shape);
let left = gate.select(ctx, sibling, current_node, selector);
let right = gate.select(ctx, current_node, sibling, selector);
current_node = poseidon.hash_fix_len_array(ctx, gate, &[left, right]);
}

let eq = gate.is_equal(ctx, current_node, root);
gate.assert_is_const(ctx, &eq, &F::ONE);
}
}
51 changes: 51 additions & 0 deletions relations/src/note.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use halo2_base::{utils::ScalarField, AssignedValue, Context};

use crate::CloneToVec;

#[derive(Clone, Copy, Debug)]
pub struct Note<F: ScalarField> {
pub zk_id: F,
pub trapdoor: F,
pub nullifier: F,
pub account_hash: F,
}

impl<F: ScalarField> Note<F> {
pub fn new(note_id: F, trapdoor: F, nullifier: F, account_hash: F) -> Self {
Self {
zk_id: note_id,
trapdoor,
nullifier,
account_hash,
}
}

pub fn load(&self, ctx: &mut Context<F>) -> CircuitNote<F> {
CircuitNote {
zk_id: ctx.load_witness(self.zk_id),
trapdoor: ctx.load_witness(self.trapdoor),
nullifier: ctx.load_witness(self.nullifier),
account_hash: ctx.load_witness(self.account_hash),
}
}
}

impl<F: ScalarField> CloneToVec<F> for Note<F> {
fn clone_to_vec(&self) -> Vec<F> {
vec![self.zk_id, self.trapdoor, self.nullifier, self.account_hash]
}
}

#[derive(Clone, Copy, Debug)]
pub struct CircuitNote<F: ScalarField> {
pub zk_id: AssignedValue<F>,
pub trapdoor: AssignedValue<F>,
pub nullifier: AssignedValue<F>,
pub account_hash: AssignedValue<F>,
}

impl<F: ScalarField> CloneToVec<AssignedValue<F>> for CircuitNote<F> {
fn clone_to_vec(&self) -> Vec<AssignedValue<F>> {
vec![self.zk_id, self.trapdoor, self.nullifier, self.account_hash]
}
}
23 changes: 23 additions & 0 deletions relations/src/operation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use halo2_base::{utils::BigPrimeField, AssignedValue, Context};

pub trait Operation<F>
where
Self: Sized,
F: BigPrimeField,
{
type OpPriv: Into<Vec<F>>;
type OpPub: Into<Vec<F>>;

fn combine(op_priv: Self::OpPriv, op_pub: Self::OpPub) -> Option<Self>;
}

pub trait CircuitOperation<F>
where
Self: Sized,
F: BigPrimeField,
{
type OpPriv: From<Vec<AssignedValue<F>>>;
type OpPub: From<Vec<AssignedValue<F>>> + Into<Vec<AssignedValue<F>>> + Clone;

fn combine(op_priv: Self::OpPriv, op_pub: Self::OpPub) -> Option<Self>;
}
2 changes: 2 additions & 0 deletions relations/src/relations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod update_account;
pub mod update_note;
95 changes: 95 additions & 0 deletions relations/src/relations/update_account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use halo2_base::{
gates::{GateChip, GateInstructions},
poseidon::hasher::{spec::OptimizedPoseidonSpec, PoseidonHasher},
utils::BigPrimeField,
AssignedValue,
};
#[allow(unused_imports)]
use halo2_base::{
Context,
QuantumCell::{Constant, Existing, Witness},
};

use crate::{
account::CircuitAccount,
poseidon_consts::{RATE, R_F, R_P, T_WIDTH},
};

pub struct UpdateAccountInput<F, A>
where
F: BigPrimeField,
A: CircuitAccount<F>,
{
//public inputs
pub old_account_hash: AssignedValue<F>,
pub new_account_hash: AssignedValue<F>,
pub operation: A::Op,

//witnesses
pub old_account: A,
}

impl<F, A> UpdateAccountInput<F, A>
where
F: BigPrimeField,
A: CircuitAccount<F>,
{
pub fn new(
old_account_hash: AssignedValue<F>,
new_account_hash: AssignedValue<F>,
operation: A::Op,
old_account: A,
) -> Self {
Self {
old_account_hash,
new_account_hash,
operation,
old_account,
}
}
}

pub fn verify_account_circuit<F, A>(
ctx: &mut Context<F>,
gate: &GateChip<F>,
poseidon: &mut PoseidonHasher<F, T_WIDTH, RATE>,
account: &A,
account_hash: AssignedValue<F>,
) where
F: BigPrimeField,
A: CircuitAccount<F>,
{
let inner_account_hash = poseidon.hash_fix_len_array(ctx, gate, &account.clone_to_vec());
let eq = gate.is_equal(ctx, account_hash, inner_account_hash);
gate.assert_is_const(ctx, &eq, &F::ONE);
}

#[allow(dead_code)]
pub fn update_account_circuit<F, A>(ctx: &mut Context<F>, input: UpdateAccountInput<F, A>)
where
F: BigPrimeField,
A: CircuitAccount<F>,
{
let gate = GateChip::<F>::default();
let mut poseidon =
PoseidonHasher::<F, T_WIDTH, RATE>::new(OptimizedPoseidonSpec::new::<R_F, R_P, 0>());
poseidon.initialize_consts(ctx, &gate);

let old_account = input.old_account;
verify_account_circuit(
ctx,
&gate,
&mut poseidon,
&old_account,
input.old_account_hash,
);

let new_account = old_account.update(input.operation, ctx, &gate);
verify_account_circuit(
ctx,
&gate,
&mut poseidon,
&new_account,
input.new_account_hash,
);
}
Loading

0 comments on commit 9c87b9c

Please sign in to comment.