Benedikt B¨unz、 Jonathan Bootle和 Dan Boneh等人2018年论文《Bulletproofs: Short Proofs for Confidential Transactions and More》,相应的代码实现库有:
https://github.com/akosba/jsnark 中提供了一个通用工具,用于构建Bulletproofs for any NP language,该工具可读取Pinocchio格式的arithmetic circuit,同时包含了将C语言编译为circuit fomat 的编译器。
本博文主要针对https://github.com/dalek-cryptography/bulletproofs 代码实现进行解析。
https://github.com/dalek-cryptography/bulletproofs
为当前速度最快的Bulletproofs算法实现,其使用https://github.com/dalek-cryptography/curve25519-dalek
中对 curve25519曲线 封装的 ristretto255
group ,当采用 AVX2 backend来进行并行计算时,对于64-bit的rangproofs,其速度要比 libsecp256k1
实现的Bulletproofs 快将近2倍。
看https://github.com/dalek-cryptography/bulletproofs/blob/main/Cargo.toml
中配置可知,https://github.com/dalek-cryptography/bulletproofs
默认启动了avx2_backend
并行计算.
[features]
default = ["std", "avx2_backend"]
avx2_backend = ["curve25519-dalek/avx2_backend"]
# yoloproofs = []
std = ["rand", "rand/std", "thiserror"]
为实现non-interactive zero-knowledge proof,其中的 Fiat-Shamir transform 采用的是 Merlin框架(https://github.com/dalek-cryptography/merlin
)。
主要见https://github.com/dalek-cryptography/bulletproofs/blob/main/src/inner_product_proof.rs
中代码:
fn test_helper_create(n: usize) { //其中n为vector size
let mut rng = rand::thread_rng();
use crate::generators::BulletproofGens;
let bp_gens = BulletproofGens::new(n, 1);
let G: Vec = bp_gens.share(0).G(n).cloned().collect();
let H: Vec = bp_gens.share(0).H(n).cloned().collect();
// Q would be determined upstream in the protocol, so we pick a random one.
let Q = RistrettoPoint::hash_from_bytes::(b"test point");
// a and b are the vectors for which we want to prove c =
let a: Vec<_> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
let b: Vec<_> = (0..n).map(|_| Scalar::random(&mut rng)).collect();
let c = inner_product(&a, &b);
let G_factors: Vec = iter::repeat(Scalar::one()).take(n).collect();
// y_inv is (the inverse of) a random challenge
let y_inv = Scalar::random(&mut rng);
let H_factors: Vec = util::exp_iter(y_inv).take(n).collect();
// P would be determined upstream, but we need a correct P to check the proof.
//
// To generate P = + + Q, compute
// P = + + Q,
// where b' = b \circ y^(-n)
let b_prime = b.iter().zip(util::exp_iter(y_inv)).map(|(bi, yi)| bi * yi);
// a.iter() has Item=&Scalar, need Item=Scalar to chain with b_prime
let a_prime = a.iter().cloned();
let P = RistrettoPoint::vartime_multiscalar_mul(
a_prime.chain(b_prime).chain(iter::once(c)),
G.iter().chain(H.iter()).chain(iter::once(&Q)),
);
let mut verifier = Transcript::new(b"innerproducttest");
let proof = InnerProductProof::create(
&mut verifier,
&Q,
&G_factors,
&H_factors,
G.clone(),
H.clone(),
a.clone(),
b.clone(),
);
let mut verifier = Transcript::new(b"innerproducttest");
assert!(proof
.verify(
n,
&mut verifier,
iter::repeat(Scalar::one()).take(n),
util::exp_iter(y_inv).take(n),
&P,
&Q,
&G,
&H
)
.is_ok());
let proof = InnerProductProof::from_bytes(proof.to_bytes().as_slice()).unwrap();
let mut verifier = Transcript::new(b"innerproducttest");
assert!(proof
.verify(
n,
&mut verifier,
iter::repeat(Scalar::one()).take(n),
util::exp_iter(y_inv).take(n),
&P,
&Q,
&G,
&H
)
.is_ok());
}
代码实现主要见https://github.com/dalek-cryptography/bulletproofs/blob/main/src/range_proof/mod.rs
:
// m为value数量,n为范围的bitsize
/// Given a bitsize `n`, test the following:
///
/// 1. Generate `m` random values and create a proof they are all in range;
/// 2. Serialize to wire format;
/// 3. Deserialize from wire format;
/// 4. Verify the proof.
fn singleparty_create_and_verify_helper(n: usize, m: usize) {
// Split the test into two scopes, so that it's explicit what
// data is shared between the prover and the verifier.
// Use bincode for serialization
//use bincode; // already present in lib.rs
// Both prover and verifier have access to the generators and the proof
let max_bitsize = 64;
let max_parties = 8;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(max_bitsize, max_parties);
// Prover's scope
let (proof_bytes, value_commitments) = {
use self::rand::Rng;
let mut rng = rand::thread_rng();
// 0. Create witness data
let (min, max) = (0u64, ((1u128 << n) - 1) as u64);
let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect();
let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect();
// 1. Create the proof
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
let (proof, value_commitments) = RangeProof::prove_multiple(
&bp_gens,
&pc_gens,
&mut transcript,
&values,
&blindings,
n,
)
.unwrap();
// 2. Return serialized proof and value commitments
(bincode::serialize(&proof).unwrap(), value_commitments)
};
// Verifier's scope
{
// 3. Deserialize
let proof: RangeProof = bincode::deserialize(&proof_bytes).unwrap();
// 4. Verify with the same customization label as above
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
assert!(proof
.verify_multiple(&bp_gens, &pc_gens, &mut transcript, &value_commitments, n)
.is_ok());
}
}
代码实现主要见https://github.com/dalek-cryptography/bulletproofs/blob/main/src/range_proof/mod.rs
:
#[test]
fn detect_dishonest_party_during_aggregation() {
use self::dealer::*;
use self::party::*;
use crate::errors::MPCError;
// Simulate four parties, two of which will be dishonest and use a 64-bit value.
let m = 4;
let n = 32;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(n, m);
use self::rand::Rng;
let mut rng = rand::thread_rng();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
// Parties 0, 2 are honest and use a 32-bit value
let v0 = rng.gen::() as u64;
let v0_blinding = Scalar::random(&mut rng);
let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap();
let v2 = rng.gen::() as u64;
let v2_blinding = Scalar::random(&mut rng);
let party2 = Party::new(&bp_gens, &pc_gens, v2, v2_blinding, n).unwrap();
// Parties 1, 3 are dishonest and use a 64-bit value
let v1 = rng.gen::();
let v1_blinding = Scalar::random(&mut rng);
let party1 = Party::new(&bp_gens, &pc_gens, v1, v1_blinding, n).unwrap();
let v3 = rng.gen::();
let v3_blinding = Scalar::random(&mut rng);
let party3 = Party::new(&bp_gens, &pc_gens, v3, v3_blinding, n).unwrap();
let dealer = Dealer::new(&bp_gens, &pc_gens, &mut transcript, n, m).unwrap();
let (party0, bit_com0) = party0.assign_position(0).unwrap();
let (party1, bit_com1) = party1.assign_position(1).unwrap();
let (party2, bit_com2) = party2.assign_position(2).unwrap();
let (party3, bit_com3) = party3.assign_position(3).unwrap();
let (dealer, bit_challenge) = dealer
.receive_bit_commitments(vec![bit_com0, bit_com1, bit_com2, bit_com3])
.unwrap();
let (party0, poly_com0) = party0.apply_challenge(&bit_challenge);
let (party1, poly_com1) = party1.apply_challenge(&bit_challenge);
let (party2, poly_com2) = party2.apply_challenge(&bit_challenge);
let (party3, poly_com3) = party3.apply_challenge(&bit_challenge);
let (dealer, poly_challenge) = dealer
.receive_poly_commitments(vec![poly_com0, poly_com1, poly_com2, poly_com3])
.unwrap();
let share0 = party0.apply_challenge(&poly_challenge).unwrap();
let share1 = party1.apply_challenge(&poly_challenge).unwrap();
let share2 = party2.apply_challenge(&poly_challenge).unwrap();
let share3 = party3.apply_challenge(&poly_challenge).unwrap();
match dealer.receive_shares(&[share0, share1, share2, share3]) {
Err(MPCError::MalformedProofShares { bad_shares }) => {
assert_eq!(bad_shares, vec![1, 3]);
}
Err(_) => {
panic!("Got wrong error type from malformed shares");
}
Ok(_) => {
panic!("The proof was malformed, but it was not detected");
}
}
}
#[test]
fn detect_dishonest_dealer_during_aggregation() {
use self::dealer::*;
use self::party::*;
use crate::errors::MPCError;
// Simulate one party
let m = 1;
let n = 32;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(n, m);
use self::rand::Rng;
let mut rng = rand::thread_rng();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
let v0 = rng.gen::() as u64;
let v0_blinding = Scalar::random(&mut rng);
let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap();
let dealer = Dealer::new(&bp_gens, &pc_gens, &mut transcript, n, m).unwrap();
// Now do the protocol flow as normal....
let (party0, bit_com0) = party0.assign_position(0).unwrap();
let (dealer, bit_challenge) = dealer.receive_bit_commitments(vec![bit_com0]).unwrap();
let (party0, poly_com0) = party0.apply_challenge(&bit_challenge);
let (_dealer, mut poly_challenge) =
dealer.receive_poly_commitments(vec![poly_com0]).unwrap();
// But now simulate a malicious dealer choosing x = 0
poly_challenge.x = Scalar::zero();
let maybe_share0 = party0.apply_challenge(&poly_challenge);
assert!(maybe_share0.unwrap_err() == MPCError::MaliciousDealer);
}
如需运行,注意切换develop 分支,同时cargo test --feature "yoloproofs"
。
其中的permutation为未知不公开的Verifiable shuffle证明。
主要思路为:
若committed x 1 , ⋯ , x n x_1,\cdots,x_n x1,⋯,xn为committed y 1 , ⋯ , y n y_1,\cdots,y_n y1,⋯,yn的某中permutation,则对于任意的challenge z z z,应有:
∏ i = 1 n ( x i − z ) = ∏ i = 1 n ( y i − z ) \prod_{i=1}^{n}(x_i-z)=\prod_{i=1}^{n}(y_i-z) ∏i=1n(xi−z)=∏i=1n(yi−z) 恒成立。
具体的代码实现见:https://github.com/dalek-cryptography/bulletproofs/blob/main/tests/r1cs.rs
:
fn kshuffle_helper(k: usize) { // k为vector size
use rand::Rng;
// Common code
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new((2 * k).next_power_of_two(), 1);
let (proof, input_commitments, output_commitments) = {
// Randomly generate inputs and outputs to kshuffle
let mut rng = rand::thread_rng();
let (min, max) = (0u64, std::u64::MAX);
let input: Vec = (0..k)
.map(|_| Scalar::from(rng.gen_range(min, max)))
.collect();
let mut output = input.clone();
output.shuffle(&mut rand::thread_rng());
let mut prover_transcript = Transcript::new(b"ShuffleProofTest");
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output).unwrap()
};
{
let mut verifier_transcript = Transcript::new(b"ShuffleProofTest");
assert!(proof
.verify(
&pc_gens,
&bp_gens,
&mut verifier_transcript,
&input_commitments,
&output_commitments
)
.is_ok());
}
}
[1] 博客 Bulletproof现有代码实现说明
[2] 博客 bulletproofs for r1cs
[3] 博客 ristretto255对外API抽象及基于Curve25519的ristretto255层实现
[4] 博客 ristretto对cofactor>1的椭圆曲线(如Curve25519等)的兼容(含Curve25519 cofactor的sage验证)
[5] 博客 Zero knowledge proofs using Bulletproofs
[6] 博客 curve25519-dalek并行运算性能
[7] 博客 Accelerating Edwards Curve Arithmetic with Parallel Formulas
[8] 博客 Merlin——零知识证明(1)理论篇