arnaucube
arnaucube/sonobe-light-btcPublic Self-hosted repository
sonobe-light-btc/src/main.rs
src/main.rsRaw
  1use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
  2use ark_crypto_primitives::snark::SNARK;
  3use ark_ff::PrimeField;
  4use ark_groth16::Groth16;
  5use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
  6use ark_light_bitcoin_client::{
  7    gadgets::BTCBlockCheckerGadget,
  8    get_block_hash, get_target, read_blocks,
  9    utils::{Block, BlockVar},
 10};
 11use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar};
 12use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 13use ark_std::rand;
 14use folding_schemes::Decider as DeciderTrait;
 15use folding_schemes::{
 16    commitment::{kzg::KZG, pedersen::Pedersen},
 17    folding::nova::{decider_eth::Decider, Nova},
 18    frontend::FCircuit,
 19};
 20use folding_schemes::{folding::nova::decider_eth_circuit::DeciderEthCircuit, FoldingScheme};
 21use num_bigint::BigUint;
 22use num_traits::Num;
 23use std::marker::PhantomData;
 24use utils::setup;
 25mod utils;
 26
 27#[derive(Clone, Debug)]
 28pub struct BTCBlockCheckerFCircuit<F: PrimeField> {
 29    _f: PhantomData<F>,
 30    block: Vec<Block>,
 31}
 32
 33impl<F: PrimeField> FCircuit<F> for BTCBlockCheckerFCircuit<F> {
 34    type Params = Vec<Block>;
 35
 36    fn new(params: Self::Params) -> Self {
 37        Self {
 38            _f: PhantomData,
 39            block: params,
 40        }
 41    }
 42
 43    fn state_len(&self) -> usize {
 44        1
 45    }
 46
 47    fn step_native(&self, i: usize, z_i: Vec<F>) -> Result<Vec<F>, folding_schemes::Error> {
 48        let new_z_i = vec![z_i[0] + F::one()];
 49
 50        // Check block hash
 51        let computed_block_hash = get_block_hash(&self.block[i].block_header);
 52        assert_eq!(computed_block_hash, self.block[i].block_hash);
 53
 54        // Check prev_block_hash
 55        assert_eq!(
 56            self.block[i].prev_block_hash,
 57            hex::encode(&self.block[i].block_header[4..36].to_vec())
 58        );
 59
 60        // Check pow
 61        let target = get_target(&self.block[i].block_header);
 62        let bigint_block_hash = BigUint::from_str_radix(&self.block[i].block_hash, 16).unwrap();
 63        assert!(BigUint::from_bytes_be(&bigint_block_hash.to_bytes_le()) < target);
 64
 65        Ok(new_z_i)
 66    }
 67
 68    fn generate_step_constraints(
 69        &self,
 70        cs: ConstraintSystemRef<F>,
 71        i: usize,
 72        z_i: Vec<ark_r1cs_std::fields::fp::FpVar<F>>,
 73    ) -> Result<Vec<ark_r1cs_std::fields::fp::FpVar<F>>, SynthesisError> {
 74        let new_z_i = vec![z_i[0].clone() + FpVar::new_constant(cs.clone(), F::one())?];
 75        let block_var = BlockVar::new_witness(cs.clone(), || Ok(self.block[i].clone()))?;
 76        let _ = BTCBlockCheckerGadget::check_block(cs.clone(), block_var.clone())?;
 77        Ok(new_z_i)
 78    }
 79}
 80
 81fn main() {}
 82
 83#[cfg(test)]
 84mod tests {
 85    use super::*;
 86
 87    #[test]
 88    fn run_main() {
 89        // this is done to avoid computing large circuits
 90        let file = include_str!("./data/btc-blocks.json");
 91        let (mut prev_block_hash, blocks) = read_blocks(20, 1, file);
 92
 93        let mut blocks_prepared = vec![];
 94        for batch in blocks.iter() {
 95            let block_hashes =
 96                serde_json::from_value::<Vec<String>>(batch.get("blockHashes").unwrap().clone())
 97                    .unwrap();
 98            let block_headers =
 99                serde_json::from_value::<Vec<Vec<u8>>>(batch.get("blockHeaders").unwrap().clone())
100                    .unwrap();
101            for (i, (block_hash, block_header)) in
102                block_hashes.iter().zip(block_headers).enumerate()
103            {
104                let block = Block {
105                    block_header,
106                    block_hash: block_hash.to_string(),
107                    prev_block_hash,
108                };
109                blocks_prepared.push(block.clone());
110                prev_block_hash = block_hash.to_string();
111            }
112        }
113
114        type NOVA = Nova<
115            Projective,
116            GVar,
117            Projective2,
118            GVar2,
119            BTCBlockCheckerFCircuit<Fr>,
120            KZG<'static, Bn254>,
121            Pedersen<Projective2>,
122        >;
123        type DECIDER = Decider<
124            Projective,
125            GVar,
126            Projective2,
127            GVar2,
128            BTCBlockCheckerFCircuit<Fr>,
129            KZG<'static, Bn254>,
130            Pedersen<Projective2>,
131            Groth16<Bn254>, // here we define the Snark to use in the decider
132            NOVA,           // here we define the FoldingScheme to use
133        >;
134
135        let n_blocks_checked = blocks_prepared.len();
136        let circuit = BTCBlockCheckerFCircuit::<Fr>::new(blocks_prepared.clone());
137        let (prover_params, poseidon_config, kzg_vk) = setup(circuit.clone());
138        let z_0 = vec![Fr::from(0)];
139        let mut nova = NOVA::init(&prover_params, circuit, z_0.clone()).unwrap();
140
141        for _ in 0..n_blocks_checked {
142            nova.prove_step().unwrap();
143            let current_state = nova.z_i[0].into_bigint();
144            println!("Checked block: {}", current_state);
145        }
146
147        let circuit = DeciderEthCircuit::<
148            Projective,
149            GVar,
150            Projective2,
151            GVar2,
152            KZG<Bn254>,
153            Pedersen<Projective2>,
154        >::from_nova::<BTCBlockCheckerFCircuit<Fr>>(nova.clone())
155        .unwrap();
156        let mut rng = rand::rngs::OsRng;
157
158        println!("Starting setup...");
159        let (g16_pk, g16_vk) =
160            Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
161
162        // decider proof generation
163        println!("Generating proof...");
164        let decider_pp = (poseidon_config.clone(), g16_pk, prover_params.cs_params);
165        let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
166
167        // decider proof verification
168        println!("Verifying proof...");
169        let decider_vp = (poseidon_config, g16_vk, kzg_vk);
170        let verified = DECIDER::verify(
171            decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof,
172        )
173        .unwrap();
174        assert!(verified);
175    }
176}