Compare commits

..

57 Commits

Author SHA1 Message Date
Ethan
41d3233223 h2 sol verifier package update. 2024-10-28 20:01:00 +08:00
Ethan
cda4b2dacd merge "main" into "verifier-router" 2024-10-28 19:30:03 +08:00
Ethan
1ddbce537f ci testing 2024-10-25 22:31:38 +08:00
Ethan Cemer
5faa56f359 Merge branch 'zkonduit:main' into verifier-router 2024-10-11 02:15:14 -05:00
Ethan
97c1397fe6 verifier manager contract. 2024-10-08 20:47:58 +08:00
Ethan
e3051b79cd *bit flip fuzzing tests 2024-10-04 17:24:57 +08:00
Ethan Cemer
fe960767df Merge branch 'main' into reusable-vk-nb 2024-09-19 20:28:15 -05:00
Ethan
d4ebf8f120 skip lstm_large in overflow b/c too big to fit on chain. 2024-09-10 13:48:36 -05:00
Ethan
6788a9c726 *comprehensive test coverage for reusable verifier 2024-09-09 17:00:42 -05:00
Ethan
9fd3799ed1 *expand reusable verifier test examples 2024-09-07 17:46:32 -05:00
Ethan
c9351c0607 *reduce extcodeopy call by 1 2024-09-05 20:10:16 -05:00
Ethan Cemer
a2a8488d67 Merge branch 'main' into reusable-vk-nb 2024-09-05 15:40:34 -05:00
Ethan
9908f5c214 *add col overflow testing for reusable verifier. 2024-09-04 21:45:29 -05:00
Ethan Cemer
66ae2f7bac Merge branch 'main' into reusable-vk-nb 2024-09-02 18:02:09 -05:00
Ethan
4d18a5a903 *rmv create_evm_vk cmd
*test reusable verifier after h2 curve updates
2024-09-02 18:01:40 -05:00
Ethan
cd1860246c main lock 2024-08-29 16:43:16 -04:00
Ethan
ad59186eb6 Merge branch 'main' into reusable-vk-nb 2024-08-29 16:41:16 -04:00
Ethan
472a505063 *update separate vk contract name 2024-08-14 13:07:37 -04:00
Ethan
1588c9735e *update lock 2024-08-13 20:22:52 -04:00
Ethan
9d876d5bf9 MV lookup packed. 2024-08-09 17:34:22 -04:00
Ethan
fd29794cdd hardcode coeff_ptr 2024-08-08 13:55:10 -04:00
Ethan
a616fbb759 packed permutation evals and challenge data. 2024-08-07 16:34:39 -04:00
Ethan
f1fe01952f Merge branch 'main' into reusable-vk-nb 2024-08-05 10:39:39 -04:00
Ethan
23f71873ce *update lock 2024-08-05 10:37:06 -04:00
Ethan
31168b0a99 *fully reusable verifier 2024-08-03 01:10:26 -05:00
Ethan
35bb286d40 *coeff_sums_computation 2024-08-01 21:11:47 -05:00
Ethan
d5944d36fe *r_evals_computation 2024-08-01 15:18:17 -05:00
Ethan
3df63d540d coeff_computations. 2024-07-30 19:18:23 -05:00
Ethan
779f82e0dc *vanish computations pcs 2024-07-29 20:35:10 -05:00
Ethan
889db3a6fe *update lock. 2024-07-29 18:16:33 -05:00
Ethan
fab08bbe9d *update cargo lock 2024-07-29 14:08:16 -05:00
Ethan
72f1892d54 *update lock 2024-07-29 06:18:31 -05:00
Ethan
f3e531c3e7 *update lock 2024-07-28 21:52:29 -05:00
Ethan
00f8dd42f6 *update lock
*revert to svm 0.8.20
2024-07-28 18:06:56 -05:00
Ethan
099726b245 update lock. 2024-07-26 22:14:41 -05:00
Ethan
d5f18495de *update lock. 2024-07-26 15:10:41 -05:00
Ethan
add04f6090 Merge branch 'main' into reusable-vk-nb 2024-07-26 15:10:03 -05:00
Ethan
6b71bdc920 use latest version of solc 2024-07-23 15:01:42 -05:00
Ethan
3e5153db9f *update lock 2024-07-23 14:59:49 -05:00
Ethan
a1dd82a3c1 *update lock 2024-07-19 18:20:26 -05:00
Ethan
f6acf241c9 Merge branch 'main' into reusable-vk-nb 2024-07-19 18:18:49 -05:00
Ethan
dbe812b88d *update lock 2024-07-12 22:14:13 -05:00
Ethan
36188ab542 *comment out JS tests for reusable verifier CI tests 2024-07-12 16:00:46 -05:00
Ethan Cemer
cd9d7c3d50 Merge branch 'main' into reusable-vk-nb 2024-07-12 15:58:16 -05:00
Ethan Cemer
f5ae49e1c5 Merge branch 'main' into reusable-vk-nb 2024-07-11 22:48:11 -05:00
Ethan
f25b420429 *update lock 2024-07-11 22:47:42 -05:00
Ethan
f59aaf80c5 *update lock 2024-07-11 15:24:28 -05:00
Ethan
257e275773 *update lock 2024-07-11 00:23:19 -05:00
Ethan
2fe0eb4b27 *update lock 2024-07-06 23:05:48 -05:00
Ethan
bdad19b83c *update lock 2024-07-03 21:00:58 -05:00
Ethan
a17aad064b *update lock 2024-07-02 21:43:38 -05:00
Ethan
985205ae40 *update lock
*hardcode sampel inputs for resuable verifiers nb
2024-07-02 00:49:34 -05:00
Ethan Cemer
b08dc28ed5 Merge branch 'main' into reusable-vk-nb 2024-07-01 17:08:23 -05:00
Ethan
b3997cd325 lazy static import 2024-06-28 09:36:18 -05:00
Ethan
83cb957299 *fix stuck integration tests. 2024-06-27 19:46:05 -05:00
Ethan
c92be15b81 *update lockfile 2024-06-26 16:11:06 -05:00
Ethan
6924797e48 *reusable verifier example nb 2024-06-25 16:10:24 -05:00
32 changed files with 1027 additions and 485 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ pkg
!AttestData.sol
!VerifierBase.sol
!LoadInstances.sol
!VerifierManager.sol
*.pf
*.vk
*.pk

2
Cargo.lock generated
View File

