Skip to content

Commit 3b7e0cc

Browse files
authored
Merge pull request #15 from yugocabrio/clean1217
refactor: parse vk_fields.json with correct limb order and 4-word header
2 parents d0db632 + 24f7cb7 commit 3b7e0cc

1 file changed

Lines changed: 29 additions & 90 deletions

File tree

src/utils.rs

Lines changed: 29 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -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")]
160160
pub 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

Comments
 (0)