diff --git a/src/codegen/template.rs b/src/codegen/template.rs index e25f723..d9de17c 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -4,7 +4,8 @@ use crate::codegen::{ }; use askama::{Error, Template}; use ruint::aliases::U256; -use std::fmt; +use std::fmt::{self, Display}; + #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] @@ -61,7 +62,7 @@ impl Halo2Verifier { } mod filters { - use std::fmt::LowerHex; + use std::fmt::{LowerHex, Display}; pub fn hex(value: impl LowerHex) -> ::askama::Result { let value = format!("{value:x}"); @@ -80,4 +81,22 @@ mod filters { Ok(string) } } + + pub fn left_pad(value: impl Display, width: usize) -> ::askama::Result { + let string = format!("{:>width$}", value, width = width); + Ok(string) + } +} + +pub fn index_coord(value: impl Display, index: &usize, coord: impl Display) -> String { + format!( + "{}_{}_{}", + value.to_string().to_uppercase(), + index, + coord.to_string().to_uppercase() + ) +} + +pub fn vk_var_name(value: impl Display) -> String { + format!("VK_{}_VAL", value.to_string().to_uppercase()) } diff --git a/src/evm.rs b/src/evm.rs index 1156ee3..c82696e 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -9,6 +9,12 @@ pub const FN_SIG_VERIFY_PROOF: [u8; 4] = [0x1e, 0x8e, 0x1e, 0x13]; /// Function signature of `verifyProof(address,bytes,uint256[])`. pub const FN_SIG_VERIFY_PROOF_WITH_VK_ADDRESS: [u8; 4] = [0xaf, 0x83, 0xa1, 0x8d]; +/// Function signature of `getVerifyingKey()`. +pub const FN_SIG_GET_VERIFYING_KEY: [u8; 4] = [0xaf, 0x7f, 0x43, 0x64]; + +/// Function signature of `getVerifyingKey(address)`. +pub const FN_SIG_GET_VERIFYING_KEY_WITH_VK_ADDRESS: [u8; 4] = [0x5f, 0xc4, 0x90, 0x4d]; + /// Encode proof into calldata to invoke `Halo2Verifier.verifyProof`. /// /// For `vk_address`: @@ -46,6 +52,32 @@ pub fn encode_calldata( .collect() } +/// Encode calldata to invoke `Halo2Verifier.getVerifyingKey`. +/// +/// For `vk_address`: +/// - Pass `None` if verifying key is embedded in `Halo2Verifier` +/// - Pass `Some(vk_address)` if verifying key is separated and deployed at `vk_address` +pub fn get_vkey_calldata(vk_address: Option<[u8; 20]>) -> Vec { + let fn_sig = if vk_address.is_some() { + FN_SIG_GET_VERIFYING_KEY_WITH_VK_ADDRESS + } else { + FN_SIG_GET_VERIFYING_KEY + }; + let vk_address = if let Some(vk_address) = vk_address { + U256::try_from_be_slice(&vk_address) + .unwrap() + .to_be_bytes::<0x20>() + .to_vec() + } else { + Vec::new() + }; + chain![ + fn_sig, // function signature + vk_address, // verifying key address + ] + .collect() +} + #[cfg(any(test, feature = "evm"))] pub(crate) mod test { pub use revm; diff --git a/src/lib.rs b/src/lib.rs index 9c6f65d..9b24261 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,11 @@ mod transcript; mod test; pub use codegen::{AccumulatorEncoding, BatchOpenScheme, SolidityGenerator}; -pub use evm::{encode_calldata, FN_SIG_VERIFY_PROOF, FN_SIG_VERIFY_PROOF_WITH_VK_ADDRESS}; +pub use evm::{ + encode_calldata, get_vkey_calldata, FN_SIG_GET_VERIFYING_KEY, + FN_SIG_GET_VERIFYING_KEY_WITH_VK_ADDRESS, FN_SIG_VERIFY_PROOF, + FN_SIG_VERIFY_PROOF_WITH_VK_ADDRESS, +}; pub use transcript::Keccak256Transcript; #[cfg(feature = "evm")] diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 81b2db6..afd13ed 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -69,6 +69,47 @@ contract Halo2Verifier { uint256 internal constant PAIRING_RHS_X_MPTR = {{ theta_mptr + 24 }}; uint256 internal constant PAIRING_RHS_Y_MPTR = {{ theta_mptr + 25 }}; + {%- if let Some(vk_) = vk %} + + // verifying key parameters + {%- for (name, chunk) in vk_.constants %} + uint256 internal constant {{ self::vk_var_name(name)|left_pad(24) }} = {{ chunk|hex_padded(64) }}; + {%- endfor %} + {%- for (x, y) in vk_.fixed_comms %} + uint256 internal constant {{ self::index_coord("fixed_comms", loop.index0, "x")|left_pad(24) }} = {{ x|hex_padded(64) }}; + uint256 internal constant {{ self::index_coord("fixed_comms", loop.index0, "y")|left_pad(24) }} = {{ y|hex_padded(64) }}; + {%- endfor %} + {%- for (x, y) in vk_.permutation_comms %} + uint256 internal constant {{ self::index_coord("permutation_comms", loop.index0, "x")|left_pad(24) }} = {{ x|hex_padded(64) }}; + uint256 internal constant {{ self::index_coord("permutation_comms", loop.index0, "y")|left_pad(24) }} = {{ y|hex_padded(64) }}; + {%- endfor %} + + function getVerifyingKey() public view returns (uint256[{{ vk_len / 32 }}] memory) { + return [ + {%- for (name, chunk) in vk_.constants %} + {{ self::vk_var_name(name)|left_pad(24) }}, + {%- endfor %} + {%- for (x, y) in vk_.fixed_comms %} + {{ self::index_coord("fixed_comms", loop.index0, "x")|left_pad(24) }}, + {{ self::index_coord("fixed_comms", loop.index0, "y")|left_pad(24) }}, + {%- endfor %} + {%- for (x, y) in vk_.permutation_comms %} + {{ self::index_coord("permutation_comms", loop.index0, "x")|left_pad(24) }}, + {{ self::index_coord("permutation_comms", loop.index0, "y")|left_pad(24) }}{%- if loop.index < vk_.permutation_comms.len() %},{%- endif %} + {%- endfor %} + ]; + } + + {%- else %} + + function getVerifyingKey(address vk) public view returns (uint256[{{ vk_len / 32 }}] memory) { + assembly { + extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + return (VK_MPTR, {{ vk_len|hex() }}) + } + } + {%- endif %} + function verifyProof( {%- match vk %} {%- when Some with (vk) %} @@ -227,17 +268,17 @@ contract Halo2Verifier { {%- when Some with (vk) %} // Load vk into memory {%- for (name, chunk) in vk.constants %} - mstore({{ vk_mptr + loop.index0 }}, {{ chunk|hex_padded(64) }}) // {{ name }} + mstore({{ vk_mptr + loop.index0 }}, {{ self::vk_var_name(name)|left_pad(24) }}) // {{ name }} {%- endfor %} {%- for (x, y) in vk.fixed_comms %} {%- let offset = vk.constants.len() %} - mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x - mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y + mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ self::index_coord("fixed_comms", loop.index0, "x")|left_pad(24) }}) // fixed_comms[{{ loop.index0 }}].x + mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ self::index_coord("fixed_comms", loop.index0, "y")|left_pad(24) }}) // fixed_comms[{{ loop.index0 }}].y {%- endfor %} {%- for (x, y) in vk.permutation_comms %} {%- let offset = vk.constants.len() + 2 * vk.fixed_comms.len() %} - mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x - mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y + mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ self::index_coord("permutation_comms", loop.index0, "x")|left_pad(24) }}) // permutation_comms[{{ loop.index0 }}].x + mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ self::index_coord("permutation_comms", loop.index0, "y")|left_pad(24) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} {%- when None %} // Copy vk into memory