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