Skip to content
Merged
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
4 changes: 3 additions & 1 deletion w3f-plonk-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[package]
name = "w3f-plonk-common"
version = "0.0.1"
version = "0.0.2"
edition = "2021"
authors = ["Sergey Vasilyev <swasilyev@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Infrastructure for creating plonk-like proofs"
keywords = ["cryptography", "plonk"]
repository = "https://github.com/w3f/ring-proof"

[dependencies]
ark-std.workspace = true
Expand Down Expand Up @@ -44,3 +45,4 @@ parallel = [
]
print-trace = ["ark-std/print-trace"]
asm = ["w3f-pcs/asm"]
test-vectors = []
2 changes: 1 addition & 1 deletion w3f-plonk-common/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<F: FftField> Domain<F> {
pub(crate) fn column(&self, mut evals: Vec<F>, hidden: bool) -> FieldColumn<F> {
let len = evals.len();
assert!(len <= self.capacity);
if self.hiding && hidden {
if self.hiding && hidden && !cfg!(feature = "test-vectors") {
evals.resize(self.capacity, F::zero());
evals.resize_with(self.domains.x1.size(), || {
F::rand(&mut getrandom_or_panic::getrandom_or_panic())
Expand Down
5 changes: 3 additions & 2 deletions w3f-ring-proof/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[package]
name = "w3f-ring-proof"
version = "0.0.1"
version = "0.0.2"
edition = "2021"
authors = ["Sergey Vasilyev <swasilyev@gmail.com>"]
license = "MIT/Apache-2.0"
description = "zk-proof of knowledge of the blinding factor for a Pedersen commitment"
keywords = ["cryptography", "ring-vrf"]
repository = "https://github.com/w3f/ring-proof"

[dependencies]
ark-std.workspace = true
Expand All @@ -16,7 +17,6 @@ ark-serialize.workspace = true
w3f-pcs.workspace = true
rayon = { workspace = true, optional = true }
w3f-plonk-common = { path="../w3f-plonk-common", default-features = false }
blake2 = { version = "0.10", default-features = false }
ark-transcript = { version = "0.0.3", default-features = false }

[dev-dependencies]
Expand Down Expand Up @@ -49,3 +49,4 @@ print-trace = [
"w3f-plonk-common/print-trace"
]
asm = [ "w3f-pcs/asm" ]
test-vectors = [ "w3f-plonk-common/test-vectors" ]
27 changes: 3 additions & 24 deletions w3f-ring-proof/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#![cfg_attr(not(feature = "std"), no_std)]

use ark_ec::twisted_edwards::{Affine, TECurveConfig};
use ark_ec::AffineRepr;
use ark_ff::PrimeField;
use ark_serialize::CanonicalSerialize;
use ark_std::rand::RngCore;
Expand All @@ -24,26 +22,6 @@ pub type RingProof<F, CS> = Proof<F, CS, RingCommitments<F, <CS as PCS<F>>::C>,
/// Polynomial Commitment Schemes.
pub use w3f_pcs::pcs;

// Try and increment hash to curve.
pub(crate) fn hash_to_curve<F: PrimeField, Curve: TECurveConfig<BaseField = F>>(
message: &[u8],
) -> Affine<Curve> {
use blake2::Digest;
let mut seed = message.to_vec();
let cnt_offset = seed.len();
seed.push(0);
loop {
let hash: [u8; 64] = blake2::Blake2b::digest(&seed[..]).into();
let x = F::from_le_bytes_mod_order(&hash);
if let Some(point) = Affine::<Curve>::get_point_from_y_unchecked(x, false) {
let point = point.clear_cofactor();
assert!(point.is_in_correct_subgroup_assuming_on_curve());
return point;
}
seed[cnt_offset] += 1;
}
}

#[derive(Clone)]
pub struct ArkTranscript(ark_transcript::Transcript);

Expand Down Expand Up @@ -121,7 +99,7 @@ mod tests {
ArkTranscript::new(b"w3f-ring-proof-test"),
);
let t_verify = start_timer!(|| "Verify");
let res = ring_verifier.verify_ring_proof(proof, result.into_affine());
let res = ring_verifier.verify(proof, result.into_affine());
end_timer!(t_verify);
assert!(res);
}
Expand Down Expand Up @@ -160,7 +138,8 @@ mod tests {
let domain = Domain::new(domain_size, true);
let h = EdwardsAffine::rand(rng);
let seed = EdwardsAffine::rand(rng);
let piop_params = PiopParams::setup(domain, h, seed);
let padding = EdwardsAffine::rand(rng);
let piop_params = PiopParams::setup(domain, h, seed, padding);

(pcs_params, piop_params)
}
Expand Down
45 changes: 27 additions & 18 deletions w3f-ring-proof/src/piop/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,38 @@ use w3f_plonk_common::gadgets::ec::AffineColumn;

use crate::piop::FixedColumns;

/// Plonk Interactive Oracle Proofs (PIOP) parameters.
#[derive(Clone)]
pub struct PiopParams<F: PrimeField, Curve: TECurveConfig<BaseField = F>> {
// Domain over which the piop is represented.
/// Domain over which the piop is represented.
pub(crate) domain: Domain<F>,

// Number of bits used to represent a jubjub scalar.
/// Number of bits used to represent a jubjub scalar.
pub(crate) scalar_bitlen: usize,

// Length of the part of the column representing the public keys (including the padding).
/// Length of the part of the column representing the public keys (including the padding).
pub keyset_part_size: usize,

// The blinding base, a point from jubjub.
/// Blinding base point.
pub(crate) h: Affine<Curve>,

// The point to start the summation from (as zero doesn't have a SW affine representation),
// should be from the jubjub prime-order subgroup complement.
/// Summation base point.
pub(crate) seed: Affine<Curve>,

// The point used to pad the actual list of public keys. Should be of an unknown dlog.
pub(crate) padding_point: Affine<Curve>,
/// The point used to pad the list of public keys.
pub(crate) padding: Affine<Curve>,
}

impl<F: PrimeField, Curve: TECurveConfig<BaseField = F>> PiopParams<F, Curve> {
pub fn setup(domain: Domain<F>, h: Affine<Curve>, seed: Affine<Curve>) -> Self {
let padding_point = crate::hash_to_curve(b"/w3f/w3f-ring-proof/padding");
/// Initialize PIOP parameters.
///
/// - `domain`: polynomials evaluation domain.
/// - `h`: Blinding base point.
/// - `seed`: Accumulation base point
/// - `padding`: The point used to pad the list of public keys.
///
/// All points should be of an unknown discrete log.
pub fn setup(
domain: Domain<F>,
h: Affine<Curve>,
seed: Affine<Curve>,
padding: Affine<Curve>,
) -> Self {
let scalar_bitlen = Curve::ScalarField::MODULUS_BIT_SIZE as usize;
// 1 accounts for the last cells of the points and bits columns that remain unconstrained
let keyset_part_size = domain.capacity - scalar_bitlen - 1;
Expand All @@ -42,7 +49,7 @@ impl<F: PrimeField, Curve: TECurveConfig<BaseField = F>> PiopParams<F, Curve> {
keyset_part_size,
h,
seed,
padding_point,
padding,
}
}

Expand All @@ -59,7 +66,7 @@ impl<F: PrimeField, Curve: TECurveConfig<BaseField = F>> PiopParams<F, Curve> {
pub fn points_column(&self, keys: &[Affine<Curve>]) -> AffineColumn<F, Affine<Curve>> {
assert!(keys.len() <= self.keyset_part_size);
let padding_len = self.keyset_part_size - keys.len();
let padding = vec![self.padding_point; padding_len];
let padding = vec![self.padding; padding_len];
let points = [keys, &padding, &self.power_of_2_multiples_of_h()].concat();
assert_eq!(points.len(), self.domain.capacity - 1);
AffineColumn::public_column(points, &self.domain)
Expand Down Expand Up @@ -107,8 +114,10 @@ mod tests {
let rng = &mut test_rng();
let h = EdwardsAffine::rand(rng);
let seed = EdwardsAffine::rand(rng);
let padding = EdwardsAffine::rand(rng);
let domain = Domain::new(1024, false);
let params = PiopParams::<Fq, BandersnatchConfig>::setup(domain, h, seed);

let params = PiopParams::<Fq, BandersnatchConfig>::setup(domain, h, seed, padding);
let t = Fr::rand(rng);
let t_bits = params.scalar_part(t);
let th = cond_sum(&t_bits, &params.power_of_2_multiples_of_h());
Expand Down
82 changes: 43 additions & 39 deletions w3f-ring-proof/src/ring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,38 @@ use crate::PiopParams;
const IDLE_ROWS: usize = ZK_ROWS + 1;

/// Commitment to a list of VRF public keys as is used as a public input to the ring proof SNARK verifier.

/// The VRF keys are (inner) curve points that we represent in the affine short Weierstrass coordinates.
///
/// The VRF keys are (inner) curve points that we represent in the affine Twisted Edwards coordinates.
/// We commit to the coordinate vectors independently using KZG on the outer curve. To make the commitment
/// updatable we use SRS in the Lagrangian form: `L1, ..., Ln`, where `Li = L_i(t)G`.
/// The commitment to a vector `a1, ..., an` is then `a1L1 + ... + anLn`.

///
/// We pad the list of keys with a `padding` point with unknown dlog up to a certain size.
/// Additionally, to make the commitment compatible with the snark,
/// we append the power-of-2 powers of the VRF blinding Pedersen base
/// `H, 2H, 4H, ..., 2^(s-1)H`, where `s` is the bitness of the VRF curve scalar field.
/// The last `IDLE_ROWS = 4` elements are set to `(0, 0)`.

///
/// Thus, the vector of points we commit to coordinatewise is
/// `pk1, ..., pkn, padding, ..., padding, H, 2H, ..., 2^(s-1)H, 0, 0, 0, 0`

// `KzgCurve` -- outer curve, subgroup of a pairing-friendly curve. We instantiate it with bls12-381 G1.
// `VrfCurveConfig` -- inner curve, the curve used by the VRF, in SW form. We instantiate it with Bandersnatch.
// `F` shared scalar field of the outer and the base field of the inner curves.
#[derive(Clone, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
pub struct Ring<
F: PrimeField,
KzgCurve: Pairing<ScalarField = F>,
VrfCurveConfig: TECurveConfig<BaseField = F>,
> {
// KZG commitments to the coordinates of the vector described above
/// KZG commitment to the x coordinates of the described vector.
pub cx: KzgCurve::G1Affine,
/// KZG commitment to the y coordinates of the described vector.
pub cy: KzgCurve::G1Affine,
// KZG commitment to a bitvector highlighting the part of the vector corresponding to the public keys.
/// KZG commitment to a bitvector highlighting the part of the vector corresponding to the public keys.
pub selector: KzgCurve::G1Affine,
// maximal number of keys the commitment can "store". For domain of size `N` it is `N - (s + IDLE_ROWS)`
/// Maximal number of keys the commitment can "store". For domain of size `N` it is `N - (s + IDLE_ROWS)`.
pub max_keys: usize,
// the number of keys "stored" in this commitment
/// Number of keys "stored" in this commitment.
pub curr_keys: usize,
// a parameter
pub padding_point: Affine<VrfCurveConfig>,
// Padding point.
pub padding: Affine<VrfCurveConfig>,
}

impl<
Expand All @@ -76,22 +73,23 @@ impl<
VrfCurveConfig: TECurveConfig<BaseField = F>,
> Ring<F, KzgCurve, VrfCurveConfig>
{
// Builds the commitment to the vector
// `padding, ..., padding, H, 2H, ..., 2^(s-1)H, 0, 0, 0, 0`.
// We compute it as a sum of commitments of 2 vectors:
// `padding, ..., padding`, and
// `0, ..., 0, (H - padding), (2H - padding), ..., (2^(s-1)H - padding), -padding, -padding, -padding, -padding`.
// The first one is `padding * G`, the second requires an `(IDLE_ROWS + s)`-msm to compute.
/// Builds the commitment to the vector
/// `padding, ..., padding, H, 2H, ..., 2^(s-1)H, 0, 0, 0, 0`.
///
/// We compute it as a sum of commitments of 2 vectors:
/// `padding, ..., padding`, and
/// `0, ..., 0, (H - padding), (2H - padding), ..., (2^(s-1)H - padding), -padding, -padding, -padding, -padding`.
/// The first one is `padding * G`, the second requires an `(IDLE_ROWS + s)`-msm to compute.
///
/// - `piop_params`: SNARK parameters
/// - `srs`: Should return `srs[range]` for `range = (piop_params.keyset_part_size..domain_size)`
/// - `g`: Generator used in the SRS
pub fn empty(
// SNARK parameters
piop_params: &PiopParams<F, VrfCurveConfig>,
// Should return `srs[range]` for `range = (piop_params.keyset_part_size..domain_size)`
srs: impl Fn(Range<usize>) -> Result<Vec<KzgCurve::G1Affine>, ()>,
// generator used in the SRS
g: KzgCurve::G1,
) -> Self {
let padding_point = piop_params.padding_point;
let (padding_x, padding_y) = padding_point.xy().unwrap(); // panics on inf, never happens
let (padding_x, padding_y) = piop_params.padding.xy().unwrap(); // panics on inf, never happens
let c1x = g * padding_x;
let c1y = g * padding_y;

Expand Down Expand Up @@ -122,19 +120,22 @@ impl<
selector,
max_keys: piop_params.keyset_part_size,
curr_keys: 0,
padding_point,
padding: piop_params.padding,
}
}

/// Appends a set key sequence to the ring.
///
/// - `keys`: Keys to append.
/// - `srs`: Should return `srs[range]` for `range = (self.curr_keys..self.curr_keys + keys.len())`
pub fn append(
&mut self,
keys: &[Affine<VrfCurveConfig>],
// Should return `srs[range]` for `range = (self.curr_keys..self.curr_keys + keys.len())`
srs: impl Fn(Range<usize>) -> Result<Vec<KzgCurve::G1Affine>, ()>,
) {
let new_size = self.curr_keys + keys.len();
assert!(new_size <= self.max_keys);
let (padding_x, padding_y) = self.padding_point.xy().unwrap();
let (padding_x, padding_y) = self.padding.xy().unwrap();
let (xs, ys): (Vec<F>, Vec<F>) = keys
.iter()
.map(|p| p.xy().unwrap())
Expand All @@ -154,17 +155,18 @@ impl<
self.curr_keys = new_size;
}

// Builds the ring from the keys provided with 2 MSMs of size `keys.len() + scalar_bitlen + 5`.
// In some cases it may be beneficial to cash the empty ring, as updating it costs 2 MSMs of size `keys.len()`.
/// Builds the ring from the keys provided with 2 MSMs of size `keys.len() + scalar_bitlen + 5`.
///
/// In some cases it may be beneficial to cash the empty ring, as updating it costs 2 MSMs of size `keys.len()`.
///
/// - `piop_params`: SNARK parameters.
/// - `srs`: full-size Lagrangian SRS.
pub fn with_keys(
// SNARK parameters
piop_params: &PiopParams<F, VrfCurveConfig>,
keys: &[Affine<VrfCurveConfig>],
// full-size Lagrangian srs
srs: &RingBuilderKey<F, KzgCurve>,
) -> Self {
let padding_point = piop_params.padding_point;
let (padding_x, padding_y) = padding_point.xy().unwrap(); // panics on inf, never happens
let (padding_x, padding_y) = piop_params.padding.xy().unwrap(); // panics on inf, never happens
let powers_of_h = piop_params.power_of_2_multiples_of_h();

// Computes
Expand Down Expand Up @@ -207,7 +209,7 @@ impl<
selector,
max_keys: piop_params.keyset_part_size,
curr_keys: keys.len(),
padding_point,
padding: piop_params.padding,
}
}

Expand All @@ -220,7 +222,7 @@ impl<
cx: KzgCurve::G1Affine,
cy: KzgCurve::G1Affine,
selector: KzgCurve::G1Affine,
padding_point: Affine<VrfCurveConfig>,
padding: Affine<VrfCurveConfig>,
) -> Self {
let max_keys =
domain_size - (VrfCurveConfig::ScalarField::MODULUS_BIT_SIZE as usize + IDLE_ROWS);
Expand All @@ -230,7 +232,7 @@ impl<
selector,
max_keys,
curr_keys: 0,
padding_point,
padding,
}
}
}
Expand Down Expand Up @@ -284,8 +286,9 @@ mod tests {
// piop params
let h = EdwardsAffine::rand(rng);
let seed = EdwardsAffine::rand(rng);
let padding = EdwardsAffine::rand(rng);
let domain = Domain::new(domain_size, true);
let piop_params = PiopParams::setup(domain, h, seed);
let piop_params = PiopParams::setup(domain, h, seed, padding);

let mut ring = TestRing::empty(&piop_params, srs, ring_builder_key.g1);
let (monimial_cx, monimial_cy) = get_monomial_commitment(&pcs_params, &piop_params, &[]);
Expand Down Expand Up @@ -315,8 +318,9 @@ mod tests {
// piop params
let h = EdwardsAffine::rand(rng);
let seed = EdwardsAffine::rand(rng);
let padding = EdwardsAffine::rand(rng);
let domain = Domain::new(domain_size, true);
let piop_params = PiopParams::setup(domain, h, seed);
let piop_params = PiopParams::setup(domain, h, seed, padding);

let ring = TestRing::empty(&piop_params, srs, ring_builder_key.g1);
let same_ring = TestRing::with_keys(&piop_params, &[], &ring_builder_key);
Expand Down
4 changes: 2 additions & 2 deletions w3f-ring-proof/src/ring_prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use w3f_plonk_common::transcript::PlonkTranscript;

use crate::piop::params::PiopParams;
use crate::piop::{FixedColumns, PiopProver, ProverKey};
use crate::RingProof;
use crate::{ArkTranscript, RingProof};

pub struct RingProver<F, CS, Curve, T>
pub struct RingProver<F, CS, Curve, T = ArkTranscript>
where
F: PrimeField,
CS: PCS<F>,
Expand Down
Loading
Loading