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

Switch to better hash to curve #46

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
4 changes: 2 additions & 2 deletions bw6/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fflonk = { git = "https://github.com/w3f/fflonk" }

rand = "0.8"
merlin = "3.0"
blake2 = "0.9"
sha2 = "0.10.6"
rayon = { version = "1", optional = true }

[dev-dependencies]
Expand Down Expand Up @@ -50,4 +50,4 @@ harness = false
default = []
std = ["ark-std/std", "ark-ff/std", "ark-ec/std", "ark-poly/std", "ark-serialize/std"]
parallel = ["std", "ark-std/parallel", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "fflonk/parallel", "rayon"]
print-trace = ["ark-std/print-trace"]
print-trace = ["ark-std/print-trace"]
168 changes: 119 additions & 49 deletions bw6/examples/recursive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ use ark_bls12_377::{G1Projective, G2Projective};
use ark_bw6_761::BW6_761;
use ark_ec::AffineRepr;
use ark_serialize::CanonicalSerialize;
use ark_std::{end_timer, start_timer};
use ark_std::test_rng;
use ark_std::{end_timer, start_timer};
use fflonk::pcs::kzg::params::{KzgCommitterKey, RawKzgVerifierKey};
use fflonk::pcs::kzg::urs::URS;
use fflonk::pcs::PcsParams;
use merlin::Transcript;
use rand::Rng;

use apk_proofs::{AccountablePublicInput, Bitmask, hash_to_curve, Keyset, KeysetCommitment, Prover, setup, SimpleProof, Verifier};
use apk_proofs::bls::{PublicKey, SecretKey, Signature};
use apk_proofs::{
hash_to_curve_g2, setup, AccountablePublicInput, Bitmask, Keyset, KeysetCommitment, Prover,
SimpleProof, Verifier,
};

// This example sketches the primary intended use case of the crate functionality:
// building communication-efficient light clients for blockchains.
Expand All @@ -41,7 +44,6 @@ use apk_proofs::bls::{PublicKey, SecretKey, Signature};
// Given it knows the (short commitment to) recent validator set, it can process signatures (proofs)
// of the other events (like a block finality) in the same way.


// Light client's state is initialized with a commitment 'C0' to the ('genesis') validator set of the era #0
// (and some technical stuff, like public parameters).

Expand All @@ -62,7 +64,6 @@ use apk_proofs::bls::{PublicKey, SecretKey, Signature};
// 3. If both checks passed and the bitmask contains enough (say, >2/3 of) signers,
// updates its state to the new commitment 'C1'.


#[derive(Clone)]
struct Validator(SecretKey);

Expand All @@ -82,7 +83,10 @@ fn new_era() {
});
}

fn get_keyset_commitment<F>(f: F) -> KeysetCommitment where F: FnOnce() -> KeysetCommitment {
fn get_keyset_commitment<F>(f: F) -> KeysetCommitment
where
F: FnOnce() -> KeysetCommitment,
{
CACHE.with(|cell| {
let mut cell = cell.borrow_mut();
let old_opt = cell.as_ref();
Expand All @@ -93,12 +97,11 @@ fn get_keyset_commitment<F>(f: F) -> KeysetCommitment where F: FnOnce() -> Keyse
*cell = new_opt;
new_val
}
Some(val) => val.clone()
Some(val) => val.clone(),
}
})
}


