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() / 20; // TMP rm '/20'
133 let circuit = BTCBlockCheckerFCircuit::<Fr>::new(blocks_prepared.clone());
134 let (fs_prover_params, kzg_vk) = setup(circuit.clone());
135 let z_0 = vec![Fr::from(0)];
136 let mut nova = NOVA::init(&fs_prover_params, circuit, z_0.clone()).unwrap();
137
138 println!("Computing folds...");
139 let start = 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 println!(
148 "Done folding. Checked {} blocks in: {:.2?}",
149 n_blocks_checked,
150 start.elapsed()
151 );
152
153 let decider_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 start = Instant::now();
167 let (g16_pk, g16_vk) =
168 Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
169 println!("Setup done in: {:.2?}", start.elapsed());
170
171 // decider proof generation
172 println!("Generating proof...");
173 let start = Instant::now();
174 let decider_pp = (g16_pk, fs_prover_params.cs_params.clone());
175 let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
176 println!("Proof generated in: {:.2?}", start.elapsed());
177
178 // decider proof verification
179 println!("Verifying proof...");
180 let verified = DECIDER::verify(
181 (g16_vk.clone(), kzg_vk.clone()),
182 nova.i,
183 nova.z_0.clone(),
184 nova.z_i.clone(),
185 &nova.U_i,
186 &nova.u_i,
187 &proof,
188 )
189 .unwrap();
190 assert!(verified);
191
192 let g16_data = Groth16Data::from(g16_vk);
193 let kzg_data = KzgData::from((
194 kzg_vk,
195 fs_prover_params.cs_params.powers_of_g[0..3].to_vec(),
196 ));
197 let nova_cyclefold_data = NovaCycleFoldData::from((g16_data, kzg_data, nova.z_0.len()));
198 let function_selector =
199 get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
200
201 let calldata: Vec<u8> = prepare_calldata(
202 function_selector,
203 nova.i,
204 nova.z_0,
205 nova.z_i,
206 &nova.U_i,
207 &nova.u_i,
208 proof,
209 )
210 .unwrap();
211
212 let decider_template = get_decider_template_for_cyclefold_decider(nova_cyclefold_data);
213
214 // save smart contract
215 save_solidity("./NovaLightBTCClientDecider.sol", &decider_template.clone());
216 // save calldata
217 fs::write("./solidity-calldata.calldata", calldata.clone()).unwrap();
218
219 let nova_cyclefold_verifier_bytecode = compile_solidity(decider_template, "NovaDecider");
220
221 let mut evm = Evm::default();
222 let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
223
224 let (_, output) = evm.call(verifier_address, calldata);
225 println!("Output: {:?}", output);
226 assert_eq!(*output.last().unwrap(), 1);
227}