Skip to content

Commit

Permalink
Merge pull request #138 from clearmatics/contract-performance
Browse files Browse the repository at this point in the history
Some contract optimizations
  • Loading branch information
AntoineRondelet committed Feb 5, 2020
2 parents 49f563f + 4be1c1b commit 7d09690
Show file tree
Hide file tree
Showing 23 changed files with 1,230 additions and 983 deletions.
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'],
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 comments on commit 7d09690

Please sign in to comment.