Skip to content

Commit 9fe822b

Browse files
authored
feat: introduce gnark-key-parser (#1770)
closes #1769 closes #1378
2 parents c380c2e + 033bc68 commit 9fe822b

File tree

10 files changed

+611
-126
lines changed

10 files changed

+611
-126
lines changed

Cargo.lock

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ members = [
2323
"lib/chain-utils",
2424
"lib/cometbls-groth16-verifier",
2525
"lib/ethereum-verifier",
26+
"lib/gnark-key-parser",
2627
"lib/ics-008-wasm-client",
2728
"lib/ics23",
2829
"lib/macros",
@@ -43,6 +44,7 @@ members = [
4344
"lib/unionlabs",
4445
"lib/voyager-message",
4546
"lib/zktrie-rs",
47+
"lib/gnark-key-parser",
4648

4749
"light-clients/cometbls-light-client",
4850
"light-clients/ethereum-light-client",
@@ -79,6 +81,7 @@ chain-utils = { path = "lib/chain-utils", default-features = false
7981
cometbls-groth16-verifier = { path = "lib/cometbls-groth16-verifier", default-features = false }
8082
contracts = { path = "generated/rust/contracts", default-features = false }
8183
ethereum-verifier = { path = "lib/ethereum-verifier", default-features = false }
84+
gnark-key-parser = { path = "lib/gnark-key-parser", default-features = false }
8285
ics008-wasm-client = { path = "lib/ics-008-wasm-client", default-features = false }
8386
ics23 = { path = "lib/ics23", default-features = false }
8487
macros = { path = "lib/macros", default-features = false }

lib/cometbls-groth16-verifier/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ version = "0.1.0"
99
[lints]
1010
workspace = true
1111

12+
[package.metadata.crane]
13+
test-include = ["lib/cometbls-groth16-verifier/verifying_key.bin"]
14+
1215
[dependencies]
1316
ark-ff = { version = "0.4.2", default-features = false }
1417
byteorder = { version = "1.4", default-features = false }
@@ -22,3 +25,7 @@ unionlabs = { workspace = true }
2225
[features]
2326
default = []
2427
std = []
28+
29+
[build-dependencies]
30+
gnark-key-parser = { workspace = true }
31+
substrate-bn = { version = "0.6", default-features = false }
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use std::{env, fs, path::Path};
2+
3+
use gnark_key_parser::VerifyingKey;
4+
use substrate_bn::{AffineG1, AffineG2};
5+
6+
pub const FQ_SIZE: usize = 32;
7+
pub const G1_SIZE: usize = 2 * FQ_SIZE;
8+
pub const G2_SIZE: usize = 2 * G1_SIZE;
9+
10+
fn parse_verifying_key(buf: &[u8]) -> String {
11+
let (n_read, parsed_key) = VerifyingKey::parse(buf).unwrap();
12+
// we expect the verifying key to be fully parsed
13+
assert_eq!(n_read, buf.len());
14+
let [alpha_g1_x0, alpha_g1_x1, alpha_g1_y0, alpha_g1_y1] =
15+
unsafe { std::mem::transmute::<AffineG1, [u128; 4]>(parsed_key.alpha_g1) };
16+
let [beta_g2_x00, beta_g2_x01, beta_g2_x10, beta_g2_x11, beta_g2_y00, beta_g2_y01, beta_g2_y10, beta_g2_y11] =
17+
unsafe { std::mem::transmute::<AffineG2, [u128; 8]>(parsed_key.beta_g2) };
18+
let [gamma_g2_x00, gamma_g2_x01, gamma_g2_x10, gamma_g2_x11, gamma_g2_y00, gamma_g2_y01, gamma_g2_y10, gamma_g2_y11] =
19+
unsafe { std::mem::transmute::<AffineG2, [u128; 8]>(parsed_key.gamma_g2) };
20+
let [delta_g2_x00, delta_g2_x01, delta_g2_x10, delta_g2_x11, delta_g2_y00, delta_g2_y01, delta_g2_y10, delta_g2_y11] =
21+
unsafe { std::mem::transmute::<AffineG2, [u128; 8]>(parsed_key.delta_g2) };
22+
let [pedersen_g_x00, pedersen_g_x01, pedersen_g_x10, pedersen_g_x11, pedersen_g_y00, pedersen_g_y01, pedersen_g_y10, pedersen_g_y11] =
23+
unsafe { std::mem::transmute::<AffineG2, [u128; 8]>(parsed_key.commitment_key.g) };
24+
let [pedersen_g_root_sigma_neg_x00, pedersen_g_root_sigma_neg_x01, pedersen_g_root_sigma_neg_x10, pedersen_g_root_sigma_neg_x11, pedersen_g_root_sigma_neg_y00, pedersen_g_root_sigma_neg_y01, pedersen_g_root_sigma_neg_y10, pedersen_g_root_sigma_neg_y11] = unsafe {
25+
std::mem::transmute::<AffineG2, [u128; 8]>(parsed_key.commitment_key.g_root_sigma_neg)
26+
};
27+
28+
let gamma_abc_size = parsed_key.gamma_abc_g1.len();
29+
let s: String = parsed_key
30+
.gamma_abc_g1
31+
.into_iter()
32+
.map(|g1| {
33+
let [g1_x0, g1_x1, g1_y0, g1_y1] =
34+
unsafe { std::mem::transmute::<AffineG1, [u128; 4]>(g1) };
35+
format!(
36+
r#"unsafe {{ core::mem::transmute::<[u128; 4], substrate_bn::AffineG1>
37+
([{g1_x0}, {g1_x1}, {g1_y0}, {g1_y1}]) }},
38+
"#
39+
)
40+
})
41+
.collect();
42+
43+
format!(
44+
r#"
45+
pub const ALPHA_G1: substrate_bn::AffineG1 = unsafe {{ core::mem::transmute::<[u128; 4], substrate_bn::AffineG1>([{alpha_g1_x0}, {alpha_g1_x1}, {alpha_g1_y0}, {alpha_g1_y1}]) }};
46+
pub const BETA_G2: substrate_bn::AffineG2 = unsafe {{ core::mem::transmute::<[u128; 8], substrate_bn::AffineG2>([{beta_g2_x00}, {beta_g2_x01}, {beta_g2_x10}, {beta_g2_x11}, {beta_g2_y00}, {beta_g2_y01}, {beta_g2_y10}, {beta_g2_y11}]) }};
47+
pub const GAMMA_G2: substrate_bn::AffineG2 = unsafe {{ core::mem::transmute::<[u128; 8], substrate_bn::AffineG2>([{gamma_g2_x00}, {gamma_g2_x01}, {gamma_g2_x10}, {gamma_g2_x11}, {gamma_g2_y00}, {gamma_g2_y01}, {gamma_g2_y10}, {gamma_g2_y11}]) }};
48+
pub const DELTA_G2: substrate_bn::AffineG2 = unsafe {{ core::mem::transmute::<[u128; 8], substrate_bn::AffineG2>([{delta_g2_x00}, {delta_g2_x01}, {delta_g2_x10}, {delta_g2_x11}, {delta_g2_y00}, {delta_g2_y01}, {delta_g2_y10}, {delta_g2_y11}]) }};
49+
pub const PEDERSEN_G: substrate_bn::AffineG2 = unsafe {{ core::mem::transmute::<[u128; 8], substrate_bn::AffineG2>([{pedersen_g_x00}, {pedersen_g_x01}, {pedersen_g_x10}, {pedersen_g_x11}, {pedersen_g_y00}, {pedersen_g_y01}, {pedersen_g_y10}, {pedersen_g_y11}]) }};
50+
pub const PEDERSEN_G_ROOT_SIGMA_NEG: substrate_bn::AffineG2 = unsafe {{ core::mem::transmute::<[u128; 8], substrate_bn::AffineG2>([{pedersen_g_root_sigma_neg_x00}, {pedersen_g_root_sigma_neg_x01}, {pedersen_g_root_sigma_neg_x10}, {pedersen_g_root_sigma_neg_x11}, {pedersen_g_root_sigma_neg_y00}, {pedersen_g_root_sigma_neg_y01}, {pedersen_g_root_sigma_neg_y10}, {pedersen_g_root_sigma_neg_y11}]) }};
51+
pub const GAMMA_ABC_G1: [substrate_bn::AffineG1; {gamma_abc_size}] = [{s}];
52+
"#
53+
)
54+
}
55+
56+
fn main() {
57+
println!("cargo:rerun-if-changed=verifying_key.bin");
58+
let out_dir = env::var_os("OUT_DIR").unwrap();
59+
let dest_path = Path::new(&out_dir).join("constants.rs");
60+
61+
let data = include_bytes!("./verifying_key.bin");
62+
let verifying_key = parse_verifying_key(data.as_slice());
63+
fs::write(dest_path, format!("{verifying_key}")).unwrap();
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![allow(clippy::unreadable_literal)]
2+
3+
include!(concat!(env!("OUT_DIR"), "/constants.rs"));

lib/cometbls-groth16-verifier/src/lib.rs

+12-126
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ extern crate alloc;
55
use alloc::vec::Vec;
66
use core::marker::PhantomData;
77

8-
use ark_ff::{vec, BigInt};
8+
use ark_ff::vec;
99
use byteorder::{BigEndian, ByteOrder};
10+
use constants::*;
1011
use hex_literal::hex;
1112
use sha2::Sha256;
1213
use sha3::Digest;
@@ -15,6 +16,8 @@ use unionlabs::{
1516
hash::H256, ibc::lightclients::cometbls::light_header::LightHeader, uint::U256, ByteArrayExt,
1617
};
1718

19+
mod constants;
20+
1821
pub const NB_PUBLIC_INPUTS: usize = 2;
1922

2023
pub const HMAC_O: &[u8] = &hex!("1F333139281E100F5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C");
@@ -26,103 +29,7 @@ pub const PRIME_R_MINUS_ONE: U256 = U256::from_limbs([
2629
3486998266802970665,
2730
]);
2831

29-
fn make_g1(x: BigInt<4>, y: BigInt<4>) -> substrate_bn::AffineG1 {
30-
substrate_bn::AffineG1::new(
31-
substrate_bn::Fq::from_u256(x.0.into()).unwrap(),
32-
substrate_bn::Fq::from_u256(y.0.into()).unwrap(),
33-
)
34-
.unwrap()
35-
}
36-
37-
fn make_g2(x0: BigInt<4>, x1: BigInt<4>, y0: BigInt<4>, y1: BigInt<4>) -> substrate_bn::AffineG2 {
38-
substrate_bn::AffineG2::new(
39-
substrate_bn::Fq2::new(
40-
substrate_bn::Fq::from_u256(x0.0.into()).unwrap(),
41-
substrate_bn::Fq::from_u256(x1.0.into()).unwrap(),
42-
),
43-
substrate_bn::Fq2::new(
44-
substrate_bn::Fq::from_u256(y0.0.into()).unwrap(),
45-
substrate_bn::Fq::from_u256(y1.0.into()).unwrap(),
46-
),
47-
)
48-
.unwrap()
49-
}
50-
51-
// TODO: this should be computed at compile time
52-
pub fn pedersen_commitment_key() -> (substrate_bn::AffineG2, substrate_bn::AffineG2) {
53-
let g_raw = hex!("257DF6F8132CB0037F7DFDF1A29B04C1FF92BA082EDA513996BA2BFA9FBD198713F0D8D8879885CA567EF99298C30C397E6FBA584658F4127713A814C06DE55A1660EBCC60C7A3AC560EFCEA5993F528EE13685D3A39694ACD74FE67C80D798A15E80642C58DB4DBE0A87F92CE3C65E962F231278353783A691FD64078BA7F34");
54-
let g_root_sigma_neg_raw = hex!("2FBFE141A7555CF7E3E86B092660B81CFB68A025AD817E45CEC0B0F2E2CA636802A104DF1C015F2307FA2859627098CDF9FDB521D61D323943343A12304E5BAF27DA3F93ECF3BFD0B3A3354AE2162A6C230C0E539B6D9F82C0826E2B006A59222C0838551CB9E5CF67DB57DE7E2250BB97807F6687F135A6EB910359BA7BDB8D");
55-
let G2Affine(_, g) = G2Affine::<BigEndian>::try_from(g_raw).expect("impossible");
56-
let G2Affine(_, g_root_sigma_neg) =
57-
G2Affine::<BigEndian>::try_from(g_root_sigma_neg_raw).expect("impossible");
58-
(g, g_root_sigma_neg)
59-
}
60-
61-
// TODO: this should be computed at compile time
62-
pub fn universal_vk() -> VerifyingKey {
63-
VerifyingKey {
64-
alpha_g1: make_g1(
65-
BigInt!("4252850302693242182654534639730627324742305503909561446344356971523664816281"),
66-
BigInt!("3971530409048238023625806606514600982127202826003358538821613170737831313919"),
67-
),
68-
beta_g2: make_g2(
69-
BigInt!("9609903744775525881338738176064678545439912439219033822736570321349357348980"),
70-
BigInt!(
71-
"11402125448377072234752634956069960846261435348550776006069399216352815312229"
72-
),
73-
BigInt!("3876014193556985028076276590285094449745398487447250532380698384573245200038"),
74-
BigInt!("6131692356384648492800758325058748831519318785594820705365176509549681793745"),
75-
),
76-
gamma_g2: make_g2(
77-
BigInt!(
78-
"15418804173338388766896385877623893969695670309009587476846726795628238714393"
79-
),
80-
BigInt!(
81-
"14882897597913405382982164467298010752166363844685258881581520272046793702095"
82-
),
83-
BigInt!("4166025151148225057462107057100265181139888889391061071239248954005945470477"),
84-
BigInt!("206728492847877950288262169260916452585500374823256459470367014125967964118"),
85-
),
86-
delta_g2: make_g2(
87-
BigInt!("2636161939055419322743684458857549714230849256995406138405588958157843793131"),
88-
BigInt!(
89-
"18711435617866698040659011365354165232283248284733617156044102129651710736892"
90-
),
91-
BigInt!(
92-
"19240355865528042255113556794397480864884450537537107687508383548050491695680"
93-
),
94-
BigInt!(
95-
"12249371269602120664445362627662636389936048209522657338249293583990077475589"
96-
),
97-
),
98-
gamma_abc_g1: vec![
99-
make_g1(
100-
BigInt!(
101-
"17683074019270049519594214298171697666582975915064153618004061598086681825921"
102-
),
103-
BigInt!(
104-
"16826145467743906176166100307225491106961753217491843100452871479833450456070"
105-
),
106-
),
107-
make_g1(
108-
BigInt!(
109-
"4999724750322169039879775285047941133298355297928988655266615607529011563466"
110-
),
111-
BigInt!(
112-
"8614448667589143428827059805500251818303043966026074735628377626634208993292"
113-
),
114-
),
115-
make_g1(
116-
BigInt!(
117-
"1184807858330365651919114999096473332175166887333719856514157833289677967559"
118-
),
119-
BigInt!(
120-
"20327610427697660249999185524229068956160879388632193295649998184224119517657"
121-
),
122-
),
123-
],
124-
}
125-
}
32+
const _: () = assert!(GAMMA_ABC_G1.len() == NB_PUBLIC_INPUTS + 1);
12633

12734
fn hmac_keccak(message: &[u8]) -> [u8; 32] {
12835
sha3::Keccak256::new()
@@ -169,7 +76,6 @@ fn hash_commitment(proof_commitment: &substrate_bn::AffineG1) -> Result<U256, Er
16976
pub const FQ_SIZE: usize = 32;
17077
pub const G1_SIZE: usize = 2 * FQ_SIZE;
17178
pub const G2_SIZE: usize = 2 * G1_SIZE;
172-
pub const COMMITMENT_HASH_SIZE: usize = 32;
17379

17480
pub struct G1Affine<FromOrder: ByteOrder>(PhantomData<FromOrder>, substrate_bn::AffineG1);
17581
pub type G1AffineBE = G1Affine<BigEndian>;
@@ -303,14 +209,12 @@ pub fn verify_zkp(
303209
header: &LightHeader,
304210
zkp: impl Into<Vec<u8>>,
305211
) -> Result<(), Error> {
306-
let (g, g_root_sigma_neg) = pedersen_commitment_key();
307212
verify_generic_zkp_2(
308-
universal_vk(),
309213
chain_id,
310214
trusted_validators_hash,
311215
header,
312-
g,
313-
g_root_sigma_neg,
216+
PEDERSEN_G,
217+
PEDERSEN_G_ROOT_SIGMA_NEG,
314218
ZKP::try_from(zkp.into().as_ref())?,
315219
)
316220
}
@@ -329,7 +233,6 @@ fn g1_to_bytes(g1_point: &G1) -> Result<[u8; 64], Error> {
329233
}
330234

331235
fn verify_generic_zkp_2(
332-
vk: VerifyingKey,
333236
chain_id: &str,
334237
trusted_validators_hash: H256,
335238
header: &LightHeader,
@@ -341,9 +244,6 @@ fn verify_generic_zkp_2(
341244
return Err(Error::InvalidChainId);
342245
}
343246
// Constant + public inputs
344-
if vk.gamma_abc_g1.len() != NB_PUBLIC_INPUTS + 1 {
345-
return Err(Error::InvalidVerifyingKey);
346-
}
347247
let decode_scalar = move |x: U256| -> Result<substrate_bn::Fr, Error> {
348248
substrate_bn::Fr::new(x.0 .0.into()).ok_or(Error::InvalidPublicInput)
349249
};
@@ -388,22 +288,11 @@ fn verify_generic_zkp_2(
388288
decode_scalar(U256::from_be_bytes(inputs_hash))?,
389289
decode_scalar(commitment_hash)?,
390290
];
391-
let initial_point = substrate_bn::G1::from(
392-
vk.gamma_abc_g1
393-
.first()
394-
.copied()
395-
.ok_or(Error::InvalidVerifyingKey)?,
396-
) + zkp.proof_commitment.into();
291+
let initial_point = substrate_bn::G1::from(GAMMA_ABC_G1[0]) + zkp.proof_commitment.into();
397292
let public_inputs_msm = public_inputs
398293
.into_iter()
399-
.zip(
400-
vk.gamma_abc_g1
401-
.into_iter()
402-
.skip(1)
403-
.map(substrate_bn::G1::from),
404-
)
294+
.zip(GAMMA_ABC_G1.into_iter().skip(1).map(substrate_bn::G1::from))
405295
.fold(initial_point, |s, (w_i, gamma_l_i)| s + gamma_l_i * w_i);
406-
// TODO: the verifying key transformation, pedersen key decoding and this negations should all be done at compile time
407296

408297
let proof_a: G1 = zkp.proof.a.into();
409298
let proof_c: G1 = zkp.proof.c.into();
@@ -429,12 +318,9 @@ fn verify_generic_zkp_2(
429318

430319
let result = substrate_bn::pairing_batch(&[
431320
(proof_a * r1, zkp.proof.b.into()),
432-
(public_inputs_msm * r1, -substrate_bn::G2::from(vk.gamma_g2)),
433-
(proof_c * r1, -substrate_bn::G2::from(vk.delta_g2)),
434-
(
435-
G1::from(vk.alpha_g1) * r1,
436-
-substrate_bn::G2::from(vk.beta_g2),
437-
),
321+
(public_inputs_msm * r1, -substrate_bn::G2::from(GAMMA_G2)),
322+
(proof_c * r1, -substrate_bn::G2::from(DELTA_G2)),
323+
(G1::from(ALPHA_G1) * r1, -substrate_bn::G2::from(BETA_G2)),
438324
// Verify pedersen proof of knowledge
439325
(pc * r2, g.into()),
440326
(pok * r2, g_root_sigma_neg.into()),
524 Bytes
Binary file not shown.

lib/gnark-key-parser/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
edition.workspace = true
3+
license-file.workspace = true
4+
name = "gnark-key-parser"
5+
repository.workspace = true
6+
version = "0.1.0"
7+
8+
[dependencies]
9+
arith = "0.2.3"
10+
ark-ff = { version = "0.4.2", default-features = false }
11+
base64 = { workspace = true, features = ["alloc"] }
12+
hex = { workspace = true, features = ["alloc"] }
13+
substrate-bn = { version = "0.6", default-features = false }
14+
thiserror = { workspace = true, default-features = false }
15+
16+
[lints]
17+
workspace = true

0 commit comments

Comments
 (0)