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::{
 15    commitment::{kzg::KZG, pedersen::Pedersen},
 16    folding::nova::{decider_eth::Decider, Nova},
 17    frontend::FCircuit,
 18};
 19use folding_schemes::{folding::nova::decider_eth::prepare_calldata, Decider as DeciderTrait};
 20use folding_schemes::{folding::nova::decider_eth_circuit::DeciderEthCircuit, FoldingScheme};
 21use num_bigint::BigUint;
 22use num_traits::Num;
 23use solidity_verifiers::{
 24    evm::{compile_solidity, save_solidity, Evm},
 25    get_decider_template_for_cyclefold_decider,
 26    utils::get_function_selector_for_nova_cyclefold_verifier,
 27};
 28use solidity_verifiers::{Groth16Data, KzgData, NovaCyclefoldData};
 29use std::{fs, marker::PhantomData, time::Instant};
 30use utils::setup;
 31mod utils;
 32
 33#[derive(Clone, Debug)]
 34pub struct BTCBlockCheckerFCircuit<F: PrimeField> {
 35    _f: PhantomData<F>,
 36    block: Vec<Block>,
 37}
 38
 39impl<F: PrimeField> FCircuit<F> for BTCBlockCheckerFCircuit<F> {
 40    type Params = Vec<Block>;
 41
 42    fn new(params: Self::Params) -> Self {
 43        Self {
 44            _f: PhantomData,
 45            block: params,
 46        }
 47    }
 48
 49    fn state_len(&self) -> usize {
 50        1
 51    }
 52
 53    fn step_native(&self, i: usize, z_i: Vec<F>) -> Result<Vec<F>, folding_schemes::Error> {
 54        let new_z_i = vec![z_i[0] + F::one()];
 55
 56        // Check block hash
 57        let computed_block_hash = get_block_hash(&self.block[i].block_header);
 58        assert_eq!(computed_block_hash, self.block[i].block_hash);
 59
 60        // Check prev_block_hash
 61        assert_eq!(
 62            self.block[i].prev_block_hash,
 63            hex::encode(&self.block[i].block_header[4..36].to_vec())
 64        );
 65
 66        // Check pow
 67        let target = get_target(&self.block[i].block_header);
 68        let bigint_block_hash = BigUint::from_str_radix(&self.block[i].block_hash, 16).unwrap();
 69        assert!(BigUint::from_bytes_be(&bigint_block_hash.to_bytes_le()) < target);
 70
 71        Ok(new_z_i)
 72    }
 73
 74    fn generate_step_constraints(
 75        &self,
 76        cs: ConstraintSystemRef<F>,
 77        i: usize,
 78        z_i: Vec<ark_r1cs_std::fields::fp::FpVar<F>>,
 79    ) -> Result<Vec<ark_r1cs_std::fields::fp::FpVar<F>>, SynthesisError> {
 80        let new_z_i = vec![z_i[0].clone() + FpVar::new_constant(cs.clone(), F::one())?];
 81        let block_var = BlockVar::new_witness(cs.clone(), || Ok(self.block[i].clone()))?;
 82        let _ = BTCBlockCheckerGadget::check_block(cs.clone(), block_var.clone())?;
 83        Ok(new_z_i)
 84    }
 85}
 86
 87fn main() {
 88    let file = include_str!("./data/btc-blocks.json");
 89    let (mut prev_block_hash, blocks) = read_blocks(100, 1, file);
 90
 91    let mut blocks_prepared = vec![];
 92    for batch in blocks.iter() {
 93        let block_hashes =
 94            serde_json::from_value::<Vec<String>>(batch.get("blockHashes").unwrap().clone())
 95                .unwrap();
 96        let block_headers =
 97            serde_json::from_value::<Vec<Vec<u8>>>(batch.get("blockHeaders").unwrap().clone())
 98                .unwrap();
 99        for (block_hash, block_header) in block_hashes.iter().zip(block_headers) {
100            let block = Block {
101                block_header,
102                block_hash: block_hash.to_string(),
103                prev_block_hash,
104            };
105            blocks_prepared.push(block.clone());
106            prev_block_hash = block_hash.to_string();
107        }
108    }
109
110    type NOVA = Nova<
111        Projective,
112        GVar,
113        Projective2,
114        GVar2,
115        BTCBlockCheckerFCircuit<Fr>,
116        KZG<'static, Bn254>,
117        Pedersen<Projective2>,
118    >;
119
120    type DECIDER = Decider<
121        Projective,
122        GVar,
123        Projective2,
124        GVar2,
125        BTCBlockCheckerFCircuit<Fr>,
126        KZG<'static, Bn254>,
127        Pedersen<Projective2>,
128        Groth16<Bn254>,
129        NOVA,
130    >;
131
132    let n_blocks_checked = blocks_prepared.len();
133    let circuit = BTCBlockCheckerFCircuit::<Fr>::new(blocks_prepared.clone());
134    let (prover_params, poseidon_config, kzg_vk) = setup(circuit.clone());
135    let z_0 = vec![Fr::from(0)];
136    let mut nova = NOVA::init(&prover_params, circuit, z_0.clone()).unwrap();
137
138    println!("Computing folds...");
139    let now = Instant::now();
140    for i in 0..n_blocks_checked {
141        let current_state = nova.z_i[0].into_bigint();
142        if i % 10 == 0 {
143            println!("--- At block: {}/{} ---", current_state, n_blocks_checked);
144        }
145        nova.prove_step().unwrap();
146    }
147    let elapsed = now.elapsed();
148    println!(
149        "Done folding. Checked {} blocks in: {:.2?}",
150        n_blocks_checked, elapsed
151    );
152
153    let circuit = DeciderEthCircuit::<
154        Projective,
155        GVar,
156        Projective2,
157        GVar2,
158        KZG<Bn254>,
159        Pedersen<Projective2>,
160    >::from_nova::<BTCBlockCheckerFCircuit<Fr>>(nova.clone())
161    .unwrap();
162    let mut rng = rand::rngs::OsRng;
163
164    // decider setup
165    println!("Starting setup...");
166    let now = Instant::now();
167    let (g16_pk, g16_vk) =
168        Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
169    let elapsed = now.elapsed();
170    println!("Setup done in: {:.2?}", elapsed);
171
172    // decider proof generation
173    println!("Generating proof...");
174    let now = Instant::now();
175    let decider_pp = (
176        poseidon_config.clone(),
177        g16_pk,
178        prover_params.clone().cs_params,
179    );
180    let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
181    let elapsed = now.elapsed();
182    println!("Proof generated in: {:.2?}", elapsed);
183
184    // decider proof verification
185    println!("Verifying proof...");
186    let verified = DECIDER::verify(
187        (g16_vk.clone(), kzg_vk.clone()),
188        nova.i,
189        nova.z_0.clone(),
190        nova.z_i.clone(),
191        &nova.U_i,
192        &nova.u_i,
193        &proof,
194    )
195    .unwrap();
196    assert!(verified);
197
198    let g16_data = Groth16Data::from(g16_vk);
199    let kzg_data = KzgData::from((
200        kzg_vk,
201        Some(prover_params.cs_params.powers_of_g[0..3].to_vec()),
202    ));
203    let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data, nova.z_0.len()));
204    let function_selector =
205        get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
206
207    let calldata: Vec<u8> = prepare_calldata(
208        function_selector,
209        nova.i,
210        nova.z_0,
211        nova.z_i,
212        &nova.U_i,
213        &nova.u_i,
214        proof,
215    )
216    .unwrap();
217
218    let decider_template = get_decider_template_for_cyclefold_decider(nova_cyclefold_data);
219    save_solidity("./NovaLightBTCClientDecider.sol", &decider_template);
220    fs::write("./solidity-calldata.calldata", calldata).unwrap();
221
222    // save calldata
223
224    // let nova_cyclefold_verifier_bytecode = compile_solidity(decider_template, "NovaDecider");
225    //
226    // let mut evm = Evm::default();
227    // let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
228    //
229    // let (_, output) = evm.call(verifier_address, calldata.clone());
230    // println!("Output: {:?}", output);
231    // assert_eq!(*output.last().unwrap(), 1);
232}