Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a922e32
ring-proof switched to SW jubjub
swasilyev Apr 9, 2026
5f1bfae
fmt
swasilyev Apr 9, 2026
ddddbfe
pasta-tree crate
swasilyev Apr 9, 2026
3ccf9d8
test_setup
swasilyev Apr 9, 2026
5872358
pasta ring works
swasilyev Apr 9, 2026
f7b0731
Cargo.tomls cleaned up
swasilyev Apr 10, 2026
a3d2683
stuff made pub mostly
swasilyev Apr 10, 2026
20cb451
shplonk openning
swasilyev Apr 10, 2026
928849f
"cosmetic" merged from master
swasilyev Apr 10, 2026
d37d0e3
cosmetic
swasilyev Apr 10, 2026
1c17388
folding fun
swasilyev Apr 16, 2026
2ffde5a
fmt
swasilyev Apr 16, 2026
e184553
merge master
swasilyev Apr 16, 2026
e615186
hiding ipa pcs
swasilyev Apr 21, 2026
a8ce9bb
Merge branch 'master' into pasta
swasilyev May 14, 2026
b665339
Merge branch 'master' into pasta
swasilyev May 15, 2026
1edc7fb
workspace toml updated
swasilyev May 15, 2026
65a8efa
workspace toml fixed
swasilyev May 15, 2026
e09d567
hiding ipa cleaned up
swasilyev May 15, 2026
9316408
verkle path struct
swasilyev May 15, 2026
438c065
per level proofs added
swasilyev May 15, 2026
dd36971
benches added
swasilyev May 15, 2026
aaa7647
auth_path cleaned up
swasilyev May 16, 2026
7440867
Merge branch 'master' into pasta
swasilyev May 17, 2026
17d9b86
master merged
swasilyev May 17, 2026
2be984d
generic-piop merged
swasilyev May 17, 2026
37b03c5
fmt
swasilyev May 17, 2026
4543955
generic-piop merged - 2 + fmt
swasilyev May 17, 2026
3559863
code cleaned up
swasilyev May 17, 2026
ba59436
tests cleaned up
swasilyev May 17, 2026
668f355
random_path
swasilyev May 17, 2026
9dc9a7d
prove/verify
swasilyev May 17, 2026
3f01d29
debug
swasilyev May 17, 2026
d0441df
non-hiding lol
swasilyev May 18, 2026
3a4482f
benches added
swasilyev May 19, 2026
29ffbc2
Merge branch 'master' into pasta
swasilyev May 20, 2026
19d9190
fix
swasilyev May 20, 2026
f9a3597
Merge branch 'master' into pasta
swasilyev May 20, 2026
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
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[workspace]
resolver = "2"
members = [
# "evm-vrfier",
# "evm-vrfier",
"pasta-tree",
"w3f-plonk-common",
"w3f-ring-proof",
# "w3f-ring-vrf-snark",
Expand All @@ -14,8 +15,8 @@ ark-ec = { version = "0.6", default-features = false }
ark-poly = { version = "0.6", default-features = false }
ark-serialize = { version = "0.6", default-features = false, features = ["derive"] }
#w3f-pcs = { version = "0.0.6", default-features = false }
#w3f-pcs = { path = "../fflonk", default-features = false }
w3f-pcs = { version = "0.0.6", git = "https://github.com/paritytech/fflonk/", default-features = false }
w3f-pcs = { path = "../fflonk", default-features = false }
#w3f-pcs = { version = "0.0.6", git = "https://github.com/paritytech/fflonk/", default-features = false }
#w3f-plonk-common = { version = "0.0.7", default-features = false }
w3f-plonk-common = { path = "w3f-plonk-common", default-features = false }
rayon = { version = "1", default-features = false }
Expand Down
53 changes: 53 additions & 0 deletions pasta-tree/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[package]
name = "pasta-tree"
version = "0.0.1"
edition = "2024"
authors = ["Sergey Vasilyev <swasilyev@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Hiding Verkle Tree over Pasta Curves"
keywords = ["cryptography", "accumulator", "verkle", "ring-vrf"]
repository = "https://github.com/w3f/ring-proof"

[dependencies]
w3f-pcs.workspace = true
w3f-plonk-common.workspace = true
w3f-ring-proof = { path = "../w3f-ring-proof" }
ark-pallas = { version = "0.5", default-features = false, features = ["curve"] }
ark-vesta = { version = "0.5", default-features = false }
ark-transcript.workspace = true
ark-std.workspace = true
ark-ff.workspace = true
ark-ec.workspace = true
ark-poly.workspace = true
ark-serialize.workspace = true
rayon = { workspace = true, optional = true }

[dev-dependencies]
ark-bls12-381.workspace = true
criterion.workspace = true

[features]
default = ["std"]
std = [
"ark-std/std",
"ark-ff/std",
"ark-ec/std",
"ark-poly/std",
"ark-serialize/std",
"w3f-pcs/std",
"w3f-plonk-common/std",
"w3f-ring-proof/std",
]
parallel = [
"std",
"rayon",
"ark-std/parallel",
"ark-ff/parallel",
"ark-ec/parallel",
"ark-poly/parallel",
"w3f-pcs/parallel",
"w3f-plonk-common/parallel",
"w3f-ring-proof/parallel",
]
print-trace = ["ark-std/print-trace", ]
asm = ["w3f-pcs/asm"]
64 changes: 64 additions & 0 deletions pasta-tree/src/auth_path/blinded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::auth_path::node::LevelWitnessWithBlinding;
use crate::{CycleParams, CycleSide};
use ark_ec::CurveGroup;
use ark_ff::PrimeField;

pub struct AuthenticationPathWithBlinding<C0: CurveGroup, C1: CurveGroup> {
pub(crate) c0_path: Vec<LevelWitnessWithBlinding<C0::Affine>>,
pub(crate) c1_path: Vec<LevelWitnessWithBlinding<C1::Affine>>,
}

#[derive(Clone, Debug)]
pub struct BlindedAuthenticationPath<C0: CurveGroup, C1: CurveGroup> {
pub(crate) c0_path: Vec<C0::Affine>,
pub(crate) c1_path: Vec<C1::Affine>,
}

impl<F0, F1, C0, C1> AuthenticationPathWithBlinding<C0, C1>
where
F0: PrimeField,
F1: PrimeField,
C0: CurveGroup<BaseField=F1, ScalarField=F0>,
C1: CurveGroup<BaseField=F0, ScalarField=F1>,
{
pub(crate) fn apply_bfs(
&self,
params: &CycleParams<C0, C1>,
) -> BlindedAuthenticationPath<C0, C1> {
let c0_path = self.c0_path.iter()
.map(|c0_level| c0_level.blinded_path_node(&params.c0_params.pcs_params).unwrap())
.collect();
let c1_path = self.c1_path.iter()
.map(|c1_level| c1_level.blinded_path_node(&params.c1_params.pcs_params).unwrap())
.collect();
BlindedAuthenticationPath {
c0_path,
c1_path,
}
}
pub(crate) fn compute_root(
&self,
params: &CycleParams<C0, C1>,
) -> Result<CycleSide<C0::Affine, C1::Affine>, ()> {
let mut c0_path_iter = self.c0_path.iter();
let c0_nodes = c0_path_iter.next().unwrap();
let mut parent_on_c1 = c0_nodes.compute_parent(&params.c1_params)?;
for c1_nodes in self.c1_path.iter() {
let next_c1_path_node = c1_nodes.blinded_path_node(&params.c1_params.pcs_params)?;
debug_assert_eq!(parent_on_c1, next_c1_path_node);
(parent_on_c1 == next_c1_path_node).ok_or(())?;
let parent_on_c0 = c1_nodes.compute_parent(&params.c0_params)?;
match c0_path_iter.next() {
Some(c0_nodes) => {
let next_c0_path_node =
c0_nodes.blinded_path_node(&params.c0_params.pcs_params)?;
debug_assert_eq!(parent_on_c0, next_c0_path_node);
(parent_on_c0 == next_c0_path_node).ok_or(())?;
parent_on_c1 = c0_nodes.compute_parent(&params.c1_params)?;
}
None => return Ok(CycleSide::C0(parent_on_c0)),
}
}
Ok(CycleSide::C1(parent_on_c1))
}
}
3 changes: 3 additions & 0 deletions pasta-tree/src/auth_path/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod blinded;
pub mod node;
pub mod path;
106 changes: 106 additions & 0 deletions pasta-tree/src/auth_path/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::CycleSideParams;
use crate::ipa_hiding::HidingIpa;
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{PrimeField, Zero};
use ark_std::rand::Rng;
use ark_std::UniformRand;

/// An element of a tree authentication path. A node on the path together with it's sibling.
/// `path_node = self.siblings[self.path_node_idx]` is the node on the path.
/// The next node on the path to the root can be computed as `parent = commit(self.siblings)`.
// TODO: the minimal witness is x-coords of the siblings, and y_i
#[derive(Clone, Debug)]
pub struct LevelWitness<G> {
pub(crate) siblings: Vec<G>,
pub(crate) path_node_idx: usize,
}

impl<G: AffineRepr> LevelWitness<G> {
pub(crate) fn new(siblings: Vec<G>, path_node_idx: usize) -> Result<Self, ()> {
debug_assert!(path_node_idx < siblings.len());
(path_node_idx < siblings.len()).ok_or(())?;
Ok(Self {
siblings,
path_node_idx,
})
}

fn x_coords(&self) -> Vec<G::BaseField> {
self.siblings.iter().map(|p| p.x()).flatten().collect()
}

pub(crate) fn path_node(&self) -> G {
self.siblings[self.path_node_idx]
}

pub(crate) fn with_blinding(
&self,
self_bf: G::ScalarField,
parent_bf: G::BaseField,
) -> LevelWitnessWithBlinding<G> {
LevelWitnessWithBlinding {
level_witness: self.clone(),
bf: G::ScalarField::zero(), //self_bf,
parent_bf: G::BaseField::zero(), //parent_bf,
}
}

pub(crate) fn with_random_blinding<R: Rng>(
&self,
parent_bf: G::BaseField,
rng: &mut R,
) -> LevelWitnessWithBlinding<G> {
self.with_blinding(G::ScalarField::rand(rng), parent_bf)
}

pub(crate) fn compute_parent<C: CurveGroup<ScalarField = G::BaseField>>(
&self,
params: &CycleSideParams<C, G>,
) -> Result<C::Affine, ()>
where
G::BaseField: PrimeField,
{
self.compute_parent_with_bf(params, C::ScalarField::zero())
}

fn compute_parent_with_bf<C: CurveGroup<ScalarField = G::BaseField>>(
&self,
params: &CycleSideParams<C, G>,
bf: C::ScalarField,
) -> Result<C::Affine, ()>
where
G::BaseField: PrimeField,
{
params.commit_nodes(&self.siblings, bf)
}
}

/// NB! It is not "blinded", meaning that the blinding factor hasn't been applied.
pub struct LevelWitnessWithBlinding<G: AffineRepr> {
pub(crate) level_witness: LevelWitness<G>,
/// the verifier gets `Ci' = siblings[i] + bf.H`
pub(crate) bf: G::ScalarField,
/// Let `Ci = c1.G1 + ... + cm.Gm` -- the non-hiding commitment to a level.
/// Provided that, instead, the verifier gets `Ci' = Ci + bf.H`,
/// when opening the commitment, the prover interprets it as a hiding commitment
/// and needs to know the parent's blinding factor to open to the same values.
/// The root is not blinded, so `parent_bf = 0` for the level below the root.
pub(crate) parent_bf: G::BaseField, // = C::ScalarField
}

impl<G: AffineRepr> LevelWitnessWithBlinding<G> {
pub(crate) fn blinded_path_node(&self, ipa_pcs: &HidingIpa<G::Group>) -> Result<G, ()> {
let blinded_path_node = ipa_pcs.reblind(self.level_witness.path_node(), G::ScalarField::zero(), self.bf)?;
Ok(blinded_path_node.0)
}

pub(crate) fn compute_parent<C: CurveGroup<ScalarField = G::BaseField>>(
&self,
params: &CycleSideParams<C, G>,
) -> Result<C::Affine, ()>
where
G::BaseField: PrimeField,
{
self.level_witness.compute_parent_with_bf(params, self.parent_bf)
}
}
117 changes: 117 additions & 0 deletions pasta-tree/src/auth_path/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use crate::auth_path::blinded::AuthenticationPathWithBlinding;
use crate::auth_path::node::LevelWitness;
use crate::{CycleParams, CycleSide};
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_std::rand::Rng;

/// A non-hiding authentication path from a leaf to the root, split between the curves of the cycle.
/// For each tree level (excluding the root), the corresponding element of the authentication path (`LevelWitness`)
/// contains the root-path node at that level, together with all its siblings.
/// That allows to recompute the parent -- the path node at the *previous* level, up to the root.
/// `path_0[0]` contains the leaf (with its siblings).
/// `commit(path_0[k].siblings) = path_1[k].siblings[path_1[k].i]`, if `path_1[k]` exists,
/// otherwise it's the root.
pub struct AuthenticationPath<C0: CurveGroup, C1: CurveGroup> {
/// Nodes on the `C0` curve.
pub c0_path: Vec<LevelWitness<C0::Affine>>,
/// Nodes on the `C1` curve.
pub c1_path: Vec<LevelWitness<C1::Affine>>,
}

impl<F0, F1, C0, C1> AuthenticationPath<C0, C1>
where
F0: PrimeField,
F1: PrimeField,
C0: CurveGroup<BaseField=F1, ScalarField=F0>,
C1: CurveGroup<BaseField=F0, ScalarField=F1>,
{
pub fn with_blinding<R: Rng>(&self, rng: &mut R) -> AuthenticationPathWithBlinding<C0, C1> {
let mut path_0 = Vec::with_capacity(self.c0_path.len());
let mut path_1 = Vec::with_capacity(self.c1_path.len());

let mut c0_path_iter = self.c0_path.iter();
let mut c0_nodes = c0_path_iter.next().unwrap(); // shouldn't be empty
let mut c0_bf = C0::ScalarField::rand(rng);
for c1_nodes in self.c1_path.iter() {
let c1_bf = C1::ScalarField::rand(rng);
path_0.push(c0_nodes.with_blinding(c0_bf, c1_bf));
match c0_path_iter.next() {
Some(c0_nodes_) => {
c0_nodes = c0_nodes_;
c0_bf = C0::ScalarField::rand(rng);
path_1.push(c1_nodes.with_blinding(c1_bf, c0_bf));
}
None => {
// then the parent of `c1_nodes` is the root
c0_bf = C0::ScalarField::zero(); // `c0_bf = 0` indicates this case
let root_bf = c0_bf;
path_1.push(c1_nodes.with_blinding(c1_bf, root_bf));
}
}
}
if !c0_bf.is_zero() {
// then `c0_nodes` are the level below the root
let root_bf = C1::ScalarField::zero();
path_0.push(c0_nodes.with_blinding(c0_bf, root_bf));
}

debug_assert_eq!(path_0.len(), self.c0_path.len());
debug_assert_eq!(path_1.len(), self.c1_path.len());

AuthenticationPathWithBlinding {
c0_path: path_0,
c1_path: path_1,
}
}

fn get_leaf(&self) -> C0::Affine {
self.c0_path[0].path_node()
}

fn compute_root(
&self,
params: &CycleParams<C0, C1>,
) -> Result<CycleSide<C0::Affine, C1::Affine>, ()> {
let mut c0_path_iter = self.c0_path.iter();
let c0_nodes = c0_path_iter.next().unwrap(); // shouldn't be empty
let mut parent_on_c1 = c0_nodes.compute_parent(&params.c1_params)?;
for c1_nodes in self.c1_path.iter() {
debug_assert_eq!(parent_on_c1, c1_nodes.path_node());
(parent_on_c1 == c1_nodes.path_node()).ok_or(())?;
let parent_on_c0 = c1_nodes.compute_parent(&params.c0_params)?;
match c0_path_iter.next() {
Some(c0_nodes) => {
debug_assert_eq!(parent_on_c0, c0_nodes.path_node());
(parent_on_c0 == c0_nodes.path_node()).ok_or(())?;
parent_on_c1 = c0_nodes.compute_parent(&params.c1_params)?;
}
None => return Ok(CycleSide::C0(parent_on_c0)),
}
}
Ok(CycleSide::C1(parent_on_c1))
}
}

#[cfg(test)]
mod tests {
use super::*;
use ark_std::{test_rng, UniformRand};
use crate::tests::{random_nodes, random_path};

#[test]
fn test_auth_path() {
let rng = &mut test_rng();

let domain_size = 2usize.pow(9);
let params = CycleParams::<ark_pallas::Projective, ark_vesta::Projective>::setup(domain_size, rng);;

let (leaf, path, root) = random_path(&params, 2, rng);

assert_eq!(path.get_leaf(), leaf);
assert_eq!(path.compute_root(&params).unwrap(), root);

let path_with_bfs = path.with_blinding(rng);
assert_eq!(path_with_bfs.compute_root(&params).unwrap(), root);
}
}
Loading
Loading