@@ -2397,7 +2397,7 @@ dependencies = [
[[package]]
name = "halo2_solidity_verifier"
version = "0.1.0"
source = "git+https://github.com/alexander-camuto/halo2-solidity-verifier?branch=ac/update-h2-curves#eede1db7f3e599112bd1186e9d1913286bdcb539"
source = "git+https://github.com/alexander-camuto/halo2-solidity-verifier?branch=vka-log#c319e229ad677ee4c7d95bdae45c2958350cfd14"
dependencies = [
"askama",
"blake2b_simd",

View File

@@ -19,8 +19,11 @@ crate-type = ["cdylib", "rlib", "staticlib"]
halo2_gadgets = { git = "https://github.com/zkonduit/halo2", branch = "ac/optional-selector-poly" }
halo2curves = { git = "https://github.com/privacy-scaling-explorations/halo2curves", rev = "b753a832e92d5c86c5c997327a9cf9de86a18851", features = [
"derive_serde",
"derive_serde",
] }
halo2_proofs = { git = "https://github.com/zkonduit/halo2", package = "halo2_proofs", branch = "ac/cache-lookup-commitments", features = [
"circuit-params",
] }
halo2_proofs = { git = "https://github.com/zkonduit/halo2", package = "halo2_proofs", branch = "ac/cache-lookup-commitments", features = ["circuit-params"] }
rand = { version = "0.8", default-features = false }
itertools = { version = "0.10.3", default-features = false }
clap = { version = "4.5.3", features = ["derive"], optional = true }
@@ -33,9 +36,9 @@ halo2_wrong_ecc = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac
snark-verifier = { git = "https://github.com/zkonduit/snark-verifier", branch = "ac/chunked-mv-lookup", features = [
"derive_serde",
] }
halo2_solidity_verifier = { git = "https://github.com/alexander-camuto/halo2-solidity-verifier", branch = "ac/update-h2-curves", optional = true }
maybe-rayon = { version = "0.1.1", default-features = false }
bincode = { version = "1.3.3", default-features = false }
halo2_solidity_verifier = { git = "https://github.com/alexander-camuto/halo2-solidity-verifier", branch = "vka-log", optional = true }
maybe-rayon = { version = "0.1.1", default_features = false }
bincode = { version = "1.3.3", default_features = false }
unzip-n = "0.1.2"
num = "0.4.1"
portable-atomic = { version = "1.6.0", optional = true }
@@ -43,10 +46,7 @@ tosubcommand = { git = "https://github.com/zkonduit/enum_to_subcommand", package
semver = { version = "1.0.22", optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
serde_json = { version = "1.0.97", features = [
"float_roundtrip",
"raw_value",
] }
serde_json = { version = "1.0.97", features = ["float_roundtrip", "raw_value"] }
# evm related deps
alloy = { git = "https://github.com/alloy-rs/alloy", version = "0.1.0", rev = "5fbf57bac99edef9d8475190109a7ea9fb7e5e83", features = [
@@ -56,23 +56,39 @@ alloy = { git = "https://github.com/alloy-rs/alloy", version = "0.1.0", rev = "5
"rpc-types-eth",
"signer-wallet",
"node-bindings",
], optional = true }
foundry-compilers = { version = "0.4.1", features = [
"svm-solc",
], optional = true }
foundry-compilers = { version = "0.4.1", features = ["svm-solc"], optional = true }
ethabi = { version = "18", optional = true }
indicatif = { version = "0.17.5", features = ["rayon"], optional = true }
gag = { version = "1.0.0", default-features = false, optional = true }
instant = { version = "0.1" }
reqwest = { version = "0.12.4", default-features = false, features = ["default-tls", "multipart", "stream"], optional = true }
reqwest = { version = "0.12.4", default-features = false, features = [
"default-tls",
"multipart",
"stream",
], optional = true }
openssl = { version = "0.10.55", features = ["vendored"], optional = true }
tokio-postgres = { version = "0.7.10", optional = true }
pg_bigdecimal = { version = "0.1.5", optional = true }
lazy_static = { version = "1.4.0", optional = true }
colored_json = { version = "3.0.1", default-features = false, optional = true }
regex = { version = "1", default-features = false, optional = true }
tokio = { version = "1.35.0", default-features = false, features = ["macros", "rt-multi-thread"], optional = true }
pyo3 = { version = "0.21.2", features = ["extension-module", "abi3-py37", "macros"], default-features = false, optional = true }
pyo3-asyncio = { git = "https://github.com/jopemachine/pyo3-asyncio/", branch="migration-pyo3-0.21", features = ["attributes", "tokio-runtime"], default-features = false, optional = true }
tokio = { version = "1.35.0", default-features = false, features = [
"macros",
"rt-multi-thread",
], optional = true }
pyo3 = { version = "0.21.2", features = [
"extension-module",
"abi3-py37",
"macros",
], default-features = false, optional = true }
pyo3-asyncio = { git = "https://github.com/jopemachine/pyo3-asyncio/", branch = "migration-pyo3-0.21", features = [
"attributes",
"tokio-runtime",
], default-features = false, optional = true }
pyo3-log = { version = "0.10.0", default-features = false, optional = true }
tract-onnx = { git = "https://github.com/sonos/tract/", rev = "40c64319291184814d9fea5fdf4fa16f5a4f7116", default-features = false, optional = true }
tabled = { version = "0.12.0", optional = true }
@@ -169,7 +185,7 @@ harness = false
[[bench]]
name = "sigmoid"
name = "relu"
harness = false
[[bench]]
@@ -177,12 +193,12 @@ name = "relu_lookupless"
harness = false
[[bench]]
name = "accum_matmul_sigmoid"
name = "accum_matmul_relu"
harness = false
[[bench]]
name = "accum_matmul_sigmoid_overflow"
name = "accum_matmul_relu_overflow"
harness = false
[[bin]]
@@ -197,7 +213,13 @@ required-features = ["ios-bindings", "uuid", "camino", "uniffi_bindgen"]
[features]
web = ["wasm-bindgen-rayon"]
default = ["ezkl", "mv-lookup", "precompute-coset", "no-banner", "parallel-poly-read"]
default = [
"ezkl",
"mv-lookup",
"precompute-coset",
"no-banner",
"parallel-poly-read",
]
onnx = ["dep:tract-onnx"]
python-bindings = ["pyo3", "pyo3-log", "pyo3-asyncio"]
ios-bindings = ["mv-lookup", "precompute-coset", "parallel-poly-read", "uniffi"]
@@ -231,7 +253,10 @@ ezkl = [
"dep:clap",
"dep:tosubcommand",
]
parallel-poly-read = ["halo2_proofs/circuit-params", "halo2_proofs/parallel-poly-read"]
parallel-poly-read = [
"halo2_proofs/circuit-params",
"halo2_proofs/parallel-poly-read",
]
mv-lookup = [
"halo2_proofs/mv-lookup",
"snark-verifier/mv-lookup",
@@ -260,4 +285,3 @@ rustflags = ["-C", "relocation-model=pic"]
lto = "fat"
codegen-units = 1
# panic = "abort"

View File

@@ -0,0 +1,146 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"name": "DeployedVerifier",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "bytecode",
"type": "bytes"
}
],
"name": "deployVerifier",
"outputs": [
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "bytecode",
"type": "bytes"
}
],
"name": "precomputeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "verifierAddresses",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@@ -64,7 +64,7 @@ impl Circuit<Fr> for MyCircuit {
&a,
BITS,
K,
&LookupOp::Sigmoid { scale: 1.0.into() },
&LookupOp::LeakyReLU { slope: 0.0.into() },
)
.unwrap();
@@ -93,7 +93,7 @@ impl Circuit<Fr> for MyCircuit {
.layout(
&mut region,
&[output.unwrap()],
Box::new(LookupOp::Sigmoid { scale: 1.0.into() }),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap();
Ok(())

View File

@@ -65,7 +65,7 @@ impl Circuit<Fr> for MyCircuit {
&a,
BITS,
k,
&LookupOp::Sigmoid { scale: 1.0.into() },
&LookupOp::LeakyReLU { slope: 0.0.into() },
)
.unwrap();
@@ -94,7 +94,7 @@ impl Circuit<Fr> for MyCircuit {
.layout(
&mut region,
&[output.unwrap()],
Box::new(LookupOp::Sigmoid { scale: 1.0.into() }),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap();
Ok(())

View File

@@ -42,7 +42,7 @@ impl Circuit<Fr> for NLCircuit {
.map(|_| VarTensor::new_advice(cs, K, 1, LEN))
.collect::<Vec<_>>();
let nl = LookupOp::Sigmoid { scale: 1.0.into() };
let nl = LookupOp::LeakyReLU { slope: 0.0.into() };
let mut config = Config::default();
@@ -68,7 +68,7 @@ impl Circuit<Fr> for NLCircuit {
.layout(
&mut region,
&[self.input.clone()],
Box::new(LookupOp::Sigmoid { scale: 1.0.into() }),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap();
Ok(())

View File

@@ -68,14 +68,7 @@ impl Circuit<Fr> for NLCircuit {
|region| {
let mut region = RegionCtx::new(region, 0, 1, 1024, 2);
config
.layout(
&mut region,
&[self.input.clone()],
Box::new(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
}),
)
.layout(&mut region, &[self.input.clone()], Box::new(PolyOp::ReLU))
.unwrap();
Ok(())
},

View File

@@ -0,0 +1,184 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// lib/openzeppelin-contracts/contracts/utils/Context.sol
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// lib/openzeppelin-contracts/contracts/access/Ownable.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
/// set the owener initialy to be the anvil test account
address private _owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor() {
_transferOwnership(msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// interface for the reusable verifier.
interface Halo2VerifierReusable {
function verifyProof(
address vkArtifact,
bytes calldata proof,
uint256[] calldata instances
) external returns (bool);
}
// Manages the deployment of all EZKL reusbale verifiers (ezkl version specific), verifiying key artifacts (circuit specific) and
// routing proof verifications to the correct VKA and associate reusable verifier.
// Helps to prevent the deployment of duplicate verifiers.
contract EZKLVerifierManager is Ownable {
/// @dev Mapping that checks if a given reusable verifier has been deployed
mapping(address => bool) public verifierAddresses;
event DeployedVerifier(address addr);
// 1. Compute the address of the verifier to be deployed
function precomputeAddress(
bytes memory bytecode
) public view returns (address) {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
uint(0),
keccak256(bytecode)
)
);
return address(uint160(uint(hash)));
}
// 2. Deploy the reusable verifier using create2
/// @param bytecode The bytecode of the reusable verifier to deploy
function deployVerifier(
bytes memory bytecode
) public returns (address addr) {
assembly {
addr := create2(
0x0, // value, hardcode to 0
add(bytecode, 0x20),
mload(bytecode),
0x0 // salt, hardcode to 0
)
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
verifierAddresses[addr] = true;
emit DeployedVerifier(addr);
}
}

View File

@@ -1,4 +1,4 @@
ezkl==15.1.2
ezkl==0.0.0
sphinx
sphinx-rtd-theme
sphinxcontrib-napoleon

View File

@@ -1,7 +1,7 @@
import ezkl
project = 'ezkl'
release = '15.1.2'
release = '0.0.0'
version = release

View File

@@ -146,8 +146,6 @@ where
let params = VarTensor::new_advice(cs, K, NUM_INNER_COLS, LEN);
let output = VarTensor::new_advice(cs, K, NUM_INNER_COLS, LEN);
let _constant = VarTensor::constant_cols(cs, K, LEN, false);
println!("INPUT COL {:#?}", input);
let mut layer_config = PolyConfig::configure(
@@ -158,11 +156,15 @@ where
);
layer_config
.configure_range_check(cs, &input, &params, (-1, 1), K)
.unwrap();
layer_config
.configure_range_check(cs, &input, &params, (0, 1023), K)
.configure_lookup(
cs,
&input,
&output,
&params,
(LOOKUP_MIN, LOOKUP_MAX),
K,
&LookupOp::LeakyReLU { slope: 0.0.into() },
)
.unwrap();
layer_config
@@ -193,11 +195,6 @@ where
) -> Result<(), Error> {
config.layer_config.layout_tables(&mut layouter).unwrap();
config
.layer_config
.layout_range_checks(&mut layouter)
.unwrap();
let x = layouter
.assign_region(
|| "mlp_4d",
@@ -227,10 +224,7 @@ where
.layout(
&mut region,
&[x.unwrap()],
Box::new(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
}),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap();

View File

@@ -53,10 +53,6 @@ impl<const LEN: usize, const LOOKUP_MIN: IntegerRep, const LOOKUP_MAX: IntegerRe
let output = VarTensor::new_advice(cs, K, 1, LEN);
// tells the config layer to add an affine op to the circuit gate
let _constant = VarTensor::constant_cols(cs, K, LEN, false);
println!("INPUT COL {:#?}", input);
let mut layer_config = PolyConfig::<F>::configure(
cs,
&[input.clone(), params.clone()],
@@ -64,12 +60,17 @@ impl<const LEN: usize, const LOOKUP_MIN: IntegerRep, const LOOKUP_MAX: IntegerRe
CheckMode::SAFE,
);
// sets up a new ReLU table and resuses it for l1 and l3 non linearities
layer_config
.configure_range_check(cs, &input, &params, (-1, 1), K)
.unwrap();
layer_config
.configure_range_check(cs, &input, &params, (0, 1023), K)
.configure_lookup(
cs,
&input,
&output,
&params,
(LOOKUP_MIN, LOOKUP_MAX),
K,
&LookupOp::LeakyReLU { slope: 0.0.into() },
)
.unwrap();
// sets up a new ReLU table and resuses it for l1 and l3 non linearities
@@ -103,11 +104,6 @@ impl<const LEN: usize, const LOOKUP_MIN: IntegerRep, const LOOKUP_MAX: IntegerRe
) -> Result<(), Error> {
config.layer_config.layout_tables(&mut layouter).unwrap();
config
.layer_config
.layout_range_checks(&mut layouter)
.unwrap();
let x = layouter
.assign_region(
|| "mlp_4d",
@@ -148,10 +144,7 @@ impl<const LEN: usize, const LOOKUP_MIN: IntegerRep, const LOOKUP_MAX: IntegerRe
.layout(
&mut region,
&[x],
Box::new(PolyOp::LeakyReLU {
scale: 1,
slope: 0.0.into(),
}),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap()
.unwrap();
@@ -191,10 +184,7 @@ impl<const LEN: usize, const LOOKUP_MIN: IntegerRep, const LOOKUP_MAX: IntegerRe
.layout(
&mut region,
&[x],
Box::new(PolyOp::LeakyReLU {
scale: 1,
slope: 0.0.into(),
}),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.unwrap();
println!("6");

View File

@@ -1,42 +0,0 @@
from torch import nn
import torch
import json
import numpy as np
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
def forward(self, x):
# reciprocal sqrt
m = 1 / torch.sqrt(x)
return m
circuit = MyModel()
x = torch.empty(1, 8).uniform_(0, 1)
out = circuit(x)
print(out)
torch.onnx.export(circuit, x, "network.onnx",
export_params=True, # store the trained parameter weights inside the model file
opset_version=17, # the ONNX version to export the model to
do_constant_folding=True, # whether to execute constant folding for optimization
input_names=['input'], # the model's input names
output_names=['output'], # the model's output names
dynamic_axes={'input': {0: 'batch_size'}, # variable length axes
'output': {0: 'batch_size'}})
d1 = ((x).detach().numpy()).reshape([-1]).tolist()
data = dict(
input_data=[d1],
)
# Serialize data into file:
json.dump(data, open("input.json", 'w'))

View File

@@ -1 +0,0 @@
{"input_data": [[0.8590779900550842, 0.4029041528701782, 0.6507361531257629, 0.9782488942146301, 0.37392884492874146, 0.6867020726203918, 0.11407750844955444, 0.362740159034729]]}

View File

@@ -1,17 +0,0 @@
pytorch2.2.2:Ź
$
input/Sqrt_output_0/Sqrt"Sqrt
1
/Sqrt_output_0output /Reciprocal"
Reciprocal
main_graphZ!
input


batch_size
b"
output


batch_size
B

View File

@@ -94,7 +94,4 @@ pub enum CircuitError {
#[error("[io] {0}")]
/// IO error
IoError(#[from] std::io::Error),
/// Invalid scale
#[error("negative scale for an op that requires positive inputs {0}")]
NegativeScale(String),
}

View File

@@ -16,6 +16,7 @@ pub enum HybridOp {
Recip {
input_scale: utils::F32,
output_scale: utils::F32,
use_range_check_for_int: bool,
},
Div {
denom: utils::F32,
@@ -44,8 +45,6 @@ pub enum HybridOp {
ReduceArgMin {
dim: usize,
},
Max,
Min,
Softmax {
input_scale: utils::F32,
output_scale: utils::F32,
@@ -80,8 +79,6 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Hybrid
| HybridOp::Less { .. }
| HybridOp::Equals { .. }
| HybridOp::GreaterEqual { .. }
| HybridOp::Max
| HybridOp::Min
| HybridOp::LessEqual { .. } => {
vec![0, 1]
}
@@ -96,14 +93,13 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Hybrid
fn as_string(&self) -> String {
match self {
HybridOp::Max => format!("MAX"),
HybridOp::Min => format!("MIN"),
HybridOp::Recip {
input_scale,
output_scale,
use_range_check_for_int,
} => format!(
"RECIP (input_scale={}, output_scale={})",
input_scale, output_scale
"RECIP (input_scale={}, output_scale={}, use_range_check_for_int={})",
input_scale, output_scale, use_range_check_for_int
),
HybridOp::Div {
denom,
@@ -166,8 +162,6 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Hybrid
values: &[ValTensor<F>],
) -> Result<Option<ValTensor<F>>, CircuitError> {
Ok(Some(match self {
HybridOp::Max => layouts::max_comp(config, region, values[..].try_into()?)?,
HybridOp::Min => layouts::min_comp(config, region, values[..].try_into()?)?,
HybridOp::SumPool {
padding,
stride,
@@ -185,13 +179,31 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Hybrid
HybridOp::Recip {
input_scale,
output_scale,
} => layouts::recip(
config,
region,
values[..].try_into()?,
integer_rep_to_felt(input_scale.0 as i128),
integer_rep_to_felt(output_scale.0 as i128),
)?,
use_range_check_for_int,
} => {
if input_scale.0.fract() == 0.0
&& output_scale.0.fract() == 0.0
&& *use_range_check_for_int
{
layouts::recip(
config,
region,
values[..].try_into()?,
integer_rep_to_felt(input_scale.0 as i128),
integer_rep_to_felt(output_scale.0 as i128),
)?
} else {
layouts::nonlinearity(
config,
region,
values.try_into()?,
&LookupOp::Recip {
input_scale: *input_scale,
output_scale: *output_scale,
},
)?
}
}
HybridOp::Div {
denom,
use_range_check_for_int,

View File

@@ -4155,48 +4155,6 @@ pub(crate) fn argmin<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
Ok(assigned_argmin)
}
/// max layout
pub(crate) fn max_comp<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
config: &BaseConfig<F>,
region: &mut RegionCtx<F>,
values: &[ValTensor<F>; 2],
) -> Result<ValTensor<F>, CircuitError> {
let is_greater = greater(config, region, values)?;
let is_less = not(config, region, &[is_greater.clone()])?;
let max_val_p1 = pairwise(
config,
region,
&[values[0].clone(), is_greater],
BaseOp::Mult,
)?;
let max_val_p2 = pairwise(config, region, &[values[1].clone(), is_less], BaseOp::Mult)?;
pairwise(config, region, &[max_val_p1, max_val_p2], BaseOp::Add)
}
/// min comp layout
pub(crate) fn min_comp<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
config: &BaseConfig<F>,
region: &mut RegionCtx<F>,
values: &[ValTensor<F>; 2],
) -> Result<ValTensor<F>, CircuitError> {
let is_greater = greater(config, region, values)?;
let is_less = not(config, region, &[is_greater.clone()])?;
let min_val_p1 = pairwise(config, region, &[values[0].clone(), is_less], BaseOp::Mult)?;
let min_val_p2 = pairwise(
config,
region,
&[values[1].clone(), is_greater],
BaseOp::Mult,
)?;
pairwise(config, region, &[min_val_p1, min_val_p2], BaseOp::Add)
}
/// max layout
pub(crate) fn max<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
config: &BaseConfig<F>,
@@ -4305,6 +4263,7 @@ pub(crate) fn sign<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
) -> Result<ValTensor<F>, CircuitError> {
let mut decomp = decompose(config, region, values, &region.base(), &region.legs())?;
// get every n elements now, which correspond to the sign bit
decomp.get_every_n(region.legs() + 1)?;
decomp.reshape(values[0].dims())?;
@@ -4321,12 +4280,10 @@ pub(crate) fn abs<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
pairwise(config, region, &[values[0].clone(), sign], BaseOp::Mult)
}
pub(crate) fn leaky_relu<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
pub(crate) fn relu<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(
config: &BaseConfig<F>,
region: &mut RegionCtx<F>,
values: &[ValTensor<F>; 1],
alpha: &utils::F32,
input_scale: &i32,
) -> Result<ValTensor<F>, CircuitError> {
let sign = sign(config, region, values)?;
@@ -4335,45 +4292,12 @@ pub(crate) fn leaky_relu<F: PrimeField + TensorType + PartialOrd + std::hash::Ha
let relu_mask = equals(config, region, &[sign, unit])?;
let positive = pairwise(
pairwise(
config,
region,
&[values[0].clone(), relu_mask.clone()],
&[values[0].clone(), relu_mask],
BaseOp::Mult,
)?;
if alpha.0 == 0. {
return Ok(positive);
}
if input_scale < &0 {
return Err(CircuitError::NegativeScale("leaky_relu".to_string()));
}
let scale_constant = create_constant_tensor(F::from(2_i32.pow(*input_scale as u32) as u64), 1);
let rescaled_positive = pairwise(config, region, &[positive, scale_constant], BaseOp::Mult)?;
let neg_mask = not(config, region, &[relu_mask])?;
let quantized_alpha = quantize_tensor(
Tensor::from([alpha.0; 1].into_iter()),
*input_scale,
&crate::graph::Visibility::Fixed,
)?;
let alpha_tensor = create_constant_tensor(quantized_alpha[0], 1);
let scaled_neg_mask = pairwise(config, region, &[neg_mask, alpha_tensor], BaseOp::Mult)?;
let neg_part = pairwise(
config,
region,
&[values[0].clone(), scaled_neg_mask],
BaseOp::Mult,
)?;
pairwise(config, region, &[rescaled_positive, neg_part], BaseOp::Add)
)
}
fn multi_dim_axes_op<F: PrimeField + TensorType + PartialOrd + std::hash::Hash>(

View File

@@ -15,32 +15,101 @@ use halo2curves::ff::PrimeField;
/// An enum representing the operations that can be used to express more complex operations via accumulation
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
pub enum LookupOp {
Div { denom: utils::F32 },
Cast { scale: utils::F32 },
Ceil { scale: utils::F32 },
Floor { scale: utils::F32 },
Round { scale: utils::F32 },
RoundHalfToEven { scale: utils::F32 },
Sqrt { scale: utils::F32 },
Rsqrt { scale: utils::F32 },
Sigmoid { scale: utils::F32 },
Ln { scale: utils::F32 },
Exp { scale: utils::F32 },
Cos { scale: utils::F32 },
ACos { scale: utils::F32 },
Cosh { scale: utils::F32 },
ACosh { scale: utils::F32 },
Sin { scale: utils::F32 },
ASin { scale: utils::F32 },
Sinh { scale: utils::F32 },
ASinh { scale: utils::F32 },
Tan { scale: utils::F32 },
ATan { scale: utils::F32 },
Tanh { scale: utils::F32 },
ATanh { scale: utils::F32 },
Erf { scale: utils::F32 },
Pow { scale: utils::F32, a: utils::F32 },
HardSwish { scale: utils::F32 },
Div {
denom: utils::F32,
},
Cast {
scale: utils::F32,
},
Max {
scale: utils::F32,
a: utils::F32,
},
Min {
scale: utils::F32,
a: utils::F32,
},
Ceil {
scale: utils::F32,
},
Floor {
scale: utils::F32,
},
Round {
scale: utils::F32,
},
RoundHalfToEven {
scale: utils::F32,
},
Sqrt {
scale: utils::F32,
},
Rsqrt {
scale: utils::F32,
},
Recip {
input_scale: utils::F32,
output_scale: utils::F32,
},
LeakyReLU {
slope: utils::F32,
},
Sigmoid {
scale: utils::F32,
},
Ln {
scale: utils::F32,
},
Exp {
scale: utils::F32,
},
Cos {
scale: utils::F32,
},
ACos {
scale: utils::F32,
},
Cosh {
scale: utils::F32,
},
ACosh {
scale: utils::F32,
},
Sin {
scale: utils::F32,
},
ASin {
scale: utils::F32,
},
Sinh {
scale: utils::F32,
},
ASinh {
scale: utils::F32,
},
Tan {
scale: utils::F32,
},
ATan {
scale: utils::F32,
},
Tanh {
scale: utils::F32,
},
ATanh {
scale: utils::F32,
},
Erf {
scale: utils::F32,
},
KroneckerDelta,
Pow {
scale: utils::F32,
a: utils::F32,
},
HardSwish {
scale: utils::F32,
},
}
impl LookupOp {
@@ -59,8 +128,16 @@ impl LookupOp {
LookupOp::Round { scale } => format!("round_{}", scale),
LookupOp::RoundHalfToEven { scale } => format!("round_half_to_even_{}", scale),
LookupOp::Pow { scale, a } => format!("pow_{}_{}", scale, a),
LookupOp::KroneckerDelta => "kronecker_delta".into(),
LookupOp::Max { scale, a } => format!("max_{}_{}", scale, a),
LookupOp::Min { scale, a } => format!("min_{}_{}", scale, a),
LookupOp::Div { denom } => format!("div_{}", denom),
LookupOp::Cast { scale } => format!("cast_{}", scale),
LookupOp::Recip {
input_scale,
output_scale,
} => format!("recip_{}_{}", input_scale, output_scale),
LookupOp::LeakyReLU { slope: a } => format!("leaky_relu_{}", a),
LookupOp::Sigmoid { scale } => format!("sigmoid_{}", scale),
LookupOp::Sqrt { scale } => format!("sqrt_{}", scale),
LookupOp::Rsqrt { scale } => format!("rsqrt_{}", scale),
@@ -106,12 +183,32 @@ impl LookupOp {
LookupOp::Pow { scale, a } => Ok::<_, TensorError>(
tensor::ops::nonlinearities::pow(&x, scale.0.into(), a.0.into()),
),
LookupOp::KroneckerDelta => {
Ok::<_, TensorError>(tensor::ops::nonlinearities::kronecker_delta(&x))
}
LookupOp::Max { scale, a } => Ok::<_, TensorError>(
tensor::ops::nonlinearities::max(&x, scale.0.into(), a.0.into()),
),
LookupOp::Min { scale, a } => Ok::<_, TensorError>(
tensor::ops::nonlinearities::min(&x, scale.0.into(), a.0.into()),
),
LookupOp::Div { denom } => Ok::<_, TensorError>(
tensor::ops::nonlinearities::const_div(&x, f32::from(*denom).into()),
),
LookupOp::Cast { scale } => Ok::<_, TensorError>(
tensor::ops::nonlinearities::const_div(&x, f32::from(*scale).into()),
),
LookupOp::Recip {
input_scale,
output_scale,
} => Ok::<_, TensorError>(tensor::ops::nonlinearities::recip(
&x,
input_scale.into(),
output_scale.into(),
)),
LookupOp::LeakyReLU { slope: a } => {
Ok::<_, TensorError>(tensor::ops::nonlinearities::leakyrelu(&x, a.0.into()))
}
LookupOp::Sigmoid { scale } => {
Ok::<_, TensorError>(tensor::ops::nonlinearities::sigmoid(&x, scale.into()))
}
@@ -191,9 +288,20 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Lookup
LookupOp::Round { scale } => format!("ROUND(scale={})", scale),
LookupOp::RoundHalfToEven { scale } => format!("ROUND_HALF_TO_EVEN(scale={})", scale),
LookupOp::Pow { a, scale } => format!("POW(scale={}, exponent={})", scale, a),
LookupOp::KroneckerDelta => "K_DELTA".into(),
LookupOp::Max { scale, a } => format!("MAX(scale={}, a={})", scale, a),
LookupOp::Min { scale, a } => format!("MIN(scale={}, a={})", scale, a),
LookupOp::Recip {
input_scale,
output_scale,
} => format!(
"RECIP(input_scale={}, output_scale={})",
input_scale, output_scale
),
LookupOp::Div { denom, .. } => format!("DIV(denom={})", denom),
LookupOp::Cast { scale } => format!("CAST(scale={})", scale),
LookupOp::Ln { scale } => format!("LN(scale={})", scale),
LookupOp::LeakyReLU { slope: a } => format!("L_RELU(slope={})", a),
LookupOp::Sigmoid { scale } => format!("SIGMOID(scale={})", scale),
LookupOp::Sqrt { scale } => format!("SQRT(scale={})", scale),
LookupOp::Erf { scale } => format!("ERF(scale={})", scale),
@@ -236,6 +344,8 @@ impl<F: PrimeField + TensorType + PartialOrd + std::hash::Hash> Op<F> for Lookup
let in_scale = inputs_scale[0];
in_scale + multiplier_to_scale(1. / scale.0 as f64)
}
LookupOp::Recip { output_scale, .. } => multiplier_to_scale(output_scale.into()),
LookupOp::KroneckerDelta => 0,
_ => inputs_scale[0],
};
Ok(scale)

View File

@@ -1,8 +1,5 @@
use crate::{
circuit::{
layouts,
utils::{self, F32},
},
circuit::layouts,
tensor::{self, Tensor, TensorError},
};
@@ -12,12 +9,9 @@ use super::{base::BaseOp, *};
/// An enum representing the operations that can be expressed as arithmetic (non lookup) operations.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PolyOp {
ReLU,
Abs,
Sign,
LeakyReLU {
slope: utils::F32,
scale: i32,
},
GatherElements {
dim: usize,
constant_idx: Option<Tensor<usize>>,
@@ -118,9 +112,9 @@ impl<
fn as_string(&self) -> String {
match &self {
PolyOp::LeakyReLU { slope: a, .. } => format!("LEAKYRELU (slope={})", a),
PolyOp::Abs => "ABS".to_string(),
PolyOp::Sign => "SIGN".to_string(),
PolyOp::ReLU => "RELU".to_string(),
PolyOp::GatherElements { dim, constant_idx } => format!(
"GATHERELEMENTS (dim={}, constant_idx{})",
dim,
@@ -204,9 +198,7 @@ impl<
Ok(Some(match self {
PolyOp::Abs => layouts::abs(config, region, values[..].try_into()?)?,
PolyOp::Sign => layouts::sign(config, region, values[..].try_into()?)?,
PolyOp::LeakyReLU { slope, scale } => {
layouts::leaky_relu(config, region, values[..].try_into()?, slope, scale)?
}
PolyOp::ReLU => layouts::relu(config, region, values[..].try_into()?)?,
PolyOp::MultiBroadcastTo { shape } => {
layouts::expand(config, region, values[..].try_into()?, shape)?
}
@@ -337,12 +329,6 @@ impl<
fn out_scale(&self, in_scales: Vec<crate::Scale>) -> Result<crate::Scale, CircuitError> {
let scale = match self {
// this corresponds to the relu operation
PolyOp::LeakyReLU {
slope: F32(0.0), ..
} => in_scales[0],
// this corresponds to the leaky relu operation with a slope which induces a change in scale
PolyOp::LeakyReLU { scale, .. } => in_scales[0] + *scale,
PolyOp::MeanOfSquares { .. } => 2 * in_scales[0],
PolyOp::Xor | PolyOp::Or | PolyOp::And | PolyOp::Not => 0,
PolyOp::Iff => in_scales[1],

View File

@@ -1379,10 +1379,7 @@ mod conv_relu_col_ultra_overflow {
.layout(
&mut region,
&[output.unwrap().unwrap()],
Box::new(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
}),
Box::new(PolyOp::ReLU),
)
.unwrap();
Ok(())
@@ -2350,14 +2347,7 @@ mod matmul_relu {
.unwrap();
let _output = config
.base_config
.layout(
&mut region,
&[output.unwrap()],
Box::new(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
}),
)
.layout(&mut region, &[output.unwrap()], Box::new(PolyOp::ReLU))
.unwrap();
Ok(())
},
@@ -2449,14 +2439,7 @@ mod relu {
|region| {
let mut region = RegionCtx::new(region, 0, 1, 2, 2);
Ok(config
.layout(
&mut region,
&[self.input.clone()],
Box::new(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
}),
)
.layout(&mut region, &[self.input.clone()], Box::new(PolyOp::ReLU))
.unwrap())
},
)
@@ -2499,11 +2482,11 @@ mod lookup_ultra_overflow {
use snark_verifier::system::halo2::transcript::evm::EvmTranscript;
#[derive(Clone)]
struct SigmoidCircuit<F: PrimeField + TensorType + PartialOrd> {
struct ReLUCircuit<F: PrimeField + TensorType + PartialOrd> {
pub input: ValTensor<F>,
}
impl Circuit<F> for SigmoidCircuit<F> {
impl Circuit<F> for ReLUCircuit<F> {
type Config = BaseConfig<F>;
type FloorPlanner = SimpleFloorPlanner;
type Params = TestParams;
@@ -2517,7 +2500,7 @@ mod lookup_ultra_overflow {
.map(|_| VarTensor::new_advice(cs, 4, 1, 3))
.collect::<Vec<_>>();
let nl = LookupOp::Sigmoid { scale: 1.0.into() };
let nl = LookupOp::LeakyReLU { slope: 0.0.into() };
let mut config = BaseConfig::default();
@@ -2550,7 +2533,7 @@ mod lookup_ultra_overflow {
.layout(
&mut region,
&[self.input.clone()],
Box::new(LookupOp::Sigmoid { scale: 1.0.into() }),
Box::new(LookupOp::LeakyReLU { slope: 0.0.into() }),
)
.map_err(|_| Error::Synthesis)
},
@@ -2563,13 +2546,13 @@ mod lookup_ultra_overflow {
#[test]
#[ignore]
fn sigmoidcircuit() {
fn relucircuit() {
// get some logs fam
crate::logger::init_logger();
// parameters
let a = Tensor::from((0..4).map(|i| Value::known(F::from(i + 1))));
let circuit = SigmoidCircuit::<F> {
let circuit = ReLUCircuit::<F> {
input: ValTensor::from(a),
};
@@ -2579,7 +2562,7 @@ mod lookup_ultra_overflow {
let pk = crate::pfsys::create_keys::<
halo2_proofs::poly::kzg::commitment::KZGCommitmentScheme<halo2curves::bn256::Bn256>,
SigmoidCircuit<F>,
ReLUCircuit<F>,
>(&circuit, &params, true)
.unwrap();

View File

@@ -95,6 +95,9 @@ pub const DEFAULT_USE_REDUCED_SRS_FOR_VERIFICATION: &str = "false";
pub const DEFAULT_ONLY_RANGE_CHECK_REBASE: &str = "false";
/// Default commitment
pub const DEFAULT_COMMITMENT: &str = "kzg";
// TODO: In prod this will be the same across all chains we deploy to using the EZKL multisig create2 deployment.
/// Default address of the verifier manager.
pub const DEFAULT_VERIFIER_MANAGER_ADDRESS: &str = "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9";
#[cfg(feature = "python-bindings")]
/// Converts TranscriptType into a PyObject (Required for TranscriptType to be compatible with Python)
@@ -187,11 +190,13 @@ pub enum ContractType {
/// Deploys a verifier contrat tailored to the circuit and not reusable
Verifier {
/// Whether to deploy a reusable verifier. This can reduce state bloat on-chain since you need only deploy a verifying key artifact (vka) for a given circuit which is significantly smaller than the verifier contract (up to 4 times smaller for large circuits)
/// Can also be used as an alternative to aggregation for verifiers that are otherwise too large to fit on-chain.
/// Can also be used as an alternative to aggregation for verifiers that are otherwise too large to fit on-chain.
reusable: bool,
},
/// Deploys a verifying key artifact that the reusable verifier loads into memory during runtime. Encodes the circuit specific data that was otherwise hardcoded onto the stack.
VerifyingKeyArtifact,
/// Manages the deployments of all reusable verifier and verifying artifact keys. Routes all the verification tx to the correct artifacts.
VerifierManager
}
impl Default for ContractType {
@@ -215,6 +220,7 @@ impl std::fmt::Display for ContractType {
reusable: false,
} => "verifier".to_string(),
ContractType::VerifyingKeyArtifact => "vka".to_string(),
ContractType::VerifierManager => "manager".to_string()
}
)
}
@@ -232,16 +238,16 @@ impl From<&str> for ContractType {
"verifier" => ContractType::Verifier { reusable: false },
"verifier/reusable" => ContractType::Verifier { reusable: true },
"vka" => ContractType::VerifyingKeyArtifact,
"manager" => ContractType::VerifierManager,
_ => {
log::error!("Invalid value for ContractType");
log::warn!("Defaulting to verifier");
ContractType::default()
}
},
}
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
/// wrapper for H160 to make it easy to parse into flag vals
pub struct H160Flag {
@@ -876,6 +882,14 @@ pub enum Commands {
/// Private secp256K1 key in hex format, 64 chars, no 0x prefix, of the account signing transactions. If None the private key will be generated by Anvil
#[arg(short = 'P', long, value_hint = clap::ValueHint::Other)]
private_key: Option<String>,
/// Deployed verifier manager contract's address
/// Used to facilitate reusable verifier and vk artifact deployment
#[arg(long, value_hint = clap::ValueHint::Other)]
addr_verifier_manager: Option<H160Flag>,
/// Deployed reusable verifier contract's address
/// Use to facilitate reusable verifier and vk artifact deployment
#[arg(long, value_hint = clap::ValueHint::Other)]
addr_reusable_verifier: Option<H160Flag>,
/// Contract type to be deployed
#[arg(long = "contract-type", short = 'C', default_value = DEFAULT_CONTRACT_DEPLOYMENT_TYPE, value_hint = clap::ValueHint::Other)]
contract: ContractType,

View File

@@ -31,7 +31,7 @@ use alloy::transports::{RpcError, TransportErrorKind};
use foundry_compilers::artifacts::Settings as SolcSettings;
use foundry_compilers::error::{SolcError, SolcIoError};
use foundry_compilers::Solc;
use halo2_solidity_verifier::encode_calldata;
use halo2_solidity_verifier::{encode_calldata, encode_deploy};
use halo2curves::bn256::{Fr, G1Affine};
use halo2curves::group::ff::PrimeField;
use itertools::Itertools;
@@ -213,6 +213,16 @@ abigen!(
}
);
// The bytecode here was generated from running solc compiler version 0.8.20 with optimization enabled and runs param set to 1.
abigen!(
#[allow(missing_docs)]
#[sol(
rpc,
bytecode = "60806040525f80546001600160a01b03191673f39fd6e51aad88f6f4ce6ab8827279cfffb92266179055348015610034575f80fd5b5061003e33610043565b610092565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6103dc8061009f5f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c80635717ecef146100645780635d34fd561461009b578063715018a6146100bb5780637a33ac87146100c55780638da5cb5b14610125578063f2fde38b1461012d575b5f80fd5b6100866100723660046102a7565b60016020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6100ae6100a93660046102e8565b610140565b6040516100929190610392565b6100c36101bf565b005b6100ae6100d33660046102e8565b8051602091820120604080516001600160f81b0319818501523060601b6001600160601b03191660218201525f6035820152605580820193909352815180820390930183526075019052805191012090565b6100ae6101d2565b6100c361013b3660046102a7565b6101e0565b5f610149610226565b5f8251602084015ff59050803b61015e575f80fd5b6001600160a01b0381165f90815260016020819052604091829020805460ff19169091179055517f27bf8213352a1c07513a54703c920b9e437940154edead05874c43279acf166c906101b2908390610392565b60405180910390a1919050565b6101c7610226565b6101d05f610258565b565b5f546001600160a01b031690565b6101e8610226565b6001600160a01b03811661021a575f604051631e4fbdf760e01b81526004016102119190610392565b60405180910390fd5b61022381610258565b50565b3361022f6101d2565b6001600160a01b0316146101d0573360405163118cdaa760e01b81526004016102119190610392565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f602082840312156102b7575f80fd5b81356001600160a01b03811681146102cd575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f602082840312156102f8575f80fd5b81356001600160401b038082111561030e575f80fd5b818401915084601f830112610321575f80fd5b813581811115610333576103336102d4565b604051601f8201601f19908116603f0116810190838211818310171561035b5761035b6102d4565b81604052828152876020848701011115610373575f80fd5b826020860160208301375f928101602001929092525095945050505050565b6001600160a01b039190911681526020019056fea26469706673582212201d85104628b308554b775f612650220008f8e318f66dc4ace466d82d70bae4e264736f6c63430008140033"
)]
EZKLVerifierManager,
"./abis/EZKLVerifierManager.json"
);
#[derive(Debug, thiserror::Error)]
pub enum EthError {
#[error("a transport error occurred: {0}")]
@@ -352,6 +362,99 @@ pub async fn deploy_contract_via_solidity(
Ok(contract)
}
pub async fn deploy_vka(
sol_code_path: PathBuf,
rpc_url: Option<&str>,
runs: usize,
private_key: Option<&str>,
contract_name: &str,
verifier_manager: H160,
reusable_verifier: H160,
) -> Result<H160, EthError> {
let (client, _) = setup_eth_backend(rpc_url, private_key).await?;
// Create an instance of the EZKLVerifierManager contract
let verifier_manager_contract = EZKLVerifierManager::new(verifier_manager, client.clone());
// Get the bytecode of the contract to be deployed
let (_, bytecode, _run_time_bytecode) =
get_contract_artifacts(sol_code_path.clone(), contract_name, runs).await?;
// Check if the reusable verifier is already deployed
let deployed_verifier: bool = verifier_manager_contract
.verifierAddresses(reusable_verifier)
.call()
.await?
._0;
if deployed_verifier == false {
panic!("The reusable verifier for this VKA has not been deployed yet.");
}
let encoded = encode_deploy(&bytecode);
debug!("encoded: {:#?}", hex::encode(&encoded));
let input: TransactionInput = encoded.into();
let tx = TransactionRequest::default()
.to(reusable_verifier)
.input(input);
debug!("transaction {:#?}", tx);
let result = client.call(&tx).await;
if let Err(e) = result {
return Err(EvmVerificationError::SolidityExecution(e.to_string()).into());
}
// Now send the tx
let _ = client.send_transaction(tx).await?;
let result = result?;
debug!("result: {:#?}", result.to_vec());
let contract = H160::from_slice(&result.to_vec()[12..32]);
return Ok(contract);
}
pub async fn deploy_reusable_verifier(
sol_code_path: PathBuf,
rpc_url: Option<&str>,
runs: usize,
private_key: Option<&str>,
contract_name: &str,
verifier_manager: H160,
) -> Result<H160, EthError> {
let (client, _) = setup_eth_backend(rpc_url, private_key).await?;
// Create an instance of the EZKLVerifierManager contract
let verifier_manager_contract = EZKLVerifierManager::new(verifier_manager, client.clone());
// Get the bytecode of the contract to be deployed
let (_, bytecode, _run_time_bytecode) =
get_contract_artifacts(sol_code_path.clone(), contract_name, runs).await?;
// Deploy the contract using the EZKLVerifierManager
let output = verifier_manager_contract
.deployVerifier(bytecode.clone().into())
.call()
.await?;
let out = verifier_manager_contract
.precomputeAddress(bytecode.clone().into())
.call()
.await?;
// assert that out == output
assert_eq!(out._0, output.addr);
// Get the deployed contract address from the receipt
let contract = output.addr;
let _ = verifier_manager_contract
.deployVerifier(bytecode.into())
.send()
.await?;
return Ok(contract);
}
///
pub async fn deploy_da_verifier_via_solidity(
settings_path: PathBuf,

View File

@@ -410,24 +410,46 @@ pub async fn run(command: Commands) -> Result<String, EZKLError> {
commitment.into(),
)
.map(|e| serde_json::to_string(&e).unwrap()),
#[cfg(not(target_arch = "wasm32"))]
Commands::DeployEvm {
sol_code_path,
rpc_url,
addr_path,
optimizer_runs,
private_key,
addr_verifier_manager,
addr_reusable_verifier,
contract,
} => {
// if contract type is either verifier/reusable
match contract {
ContractType::Verifier { reusable: true } => {
if addr_verifier_manager.is_none() {
panic!("Must pass a verifier manager address for reusable verifier")
}
}
ContractType::VerifyingKeyArtifact => {
if addr_verifier_manager.is_none() || addr_reusable_verifier.is_none() {
panic!(
"Must pass a verifier manager address and reusable verifier address for verifying key artifact"
)
}
}
_ => {}
};
deploy_evm(
sol_code_path.unwrap_or(DEFAULT_SOL_CODE.into()),
rpc_url,
addr_path.unwrap_or(DEFAULT_CONTRACT_ADDRESS.into()),
optimizer_runs,
private_key,
addr_verifier_manager.map(|s| s.into()),
addr_reusable_verifier.map(|s| s.into()),
contract,
)
.await
}
#[cfg(not(target_arch = "wasm32"))]
Commands::DeployEvmDataAttestation {
data,
settings_path,
@@ -671,10 +693,10 @@ pub(crate) async fn get_srs_cmd(
let srs_uri = format!("{}{}", PUBLIC_SRS_URL, k);
let mut reader = Cursor::new(fetch_srs(&srs_uri).await?);
// check the SRS
let pb = init_spinner();
pb.set_message("Validating SRS (this may take a while) ...");
let pb = init_spinner();
pb.set_message("Validating SRS (this may take a while) ...");
let params = ParamsKZG::<Bn256>::read(&mut reader)?;
pb.finish_with_message("SRS validated.");
pb.finish_with_message("SRS validated.");
info!("Saving SRS to disk...");
let mut file = std::fs::File::create(get_srs_path(k, srs_path.clone(), commitment))?;
@@ -727,7 +749,7 @@ pub(crate) async fn gen_witness(
None
};
let mut input = circuit.load_graph_input(&data).await?;
let mut input = circuit.load_graph_input(&data).await?;
#[cfg(any(not(feature = "ezkl"), target_arch = "wasm32"))]
let mut input = circuit.load_graph_input(&data)?;
@@ -1417,6 +1439,7 @@ pub(crate) async fn create_evm_verifier(
Ok(String::new())
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) async fn create_evm_vka(
vk_path: PathBuf,
srs_path: Option<PathBuf>,
@@ -1445,9 +1468,20 @@ pub(crate) async fn create_evm_vka(
num_instance,
);
let vk_solidity = generator.render_separately()?.1;
let (reusable_verifier, vk_solidity) = generator.render_separately()?;
File::create(sol_code_path.clone())?.write_all(vk_solidity.as_bytes())?;
// Remove the first line of vk_solidity (license identifier). Same license identifier for all contracts in this .sol
let vk_solidity = vk_solidity
.lines()
.skip(1)
.collect::<Vec<&str>>()
.join("\n");
// We store each contracts to the same file...
// We need to do this so that during the deployment transaction we make sure
// verifier manager links the VKA to the correct reusable_verifier.
let combined_solidity = format!("{}\n\n{}", reusable_verifier, vk_solidity);
File::create(sol_code_path.clone())?.write_all(combined_solidity.as_bytes())?;
// fetch abi of the contract
let (abi, _, _) = get_contract_artifacts(sol_code_path, "Halo2VerifyingArtifact", 0).await?;
@@ -1565,21 +1599,51 @@ pub(crate) async fn deploy_evm(
addr_path: PathBuf,
runs: usize,
private_key: Option<String>,
verifier_manager: Option<alloy::primitives::Address>,
reusable_verifier: Option<alloy::primitives::Address>,
contract: ContractType,
) -> Result<String, EZKLError> {
use crate::eth::{deploy_reusable_verifier, deploy_vka};
let contract_name = match contract {
ContractType::Verifier { reusable: false } => "Halo2Verifier",
ContractType::Verifier { reusable: true } => "Halo2VerifierReusable",
ContractType::VerifyingKeyArtifact => "Halo2VerifyingArtifact",
ContractType::VerifierManager => "EZKLVerifierManager",
};
let contract_address = if contract_name == "Halo2VerifierReusable" {
// Use VerifierManager to deploy the contract
deploy_reusable_verifier(
sol_code_path,
rpc_url.as_deref(),
runs,
private_key.as_deref(),
contract_name,
verifier_manager.unwrap(),
)
.await?
} else if contract_name == "Halo2VerifyingArtifact" {
deploy_vka(
sol_code_path,
rpc_url.as_deref(),
runs,
private_key.as_deref(),
contract_name,
verifier_manager.unwrap(),
reusable_verifier.unwrap(),
)
.await?
} else {
deploy_contract_via_solidity(
sol_code_path,
rpc_url.as_deref(),
runs,
private_key.as_deref(),
contract_name,
)
.await?
};
let contract_address = deploy_contract_via_solidity(
sol_code_path,
rpc_url.as_deref(),
runs,
private_key.as_deref(),
contract_name,
)
.await?;
info!("Contract deployed at: {:#?}", contract_address);
@@ -2021,7 +2085,7 @@ pub(crate) fn mock_aggregate(
}
}
// proof aggregation
let pb = {
let pb = {
let pb = init_spinner();
pb.set_message("Aggregating (may take a while)...");
pb
@@ -2032,7 +2096,7 @@ pub(crate) fn mock_aggregate(
let prover = halo2_proofs::dev::MockProver::run(logrows, &circuit, vec![circuit.instances()])
.map_err(|e| ExecutionError::MockProverError(e.to_string()))?;
prover.verify().map_err(ExecutionError::VerifyError)?;
pb.finish_with_message("Done.");
pb.finish_with_message("Done.");
Ok(String::new())
}
@@ -2126,7 +2190,7 @@ pub(crate) fn aggregate(
}
// proof aggregation
let pb = {
let pb = {
let pb = init_spinner();
pb.set_message("Aggregating (may take a while)...");
pb
@@ -2275,7 +2339,7 @@ pub(crate) fn aggregate(
);
snark.save(&proof_path)?;
pb.finish_with_message("Done.");
pb.finish_with_message("Done.");
Ok(snark)
}

View File

@@ -763,41 +763,81 @@ pub fn new_op_from_onnx(
.map(|(i, _)| i)
.collect::<Vec<_>>();
if inputs.len() == 2 {
if const_inputs.len() > 0 {
let const_idx = const_inputs[0];
let boxed_op = inputs[const_idx].opkind();
let unit = if let Some(c) = extract_const_raw_values(boxed_op) {
if c.len() == 1 {
c[0]
} else {
return Err(GraphError::InvalidDims(idx, "max".to_string()));
}
} else {
return Err(GraphError::OpMismatch(idx, "Max".to_string()));
};
if unit == 0. {
if let Some(node) = inputs.get_mut(const_idx) {
node.decrement_use();
deleted_indices.push(const_idx);
}
SupportedOp::Linear(PolyOp::LeakyReLU {
slope: 0.0.into(),
scale: 1,
})
} else {
SupportedOp::Hybrid(HybridOp::Max)
}
if const_inputs.len() != 1 {
return Err(GraphError::OpMismatch(idx, "Max".to_string()));
}
let const_idx = const_inputs[0];
let boxed_op = inputs[const_idx].opkind();
let unit = if let Some(c) = extract_const_raw_values(boxed_op) {
if c.len() == 1 {
c[0]
} else {
SupportedOp::Hybrid(HybridOp::Max)
return Err(GraphError::InvalidDims(idx, "max".to_string()));
}
} else {
return Err(GraphError::OpMismatch(idx, "Max".to_string()));
};
if inputs.len() == 2 {
if let Some(node) = inputs.get_mut(const_idx) {
node.decrement_use();
deleted_indices.push(const_idx);
}
if unit == 0. {
SupportedOp::Linear(PolyOp::ReLU)
} else {
// get the non-constant index
let non_const_idx = if const_idx == 0 { 1 } else { 0 };
SupportedOp::Nonlinear(LookupOp::Max {
scale: scale_to_multiplier(inputs[non_const_idx].out_scales()[0]).into(),
a: crate::circuit::utils::F32(unit),
})
}
} else {
return Err(GraphError::InvalidDims(idx, "max".to_string()));
}
}
"Min" => {
// Extract the min value
// first find the input that is a constant
// and then extract the value
let const_inputs = inputs
.iter()
.enumerate()
.filter(|(_, n)| n.is_constant())
.map(|(i, _)| i)
.collect::<Vec<_>>();
if const_inputs.len() != 1 {
return Err(GraphError::OpMismatch(idx, "Min".to_string()));
}
let const_idx = const_inputs[0];
let boxed_op = inputs[const_idx].opkind();
let unit = if let Some(c) = extract_const_raw_values(boxed_op) {
if c.len() == 1 {
c[0]
} else {
return Err(GraphError::InvalidDims(idx, "min".to_string()));
}
} else {
return Err(GraphError::OpMismatch(idx, "Min".to_string()));
};
if inputs.len() == 2 {
SupportedOp::Hybrid(HybridOp::Min)
if let Some(node) = inputs.get_mut(const_idx) {
node.decrement_use();
deleted_indices.push(const_idx);
}
// get the non-constant index
let non_const_idx = if const_idx == 0 { 1 } else { 0 };
SupportedOp::Nonlinear(LookupOp::Min {
scale: scale_to_multiplier(inputs[non_const_idx].out_scales()[0]).into(),
a: crate::circuit::utils::F32(unit),
})
} else {
return Err(GraphError::InvalidDims(idx, "min".to_string()));
}
@@ -809,6 +849,7 @@ pub fn new_op_from_onnx(
SupportedOp::Hybrid(HybridOp::Recip {
input_scale: (scale_to_multiplier(in_scale) as f32).into(),
output_scale: (scale_to_multiplier(max_scale) as f32).into(),
use_range_check_for_int: true,
})
}
@@ -823,9 +864,8 @@ pub fn new_op_from_onnx(
}
};
SupportedOp::Linear(PolyOp::LeakyReLU {
SupportedOp::Nonlinear(LookupOp::LeakyReLU {
slope: crate::circuit::utils::F32(leaky_op.alpha),
scale: scales.params,
})
}
"Scan" => {
@@ -1106,17 +1146,10 @@ pub fn new_op_from_onnx(
if c.raw_values.len() > 1 {
unimplemented!("only support scalar pow")
}
let exponent = c.raw_values[0];
if exponent.fract() == 0.0 {
SupportedOp::Linear(PolyOp::Pow(exponent as u32))
} else {
SupportedOp::Nonlinear(LookupOp::Pow {
scale: scale_to_multiplier(inputs[0].out_scales()[0]).into(),
a: crate::circuit::utils::F32(exponent),
})
}
SupportedOp::Nonlinear(LookupOp::Pow {
scale: scale_to_multiplier(inputs[0].out_scales()[0]).into(),
a: crate::circuit::utils::F32(c.raw_values[0]),
})
} else {
unimplemented!("only support constant pow for now")
}

View File

@@ -1553,6 +1553,35 @@ pub mod nonlinearities {
.unwrap()
}
/// Applies Kronecker delta to a tensor of integers.
/// # Arguments
/// * `a` - Tensor
/// # Examples
/// ```
/// use ezkl::tensor::Tensor;
/// use ezkl::fieldutils::IntegerRep;
/// use ezkl::tensor::ops::nonlinearities::kronecker_delta;
/// let x = Tensor::<IntegerRep>::new(
/// Some(&[2, 15, 2, 1, 1, 0]),
/// &[2, 3],
/// ).unwrap();
/// let result = kronecker_delta(&x);
/// let expected = Tensor::<IntegerRep>::new(Some(&[0, 0, 0, 0, 0, 1]), &[2, 3]).unwrap();
/// assert_eq!(result, expected);
/// ```
pub fn kronecker_delta<T: TensorType + std::cmp::PartialEq + Send + Sync>(
a: &Tensor<T>,
) -> Tensor<T> {
a.par_enum_map(|_, a_i| {
if a_i == T::zero().unwrap() {
Ok::<_, TensorError>(T::one().unwrap())
} else {
Ok::<_, TensorError>(T::zero().unwrap())
}
})
.unwrap()
}
/// Elementwise applies sigmoid to a tensor of integers.
/// # Arguments
///

Binary file not shown.

View File

@@ -205,7 +205,7 @@ mod native_tests {
"1l_tiny_div",
];
const TESTS: [&str; 95] = [
const TESTS: [&str; 94] = [
"1l_mlp", //0
"1l_slice",
"1l_concat",
@@ -304,7 +304,6 @@ mod native_tests {
"lstm_large", // 91
"lstm_medium", // 92
"lenet_5", // 93
"rsqrt", // 94
];
const WASM_TESTS: [&str; 46] = [
@@ -543,7 +542,7 @@ mod native_tests {
}
});
seq!(N in 0..=94 {
seq!(N in 0..=93 {
#(#[test_case(TESTS[N])])*
#[ignore]
@@ -1001,13 +1000,21 @@ mod native_tests {
use crate::native_tests::run_js_tests;
use ezkl::logger::init_logger;
use crate::native_tests::lazy_static;
use std::sync::Once;
// Global variables to store verifier hashes and identical verifiers
lazy_static! {
// create a new variable of type
static ref REUSABLE_VERIFIER_ADDR: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
static ref ANVIL_INSTANCE: std::sync::Mutex<Option<std::process::Child>> = std::sync::Mutex::new(None);
}
static INIT: Once = Once::new();
fn initialize() {
INIT.call_once(|| {
let anvil_child = crate::native_tests::start_anvil(false, Hardfork::Latest);
*ANVIL_INSTANCE.lock().unwrap() = Some(anvil_child);
});
}
/// Currently only on chain inputs that return a non-negative value are supported.
const TESTS_ON_CHAIN_INPUT: [&str; 17] = [
@@ -1119,9 +1126,10 @@ mod native_tests {
});
seq!(N in 0..4 {
seq!(N in 0..=93 {
#(#[test_case(TESTS[N])])*
fn kzg_evm_prove_and_verify_reusable_verifier_(test: &str) {
initialize();
crate::native_tests::init_binary();
let test_dir = TempDir::new(test).unwrap();
let path = test_dir.path().to_str().unwrap(); crate::native_tests::mv_test_(path, test);
@@ -1129,28 +1137,18 @@ mod native_tests {
init_logger();
log::error!("Running kzg_evm_prove_and_verify_reusable_verifier_ for test: {}", test);
// default vis
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "private", "private", "public", &mut REUSABLE_VERIFIER_ADDR.lock().unwrap(), false);
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "private", "private", "public", false);
// public/public vis
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "public", "private", "public", &mut Some(reusable_verifier_address), false);
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "public", "private", "public", false);
// hashed input
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "hashed", "private", "public", &mut Some(reusable_verifier_address), false);
match REUSABLE_VERIFIER_ADDR.try_lock() {
Ok(mut addr) => {
*addr = Some(reusable_verifier_address.clone());
log::error!("Reusing the same verifeir deployed at address: {}", reusable_verifier_address);
}
Err(_) => {
log::error!("Failed to acquire lock on REUSABLE_VERIFIER_ADDR");
}
}
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "hashed", "private", "public", false);
test_dir.close().unwrap();
}
#(#[test_case(TESTS[N])])*
fn kzg_evm_prove_and_verify_reusable_verifier_with_overflow_(test: &str) {
initialize();
// verifier too big to fit on chain with overflow calibration target
if test == "1l_eltwise_div" || test == "lenet_5" || test == "ltsf" || test == "lstm_large" {
return;
@@ -1162,24 +1160,13 @@ mod native_tests {
init_logger();
log::error!("Running kzg_evm_prove_and_verify_reusable_verifier_with_overflow_ for test: {}", test);
// default vis
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "private", "private", "public", &mut REUSABLE_VERIFIER_ADDR.lock().unwrap(), true);
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "private", "private", "public", true);
// public/public vis
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "public", "private", "public", &mut Some(reusable_verifier_address), true);
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "public", "private", "public", true);
// hashed input
let reusable_verifier_address: String = kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "hashed", "private", "public", &mut Some(reusable_verifier_address), true);
match REUSABLE_VERIFIER_ADDR.try_lock() {
Ok(mut addr) => {
*addr = Some(reusable_verifier_address.clone());
log::error!("Reusing the same verifeir deployed at address: {}", reusable_verifier_address);
}
Err(_) => {
log::error!("Failed to acquire lock on REUSABLE_VERIFIER_ADDR");
}
}
kzg_evm_prove_and_verify_reusable_verifier(2, path, test.to_string(), "hashed", "private", "public", true);
test_dir.close().unwrap();
}
});
@@ -2229,9 +2216,8 @@ mod native_tests {
input_visibility: &str,
param_visibility: &str,
output_visibility: &str,
reusable_verifier_address: &mut Option<String>,
overflow: bool,
) -> String {
) {
let anvil_url = ANVIL_URL.as_str();
prove_and_verify(
@@ -2254,57 +2240,82 @@ mod native_tests {
let vk_arg = format!("{}/{}/key.vk", test_dir, example_name);
let rpc_arg = format!("--rpc-url={}", anvil_url);
// addr path for verifier manager contract
let addr_path_arg = format!("--addr-path={}/{}/addr.txt", test_dir, example_name);
let verifier_manager_arg: String;
let settings_arg = format!("--settings-path={}", settings_path);
// reusable verifier sol_arg
let sol_arg = format!("--sol-code-path={}/{}/kzg.sol", test_dir, example_name);
// if the reusable verifier address is not set, create the verifier
let deployed_addr_arg = match reusable_verifier_address {
Some(addr) => addr.clone(),
None => {
// create the reusable verifier
let args = vec![
"create-evm-verifier",
"--vk-path",
&vk_arg,
&settings_arg,
&sol_arg,
"--reusable",
];
// create the reusable verifier
let args = vec![
"create-evm-verifier",
"--vk-path",
&vk_arg,
&settings_arg,
&sol_arg,
"--reusable",
];
let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR))
.args(&args)
.status()
.expect("failed to execute process");
assert!(status.success());
let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR))
.args(&args)
.status()
.expect("failed to execute process");
assert!(status.success());
// deploy the verifier
let args = vec![
"deploy-evm",
rpc_arg.as_str(),
addr_path_arg.as_str(),
sol_arg.as_str(),
"-C=verifier/reusable",
];
// deploy the verifier manager
let args = vec![
"deploy-evm",
rpc_arg.as_str(),
addr_path_arg.as_str(),
// set the sol code path to be contracts/VerifierManager.sol relative to root
"--sol-code-path=contracts/VerifierManager.sol",
"-C=manager",
];
let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR))
.args(&args)
.status()
.expect("failed to execute process");
assert!(status.success());
let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR))
.args(&args)
.status()
.expect("failed to execute process");
assert!(status.success());
// read in the address
let addr =
std::fs::read_to_string(format!("{}/{}/addr.txt", test_dir, example_name))
.expect("failed to read address file");
// read in the address of the verifier manager
let addr = std::fs::read_to_string(format!("{}/{}/addr.txt", test_dir, example_name))
.expect("failed to read address file");
let deployed_addr_arg = format!("--addr-verifier={}", addr);
// set the reusable verifier address
*reusable_verifier_address = Some(addr);
deployed_addr_arg
}
verifier_manager_arg = format!("--addr-verifier-manager={}", addr);
// if the reusable verifier address is not set, deploy the verifier manager and then create the verifier
let rv_addr = {
// addr path for rv contract
let addr_path_arg = format!("--addr-path={}/{}/addr_rv.txt", test_dir, example_name);
// deploy the reusable verifier via the verifier router.
let args = vec![
"deploy-evm",
rpc_arg.as_str(),
addr_path_arg.as_str(),
sol_arg.as_str(),
verifier_manager_arg.as_str(),
"-C=verifier/reusable",
];
let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR))
.args(&args)
.status()
.expect("failed to execute process");
assert!(status.success());
// read in the address of the verifier manager
let addr =
std::fs::read_to_string(format!("{}/{}/addr_rv.txt", test_dir, example_name))
.expect("failed to read address file");
addr
};
let addr_path_arg_vk = format!("--addr-path={}/{}/addr_vk.txt", test_dir, example_name);
let sol_arg_vk: String = format!("--sol-code-path={}/{}/vk.sol", test_dir, example_name);
// create the verifier
let addr_path_arg_vk = format!("--addr-path={}/{}/addr_vk.txt", test_dir, example_name);
let sol_arg_vk: String = format!("--sol-code-path={}/{}/vk.sol", test_dir, example_name);
// create the verifier
@@ -2322,11 +2333,15 @@ mod native_tests {
.expect("failed to execute process");
assert!(status.success());
// deploy the vka
let rv_addr_arg = format!("--addr-reusable-verifier={}", rv_addr);
// deploy the vka via the "DeployVKA" command on the reusable verifier
let args = vec![
"deploy-evm",
rpc_arg.as_str(),
addr_path_arg_vk.as_str(),
verifier_manager_arg.as_str(),
rv_addr_arg.as_str(),
sol_arg_vk.as_str(),
"-C=vka",
];
@@ -2356,6 +2371,8 @@ mod native_tests {
assert!(status.success());
let deployed_addr_arg = format!("--addr-verifier={}", rv_addr);
// now verify the proof
let pf_arg = format!("{}/{}/proof.pf", test_dir, example_name);
let args = vec![
@@ -2415,9 +2432,6 @@ mod native_tests {
i
);
}
// Returned deploy_addr_arg for reusable verifier
deployed_addr_arg
}
// run js browser evm verify tests for a given example

View File

@@ -124,40 +124,41 @@ mod py_tests {
}
const TESTS: [&str; 34] = [
"ezkl_demo_batch.ipynb", // 0
"proof_splitting.ipynb", // 1
"variance.ipynb", // 2
"mnist_gan.ipynb", // 3
"keras_simple_demo.ipynb", // 4
"mnist_gan_proof_splitting.ipynb", // 5
"hashed_vis.ipynb", // 6
"simple_demo_all_public.ipynb", // 7
"data_attest.ipynb", // 8
"little_transformer.ipynb", // 9
"simple_demo_aggregated_proofs.ipynb", // 10
"ezkl_demo.ipynb", // 11
"lstm.ipynb", // 12
"set_membership.ipynb", // 13
"decision_tree.ipynb", // 14
"random_forest.ipynb", // 15
"gradient_boosted_trees.ipynb", // 16
"xgboost.ipynb", // 17
"lightgbm.ipynb", // 18
"svm.ipynb", // 19
"simple_demo_public_input_output.ipynb", // 20
"simple_demo_public_network_output.ipynb", // 21
"gcn.ipynb", // 22
"linear_regression.ipynb", // 23
"stacked_regression.ipynb", // 24
"data_attest_hashed.ipynb", // 25
"kzg_vis.ipynb", // 26
"kmeans.ipynb", // 27
"solvency.ipynb", // 28
"sklearn_mlp.ipynb", // 29
"generalized_inverse.ipynb", // 30
"mnist_classifier.ipynb", // 31
"world_rotation.ipynb", // 32
"logistic_regression.ipynb", // 33
"ezkl_demo_batch.ipynb",
"proof_splitting.ipynb", // 0
"variance.ipynb",
"mnist_gan.ipynb",
// "mnist_vae.ipynb",
"keras_simple_demo.ipynb",
"mnist_gan_proof_splitting.ipynb", // 4
"hashed_vis.ipynb", // 5
"simple_demo_all_public.ipynb",
"data_attest.ipynb",
"little_transformer.ipynb",
"simple_demo_aggregated_proofs.ipynb",
"ezkl_demo.ipynb", // 10
"lstm.ipynb",
"set_membership.ipynb", // 12
"decision_tree.ipynb",
"random_forest.ipynb",
"gradient_boosted_trees.ipynb", // 15
"xgboost.ipynb",
"lightgbm.ipynb",
"svm.ipynb",
"simple_demo_public_input_output.ipynb",
"simple_demo_public_network_output.ipynb", // 20
"gcn.ipynb",
"linear_regression.ipynb",
"stacked_regression.ipynb",
"data_attest_hashed.ipynb",
"kzg_vis.ipynb", // 25
"kmeans.ipynb",
"solvency.ipynb",
"sklearn_mlp.ipynb",
"generalized_inverse.ipynb",
"mnist_classifier.ipynb", // 30
"world_rotation.ipynb",
"logistic_regression.ipynb",
];
macro_rules! test_func {

View File

@@ -1 +1 @@
[{"type":"function","name":"verifyProof","inputs":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"uint256[]","name":"instances","type":"uint256[]"}],"outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable"}]
[{"type":"function","name":"deployVKA","inputs":[{"name":"bytecode","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"addr","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"precomputeAddress","inputs":[{"name":"bytecode","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"verifyProof","inputs":[{"name":"vk","type":"address","internalType":"address"},{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"instances","type":"uint256[]","internalType":"uint256[]"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"vkaLog","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"event","name":"DeployedVKArtifact","inputs":[{"name":"vka","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"error","name":"UnloggedVka","inputs":[{"name":"vka","type":"address","internalType":"address"}]}]

2
vk.abi
View File

@@ -1 +1 @@
[{"type":"constructor","inputs":[]}]
[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"}]