Skip to content

Commit fa580de

Browse files
authored
feat(sol): EVM verifier (#748)
# Rationale for this change We want an EVM verifier. # What changes are included in this PR? * This adds a minor helper method (primarily to achive 100% unit test coverage). * Adds the complete, main, verifier body. * Adds Rust+EVM tests. # Are these changes tested? Yes
2 parents 8985336 + 55e0733 commit fa580de

File tree

7 files changed

+804
-0
lines changed

7 files changed

+804
-0
lines changed

crates/proof-of-sql/src/proof_primitive/hyperkzg/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub use scalar::BNScalar;
1212
mod public_setup;
1313
#[cfg(feature = "std")]
1414
pub use public_setup::deserialize_flat_compressed_hyperkzg_public_setup_from_reader;
15+
#[cfg(all(test, feature = "hyperkzg_proof"))]
16+
pub use public_setup::load_small_setup_for_testing;
1517
pub use public_setup::{
1618
deserialize_flat_compressed_hyperkzg_public_setup_from_slice, HyperKZGPublicSetup,
1719
HyperKZGPublicSetupOwned,
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
use crate::{
2+
base::database::{
3+
owned_table_utility::{bigint, owned_table},
4+
CommitmentAccessor, OwnedTableTestAccessor,
5+
},
6+
proof_primitive::hyperkzg::{self, HyperKZGCommitment, HyperKZGCommitmentEvaluationProof},
7+
sql::{
8+
evm_proof_plan::EVMProofPlan,
9+
parse::QueryExpr,
10+
proof::{ProofPlan, VerifiableQueryResult},
11+
proof_plans::DynProofPlan,
12+
},
13+
};
14+
use ark_ec::{AffineRepr, CurveGroup};
15+
use ark_ff::PrimeField;
16+
use itertools::Itertools;
17+
18+
fn evm_verifier_with_extra_args(
19+
plan: &DynProofPlan,
20+
verifiable_result: &VerifiableQueryResult<HyperKZGCommitmentEvaluationProof>,
21+
accessor: &impl CommitmentAccessor<HyperKZGCommitment>,
22+
extra_args: &[&'static str],
23+
) -> bool {
24+
let commitments = plan
25+
.get_column_references()
26+
.into_iter()
27+
.map(|c| accessor.get_commitment(c))
28+
.flat_map(|c| {
29+
c.commitment
30+
.into_affine()
31+
.xy()
32+
.map_or(["0".to_string(), "0".to_string()], |(x, y)| {
33+
[x.into_bigint().to_string(), y.into_bigint().to_string()]
34+
})
35+
})
36+
.join(",");
37+
let table_lengths = plan
38+
.get_table_references()
39+
.into_iter()
40+
.map(|t| accessor.get_length(&t).to_string())
41+
.join(",");
42+
43+
let bincode_options = bincode::config::standard()
44+
.with_fixed_int_encoding()
45+
.with_big_endian();
46+
let query_bytes =
47+
bincode::serde::encode_to_vec(EVMProofPlan::new(plan.clone()), bincode_options).unwrap();
48+
let proof_bytes =
49+
bincode::serde::encode_to_vec(&verifiable_result.proof, bincode_options).unwrap();
50+
let result_bytes =
51+
bincode::serde::encode_to_vec(&verifiable_result.result, bincode_options).unwrap();
52+
53+
std::process::Command::new("../../solidity/scripts/pre_forge.sh")
54+
.arg("script")
55+
.arg("-vvvvv")
56+
.args(extra_args)
57+
.args(["--tc", "VerifierTest"])
58+
.args(["--sig", "verify(bytes,bytes,bytes,uint256[],uint256[])"])
59+
.arg("./test/verifier/Verifier.t.post.sol")
60+
.args([
61+
dbg!(hex::encode(&result_bytes)),
62+
dbg!(hex::encode(&query_bytes)),
63+
dbg!(hex::encode(&proof_bytes)),
64+
])
65+
.arg(dbg!(format!("[{table_lengths}]")))
66+
.arg(dbg!(format!("[{commitments}]")))
67+
.output()
68+
.unwrap()
69+
.status
70+
.success()
71+
}
72+
fn evm_verifier_all(
73+
plan: &DynProofPlan,
74+
verifiable_result: &VerifiableQueryResult<HyperKZGCommitmentEvaluationProof>,
75+
accessor: &impl CommitmentAccessor<HyperKZGCommitment>,
76+
) -> bool {
77+
evm_verifier_with_extra_args(plan, verifiable_result, accessor, &[])
78+
&& evm_verifier_with_extra_args(plan, verifiable_result, accessor, &["--via-ir"])
79+
&& evm_verifier_with_extra_args(plan, verifiable_result, accessor, &["--optimize"])
80+
&& evm_verifier_with_extra_args(
81+
plan,
82+
verifiable_result,
83+
accessor,
84+
&["--optimize", "--via-ir"],
85+
)
86+
}
87+
88+
#[ignore = "This test requires the forge binary to be present"]
89+
#[test]
90+
fn we_can_verify_a_simple_filter_using_the_evm() {
91+
let (ps, vk) = hyperkzg::load_small_setup_for_testing();
92+
93+
let accessor = OwnedTableTestAccessor::<HyperKZGCommitmentEvaluationProof>::new_from_table(
94+
"namespace.table".parse().unwrap(),
95+
owned_table([
96+
bigint("a", [5, 3, 2, 5, 3, 2]),
97+
bigint("b", [0, 1, 2, 3, 4, 5]),
98+
]),
99+
0,
100+
&ps[..],
101+
);
102+
let query = QueryExpr::try_new(
103+
"SELECT b FROM table WHERE a = 5".parse().unwrap(),
104+
"namespace".into(),
105+
&accessor,
106+
)
107+
.unwrap();
108+
let plan = query.proof_expr();
109+
110+
let verifiable_result = VerifiableQueryResult::<HyperKZGCommitmentEvaluationProof>::new(
111+
&EVMProofPlan::new(plan.clone()),
112+
&accessor,
113+
&&ps[..],
114+
&[],
115+
)
116+
.unwrap();
117+
118+
verifiable_result
119+
.clone()
120+
.verify(&EVMProofPlan::new(plan.clone()), &accessor, &&vk, &[])
121+
.unwrap();
122+
123+
assert!(evm_verifier_all(plan, &verifiable_result, &accessor));
124+
}
125+
126+
#[ignore = "This test requires the forge binary to be present"]
127+
#[test]
128+
fn we_can_verify_a_simple_filter_with_negative_literal_using_the_evm() {
129+
let (ps, vk) = hyperkzg::load_small_setup_for_testing();
130+
131+
let accessor = OwnedTableTestAccessor::<HyperKZGCommitmentEvaluationProof>::new_from_table(
132+
"namespace.table".parse().unwrap(),
133+
owned_table([
134+
bigint("a", [5, 3, -2, 5, 3, -2]),
135+
bigint("b", [0, 1, 2, 3, 4, 5]),
136+
]),
137+
0,
138+
&ps[..],
139+
);
140+
let query = QueryExpr::try_new(
141+
"SELECT b FROM table WHERE a = -2".parse().unwrap(),
142+
"namespace".into(),
143+
&accessor,
144+
)
145+
.unwrap();
146+
let plan = query.proof_expr();
147+
let verifiable_result = VerifiableQueryResult::<HyperKZGCommitmentEvaluationProof>::new(
148+
&EVMProofPlan::new(plan.clone()),
149+
&accessor,
150+
&&ps[..],
151+
&[],
152+
)
153+
.unwrap();
154+
155+
assert!(evm_verifier_all(plan, &verifiable_result, &accessor));
156+
157+
verifiable_result
158+
.clone()
159+
.verify(&EVMProofPlan::new(plan.clone()), &accessor, &&vk, &[])
160+
.unwrap();
161+
}
162+
163+
#[ignore = "This test requires the forge binary to be present"]
164+
#[test]
165+
fn we_can_verify_a_complex_filter_using_the_evm() {
166+
let (ps, vk) = hyperkzg::load_small_setup_for_testing();
167+
168+
let accessor = OwnedTableTestAccessor::<HyperKZGCommitmentEvaluationProof>::new_from_table(
169+
"namespace.table".parse().unwrap(),
170+
owned_table([
171+
bigint("a", [5, 3, 2, 5, 3, 2, 102, 104, 107, 108]),
172+
bigint("b", [0, 1, 2, 3, 4, 5, 33, 44, 55, 6]),
173+
bigint("c", [6, 7, 8, 9, 10, 11, 14, 15, 73, 23]),
174+
bigint("d", [5, 7, 2, 5, 4, 1, 12, 22, 22, 22]),
175+
]),
176+
0,
177+
&ps[..],
178+
);
179+
let query = QueryExpr::try_new(
180+
"SELECT b,c FROM table WHERE a = d".parse().unwrap(),
181+
"namespace".into(),
182+
&accessor,
183+
)
184+
.unwrap();
185+
let plan = query.proof_expr();
186+
let verifiable_result = VerifiableQueryResult::<HyperKZGCommitmentEvaluationProof>::new(
187+
&EVMProofPlan::new(plan.clone()),
188+
&accessor,
189+
&&ps[..],
190+
&[],
191+
)
192+
.unwrap();
193+
194+
assert!(evm_verifier_all(plan, &verifiable_result, &accessor));
195+
196+
verifiable_result
197+
.clone()
198+
.verify(&EVMProofPlan::new(plan.clone()), &accessor, &&vk, &[])
199+
.unwrap();
200+
}

crates/proof-of-sql/src/sql/evm_proof_plan/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ mod proof_plan;
88
mod tests;
99

1010
pub use proof_plan::EVMProofPlan;
11+
12+
#[cfg(all(test, feature = "hyperkzg_proof"))]
13+
mod evm_tests;

solidity/src/builder/VerificationBuilder.pre.sol

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,4 +826,29 @@ library VerificationBuilder {
826826
__values := builder_get_table_chi_evaluations(__builder)
827827
}
828828
}
829+
830+
/// @notice Checks if the aggregate evaluation is non-zero and triggers an error if so
831+
/// @custom:as-yul-wrapper
832+
/// #### Wrapped Yul Function
833+
/// ##### Signature
834+
/// ```yul
835+
/// builder_check_aggregate_evaluation(builder_ptr)
836+
/// ```
837+
/// ##### Parameters
838+
/// * `builder_ptr` - memory pointer to the builder struct region
839+
/// @param __builder The builder struct
840+
function __checkAggregateEvaluation(Builder memory __builder) internal pure {
841+
assembly {
842+
// IMPORT-YUL ../base/Errors.sol
843+
function err(code) {
844+
revert(0, 0)
845+
}
846+
function builder_check_aggregate_evaluation(builder_ptr) {
847+
if mload(add(builder_ptr, BUILDER_AGGREGATE_EVALUATION_OFFSET)) {
848+
err(ERR_AGGREGATE_EVALUATION_MISMATCH)
849+
}
850+
}
851+
builder_check_aggregate_evaluation(__builder)
852+
}
853+
}
829854
}

0 commit comments

Comments
 (0)