8000 Some contract optimizations by dtebbs · Pull Request #138 · clearmatics/zeth · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Some contract optimizations #138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Feb 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c25191d
pyClient: print gas costs of mix transactions
dtebbs Dec 9, 2019
1d1c7ac
contracts: use istanbul version of ganache-cli
dtebbs Dec 10, 2019
7b15419
pyclient: compile contracts with optimizations
dtebbs Dec 9, 2019
71d1188
contracts: remove unnecessary data passed to/from precompiled contracts
dtebbs Dec 9, 2019
30d03b3
contracts: negate Proof.A instead of 3 RHS elements
dtebbs Dec 9, 2019
9e3b0e5
contracts: simplify some structs, remove pointers and save some gas
dtebbs Dec 9, 2019
e12c694
contracts: avoid reading whole verification key into memory. optimize…
dtebbs Dec 9, 2019
a4d5223
contracts: assembly version of pairing check
dtebbs Dec 9, 2019
a2a93ae
contracts: small style fixes
dtebbs Dec 10, 2019
ec9b8e5
contracts: more efficient computation of merkle root without keeping …
dtebbs Dec 10, 2019
437d7ff
contracts: better mem usage in mimc7 hash and cache seed
dtebbs Dec 10, 2019
7583a94
contracts: MiMC7 as a library rather than separate contract
dtebbs Dec 10, 2019
6fcb864
contracts: statically sized merkle tree data structures
dtebbs Dec 10, 2019
fb567a5
contracts: some indentation consistency
dtebbs Dec 10, 2019
7092c99
contracts: consistent formatting
dtebbs Dec 16, 2019
676247c
contracts: solium and check command
dtebbs Dec 17, 2019
b0c8c19
travis: run solium check
dtebbs Dec 17, 2019
c89f216
contracts: optimize vk parameter and signature verification
dtebbs Dec 17, 2019
5f94de7
contracts: improve memory use in diges 8000 t unpacking
dtebbs Dec 18, 2019
92c5b5f
pyclient: flatten G2 element in groth16 proofs to save a bit of gas
dtebbs Dec 19, 2019
d269ec0
contracts: merge verifier and mixer, and use const-sized array for in…
dtebbs Dec 20, 2019
206e706
contracts: remove some constants which are subject to change from com…
dtebbs Jan 8, 2020
b5b22ff
pyClient: use common code for deployment in mixer test
dtebbs Jan 30, 2020
3b88ed7
pyClient: abstraction of solcx compile method
dtebbs Jan 30, 2020
a0d7ec2
contracts: fixes based on PR comments
dtebbs Feb 4, 2020
bb90e5a
contracts: consistent method declaration everywhere
dtebbs Feb 4, 2020
b267c1c
contracts: consistent declaration of foor loop variables
dtebbs Feb 4, 2020
4be1c1b
contracts: use uint256 instead of uint
dtebbs Feb 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ addons:

matrix:
include:
- env: CI_TASK=contracts_check
os: linux
dist: bionic
language: node_js
node_js:
- "10.16.3"
- env: CI_TASK=pyclient_check CI_USE_DOCKER=1
os: linux
language: minimal
Expand Down
6 changes: 5 additions & 1 deletion pyClient/commands/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ def _do_sync() -> int:

if wait_tx:
_do_sync()
web3.eth.waitForTransactionReceipt(wait_tx, 10000)
tx_receipt = web3.eth.waitForTransactionReceipt(wait_tx, 10000)
gas_used = tx_receipt.gasUsed
status = tx_receipt.status
print(f"{wait_tx[0:8]}: gasUsed={gas_used}, status={status}")

return _do_sync()


Expand Down
48 changes: 14 additions & 34 deletions pyClient/test/test_contract_base_mixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
#
# SPDX-License-Identifier: LGPL-3.0+

import os
from typing import Any
from solcx import compile_files # type: ignore
import test_commands.mock as mock

from zeth.constants import JS_INPUTS, JS_OUTPUTS, ZETH_MERKLE_TREE_DEPTH,\
PUBLIC_VALUE_LENGTH
import zeth.contracts as contracts
from zeth.joinsplit import ZethClient
from zeth.zksnark import get_zksnark_provider
from typing import Any
import test_commands.mock as mock


# The UNPACKED_PRIMARY_INPUTS variable represents a dummy primary input,
Expand Down Expand Up @@ -149,34 +147,16 @@ def main() -> None:
# Ethereum addresses
deployer_eth_address = eth.accounts[0]

contracts_dir = os.environ['ZETH_CONTRACTS_DIR']
path_to_mixer = os.path.join(contracts_dir, "BaseMixer.sol")
compiled_sol = compile_files([path_to_mixer])
mixer_interface = compiled_sol[path_to_mixer + ':' + "BaseMixer"]

hasher_interface, _ = contracts.compile_util_contracts()
# Deploy MiMC contract
_, hasher_address = contracts.deploy_mimc_contract(
web3, hasher_interface, deployer_eth_address)

token_address = "0x0000000000000000000000000000000000000000"

mixer = web3.eth.contract(
abi=mixer_interface['abi'], bytecode=mixer_interface['bin'])
tx_hash = mixer.constructor(
depth=ZETH_MERKLE_TREE_DEPTH,
token_address=token_address,
hasher_address=hasher_address
).transact({'from': deployer_eth_address})

# Get tx receipt to get Mixer contract address
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
mixer_address = tx_receipt['contractAddress']
# Get the mixer contract instance
mixer_instance = web3.eth.contract(
address=mixer_address,
abi=mixer_interface['abi']
)
zksnark = get_zksnark_provider("GROTH16")
prover_client = mock.open_test_prover_client()
zeth_client = ZethClient.deploy(
web3,
prover_client,
ZETH_MERKLE_TREE_DEPTH,
deployer_eth_address,
zksnark)

mixer_instance = zeth_client.mixer_instance

# We can now call the instance and test its functions.
print("[INFO] 4. Running tests")
Expand Down
5 changes: 0 additions & 5 deletions pyClient/zeth/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,15 @@

# GROTH16 constants
GROTH16_ZKSNARK: str = "GROTH16"
GROTH16_VERIFIER_CONTRACT: str = "Groth16Verifier"
GROTH16_MIXER_CONTRACT: str = "Groth16Mixer"

# PGHR13 constants
PGHR13_ZKSNARK: str = "PGHR13"
PGHR13_VERIFIER_CONTRACT: str = "Pghr13Verifier"
PGHR13_MIXER_CONTRACT: str = "Pghr13Mixer"

# Set of valid snarks
VALID_ZKSNARKS: List[str] = [GROTH16_ZKSNARK, PGHR13_ZKSNARK]

# OTSCHNORR constants
SCHNORR_VERIFIER_CONTRACT: str = "OTSchnorrVerifier"

# Merkle tree depth
ZETH_MERKLE_TREE_DEPTH: int = 4

Expand Down
166 changes: 23 additions & 143 deletions pyClient/zeth/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# SPDX-License-Identifier: LGPL-3.0+

from __future__ import annotations
import zeth.constants as constants
from zeth.encryption import EncryptionPublicKey, encode_encryption_public_key
from zeth.signing import SigningVerificationKey
from zeth.zksnark import IZKSnarkProvider, GenericProof, GenericVerificationKey
Expand All @@ -14,7 +13,7 @@

import os
from web3 import Web3 # type: ignore
from solcx import compile_files, set_solc_version, install_solc
import solcx
from typing import Tuple, Dict, List, Iterator, Optional, Any

# Avoid trying to read too much data into memory
Expand Down Expand Up @@ -77,71 +76,48 @@ def get_block_number(web3: Any) -> int:


def install_sol() -> None:
install_solc(SOL_COMPILER_VERSION)
solcx.install_solc(SOL_COMPILER_VERSION)


def compile_contracts(
zksnark: IZKSnarkProvider) -> Tuple[Interface, Interface, Interface]:
contracts_dir = get_contracts_dir()
(proof_verifier_name, mixer_name) = zksnark.get_contract_names()
otsig_verifier_name = constants.SCHNORR_VERIFIER_CONTRACT

path_to_proof_verifier = os.path.join(
contracts_dir, proof_verifier_name + ".sol")
path_to_otsig_verifier = os.path.join(
contracts_dir, otsig_verifier_name + ".sol")
path_to_mixer = os.path.join(contracts_dir, mixer_name + ".sol")

