From 880137b6a9f1d846e19deee78cc16a22e822fd06 Mon Sep 17 00:00:00 2001 From: Duncan Tebbs Date: Tue, 22 Dec 2020 10:17:36 +0000 Subject: [PATCH 1/3] ci: enable contract tests --- client/Makefile | 8 ++++-- scripts/ci | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/client/Makefile b/client/Makefile index 8be9862d9..569ba9a62 100644 --- a/client/Makefile +++ b/client/Makefile @@ -39,5 +39,9 @@ test: ${PROTOBUF_OUTPUT} python -m unittest test_contracts: ${PROTOBUF_OUTPUT} - python test_commands/test_contract_base_mixer.py - python test_commands/test_merkle_tree_contract.py + python -m test_commands.test_altbn128_mixer_base + python -m test_commands.test_bls12_377_contract + python -m test_commands.test_bw6_761_contract + python -m test_commands.test_groth16_bls12_377_contract + python -m test_commands.test_merkle_tree_contract + python -m test_commands.test_mimc_contract diff --git a/scripts/ci b/scripts/ci index a7419a899..e64d0bacd 100755 --- a/scripts/ci +++ b/scripts/ci @@ -7,6 +7,59 @@ echo "running against commit: "`git log --oneline --no-decorate -n 1` set -x set -e +function _setup_client() { + pushd client + python3 -m venv env + . env/bin/activate + pip install --upgrade pip --progress-bar off + pip install --upgrade setuptools wheel + make setup + + deactivate + popd +} + +function _ganache_is_active() { + curl -sf \ + -H "Content-Type: application/json" \ + -X POST \ + --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[]}' \ + http://localhost:8545 +} + +function _start_ganache() { + pushd zeth_contracts + + npm run testrpc > ganache.stdout & + echo $! > ganache.pid + + # Wait for ganache to be active + while ! _ganache_is_active ; do + echo "_start_ganache: waiting for ganache ..." + sleep 1 + done + echo "_start_ganache: ganache is active" + + popd +} + +function _stop_ganache() { + pushd zeth_contracts + if ! [ -e ganache.pid ] ; then + echo "_stop_ganache: no PID file" + return 1 + fi + + pid=`cat ganache.pid` + while (kill "${pid}") ; do + sleep 0.5 + done + rm ganache.pid + echo "_stop_ganache: ganache stopped" + + popd +} + function check_format() { scripts/format git diff --no-ext-diff | head -n 20 > format_errors @@ -27,15 +80,28 @@ function check_contracts() { npm install --unsafe-perm npm run check popd + + # Run contract tests (in python) + _setup_client + + _start_ganache + + pushd client + . env/bin/activate + make test_contracts + deactivate + popd + + _stop_ganache + } function check_client() { + _setup_client + pushd client - python3 -m venv env . env/bin/activate - pip install --upgrade pip --progress-bar off - pip install --upgrade setuptools wheel - make setup + make check deactivate From 93f0bed209345e4784ca535c283e1fb60c81054d Mon Sep 17 00:00:00 2001 From: Duncan Tebbs Date: Tue, 22 Dec 2020 11:47:44 +0000 Subject: [PATCH 2/3] client: separate contract tests and use unittest framework --- client/Makefile | 10 +- client/test_commands/test_mimc_contract.py | 60 ----------- client/test_contracts/__init__.py | 5 + .../test_altbn128_mixer_base.py | 99 +++++++++---------- .../test_bls12_377_contract.py | 95 +++++++++--------- .../test_bw6_761_contract.py | 96 +++++++++--------- .../test_groth16_bls12_377_contract.py | 89 ++++++++--------- .../test_merkle_tree_contract.py | 81 +++++++-------- client/test_contracts/test_mimc_contract.py | 51 ++++++++++ 9 files changed, 269 insertions(+), 317 deletions(-) delete mode 100644 client/test_commands/test_mimc_contract.py create mode 100644 client/test_contracts/__init__.py rename client/{test_commands => test_contracts}/test_altbn128_mixer_base.py (65%) rename client/{test_commands => test_contracts}/test_bls12_377_contract.py (68%) rename client/{test_commands => test_contracts}/test_bw6_761_contract.py (72%) rename client/{test_commands => test_contracts}/test_groth16_bls12_377_contract.py (67%) rename client/{test_commands => test_contracts}/test_merkle_tree_contract.py (53%) create mode 100644 client/test_contracts/test_mimc_contract.py diff --git a/client/Makefile b/client/Makefile index 569ba9a62..9cc596444 100644 --- a/client/Makefile +++ b/client/Makefile @@ -33,15 +33,11 @@ syntax: ${PROTOBUF_OUTPUT} mypy zeth/cli/zeth zeth/helper/zeth_helper mypy -p tests mypy -p test_commands + mypy -p test_contracts pylint zeth.core zeth.cli zeth.helper tests test_commands test: ${PROTOBUF_OUTPUT} - python -m unittest + python -m unittest discover tests test_contracts: ${PROTOBUF_OUTPUT} - python -m test_commands.test_altbn128_mixer_base - python -m test_commands.test_bls12_377_contract - python -m test_commands.test_bw6_761_contract - python -m test_commands.test_groth16_bls12_377_contract - python -m test_commands.test_merkle_tree_contract - python -m test_commands.test_mimc_contract + python -m unittest discover test_contracts diff --git a/client/test_commands/test_mimc_contract.py b/client/test_commands/test_mimc_contract.py deleted file mode 100644 index 1c986c14d..000000000 --- a/client/test_commands/test_mimc_contract.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2015-2021 Clearmatics Technologies Ltd -# -# SPDX-License-Identifier: LGPL-3.0+ - -from zeth.core.utils import get_contracts_dir -from zeth.core.contracts import InstanceDescription -from zeth.core.mimc import MiMC7, MiMC31 -from zeth.cli.utils import get_eth_network, open_web3_from_network -from os.path import join -import sys -from typing import Any - - -def test_mimc7(instance: Any) -> None: - # pylint: disable=line-too-long - x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa - y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa - # pylint: enable=line-too-long - h = MiMC7().hash(x, y) - - result = instance.functions.test_mimc7(x, y).call() - assert result == h - - -def test_mimc31(instance: Any) -> None: - # pylint: disable=line-too-long - x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa - y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa - # pylint: enable=line-too-long - h = MiMC31().hash(x, y) - - result = instance.functions.test_mimc31(x, y).call() - - assert result == h - - -def main() -> int: - web3: Any = open_web3_from_network(get_eth_network(None)) - contracts_dir = get_contracts_dir() - contract_instance_desc = InstanceDescription.deploy( - web3, - join(contracts_dir, "MiMC_test.sol"), - "MiMC_test", - web3.eth.accounts[0], # pylint: disable=no-member - None, - 500000, - {"allow_paths": contracts_dir}) - contract_instance = contract_instance_desc.instantiate(web3) - - test_mimc7(contract_instance) - test_mimc31(contract_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/client/test_contracts/__init__.py b/client/test_contracts/__init__.py new file mode 100644 index 000000000..f50145434 --- /dev/null +++ b/client/test_contracts/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/test_altbn128_mixer_base.py b/client/test_contracts/test_altbn128_mixer_base.py similarity index 65% rename from client/test_commands/test_altbn128_mixer_base.py rename to client/test_contracts/test_altbn128_mixer_base.py index 3cb4d0cbe..267d3175c 100644 --- a/client/test_commands/test_altbn128_mixer_base.py +++ b/client/test_contracts/test_altbn128_mixer_base.py @@ -7,6 +7,7 @@ from zeth.core.constants import \ JS_INPUTS, ZETH_PUBLIC_UNIT_VALUE, ZETH_MERKLE_TREE_DEPTH import test_commands.mock as mock +from unittest import TestCase from typing import Any # pylint: disable=line-too-long @@ -91,58 +92,46 @@ PACKED_PRIMARY_INPUTS = \ [ROOT] + COMMITMENTS + NULLIFIERS + [HSIG] + HTAGS + [RESIDUAL_BITS] - -def test_assemble_nullifiers(mixer_instance: Any) -> None: - # Test retrieving nullifiers - print("--- test_assemble_nullifiers") - for i in range(JS_INPUTS): - res = mixer_instance.functions.\ - assemble_nullifier_test(i, PACKED_PRIMARY_INPUTS).call() - val = int.from_bytes(res, byteorder="big") - assert val == NULLIFIERS[i], f"expected: {NULLIFIERS[i]}, got: {val}" - - -def test_assemble_hsig(mixer_instance: Any) -> None: - # Test retrieving hsig - print("--- test_assemble_hsig") - res = mixer_instance.functions.\ - assemble_hsig_test(PACKED_PRIMARY_INPUTS).call() - hsig = int.from_bytes(res, byteorder="big") - assert hsig == HSIG, f"expected: {HSIG}, got {hsig}" - - -def test_assemble_vpub(mixer_instance: Any) -> None: - # Test retrieving public values - print("--- test_assemble_vpub") - v_in, v_out = mixer_instance.functions.assemble_public_values_test( - PACKED_PRIMARY_INPUTS[-1]).call() - v_in_expect = VPUB[0] * ZETH_PUBLIC_UNIT_VALUE - v_out_expect = VPUB[1] * ZETH_PUBLIC_UNIT_VALUE - assert v_in == v_in_expect, f"expected: {v_in_expect}, got: {v_in}" - assert v_out == v_out_expect, f"expected: {v_out_expect}, got: {v_out}" - - -def main() -> None: - print("Deploying AltBN128MixerBase_test.sol") - _web3, eth = mock.open_test_web3() - deployer_eth_address = eth.accounts[0] - _mixer_interface, mixer_instance = mock.deploy_contract( - eth, - deployer_eth_address, - "AltBN128MixerBase_test", - { - 'mk_depth': ZETH_MERKLE_TREE_DEPTH, - }) - - print("Testing ...") - test_assemble_nullifiers(mixer_instance) - test_assemble_vpub(mixer_instance) - test_assemble_hsig(mixer_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == '__main__': - main() +MIXER_INSTANCE: Any = None + + +class TestAltBN128MixerBaseContract(TestCase): + + @staticmethod + def setUpClass() -> None: + print("Deploying AltBN128MixerBase_test.sol") + _web3, eth = mock.open_test_web3() + deployer_eth_address = eth.accounts[0] + _mixer_interface, mixer_instance = mock.deploy_contract( + eth, + deployer_eth_address, + "AltBN128MixerBase_test", + { + 'mk_depth': ZETH_MERKLE_TREE_DEPTH, + }) + global MIXER_INSTANCE # pylint: disable=global-statement + MIXER_INSTANCE = mixer_instance + + def test_assemble_nullifiers(self) -> None: + # Test retrieving nullifiers + for i in range(JS_INPUTS): + res = MIXER_INSTANCE.functions.\ + assemble_nullifier_test(i, PACKED_PRIMARY_INPUTS).call() + val = int.from_bytes(res, byteorder="big") + self.assertEqual(NULLIFIERS[i], val) + + def test_assemble_hsig(self) -> None: + # Test retrieving hsig + res = MIXER_INSTANCE.functions.\ + assemble_hsig_test(PACKED_PRIMARY_INPUTS).call() + hsig = int.from_bytes(res, byteorder="big") + self.assertEqual(HSIG, hsig) + + def test_assemble_vpub(self) -> None: + # Test retrieving public values + v_in, v_out = MIXER_INSTANCE.functions.assemble_public_values_test( + PACKED_PRIMARY_INPUTS[-1]).call() + v_in_expect = VPUB[0] * ZETH_PUBLIC_UNIT_VALUE + v_out_expect = VPUB[1] * ZETH_PUBLIC_UNIT_VALUE + self.assertEqual(v_in_expect, v_in) + self.assertEqual(v_out_expect, v_out) diff --git a/client/test_commands/test_bls12_377_contract.py b/client/test_contracts/test_bls12_377_contract.py similarity index 68% rename from client/test_commands/test_bls12_377_contract.py rename to client/test_contracts/test_bls12_377_contract.py index c75bd7703..77e333c29 100644 --- a/client/test_commands/test_bls12_377_contract.py +++ b/client/test_contracts/test_bls12_377_contract.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: LGPL-3.0+ from test_commands import mock +from unittest import TestCase from typing import Any # Data generated by libzeth/tests/core/ec_operation_data_test. Statements to be @@ -74,53 +75,47 @@ def _b32(hex_value: str) -> bytes: ] -def test_bls12_ecadd(bls12_instance: Any) -> None: - """ - Check that [6] == [2] + [4] - """ - result = bls12_instance.functions.testECAdd(G1_2 + G1_4).call() - assert result == G1_6 - - -def test_bls12_ecmul(bls12_instance: Any) -> None: - """ - Check that [-8] == -2 * [4] - """ - result = bls12_instance.functions.testECMul(G1_4 + FR_MINUS_2).call() - assert result == G1_MINUS_8 - - -def test_bls12_ecpairing(bls12_instance: Any) -> None: - """ - Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 - """ - # Note, return result here is uint256(1) or uint256(0) depending on the - # pairing check result. - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 - result = bls12_instance.functions.testECPairing(points).call() - assert result == 1 - - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 - result = bls12_instance.functions.testECPairing(points).call() - assert result == 0 - - -def main() -> None: - _web3, eth = mock.open_test_web3() - _bls12_interface, bls12_instance = mock.deploy_contract( - eth, - eth.accounts[0], - "BLS12_377_test", - {}) - - test_bls12_ecadd(bls12_instance) - test_bls12_ecmul(bls12_instance) - test_bls12_ecpairing(bls12_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == "__main__": - main() +BLS12_INSTANCE: Any = None + + +class TestBLS12_377Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + print("Deploying BLS12_377_test.sol") + _web3, eth = mock.open_test_web3() + _bls12_interface, bls12_instance = mock.deploy_contract( + eth, + eth.accounts[0], + "BLS12_377_test", + {}) + global BLS12_INSTANCE # pylint: disable=global-statement + BLS12_INSTANCE = bls12_instance + + def test_bls12_ecadd(self) -> None: + """ + Check that [6] == [2] + [4] + """ + result = BLS12_INSTANCE.functions.testECAdd(G1_2 + G1_4).call() + self.assertEqual(G1_6, result) + + def test_bls12_ecmul(self) -> None: + """ + Check that [-8] == -2 * [4] + """ + result = BLS12_INSTANCE.functions.testECMul(G1_4 + FR_MINUS_2).call() + self.assertEqual(G1_MINUS_8, result) + + def test_bls12_ecpairing(self) -> None: + """ + Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 + """ + # Note, return result here is uint256(1) or uint256(0) depending on the + # pairing check result. + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 + result = BLS12_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(1, result) + + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 + result = BLS12_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(0, result) diff --git a/client/test_commands/test_bw6_761_contract.py b/client/test_contracts/test_bw6_761_contract.py similarity index 72% rename from client/test_commands/test_bw6_761_contract.py rename to client/test_contracts/test_bw6_761_contract.py index fcbf1045d..ba35a6d16 100644 --- a/client/test_commands/test_bw6_761_contract.py +++ b/client/test_contracts/test_bw6_761_contract.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: LGPL-3.0+ from test_commands import mock +from unittest import TestCase from typing import Any # Data generated by libzeth/tests/core/ec_operation_data_test. Statements to be @@ -80,54 +81,47 @@ def _b32(hex_value: str) -> bytes: _b32("ed5512a98239b73fdff458e7c0e09b9d246e3f5681429affc2b1f7e2fcbd1141"), ] - -def test_bw6_ecadd(bw6_instance: Any) -> None: - """ - Check that [6] == [2] + [4] - """ - result = bw6_instance.functions.testECAdd(G1_2 + G1_4).call() - assert result == G1_6 - - -def test_bw6_ecmul(bw6_instance: Any) -> None: - """ - Check that [-8] == -2 * [4] - """ - result = bw6_instance.functions.testECMul(G1_4 + FR_MINUS_2).call() - assert result == G1_MINUS_8 - - -def test_bw6_ecpairing(bw6_instance: Any) -> None: - """ - Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 - """ - # Note, return result here is uint256(1) or uint256(0) depending on the - # pairing check result. - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 - result = bw6_instance.functions.testECPairing(points).call() - assert result == 1 - - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 - result = bw6_instance.functions.testECPairing(points).call() - assert result == 0 - - -def main() -> None: - _web3, eth = mock.open_test_web3() - _bw6_interface, bw6_instance = mock.deploy_contract( - eth, - eth.accounts[0], - "BW6_761_test", - {}) - - test_bw6_ecadd(bw6_instance) - test_bw6_ecmul(bw6_instance) - test_bw6_ecpairing(bw6_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == "__main__": - main() +BW6_INSTANCE: Any = None + + +class TestBW6_761Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + print("Deploying BW6_761_test.sol") + _web3, eth = mock.open_test_web3() + _bw6_interface, bw6_instance = mock.deploy_contract( + eth, + eth.accounts[0], + "BW6_761_test", + {}) + global BW6_INSTANCE # pylint: disable=global-statement + BW6_INSTANCE = bw6_instance + + def test_bw6_ecadd(self) -> None: + """ + Check that [6] == [2] + [4] + """ + result = BW6_INSTANCE.functions.testECAdd(G1_2 + G1_4).call() + self.assertEqual(G1_6, result) + + def test_bw6_ecmul(self) -> None: + """ + Check that [-8] == -2 * [4] + """ + result = BW6_INSTANCE.functions.testECMul(G1_4 + FR_MINUS_2).call() + self.assertEqual(G1_MINUS_8, result) + + def test_bw6_ecpairing(self) -> None: + """ + Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 + """ + # Note, return result here is uint256(1) or uint256(0) depending on the + # pairing check result. + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 + result = BW6_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(1, result) + + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 + result = BW6_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(0, result) diff --git a/client/test_commands/test_groth16_bls12_377_contract.py b/client/test_contracts/test_groth16_bls12_377_contract.py similarity index 67% rename from client/test_commands/test_groth16_bls12_377_contract.py rename to client/test_contracts/test_groth16_bls12_377_contract.py index acb6caa02..6f0ab4d74 100644 --- a/client/test_commands/test_groth16_bls12_377_contract.py +++ b/client/test_contracts/test_groth16_bls12_377_contract.py @@ -8,7 +8,7 @@ from zeth.cli.utils import get_eth_network, open_web3_from_network from tests.test_pairing import BLS12_377_PAIRING from os.path import join -import sys +from unittest import TestCase from typing import List, Any # pylint: disable=line-too-long @@ -63,51 +63,42 @@ ] # pylint: enable=line-too-long - -def _invoke_groth16_bls12_377_verify( - contract_instance: Any, - vk: Groth16.VerificationKey, - proof: Groth16.Proof, - inputs: List[str]) -> bool: - vk_evm = Groth16.verification_key_to_contract_parameters( - vk, BLS12_377_PAIRING) - proof_evm = Groth16.proof_to_contract_parameters(proof, BLS12_377_PAIRING) - inputs_evm = hex_list_to_uint256_list(inputs) - return contract_instance.functions.test_verify( - vk_evm, proof_evm, inputs_evm).call() - - -def test_groth16_bls12_377_valid(contract_instance: Any) -> None: - assert _invoke_groth16_bls12_377_verify( - contract_instance, VERIFICATION_KEY, PROOF, INPUTS_VALID) - - -def test_groth16_bls12_377_invalid(contract_instance: Any) -> None: - assert not _invoke_groth16_bls12_377_verify( - contract_instance, VERIFICATION_KEY, PROOF, INPUTS_INVALID) - - -def main() -> int: - web3: Any = open_web3_from_network(get_eth_network(None)) - contracts_dir = get_contracts_dir() - contract_instance_desc = InstanceDescription.deploy( - web3, - join(contracts_dir, "Groth16BLS12_377_test.sol"), - "Groth16BLS12_377_test", - web3.eth.accounts[0], # pylint: disable=no-member - None, - 500000, - {"allow_paths": contracts_dir}) - contract_instance = contract_instance_desc.instantiate(web3) - - test_groth16_bls12_377_valid(contract_instance) - test_groth16_bls12_377_invalid(contract_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) +CONTRACT_INSTANCE: Any = None + + +class TestGroth16BLS12_377Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + web3: Any = open_web3_from_network(get_eth_network(None)) + contracts_dir = get_contracts_dir() + contract_instance_desc = InstanceDescription.deploy( + web3, + join(contracts_dir, "Groth16BLS12_377_test.sol"), + "Groth16BLS12_377_test", + web3.eth.accounts[0], # pylint: disable=no-member + None, + 500000, + {"allow_paths": contracts_dir}) + global CONTRACT_INSTANCE # pylint: disable=global-statement + CONTRACT_INSTANCE = contract_instance_desc.instantiate(web3) + + @staticmethod + def _invoke_groth16_bls12_377_verify( + vk: Groth16.VerificationKey, + proof: Groth16.Proof, + inputs: List[str]) -> bool: + vk_evm = Groth16.verification_key_to_contract_parameters( + vk, BLS12_377_PAIRING) + proof_evm = Groth16.proof_to_contract_parameters(proof, BLS12_377_PAIRING) + inputs_evm = hex_list_to_uint256_list(inputs) + return CONTRACT_INSTANCE.functions.test_verify( + vk_evm, proof_evm, inputs_evm).call() + + def test_groth16_bls12_377_valid(self) -> None: + self.assertTrue(self._invoke_groth16_bls12_377_verify( + VERIFICATION_KEY, PROOF, INPUTS_VALID)) + + def test_groth16_bls12_377_invalid(self) -> None: + self.assertFalse(self._invoke_groth16_bls12_377_verify( + VERIFICATION_KEY, PROOF, INPUTS_INVALID)) diff --git a/client/test_commands/test_merkle_tree_contract.py b/client/test_contracts/test_merkle_tree_contract.py similarity index 53% rename from client/test_commands/test_merkle_tree_contract.py rename to client/test_contracts/test_merkle_tree_contract.py index 7e6523aa6..98592ff87 100644 --- a/client/test_commands/test_merkle_tree_contract.py +++ b/client/test_contracts/test_merkle_tree_contract.py @@ -9,8 +9,10 @@ from zeth.core.utils import extend_32bytes from zeth.core.mimc import MiMC7 from typing import Any +from unittest import TestCase import test_commands.mock as mock + TEST_VALUES = [ extend_32bytes(bytes.fromhex("f0")), extend_32bytes(bytes.fromhex("f1")), @@ -30,30 +32,30 @@ extend_32bytes(bytes.fromhex("ff")), ] - -def assert_root(expected_root: bytes, actual_root: bytes, msg: str) -> None: - if actual_root != expected_root: - print(f"FAILED: {msg}") - print(f"Expected: {expected_root.hex()}") - print(f"Actual: {actual_root.hex()}") - raise Exception("failed") +MKTREE_INSTANCE: Any = None -def test_tree_empty(contract: Any) -> None: - mktree = MerkleTree.empty_with_depth(ZETH_MERKLE_TREE_DEPTH, MiMC7()) - expected_root = mktree.recompute_root() - root = contract.functions.testAddLeaves([], []).call() - assert_root(expected_root, root, "test_tree_empty") +class TestMerkleTreeContract(TestCase): + @staticmethod + def setUpClass() -> None: + _web3, eth = mock.open_test_web3() + deployer_eth_address = eth.accounts[0] + _mktree_interface, mktree_instance = mock.deploy_contract( + eth, + deployer_eth_address, + "MerkleTreeMiMC7_test", + {'treeDepth': ZETH_MERKLE_TREE_DEPTH}) + global MKTREE_INSTANCE # pylint: disable=global-statement + MKTREE_INSTANCE = mktree_instance -def test_tree_partial(contract: Any) -> None: - """ - Send a series of different arrays of leaves to the contract and check that - the root is as expected. Send as 2 batches, to test updating the tree, from - various states. - """ + def test_tree_empty(self) -> None: + mktree = MerkleTree.empty_with_depth(ZETH_MERKLE_TREE_DEPTH, MiMC7()) + expected_root = mktree.recompute_root() + root = MKTREE_INSTANCE.functions.testAddLeaves([], []).call() + self.assertEqual(expected_root, root, "test_tree_empty") - def _test_partial(num_entries: int, step: int = 1) -> None: + def _test_partial(self, num_entries: int, step: int = 1) -> None: """ Take the first 'num_entries' from TEST_VALUES. Cut them at each possible place and submit them as two halves to the contract, receiving back the @@ -70,33 +72,22 @@ def _test_partial(num_entries: int, step: int = 1) -> None: print(f"_test_partial: num_entries={num_entries}, cut={cut}") first = leaves[:cut] second = leaves[cut:] - root = contract.functions.testAddLeaves(first, second).call() - assert_root( + root = MKTREE_INSTANCE.functions.testAddLeaves(first, second).call() + self.assertEqual( expected_root, root, f"num_entries: {num_entries}, cut: {cut}: ") - # Perform the filling tests using arrays of these sizes - _test_partial(1) - _test_partial(7) - _test_partial(8) - _test_partial(9) - _test_partial(15, 3) - _test_partial(16, 3) - - -def main() -> None: - _web3, eth = mock.open_test_web3() - deployer_eth_address = eth.accounts[0] - _mktree_interface, mktree_instance = mock.deploy_contract( - eth, - deployer_eth_address, - "MerkleTreeMiMC7_test", - {'treeDepth': ZETH_MERKLE_TREE_DEPTH}) - - test_tree_empty(mktree_instance) - test_tree_partial(mktree_instance) - - -if __name__ == '__main__': - main() + def test_tree_partial(self) -> None: + """ + Send a series of different arrays of leaves to the contract and check that + the root is as expected. Send as 2 batches, to test updating the tree, + from various states. + """ + # Perform the filling tests using arrays of these sizes + self._test_partial(1) + self._test_partial(7) + self._test_partial(8) + self._test_partial(9) + self._test_partial(15, 3) + self._test_partial(16, 3) diff --git a/client/test_contracts/test_mimc_contract.py b/client/test_contracts/test_mimc_contract.py new file mode 100644 index 000000000..056988c6e --- /dev/null +++ b/client/test_contracts/test_mimc_contract.py @@ -0,0 +1,51 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.core.utils import get_contracts_dir +from zeth.core.contracts import InstanceDescription +from zeth.core.mimc import MiMC7, MiMC31 +from zeth.cli.utils import get_eth_network, open_web3_from_network +from os.path import join +from unittest import TestCase +from typing import Any + +CONTRACT_INSTANCE: Any = None + + +class TestMiMCContract(TestCase): + + @staticmethod + def setUpClass() -> None: + web3: Any = open_web3_from_network(get_eth_network(None)) + contracts_dir = get_contracts_dir() + contract_instance_desc = InstanceDescription.deploy( + web3, + join(contracts_dir, "MiMC_test.sol"), + "MiMC_test", + web3.eth.accounts[0], # pylint: disable=no-member + None, + 500000, + {"allow_paths": contracts_dir}) + global CONTRACT_INSTANCE # pylint: disable=global-statement + CONTRACT_INSTANCE = contract_instance_desc.instantiate(web3) + + def test_mimc7(self) -> None: + # pylint: disable=line-too-long + x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa + y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa + # pylint: enable=line-too-long + h = MiMC7().hash(x, y) + + result = CONTRACT_INSTANCE.functions.test_mimc7(x, y).call() + self.assertEqual(h, result) + + def test_mimc31(self) -> None: + # pylint: disable=line-too-long + x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa + y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa + # pylint: enable=line-too-long + h = MiMC31().hash(x, y) + + result = CONTRACT_INSTANCE.functions.test_mimc31(x, y).call() + self.assertEqual(h, result) From b8e15abcfe2dfca3d09972f5174e6183f46420e1 Mon Sep 17 00:00:00 2001 From: Duncan Tebbs Date: Tue, 22 Dec 2020 11:48:13 +0000 Subject: [PATCH 3/3] ci: cache npm and pip in contract and client checks --- .github/workflows/onpush-checks.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/onpush-checks.yml b/.github/workflows/onpush-checks.yml index c93f1861e..4955c285c 100644 --- a/.github/workflows/onpush-checks.yml +++ b/.github/workflows/onpush-checks.yml @@ -15,6 +15,22 @@ jobs: - uses: actions/setup-node@v1 with: node-version: 10 + - name: Cache npm + uses: actions/cache@v1 + with: + path: ~/.npm + key: check-contracts-npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + # Cache ganache-cli/node_modules to avoid recompiling native code + - name: Cache depends/ganache-cli/node_modules + uses: actions/cache@v1 + with: + path: depends/ganache-cli/node_modules + key: check-contracts-ganache-node-modules-${{ runner.os }}-${{ hashFiles('depends/ganache-cli/package-lock.json') }} + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: check-contracts-pip-${{ runner.os }}-${{ hashFiles('client/setup.py') }} - name: Check Contracts run: scripts/ci check_contracts @@ -24,7 +40,12 @@ jobs: - uses: actions/checkout@v1 with: submodules: recursive - - name: Execute + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: check-client-pip-${{ runner.os }}-${{ hashFiles('client/setup.py') }} + - name: Check Client run: scripts/ci check_client check-cpp-linux: