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