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}