impl Validator {
fn new<R: Rng>(rng: &mut R) -> Self {
Self(SecretKey::new(rng))
Expand All @@ -108,7 +111,11 @@ impl Validator {
(&self.0).into()
}

fn approve(&self, new_validator_set: &ValidatorSet, kzg_pk: &KzgCommitterKey<ark_bw6_761::G1Affine>) -> Approval {
fn approve(
&self,
new_validator_set: &ValidatorSet,
kzg_pk: &KzgCommitterKey<ark_bw6_761::G1Affine>,
) -> Approval {
// Computing the commitment to the new validator set is a time consuming operation.
// In real-world deployments it is run by each validator, hence in parallel.
// We model that by sharing the commitment generated by the first validator among others.
Expand All @@ -132,29 +139,33 @@ struct ValidatorSet {

impl ValidatorSet {
fn new<R: Rng>(size: usize, quorum: usize, rng: &mut R) -> Self {
let validators = (0..size)
.map(|_| Validator::new(rng))
.collect();
let validators = (0..size).map(|_| Validator::new(rng)).collect();
Self { validators, quorum }
}

fn public_keys(&self) -> Vec<PublicKey> {
self.validators.iter()
.map(|v| v.public_key())
.collect()
self.validators.iter().map(|v| v.public_key()).collect()
}

fn raw_public_keys(&self) -> Vec<G1Projective> {
self.public_keys().iter().map(|pk| pk.0).collect()
}

fn rotate<R: Rng>(&self, kzg_pk: &KzgCommitterKey<ark_bw6_761::G1Affine>, rng: &mut R) -> (ValidatorSet, Vec<Approval>) {
fn rotate<R: Rng>(
&self,
kzg_pk: &KzgCommitterKey<ark_bw6_761::G1Affine>,
rng: &mut R,
) -> (ValidatorSet, Vec<Approval>) {
new_era();
let new_validator_set = ValidatorSet::new(self.size(), self.quorum, rng);

let t_approval = start_timer!(|| format!("Each (honest) validators computes the commitment to the new validator set of size {} and signs the commitment", new_validator_set.size()));
let t_approval = start_timer!(|| {
format!("Each (honest) validators computes the commitment to the new validator set of size {} and signs the commitment", new_validator_set.size())
});

let approvals = self.validators.iter()
let approvals = self
.validators
.iter()
.filter(|_| rng.gen_bool(test_rng().gen_range(6..10) as f64 / 10.0))
.map(|v| v.approve(&new_validator_set, kzg_pk))
.collect();
Expand Down Expand Up @@ -189,16 +200,25 @@ impl LightClient {
}
}

fn verify_aggregates(&mut self,
public_input: AccountablePublicInput,
proof: &SimpleProof,
aggregate_signature: &Signature,
new_validator_set_commitment: KeysetCommitment) {
fn verify_aggregates(
&mut self,
public_input: AccountablePublicInput,
proof: &SimpleProof,
aggregate_signature: &Signature,
new_validator_set_commitment: KeysetCommitment,
) {
let n_signers = public_input.bitmask.count_ones();
let t_verification = start_timer!(|| format!("Light client verifies light client proof for {} signers", n_signers));
let t_verification = start_timer!(|| format!(
"Light client verifies light client proof for {} signers",
n_signers
));

let t_apk = start_timer!(|| "apk proof verification");
let verifier = Verifier::new(self.kzg_vk.clone(), self.current_validator_set_commitment.clone(), Transcript::new(b"apk_proof"));
let verifier = Verifier::new(
self.kzg_vk.clone(),
self.current_validator_set_commitment.clone(),
Transcript::new(b"apk_proof"),
);
assert!(verifier.verify_simple(&public_input, &proof));
end_timer!(t_apk);

Expand All @@ -208,7 +228,12 @@ impl LightClient {
assert!(aggregate_public_key.verify(&aggregate_signature, &message));
end_timer!(t_bls);

assert!(n_signers >= self.quorum, "{} signers don't make the quorum of {}", n_signers, self.quorum);
assert!(
n_signers >= self.quorum,
"{} signers don't make the quorum of {}",
n_signers,
self.quorum
);

self.current_validator_set_commitment = new_validator_set_commitment;

Expand All @@ -223,7 +248,11 @@ struct TrustlessHelper {
}

impl TrustlessHelper {
fn new(genesis_validator_set: ValidatorSet, genesis_validator_set_commitment: &KeysetCommitment, kzg_params: URS<BW6_761>) -> Self {
fn new(
genesis_validator_set: ValidatorSet,
genesis_validator_set_commitment: &KeysetCommitment,
kzg_params: URS<BW6_761>,
) -> Self {
let prover = Prover::new(
Keyset::new(genesis_validator_set.raw_public_keys()),
genesis_validator_set_commitment,
Expand All @@ -237,20 +266,33 @@ impl TrustlessHelper {
}
}

fn aggregate_approvals(&mut self, new_validator_set: ValidatorSet, approvals: Vec<Approval>) -> (AccountablePublicInput, SimpleProof, Signature, KeysetCommitment) {
let t_approval = start_timer!(|| format!("Helper aggregated {} individual signatures on the same commitment and generates accountable light client proof of them", approvals.len()));
fn aggregate_approvals(
&mut self,
new_validator_set: ValidatorSet,
approvals: Vec<Approval>,
) -> (
AccountablePublicInput,
SimpleProof,
Signature,
KeysetCommitment,
) {
let t_approval = start_timer!(|| {
format!("Helper aggregated {} individual signatures on the same commitment and generates accountable light client proof of them", approvals.len())
});

let new_validator_set_commitment = &approvals[0].comm;
let actual_signers = approvals.iter()
.map(|a| &a.pk)
.collect::<HashSet<_>>();
let actual_signers_bitmask = self.current_validator_set.public_keys().iter()
let actual_signers = approvals.iter().map(|a| &a.pk).collect::<HashSet<_>>();
let actual_signers_bitmask = self
.current_validator_set
.public_keys()
.iter()
.map(|pk| actual_signers.contains(pk))
.collect::<Vec<_>>();

let (proof, public_input) = self.prover.prove_simple(Bitmask::from_bits(&actual_signers_bitmask));
let signatures = approvals.iter()
.map(|a| &a.sig);
let (proof, public_input) = self
.prover
.prove_simple(Bitmask::from_bits(&actual_signers_bitmask));
let signatures = approvals.iter().map(|a| &a.sig);
let aggregate_signature = Signature::aggregate(signatures);

self.current_validator_set = new_validator_set.clone();
Expand All @@ -264,26 +306,40 @@ impl TrustlessHelper {
end_timer!(t_approval);
println!();

(public_input, proof, aggregate_signature, new_validator_set_commitment.clone())
(
public_input,
proof,
aggregate_signature,
new_validator_set_commitment.clone(),
)
}
}

fn hash_commitment(commitment: &KeysetCommitment) -> G2Projective {
let mut buf = vec![0u8; commitment.compressed_size()];
commitment.serialize_compressed(&mut buf[..]).unwrap();
hash_to_curve(&buf)
hash_to_curve_g2(&buf)
}

fn main() {
let mut args= std::env::args();
let mut args = std::env::args();
args.next();

let log_n: usize = args.next().unwrap_or("4".to_string())
.parse().expect("invalid LOG_N");
let n_eras: usize = args.next().unwrap_or("10".to_string())
.parse().expect("invalid N_ERAS");

print!("Running a chain with 2^{}-1 validators for {} eras. ", log_n, n_eras);
let log_n: usize = args
.next()
.unwrap_or("4".to_string())
.parse()
.expect("invalid LOG_N");
let n_eras: usize = args
.next()
.unwrap_or("10".to_string())
.parse()
.expect("invalid N_ERAS");

print!(
"Running a chain with 2^{}-1 validators for {} eras. ",
log_n, n_eras
);
println!("To change the values run with '--example recursive LOG_N N_ERAS'\n");

let rng = &mut test_rng(); // Don't use in production code!
Expand All @@ -297,16 +353,30 @@ fn main() {
let keyset_size = (1 << log_n) - 1;
let quorum = ((keyset_size * 2) / 3) + 1;

println!("\nGenesis: validator set size = {}, quorum = {}\n", keyset_size, quorum);
println!(
"\nGenesis: validator set size = {}, quorum = {}\n",
keyset_size, quorum
);

let t_genesis = start_timer!(|| format!("Computing commitment to the set of initial {} validators", keyset_size));
let t_genesis = start_timer!(|| format!(
"Computing commitment to the set of initial {} validators",
keyset_size
));
let genesis_validator_set = ValidatorSet::new(keyset_size, quorum, rng);
let keyset = Keyset::new(genesis_validator_set.raw_public_keys());
let genesis_validator_set_commitment = keyset.commit(&kzg_params.ck());
end_timer!(t_genesis);

let mut helper = TrustlessHelper::new(genesis_validator_set.clone(), &genesis_validator_set_commitment, kzg_params.clone());
let mut light_client = LightClient::init(kzg_params.raw_vk(), genesis_validator_set_commitment, quorum);
let mut helper = TrustlessHelper::new(
genesis_validator_set.clone(),
&genesis_validator_set_commitment,
kzg_params.clone(),
);
let mut light_client = LightClient::init(
kzg_params.raw_vk(),
genesis_validator_set_commitment,
quorum,
);

let mut current_validator_set = genesis_validator_set;

Expand All @@ -326,4 +396,4 @@ fn main() {

current_validator_set = new_validator_set;
}
}
}
23 changes: 14 additions & 9 deletions bw6/src/keyset.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use ark_bls12_377::G1Projective;
use ark_bw6_761::Fr;
use ark_ec::CurveGroup;
use ark_poly::{EvaluationDomain, Evaluations, Radix2EvaluationDomain};
use ark_poly::univariate::DensePolynomial;
use ark_poly::{EvaluationDomain, Evaluations, Radix2EvaluationDomain};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use fflonk::pcs::{CommitterKey, PCS};
use fflonk::pcs::kzg::params::KzgCommitterKey;
use fflonk::pcs::{CommitterKey, PCS};

use crate::{hash_to_curve, NewKzgBw6};
use crate::domains::Domains;
use crate::{hash_to_curve_g1, NewKzgBw6};

// Polynomial commitment to the vector of public keys.
// Let 'pks' be such a vector that commit(pks) == KeysetCommitment::pks_comm, also let
Expand Down Expand Up @@ -57,11 +57,12 @@ impl Keyset {

let mut padded_pks = pks.clone();
// a point with unknown discrete log
let padding_pk = hash_to_curve::<ark_bls12_377::G1Projective>(b"apk-proofs");
let padding_pk = hash_to_curve_g1(b"apk-proofs");
padded_pks.resize(domain.size(), padding_pk);

// convert into affine coordinates to commit
let (pks_x, pks_y) = G1Projective::normalize_batch(&padded_pks).iter()
let (pks_x, pks_y) = G1Projective::normalize_batch(&padded_pks)
.iter()
.map(|p| (p.x, p.y))
.unzip();
let pks_x_poly = Evaluations::from_vec_and_domain(pks_x, domain).interpolate();
Expand All @@ -81,14 +82,17 @@ impl Keyset {

pub fn amplify(&mut self) {
let domains = Domains::new(self.domain.size());
let pks_evals_x4 = self.pks_polys.clone().map(|z| domains.amplify_polynomial(&z));
let pks_evals_x4 = self
.pks_polys
.clone()
.map(|z| domains.amplify_polynomial(&z));
self.pks_evals_x4 = Some(pks_evals_x4);
}

pub fn commit(&self, kzg_pk: &KzgCommitterKey<ark_bw6_761::G1Affine>) -> KeysetCommitment {
assert!(self.domain.size() <= kzg_pk.max_degree() + 1);
let pks_x_comm= NewKzgBw6::commit(kzg_pk, &self.pks_polys[0]).0;
let pks_y_comm= NewKzgBw6::commit(kzg_pk, &self.pks_polys[1]).0;
let pks_x_comm = NewKzgBw6::commit(kzg_pk, &self.pks_polys[0]).0;
let pks_y_comm = NewKzgBw6::commit(kzg_pk, &self.pks_polys[1]).0;
KeysetCommitment {
pks_comm: (pks_x_comm, pks_y_comm),
log_domain_size: self.domain.log_size_of_group,
Expand All @@ -97,7 +101,8 @@ impl Keyset {

pub fn aggregate(&self, bitmask: &[bool]) -> ark_bls12_377::G1Projective {
assert_eq!(bitmask.len(), self.size());
bitmask.iter()
bitmask
.iter()
.zip(self.pks.iter())
.filter(|(b, _p)| **b)
.map(|(_b, p)| p)
Expand Down
Loading