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;
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
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn run_main() {
89 // this is done to avoid computing large circuits
90 let file = include_str!("./data/btc-blocks.json");
91 let (mut prev_block_hash, blocks) = read_blocks(20, 1, file);
92
93 let mut blocks_prepared = vec![];
94 for batch in blocks.iter() {
95 let block_hashes =
96 serde_json::from_value::<Vec<String>>(batch.get("blockHashes").unwrap().clone())
97 .unwrap();
98 let block_headers =
99 serde_json::from_value::<Vec<Vec<u8>>>(batch.get("blockHeaders").unwrap().clone())
100 .unwrap();
101 for (i, (block_hash, block_header)) in
102 block_hashes.iter().zip(block_headers).enumerate()
103 {
104 let block = Block {
105 block_header,
106 block_hash: block_hash.to_string(),
107 prev_block_hash,
108 };
109 blocks_prepared.push(block.clone());
110 prev_block_hash = block_hash.to_string();
111 }
112 }
113
114 type NOVA = Nova<
115 Projective,
116 GVar,
117 Projective2,
118 GVar2,
119 BTCBlockCheckerFCircuit<Fr>,
120 KZG<'static, Bn254>,
121 Pedersen<Projective2>,
122 >;
123 type DECIDER = Decider<
124 Projective,
125 GVar,
126 Projective2,
127 GVar2,
128 BTCBlockCheckerFCircuit<Fr>,
129 KZG<'static, Bn254>,
130 Pedersen<Projective2>,
131 Groth16<Bn254>, // here we define the Snark to use in the decider
132 NOVA, // here we define the FoldingScheme to use
133 >;
134
135 let n_blocks_checked = blocks_prepared.len();
136 let circuit = BTCBlockCheckerFCircuit::<Fr>::new(blocks_prepared.clone());
137 let (prover_params, poseidon_config, kzg_vk) = setup(circuit.clone());
138 let z_0 = vec![Fr::from(0)];
139 let mut nova = NOVA::init(&prover_params, circuit, z_0.clone()).unwrap();
140
141 for _ in 0..n_blocks_checked {
142 nova.prove_step().unwrap();
143 let current_state = nova.z_i[0].into_bigint();
144 println!("Checked block: {}", current_state);
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 println!("Starting setup...");
159 let (g16_pk, g16_vk) =
160 Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
161
162 // decider proof generation
163 println!("Generating proof...");
164 let decider_pp = (poseidon_config.clone(), g16_pk, prover_params.cs_params);
165 let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
166
167 // decider proof verification
168 println!("Verifying proof...");
169 let decider_vp = (poseidon_config, g16_vk, kzg_vk);
170 let verified = DECIDER::verify(
171 decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof,
172 )
173 .unwrap();
174 assert!(verified);
175 }
176}