@ -0,0 +1 @@ |
|||||
|
/target |
@ -0,0 +1,30 @@ |
|||||
|
[package] |
||||
|
name = "folding-schemes-btc" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
|
||||
|
[dependencies] |
||||
|
ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" } |
||||
|
folding-schemes = { git = "https://github.com/privacy-scaling-explorations/folding-schemes.git", package = "folding-schemes", branch="feature/decider-link-groth16-kzg" } |
||||
|
ark-light-bitcoin-client = { git = "https://github.com/dmpierre/ark-light-bitcoin-client.git" } |
||||
|
ark-ff = "0.4.0" |
||||
|
ark-relations = "0.4.0" |
||||
|
serde_json = "1.0.114" |
||||
|
num-bigint = "0.4.3" |
||||
|
num-traits = "0.2.15" |
||||
|
hex = "0.4.3" |
||||
|
hex-literal = "0.4.1" |
||||
|
ark-bn254 = {version="0.4.0", features=["r1cs"]} |
||||
|
ark-grumpkin = {version="0.4.0", features=["r1cs"]} |
||||
|
ark-std = "0.4.0" |
||||
|
ark-ec = "0.4.0" |
||||
|
ark-crypto-primitives = "0.4.0" |
||||
|
ark-poly-commit = "0.4.0" |
||||
|
ark-groth16 = "0.4.0" |
||||
|
|
||||
|
[patch.crates-io] |
||||
|
ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" } |
||||
|
ark-bn254 = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"} |
||||
|
ark-grumpkin = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"} |
@ -0,0 +1,6 @@ |
|||||
|
#!/bin/bash |
||||
|
# script for setting up a new machine to generate light client btc proofs |
||||
|
sudo apt-get update |
||||
|
curl https://sh.rustup.rs -sSf | sh -s -- -y # install rust, defaulting to yes |
||||
|
. "$HOME/.cargo/env" |
||||
|
|
@ -0,0 +1,176 @@ |
|||||
|
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
|
||||
|
use ark_crypto_primitives::snark::SNARK;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_groth16::Groth16;
|
||||
|
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
use ark_light_bitcoin_client::{
|
||||
|
gadgets::BTCBlockCheckerGadget,
|
||||
|
get_block_hash, get_target, read_blocks,
|
||||
|
utils::{Block, BlockVar},
|
||||
|
};
|
||||
|
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar};
|
||||
|
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
|
||||
|
use ark_std::rand;
|
||||
|
use folding_schemes::Decider as DeciderTrait;
|
||||
|
use folding_schemes::{
|
||||
|
commitment::{kzg::KZG, pedersen::Pedersen},
|
||||
|
folding::nova::{decider_eth::Decider, Nova},
|
||||
|
frontend::FCircuit,
|
||||
|
};
|
||||
|
use folding_schemes::{folding::nova::decider_eth_circuit::DeciderEthCircuit, FoldingScheme};
|
||||
|
use num_bigint::BigUint;
|
||||
|
use num_traits::Num;
|
||||
|
use std::marker::PhantomData;
|
||||
|
use utils::setup;
|
||||
|
mod utils;
|
||||
|
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct BTCBlockCheckerFCircuit<F: PrimeField> {
|
||||
|
_f: PhantomData<F>,
|
||||
|
block: Vec<Block>,
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> FCircuit<F> for BTCBlockCheckerFCircuit<F> {
|
||||
|
type Params = Vec<Block>;
|
||||
|
|
||||
|
fn new(params: Self::Params) -> Self {
|
||||
|
Self {
|
||||
|
_f: PhantomData,
|
||||
|
block: params,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn state_len(&self) -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
|
||||
|
fn step_native(&self, i: usize, z_i: Vec<F>) -> Result<Vec<F>, folding_schemes::Error> {
|
||||
|
let new_z_i = vec![z_i[0] + F::one()];
|
||||
|
|
||||
|
// Check block hash
|
||||
|
let computed_block_hash = get_block_hash(&self.block[i].block_header);
|
||||
|
assert_eq!(computed_block_hash, self.block[i].block_hash);
|
||||
|
|
||||
|
// Check prev_block_hash
|
||||
|
assert_eq!(
|
||||
|
self.block[i].prev_block_hash,
|
||||
|
hex::encode(&self.block[i].block_header[4..36].to_vec())
|
||||
|
);
|
||||
|
|
||||
|
// Check pow
|
||||
|
let target = get_target(&self.block[i].block_header);
|
||||
|
let bigint_block_hash = BigUint::from_str_radix(&self.block[i].block_hash, 16).unwrap();
|
||||
|
assert!(BigUint::from_bytes_be(&bigint_block_hash.to_bytes_le()) < target);
|
||||
|
|
||||
|
Ok(new_z_i)
|
||||
|
}
|
||||
|
|
||||
|
fn generate_step_constraints(
|
||||
|
&self,
|
||||
|
cs: ConstraintSystemRef<F>,
|
||||
|
i: usize,
|
||||
|
z_i: Vec<ark_r1cs_std::fields::fp::FpVar<F>>,
|
||||
|
) -> Result<Vec<ark_r1cs_std::fields::fp::FpVar<F>>, SynthesisError> {
|
||||
|
let new_z_i = vec![z_i[0].clone() + FpVar::new_constant(cs.clone(), F::one())?];
|
||||
|
let block_var = BlockVar::new_witness(cs.clone(), || Ok(self.block[i].clone()))?;
|
||||
|
let _ = BTCBlockCheckerGadget::check_block(cs.clone(), block_var.clone())?;
|
||||
|
Ok(new_z_i)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn main() {}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod tests {
|
||||
|
use super::*;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn run_main() {
|
||||
|
// this is done to avoid computing large circuits
|
||||
|
let file = include_str!("./data/btc-blocks.json");
|
||||
|
let (mut prev_block_hash, blocks) = read_blocks(20, 1, file);
|
||||
|
|
||||
|
let mut blocks_prepared = vec![];
|
||||
|
for batch in blocks.iter() {
|
||||
|
let block_hashes =
|
||||
|
serde_json::from_value::<Vec<String>>(batch.get("blockHashes").unwrap().clone())
|
||||
|
.unwrap();
|
||||
|
let block_headers =
|
||||
|
serde_json::from_value::<Vec<Vec<u8>>>(batch.get("blockHeaders").unwrap().clone())
|
||||
|
.unwrap();
|
||||
|
for (i, (block_hash, block_header)) in
|
||||
|
block_hashes.iter().zip(block_headers).enumerate()
|
||||
|
{
|
||||
|
let block = Block {
|
||||
|
block_header,
|
||||
|
block_hash: block_hash.to_string(),
|
||||
|
prev_block_hash,
|
||||
|
};
|
||||
|
blocks_prepared.push(block.clone());
|
||||
|
prev_block_hash = block_hash.to_string();
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
type NOVA = Nova<
|
||||
|
Projective,
|
||||
|
GVar,
|
||||
|
Projective2,
|
||||
|
GVar2,
|
||||
|
BTCBlockCheckerFCircuit<Fr>,
|
||||
|
KZG<'static, Bn254>,
|
||||
|
Pedersen<Projective2>,
|
||||
|
>;
|
||||
|
type DECIDER = Decider<
|
||||
|
Projective,
|
||||
|
GVar,
|
||||
|
Projective2,
|
||||
|
GVar2,
|
||||
|
BTCBlockCheckerFCircuit<Fr>,
|
||||
|
KZG<'static, Bn254>,
|
||||
|
Pedersen<Projective2>,
|
||||
|
Groth16<Bn254>, // here we define the Snark to use in the decider
|
||||
|
NOVA, // here we define the FoldingScheme to use
|
||||
|
>;
|
||||
|
|
||||
|
let n_blocks_checked = blocks_prepared.len();
|
||||
|
let circuit = BTCBlockCheckerFCircuit::<Fr>::new(blocks_prepared.clone());
|
||||
|
let (prover_params, poseidon_config, kzg_vk) = setup(circuit.clone());
|
||||
|
let z_0 = vec![Fr::from(0)];
|
||||
|
let mut nova = NOVA::init(&prover_params, circuit, z_0.clone()).unwrap();
|
||||
|
|
||||
|
for _ in 0..n_blocks_checked {
|
||||
|
nova.prove_step().unwrap();
|
||||
|
let current_state = nova.z_i[0].into_bigint();
|
||||
|
println!("Checked block: {}", current_state);
|
||||
|
}
|
||||
|
|
||||
|
let circuit = DeciderEthCircuit::<
|
||||
|
Projective,
|
||||
|
GVar,
|
||||
|
Projective2,
|
||||
|
GVar2,
|
||||
|
KZG<Bn254>,
|
||||
|
Pedersen<Projective2>,
|
||||
|
>::from_nova::<BTCBlockCheckerFCircuit<Fr>>(nova.clone())
|
||||
|
.unwrap();
|
||||
|
let mut rng = rand::rngs::OsRng;
|
||||
|
|
||||
|
println!("Starting setup...");
|
||||
|
let (g16_pk, g16_vk) =
|
||||
|
Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
|
||||
|
|
||||
|
// decider proof generation
|
||||
|
println!("Generating proof...");
|
||||
|
let decider_pp = (poseidon_config.clone(), g16_pk, prover_params.cs_params);
|
||||
|
let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
|
||||
|
|
||||
|
// decider proof verification
|
||||
|
println!("Verifying proof...");
|
||||
|
let decider_vp = (poseidon_config, g16_vk, kzg_vk);
|
||||
|
let verified = DECIDER::verify(
|
||||
|
decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
assert!(verified);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,46 @@ |
|||||
|
use crate::BTCBlockCheckerFCircuit;
|
||||
|
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
|
||||
|
use ark_crypto_primitives::sponge::poseidon::PoseidonConfig;
|
||||
|
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
|
||||
|
use folding_schemes::{
|
||||
|
commitment::{
|
||||
|
kzg::{ProverKey as KZGProverKey, KZG},
|
||||
|
pedersen::Pedersen,
|
||||
|
CommitmentScheme,
|
||||
|
},
|
||||
|
folding::nova::{get_cs_params_len, ProverParams},
|
||||
|
transcript::poseidon::poseidon_test_config,
|
||||
|
};
|
||||
|
|
||||
|
pub fn setup(
|
||||
|
circuit: BTCBlockCheckerFCircuit<Fr>,
|
||||
|
) -> (
|
||||
|
ProverParams<Projective, Projective2, KZG<'static, Bn254>, Pedersen<Projective2>>,
|
||||
|
PoseidonConfig<Fr>,
|
||||
|
KZGVerifierKey<Bn254>,
|
||||
|
) {
|
||||
|
let mut rng = ark_std::test_rng();
|
||||
|
let poseidon_config = poseidon_test_config::<Fr>();
|
||||
|
|
||||
|
let (cs_len, cf_cs_len) =
|
||||
|
get_cs_params_len::<Projective, GVar, Projective2, GVar2, BTCBlockCheckerFCircuit<Fr>>(
|
||||
|
&poseidon_config,
|
||||
|
circuit,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
let (kzg_pk, kzg_vk): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) =
|
||||
|
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
|
||||
|
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
|
||||
|
|
||||
|
(
|
||||
|
ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> {
|
||||
|
poseidon_config: poseidon_config.clone(),
|
||||
|
cs_params: kzg_pk.clone(),
|
||||
|
cf_cs_params: cf_pedersen_params,
|
||||
|
},
|
||||
|
poseidon_config,
|
||||
|
kzg_vk,
|
||||
|
)
|
||||
|
}
|