@@ -158,110 +158,49 @@ pub fn load_proof(proof_bytes: &[u8]) -> Proof {
158158/// Load a VerificationKey from a JSON string containing an array of hex‐encoded field‐elements.
159159#[ cfg( feature = "serde_json" ) ]
160160pub fn load_vk_from_json ( json_data : & str ) -> VerificationKey {
161- // Parse JSON into Vec<String>
162161 let vk_fields: Vec < String > = serde_json:: from_str ( json_data) . unwrap ( ) ;
163- // Expect v0.87.0 header(3) + 28*4 limbs
164- assert ! ( vk_fields. len( ) >= 3 , "VK JSON must contain header elements" ) ;
162+ // Header (fields): [circuit_size, public_inputs_size, pub_inputs_offset, reserved]
163+ const HEADER_WORDS : usize = 4 ;
164+ const NUM_POINTS : usize = 27 ;
165+ let expected_len = HEADER_WORDS + NUM_POINTS * 4 ;
166+ assert ! (
167+ vk_fields. len( ) >= expected_len,
168+ "VK JSON must contain at least {} elements (got {})" ,
169+ expected_len,
170+ vk_fields. len( )
171+ ) ;
165172
166- // Helper to parse hex field element to u64 (fits for small header values)
167173 fn parse_u64_hex ( s : & str ) -> u64 {
168174 let x = BigUint :: from_str_radix ( s. trim_start_matches ( "0x" ) , 16 ) . unwrap ( ) ;
169175 x. to_u64_digits ( ) . get ( 0 ) . copied ( ) . unwrap_or ( 0 )
170176 }
171177
172- // Parse VK header (barretenberg v0.87.0):
173- // [0] log_circuit_size, [1] num_public_inputs, [2] pub_inputs_offset
174- let h0 = parse_u64_hex ( & vk_fields[ 0 ] ) ;
175- let public_inputs_size = if vk_fields. len ( ) > 1 {
176- parse_u64_hex ( & vk_fields[ 1 ] )
177- } else {
178- 0
179- } ;
180- let pub_inputs_offset = if vk_fields. len ( ) > 2 {
181- parse_u64_hex ( & vk_fields[ 2 ] )
182- } else {
183- 0
184- } ;
185- // Some builds store circuit_size in [0], others store log2(circuit_size).
186- // If the value is a power of two, treat it as circuit_size and compute log; otherwise treat it as log value.
187- let ( circuit_size_u64, log_circuit_size) = if h0 != 0 && ( h0 & ( h0 - 1 ) ) == 0 {
188- let mut lg = 0u64 ;
189- let mut n = h0;
190- while n > 1 {
191- n >>= 1 ;
192- lg += 1 ;
193- }
194- ( h0, lg)
195- } else {
196- let cs = 1u64 . checked_shl ( h0 as u32 ) . expect ( "circuit_size too large" ) ;
197- ( cs, h0)
198- } ;
199-
200- // Fixed for v0.87.0: header_len=3, limbs_per_point=4 (lo_x, hi_x, lo_y, hi_y).
201- let mut field_index = 3usize ;
202- // Attempt to auto-synchronize start of G1 limbs in vk_fields.json by sliding until a valid point is found.
203- {
204- let max_probe = 16usize ;
205- let len = vk_fields. len ( ) ;
206- ' outer: for offset in 0 ..max_probe {
207- if field_index + offset + 3 >= len {
208- break ;
209- }
210- let ix = field_index + offset;
211- let parse = |i : usize | {
212- BigUint :: from_str_radix ( vk_fields[ i] . trim_start_matches ( "0x" ) , 16 ) . unwrap ( )
213- } ;
214- let lx = parse ( ix) ;
215- let hx = parse ( ix + 1 ) ;
216- let ly = parse ( ix + 2 ) ;
217- let hy = parse ( ix + 3 ) ;
218- let pt = read_g1_from_limbs ( & lx, & hx, & ly, & hy) ;
219- let aff = G1Affine :: new_unchecked ( pt. x , pt. y ) ;
220- if aff. is_on_curve ( ) && aff. is_in_correct_subgroup_assuming_on_curve ( ) {
221- field_index = ix;
222- break ' outer;
223- }
224- }
225- }
178+ let circuit_size = parse_u64_hex ( & vk_fields[ 0 ] ) ;
179+ let public_inputs_size = parse_u64_hex ( & vk_fields[ 1 ] ) ;
180+ let pub_inputs_offset = parse_u64_hex ( & vk_fields[ 2 ] ) ;
181+ let log_circuit_size = circuit_size. trailing_zeros ( ) as u64 ;
226182
227- // Safe reader: 4 limbs → G1 using fixed v0.87.0 encoding (lo136, hi<=118) per coordinate.
228- // Falls back to a couple of alternative assemblies if on-curve check fails.
229- fn read_g1_from_limbs ( lx : & BigUint , hx : & BigUint , ly : & BigUint , hy : & BigUint ) -> G1Point {
230- // Primary: lo | (hi << 136)
231- let assemble = |lo : & BigUint , hi : & BigUint , shift : u32 | -> BigUint { lo | ( hi << shift) } ;
232- let mut try_pairs: [ ( [ & BigUint ; 2 ] , [ & BigUint ; 2 ] ) ; 2 ] =
233- [ ( [ lx, hx] , [ ly, hy] ) , ( [ ly, hy] , [ lx, hx] ) ] ;
234- let shifts = [ 136u32 , 128u32 ] ;
235- for & shift in & shifts {
236- for & ( ref ax, ref ay) in & try_pairs {
237- let bx = assemble ( ax[ 0 ] , ax[ 1 ] , shift) ;
238- let by = assemble ( ay[ 0 ] , ay[ 1 ] , shift) ;
239- let x = biguint_to_fq_mod ( & bx) ;
240- let y = biguint_to_fq_mod ( & by) ;
241- let aff = G1Affine :: new_unchecked ( x, y) ;
242- if aff. is_on_curve ( ) && aff. is_in_correct_subgroup_assuming_on_curve ( ) {
243- return G1Point { x : aff. x , y : aff. y } ;
244- }
245- }
246- }
247- G1Point {
248- x : Fq :: from ( 0u64 ) ,
249- y : Fq :: from ( 0u64 ) ,
250- }
183+ // Safe reader: 4 limbs → G1 using fixed v0.87.0 encoding (hi_x, lo_x, hi_y, lo_y) with 136-bit split.
184+ fn read_g1_from_limbs ( hx : & BigUint , lx : & BigUint , hy : & BigUint , ly : & BigUint ) -> G1Point {
185+ let assemble = |hi : & BigUint , lo : & BigUint | -> BigUint { hi | ( lo << 136 ) } ;
186+ let x = biguint_to_fq_mod ( & assemble ( hx, lx) ) ;
187+ let y = biguint_to_fq_mod ( & assemble ( hy, ly) ) ;
188+ G1Point { x, y }
251189 }
252190
191+ let mut field_index = HEADER_WORDS ;
253192 macro_rules! read_g1 {
254193 ( ) => { {
255- let low_x = & vk_fields[ field_index] ;
256- let high_x = & vk_fields[ field_index + 1 ] ;
257- let low_y = & vk_fields[ field_index + 2 ] ;
258- let high_y = & vk_fields[ field_index + 3 ] ;
259- let lx = BigUint :: from_str_radix( low_x. trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
194+ let high_x = & vk_fields[ field_index] ;
195+ let low_x = & vk_fields[ field_index + 1 ] ;
196+ let high_y = & vk_fields[ field_index + 2 ] ;
197+ let low_y = & vk_fields[ field_index + 3 ] ;
260198 let hx = BigUint :: from_str_radix( high_x. trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
261- let ly = BigUint :: from_str_radix( low_y . trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
199+ let lx = BigUint :: from_str_radix( low_x . trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
262200 let hy = BigUint :: from_str_radix( high_y. trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
201+ let ly = BigUint :: from_str_radix( low_y. trim_start_matches( "0x" ) , 16 ) . unwrap( ) ;
263202 field_index += 4 ;
264- read_g1_from_limbs( & lx , & hx , & ly , & hy )
203+ read_g1_from_limbs( & hx , & lx , & hy , & ly )
265204 } } ;
266205 }
267206
@@ -298,7 +237,7 @@ pub fn load_vk_from_json(json_data: &str) -> VerificationKey {
298237 let lagrange_last = read_g1 ! ( ) ;
299238
300239 VerificationKey {
301- circuit_size : circuit_size_u64 ,
240+ circuit_size,
302241 log_circuit_size,
303242 public_inputs_size,
304243 pub_inputs_offset,
0 commit comments