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}