set_solc_version(SOL_COMPILER_VERSION)
compiled_sol = compile_files(
[path_to_proof_verifier, path_to_otsig_verifier, path_to_mixer])

proof_verifier_interface = \
compiled_sol[path_to_proof_verifier + ':' + proof_verifier_name]
otsig_verifier_interface = \
compiled_sol[path_to_otsig_verifier + ':' + otsig_verifier_name]
mixer_interface = compiled_sol[path_to_mixer + ':' + mixer_name]

return (proof_verifier_interface, otsig_verifier_interface, mixer_interface)
def compile_files(files: List[str]) -> Any:
"""
Wrapper around solcx which ensures the required version of the compiler is
used.
"""
solcx.set_solc_version(SOL_COMPILER_VERSION)
return solcx.compile_files(files, optimize=True)


def compile_util_contracts() -> Tuple[Interface, Interface]:
def compile_mixer(zksnark: IZKSnarkProvider) -> Interface:
contracts_dir = get_contracts_dir()
path_to_pairing = os.path.join(contracts_dir, "Pairing.sol")
path_to_mimc7 = os.path.join(contracts_dir, "MiMC7.sol")
path_to_tree = os.path.join(contracts_dir, "MerkleTreeMiMC7.sol")
set_solc_version(SOL_COMPILER_VERSION)
compiled_sol = compile_files(
[path_to_pairing, path_to_mimc7, path_to_tree])
mimc_interface = compiled_sol[path_to_mimc7 + ':' + "MiMC7"]
tree_interface = compiled_sol[path_to_tree + ':' + "MerkleTreeMiMC7"]
return mimc_interface, tree_interface
mixer_name = zksnark.get_contract_name()
path_to_mixer = os.path.join(contracts_dir, mixer_name + ".sol")
compiled_sol = compile_files([path_to_mixer])
return compiled_sol[path_to_mixer + ':' + mixer_name]


def deploy_mixer(
web3: Any,
proof_verifier_address: str,
otsig_verifier_address: str,
mixer_interface: Interface,
mk_tree_depth: int,
mixer_interface: Interface,
vk: GenericVerificationKey,
deployer_address: str,
deployment_gas: int,
token_address: str,
hasher_address: str) -> Tuple[Any, str]:
zksnark: IZKSnarkProvider) -> Tuple[Any, str]:
"""
Common function to deploy a mixer contract. Returns the mixer and the
initial merkle root of the commitment tree
"""
# Deploy the Mixer contract once the Verifier is successfully deployed
# Deploy the Mixer
mixer = web3.eth.contract(
abi=mixer_interface['abi'], bytecode=mixer_interface['bin'])

