2 个版本

0.1.2 2021 年 1 月 17 日
0.1.1 2021 年 1 月 4 日

#42 in #gadget

MIT/Apache

405KB
10K SLoC

ysbell

zsnark


lib.rs:

bellperson 是一个用于构建 zk-SNARK 电路的 crate。它提供电路特性和原始结构,以及基本电路实例,如布尔值和数字抽象。

示例电路

假设我们想要编写一个电路,证明我们知道使用 SHA-256d(调用 SHA-256 两次)计算出的某些散列的预像。预像必须有一个事先已知的固定长度(因为电路参数将取决于它),但可以具有任何其他值。我们采取以下策略

  • 为预像的每个位提供证据。
  • 在电路内部计算 hash = SHA-256d(preimage)
  • 使用多标量打包将 hash 作为公共输入公开。
use bellperson::{
    gadgets::{
        boolean::{AllocatedBit, Boolean},
        multipack,
        sha256::sha256,
    },
    groth16, Circuit, ConstraintSystem, SynthesisError,
};
use paired::{bls12_381::Bls12, Engine};
use rand::rngs::OsRng;
use sha2::{Digest, Sha256};

/// Our own SHA-256d gadget. Input and output are in little-endian bit order.
fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
    mut cs: CS,
    data: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> {
    // Flip endianness of each input byte
    let input: Vec<_> = data
        .chunks(8)
        .map(|c| c.iter().rev())
        .flatten()
        .cloned()
        .collect();

    let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
    let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;

    // Flip endianness of each output byte
    Ok(res
        .chunks(8)
        .map(|c| c.iter().rev())
        .flatten()
        .cloned()
        .collect())
}

struct MyCircuit {
    /// The input to SHA-256d we are proving that we know. Set to `None` when we
    /// are verifying a proof (and do not have the witness data).
    preimage: Option<[u8; 80]>,
}

impl<E: Engine> Circuit<E> for MyCircuit {
    fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
        // Compute the values for the bits of the preimage. If we are verifying a proof,
        // we still need to create the same constraints, so we return an equivalent-size
        // Vec of None (indicating that the value of each bit is unknown).
        let bit_values = if let Some(preimage) = self.preimage {
            preimage
                .iter()
                .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
                .flatten()
                .map(|b| Some(b))
                .collect()
        } else {
            vec![None; 80 * 8]
        };
        assert_eq!(bit_values.len(), 80 * 8);

        // Witness the bits of the preimage.
        let preimage_bits = bit_values
            .into_iter()
            .enumerate()
            // Allocate each bit.
            .map(|(i, b)| {
                AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
            })
            // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
            .map(|b| b.map(Boolean::from))
            .collect::<Result<Vec<_>, _>>()?;

        // Compute hash = SHA-256d(preimage).
        let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;

        // Expose the vector of 32 boolean variables as compact public inputs.
        multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
    }
}

// Create parameters for our circuit. In a production deployment these would
// be generated securely using a multiparty computation.
let params = {
    let c = MyCircuit { preimage: None };
    groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
};

// Prepare the verification key (for proof verification).
let pvk = groth16::prepare_verifying_key(&params.vk);

// Pick a preimage and compute its hash.
let preimage = [42; 80];
let hash = Sha256::digest(&Sha256::digest(&preimage));

// Create an instance of our circuit (with the preimage as a witness).
let c = MyCircuit {
    preimage: Some(preimage),
};

// Create a Groth16 proof with our parameters.
let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();

// Pack the hash as inputs for proof verification.
let hash_bits = multipack::bytes_to_bits_le(&hash);
let inputs = multipack::compute_multipacking::<Bls12>(&hash_bits);

// Check the proof!
assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap());

路线图

bellperson 正在被重构为一个通用证明库。目前它是配对特定的,不同的证明系统需要作为子模块实现。重构后,bellperson 将使用 [ff] 和 group crates 进行泛型化,而特定的证明系统将是独立的 crate,它们会引入所需的依赖项。

依赖项

~2.7–5MB
~107K SLoC