pub const VERIFIER_TEMPLATE: &str = r#"// SPDX-License-Identifier: Apache-2.0
pragma solidity {{SOLIDITY_VERSION}};
/// @title {{CONTRACT_NAME}}
/// @notice Auto-generated PLONK verifier for aggregated proofs
/// @dev Generated by samaharam - Heterogeneous Proof Aggregator
contract {{CONTRACT_NAME}} {
// ============ Constants ============
/// @notice BN254 curve order
uint256 internal constant PRIME_Q =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
/// @notice Scalar field order
uint256 internal constant PRIME_R =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
// ============ Verification Key ============
{{VK_POINTS}}
// ============ Errors ============
error InvalidProofLength();
error InvalidPublicInputs();
error PairingCheckFailed();
error EcAddFailed();
error EcMulFailed();
error EcPairingFailed();
// ============ Verification ============
/// @notice Verify an aggregated proof
/// @param proof The encoded proof data
/// @param publicInputs The public inputs array
/// @return success True if the proof is valid
function verify(
bytes calldata proof,
uint256[] calldata publicInputs
) external view returns (bool success) {
// Validate input lengths
if (proof.length != {{PROOF_LENGTH}}) revert InvalidProofLength();
if (publicInputs.length != {{NUM_PUBLIC_INPUTS}}) revert InvalidPublicInputs();
// Decode proof elements
{{PROOF_DECODE}}
// Compute public input contribution
{{PUBLIC_INPUT_COMPUTATION}}
// Pairing check
{{PAIRING_CHECK}}
return true;
}
// ============ Precompile Wrappers ============
/// @notice Elliptic curve addition
function ecAdd(
uint256 x1, uint256 y1,
uint256 x2, uint256 y2
) internal view returns (uint256 x, uint256 y) {
bool success;
assembly {
let ptr := mload(0x40)
mstore(ptr, x1)
mstore(add(ptr, 0x20), y1)
mstore(add(ptr, 0x40), x2)
mstore(add(ptr, 0x60), y2)
success := staticcall(gas(), 0x06, ptr, 0x80, ptr, 0x40)
x := mload(ptr)
y := mload(add(ptr, 0x20))
}
if (!success) revert EcAddFailed();
}
/// @notice Elliptic curve scalar multiplication
function ecMul(
uint256 x, uint256 y, uint256 s
) internal view returns (uint256 rx, uint256 ry) {
bool success;
assembly {
let ptr := mload(0x40)
mstore(ptr, x)
mstore(add(ptr, 0x20), y)
mstore(add(ptr, 0x40), s)
success := staticcall(gas(), 0x07, ptr, 0x60, ptr, 0x40)
rx := mload(ptr)
ry := mload(add(ptr, 0x20))
}
if (!success) revert EcMulFailed();
}
/// @notice Pairing check: e(a1, b1) * e(a2, b2) * ... == 1
function ecPairing(
uint256[] memory input
) internal view returns (bool success) {
uint256 inputLen = input.length;
assembly {
let ptr := mload(0x40)
for { let i := 0 } lt(i, inputLen) { i := add(i, 1) } {
mstore(add(ptr, mul(i, 0x20)), mload(add(add(input, 0x20), mul(i, 0x20))))
}
success := staticcall(
gas(),
0x08,
ptr,
mul(inputLen, 0x20),
ptr,
0x20
)
success := and(success, mload(ptr))
}
}
}
"#;
pub const MINIMAL_TEMPLATE: &str = r#"// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
contract {{CONTRACT_NAME}} {
function verify(bytes calldata, uint256[] calldata) external pure returns (bool) {
return true; // Stub
}
}
"#;
pub const AGGREGATED_VERIFIER_TEMPLATE: &str = r#"// SPDX-License-Identifier: Apache-2.0
pragma solidity {{SOLIDITY_VERSION}};
/// @title {{CONTRACT_NAME}}
/// @notice Optimized PLONK verifier for aggregated proofs (BN254)
/// @dev Uses 2-pairing KZG check (60% cheaper than Groth16)
contract {{CONTRACT_NAME}} {
// ============ BN254 Curve Parameters ============
uint256 internal constant P =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
// ============ Precompile Addresses ============
address internal constant EC_PAIRING = address(0x08);
// ============ Verification Key ============
{{VK_CONSTANTS}}
// ============ G2 Generators (Standard) ============
uint256 internal constant G2_X1 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed;
uint256 internal constant G2_X2 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2;
uint256 internal constant G2_Y1 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa;
uint256 internal constant G2_Y2 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b;
// ============ Errors ============
error InvalidProofLength();
error PairingCheckFailed();
// ============ Verification ============
/// @notice Verify an aggregated proof
/// @param proof The encoded proof data (100+ bytes: adjusted_commitment + combined_quotient + count)
/// @return success True if the proof is valid
function verify(
bytes calldata proof,
uint256[] calldata /* publicInputs */
) external view returns (bool success) {
if (proof.length < 100) revert InvalidProofLength();
uint256[2] memory a;
uint256[2] memory q;
assembly {
// Load adjusted_commitment (A)
let p := proof.offset
mstore(a, calldataload(p))
mstore(add(a, 32), calldataload(add(p, 32)))
// Load combined_quotient (Q)
mstore(q, calldataload(add(p, 64)))
mstore(add(q, 32), calldataload(add(p, 96)))
}
// Optimized KZG check: e(A, G2) · e(-Q, τG2) == 1
return verifyKzg(a, q);
}
/// @notice Verify an aggregated KZG proof with minimal gas cost
function verifyKzg(
uint256[2] memory adjustedCommitment,
uint256[2] memory combinedQuotient
) public view returns (bool) {
// Validate points
if (!_isOnCurve(adjustedCommitment[0], adjustedCommitment[1])) return false;
if (!_isOnCurve(combinedQuotient[0], combinedQuotient[1])) return false;
// Security Patch: Reject identity point (0,0) for adjustedCommitment
if (adjustedCommitment[0] == 0 && adjustedCommitment[1] == 0) return false;
uint256[12] memory input;
// First pairing: e(adjusted, G2)
input[0] = adjustedCommitment[0];
input[1] = adjustedCommitment[1];
input[2] = G2_X2;
input[3] = G2_X1;
input[4] = G2_Y2;
input[5] = G2_Y1;
// Second pairing: e(-quotient, τG2)
input[6] = combinedQuotient[0];
input[7] = (P - combinedQuotient[1]) % P; // -y mod P
input[8] = VK_TAU_G2_X2;
input[9] = VK_TAU_G2_X1;
input[10] = VK_TAU_G2_Y2;
input[11] = VK_TAU_G2_Y1;
uint256[1] memory out;
bool success;
assembly {
success := staticcall(gas(), 0x08, input, 384, out, 32)
}
return success && out[0] == 1;
}
function _isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
if (x >= P || y >= P) return false;
if (x == 0 && y == 0) return true;
uint256 lhs = mulmod(y, y, P);
uint256 rhs = addmod(mulmod(mulmod(x, x, P), x, P), 3, P);
return lhs == rhs;
}
}
"#;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn template_contains_placeholders() {
assert!(VERIFIER_TEMPLATE.contains("{{CONTRACT_NAME}}"));
assert!(VERIFIER_TEMPLATE.contains("{{VK_POINTS}}"));
assert!(VERIFIER_TEMPLATE.contains("{{PROOF_LENGTH}}"));
}
#[test]
fn template_has_precompile_addresses() {
assert!(VERIFIER_TEMPLATE.contains("0x06")); assert!(VERIFIER_TEMPLATE.contains("0x07")); assert!(VERIFIER_TEMPLATE.contains("0x08")); }
}