verification_key_params = zksnark.verification_key_parameters(vk)
tx_hash = mixer.constructor(
snark_ver=proof_verifier_address,
sig_ver=otsig_verifier_address,
mk_depth=mk_tree_depth,
token=token_address,
hasher=hasher_address
**verification_key_params
).transact({'from': deployer_address, 'gas': deployment_gas})
# Get tx receipt to get Mixer contract address
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
Expand All @@ -159,96 +135,6 @@ def deploy_mixer(
return(mixer, initial_root)


def deploy_otschnorr_contracts(
web3: Any,
verifier: Any,
deployer_address: str,
deployment_gas: int) -> str:
"""
Deploy the verifier used with OTSCHNORR
"""
# Deploy the verifier contract with the good verification key
tx_hash = verifier.constructor().transact(
{'from': deployer_address, 'gas': deployment_gas})

# Get tx receipt to get Verifier contract address
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
verifier_address = tx_receipt['contractAddress']
return verifier_address


def deploy_contracts(
web3: Any,
mk_tree_depth: int,
proof_verifier_interface: Interface,
otsig_verifier_interface: Interface,
mixer_interface: Interface,
hasher_interface: Interface,
vk: GenericVerificationKey,
deployer_address: str,
deployment_gas: int,
token_address: str,
zksnark: IZKSnarkProvider) -> Tuple[Any, str]:
"""
Deploy the mixer contract with the given merkle tree depth and returns an
instance of the mixer along with the initial merkle tree root to use for
the first zero knowledge payments
"""
# Deploy the proof verifier contract with the good verification key
proof_verifier = web3.eth.contract(
abi=proof_verifier_interface['abi'],
< 10000 span class='blob-code-inner blob-code-marker js-skip-tagsearch' data-code-marker="-"> bytecode=proof_verifier_interface['bin']
)

verifier_constr_params = zksnark.verifier_constructor_parameters(vk)
tx_hash = proof_verifier.constructor(**verifier_constr_params) \
.transact({'from': deployer_address, 'gas': deployment_gas})
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
proof_verifier_address = tx_receipt['contractAddress']

# Deploy MiMC contract
_, hasher_address = deploy_mimc_contract(
web3, hasher_interface, deployer_address)

# Deploy the one-time signature verifier contract
otsig_verifier = web3.eth.contract(
abi=otsig_verifier_interface['abi'],
bytecode=otsig_verifier_interface['bin'])
otsig_verifier_address = deploy_otschnorr_contracts(
web3, otsig_verifier, deployer_address, deployment_gas)

return deploy_mixer(
web3,
proof_verifier_address,
otsig_verifier_address,
mixer_interface,
mk_tree_depth,
deployer_address,
deployment_gas,
token_address,
hasher_address)


def deploy_mimc_contract(
web3: Any,
interface: Interface,
account: str) -> Tuple[Any, str]:
"""
Deploy mimc contract
"""
contract = web3.eth.contract(abi=interface['abi'], bytecode=interface['bin'])
tx_hash = contract.constructor().transact({'from': account})
# Get tx receipt to get Mixer contract address
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
address = tx_receipt['contractAddress']
# Get the mixer contract instance
instance = web3.eth.contract(
address=address,
abi=interface['abi']
)
return instance, address


def deploy_tree_contract(
web3: Any,
interface: Interface,
Expand Down Expand Up @@ -290,11 +176,12 @@ def mix(
"""
pk_sender_encoded = encode_encryption_public_key(pk_sender)
proof_params = zksnark.mixer_proof_parameters(parsed_proof)
inputs = hex_to_int(parsed_proof["inputs"])
tx_hash = mixer_instance.functions.mix(
*proof_params,
[[int(vk.ppk[0]), int(vk.ppk[1])], [int(vk.spk[0]), int(vk.spk[1])]],
[int(vk.ppk[0]), int(vk.ppk[1]), int(vk.spk[0]), int(vk.spk[1])],
sigma,
hex_to_int(parsed_proof["inputs"]),
inputs,
pk_sender_encoded,
ciphertext1,
ciphertext2,
Expand Down Expand Up @@ -447,13 +334,6 @@ def _extract_note(log_commit: Any, log_ciph: Any) -> Tuple[int, bytes, bytes]:
log_commit, log_ciph in zip(log_commitments, log_ciphertexts)]


def mimc_hash(instance: Any, m: bytes, k: bytes, seed: bytes) -> bytes:
"""
Call the hash method of MiMC contract
"""
return instance.functions.hash(m, k, seed).call()


def get_commitments(mixer_instance: Any) -> List[bytes]:
"""
Query the Zeth contact to get the list of commitments
Expand Down
9 changes: 2 additions & 7 deletions pyClient/zeth/joinsplit.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,11 @@ def deploy(
write_verification_key(vk_json)

print("[INFO] 3. VK written, deploying smart contracts...")
(proof_verifier_interface, otsig_verifier_interface, mixer_interface) = \
contracts.compile_contracts(zksnark)
hasher_interface, _ = contracts.compile_util_contracts()
(mixer_instance, initial_merkle_root) = contracts.deploy_contracts(
mixer_interface = contracts.compile_mixer(zksnark)
(mixer_instance, initial_merkle_root) = contracts.deploy_mixer(
web3,
mk_tree_depth,
proof_verifier_interface,
otsig_verifier_interface,
mixer_interface,
hasher_interface,
vk_json,
deployer_eth_address,
deploy_gas.wei,
Expand Down
1 change: 0 additions & 1 deletion pyClient/zeth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ def compute_merkle_path(
# index of the root node)
address = int(address/2) - 1
else:
print("append note at address: " + str(address + 1))
merkle_path.append(Web3.toHex(byte_tree[address + 1])[2:])
address = int(address/2)
return merkle_path
Expand Down
Loading
0