From b06a437ee17685bf066a5789b5f8d1a2f21cc309 Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 22 Nov 2022 13:13:41 +0800 Subject: [PATCH] Dapp and foundry tests CI (#283) --- .github/workflows/dapp-build.yml | 25 + .github/workflows/foundry-build.yml | 26 + .../workflows/{ci.yml => hardhat-build.yml} | 2 +- contracts/bridge/.dapprc | 13 +- contracts/bridge/.gas-snapshot | 238 +++-- contracts/bridge/Makefile | 3 +- contracts/bridge/bin/coverage.sh | 15 + contracts/bridge/nix/sources.json | 6 +- contracts/bridge/nix/sources.nix | 22 +- contracts/bridge/shell.nix | 2 +- .../src/test/fee-market/FeeMarket.t.sol | 8 + .../src/test/fee-market/SimpleFeeMarket.t.sol | 983 +++++++++--------- contracts/bridge/src/test/utils/Math.p.sol | 44 + contracts/bridge/src/test/utils/Math.t.sol | 25 + contracts/bridge/src/utils/Math.sol | 1 + 15 files changed, 799 insertions(+), 614 deletions(-) create mode 100644 .github/workflows/dapp-build.yml create mode 100644 .github/workflows/foundry-build.yml rename .github/workflows/{ci.yml => hardhat-build.yml} (98%) create mode 100755 contracts/bridge/bin/coverage.sh create mode 100644 contracts/bridge/src/test/utils/Math.p.sol diff --git a/.github/workflows/dapp-build.yml b/.github/workflows/dapp-build.yml new file mode 100644 index 000000000..e04d34529 --- /dev/null +++ b/.github/workflows/dapp-build.yml @@ -0,0 +1,25 @@ +name: "Dapp Build" +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: contracts/bridge + steps: + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v18 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v11 + with: + name: dapp + + - name: test + run: nix-shell --run 'make test' diff --git a/.github/workflows/foundry-build.yml b/.github/workflows/foundry-build.yml new file mode 100644 index 000000000..f2a395a59 --- /dev/null +++ b/.github/workflows/foundry-build.yml @@ -0,0 +1,26 @@ +name: "Foundry Build" +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + FOUNDRY_PROFILE: ci + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: contracts/bridge + steps: + - uses: actions/checkout@v3 + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: test + run: forge test diff --git a/.github/workflows/ci.yml b/.github/workflows/hardhat-build.yml similarity index 98% rename from .github/workflows/ci.yml rename to .github/workflows/hardhat-build.yml index 1243183e3..73114221e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/hardhat-build.yml @@ -1,4 +1,4 @@ -name: CI +name: "Hardhat Build" on: push: diff --git a/contracts/bridge/.dapprc b/contracts/bridge/.dapprc index 6e666ae07..3560e36f3 100644 --- a/contracts/bridge/.dapprc +++ b/contracts/bridge/.dapprc @@ -1,6 +1,6 @@ # Make dependencies available export DAPP_REMAPPINGS=$(cat remappings.txt) -export DAPP_SRC=flat +export DAPP_SRC=src export DAPP_OUT=out export DAPP_EVM_VERSION=istanbul export DAPP_JSON=out/dapp.sol.json @@ -8,7 +8,6 @@ export DAPP_JSON=out/dapp.sol.json export DAPP_SOLC_VERSION=0.7.6 # If you're getting an "invalid character at offset" error, comment this out. export DAPP_LINK_TEST_LIBRARIES=0 -export DAPP_TEST_VERBOSITY=1 export DAPP_TEST_SMTTIMEOUT=500000 # Optimize your contracts before deploying to reduce runtime execution costs. @@ -17,9 +16,13 @@ export DAPP_SKIP_BUILD= export DAPP_BUILD_OPTIMIZE=1 export DAPP_BUILD_OPTIMIZE_RUNS=999999 -if [ "$DEEP_FUZZ" = "true" ] +if [ "$CI" = "true" ] then - export DAPP_TEST_FUZZ_RUNS=50000 # Fuzz for a long time if DEEP_FUZZ is set to true. + export DAPP_TEST_FUZZ_RUNS=10000 + export DAPP_TEST_MAX_ITERATIONS=6 + export DAPP_TEST_VERBOSITY=0 else - export DAPP_TEST_FUZZ_RUNS=100 # Only fuzz briefly if DEEP_FUZZ is not set to true. + export DAPP_TEST_FUZZ_RUNS=100 + export DAPP_TEST_MAX_ITERATIONS=3 + export DAPP_TEST_VERBOSITY=3 fi diff --git a/contracts/bridge/.gas-snapshot b/contracts/bridge/.gas-snapshot index 71a088f82..e9163621c 100644 --- a/contracts/bridge/.gas-snapshot +++ b/contracts/bridge/.gas-snapshot @@ -1,97 +1,96 @@ -test_move_relayer() (gas: 336813) -testFail_enroll_2() (gas: 39427) -test_settle_when_a_relay_and_b_confirm_late_all_slash() (gas: 671394) -test_set_setter() (gas: 2816) -test_settle_when_b_relay_and_c_confirm_at_a_slot() (gas: 711798) -test_assign() (gas: 596711) -test_initial_state() (gas: 12427) -test_set_outbound() (gas: 26445) -testFail_exit_2() (gas: 42353) -test_constructor_args() (gas: 10714) -test_exit() (gas: 64677) -test_add_relayer() (gas: 297662) -test_join() (gas: 105361) -test_settle_when_a_relay_and_a_confirm_at_b_slot() (gas: 712232) -test_settle_when_a_relay_and_b_confirm_late_half_slash() (gas: 691299) -test_settle_when_a_relay_and_b_confirm_at_c_slot() (gas: 690004) -test_enroll() (gas: 300951) -test_settle_when_a_relay_and_b_confirm_at_a_slot() (gas: 711798) -test_settle_when_a_relay_and_b_confirm_late() (gas: 691364) -test_leave() (gas: 136212) -test_settle_when_a_relay_and_confirm_at_a_slot() (gas: 722726) -test_market_status() (gas: 299597) -testFail_exit_3() (gas: 89164) -testFail_enroll_1() (gas: 152967) -testFail_exit_1() (gas: 4050) -test_remove_relayer() (gas: 307936) -test_settle_when_b_relay_and_b_confirm_at_b_slot() (gas: 712190) -test_move_relayer() (gas: 336553) -testFail_enroll_2() (gas: 39404) -test_settle_when_a_relay_and_b_confirm_late_all_slash() (gas: 466689) -test_set_setter() (gas: 2659) -test_settle_when_b_relay_and_c_confirm_at_a_slot() (gas: 466361) -test_assign() (gas: 430851) -test_initial_state() (gas: 12295) -test_set_outbound() (gas: 26456) -test_settle_when_b_relay_and_b_confirm_at_a_slot_late() (gas: 466783) -testFail_exit_2() (gas: 42329) -test_constructor_args() (gas: 5388) -test_exit() (gas: 64675) -test_add_relayer() (gas: 297537) -test_settle_when_a_relay_and_a_confirm_at_a_slot() (gas: 466784) -test_join() (gas: 105157) -test_settle_when_a_relay_and_b_confirm_late_half_slash() (gas: 466783) -test_enroll() (gas: 300673) -test_settle_when_a_relay_and_b_confirm_at_a_slot() (gas: 464554) -test_leave() (gas: 136110) -test_settle_when_a_relay_and_confirm_at_a_slot() (gas: 473889) -test_settle_when_b_relay_and_b_confirm_late_all_slash() (gas: 446811) -test_market_status() (gas: 295737) -testFail_exit_3() (gas: 89139) -testFail_enroll_1() (gas: 152871) -testFail_exit_1() (gas: 4028) -test_remove_relayer() (gas: 307792) -testFail_receive_messages_proof0() (gas: 32231) -testFail_receive_messages_proof1() (gas: 33388) -testFail_receive_messages_proof2() (gas: 33397) -test_constructor_args() (gas: 9536) -test_receive_messages_proof() (gas: 90737) -testFail_receive_messages_proof3() (gas: 33493) -testFail_receive_messages_proof5() (gas: 33555) -test_receive_messages_proof_multi1() (gas: 166377) -testFail_receive_messages_proof4() (gas: 33502) -test_receive_messages_proof_multi0() (gas: 109842) -test_encode_message_key() (gas: 1471) -test_contructor_args() (gas: 2961) -test_send_message() (gas: 58836) +test_move_relayer() (gas: 336967) +testFail_enroll_2() (gas: 39438) +test_settle_when_a_relay_and_b_confirm_late_all_slash() (gas: 678674) +test_set_setter() (gas: 2860) +test_settle_when_b_relay_and_c_confirm_at_a_slot() (gas: 719081) +test_assign() (gas: 604588) +test_initial_state() (gas: 12471) +test_set_outbound() (gas: 26401) +testFail_exit_2() (gas: 42375) +test_constructor_args() (gas: 10499) +test_exit() (gas: 64743) +test_add_relayer() (gas: 298091) +test_join() (gas: 105626) +test_settle_when_a_relay_and_a_confirm_at_b_slot() (gas: 719481) +test_settle_when_a_relay_and_b_confirm_late_half_slash() (gas: 698637) +test_settle_when_a_relay_and_b_confirm_at_c_slot() (gas: 697241) +test_enroll() (gas: 301293) +test_settle_when_a_relay_and_b_confirm_at_a_slot() (gas: 719059) +test_settle_when_a_relay_and_b_confirm_late() (gas: 698616) +test_leave() (gas: 136465) +test_settle_when_a_relay_and_confirm_at_a_slot() (gas: 730119) +test_market_status() (gas: 308036) +testFail_exit_3() (gas: 89186) +testFail_enroll_1() (gas: 153034) +testFail_exit_1() (gas: 4006) +test_remove_relayer() (gas: 308016) +test_settle_when_b_relay_and_b_confirm_at_b_slot() (gas: 719439) +test_move_relayer() (gas: 336685) +testFail_enroll_2() (gas: 39392) +test_settle_when_a_relay_and_b_confirm_late_all_slash() (gas: 467011) +test_settle_when_b_relay_and_c_confirm_at_a_slot() (gas: 467200) +test_assign() (gas: 432251) +test_initial_state() (gas: 12472) +test_settle_when_b_relay_and_b_confirm_at_a_slot_late() (gas: 467055) +testFail_exit_2() (gas: 42352) +test_constructor_args() (gas: 5281) +test_exit() (gas: 64875) +test_add_relayer() (gas: 297683) +test_settle_when_a_relay_and_a_confirm_at_a_slot() (gas: 467056) +test_join() (gas: 105735) +test_settle_when_a_relay_and_b_confirm_late_half_slash() (gas: 467077) +test_enroll() (gas: 300973) +test_settle_when_a_relay_and_b_confirm_at_a_slot() (gas: 465393) +test_leave() (gas: 136255) +test_settle_when_a_relay_and_confirm_at_a_slot() (gas: 475016) +test_settle_when_b_relay_and_b_confirm_late_all_slash() (gas: 447089) +test_market_status() (gas: 304236) +testFail_exit_3() (gas: 89207) +testFail_enroll_1() (gas: 152915) +testFail_exit_1() (gas: 3984) +test_remove_relayer() (gas: 307741) +testFail_receive_messages_proof0() (gas: 32239) +testFail_receive_messages_proof1() (gas: 33425) +testFail_receive_messages_proof2() (gas: 33434) +test_constructor_args() (gas: 7176) +test_receive_messages_proof() (gas: 88933) +testFail_receive_messages_proof3() (gas: 33530) +testFail_receive_messages_proof5() (gas: 33592) +test_receive_messages_proof_multi1() (gas: 164539) +testFail_receive_messages_proof4() (gas: 33539) +test_receive_messages_proof_multi0() (gas: 107940) +test_encode_message_key() (gas: 1449) +test_contructor_args() (gas: 1627) +test_send_message() (gas: 58873) test_message_hash() (gas: 784) -testFail_receive_multi_messages_delivery_proof0() (gas: 179365) -testFail_receive_messages_delivery_proof0() (gas: 60813) -test_constructor_args() (gas: 8709) -testFail_receive_messages_delivery_proof4() (gas: 61267) -test_receive_multi_messages_delivery_proof1() (gas: 197313) -testFail_receive_messages_delivery_proof5() (gas: 61173) -test_send_multi_message() (gas: 170207) -testFail_receive_messages_delivery_proof2() (gas: 60859) -testFail_receive_messages_delivery_proof1() (gas: 60881) -test_receive_messages_delivery_proof() (gas: 72336) -testFail_receive_messages_delivery_proof3() (gas: 60929) -test_receive_multi_messages_delivery_proof0() (gas: 175062) -test_encode_message_key() (gas: 1483) -test_contructor_args() (gas: 2961) +testFail_receive_multi_messages_delivery_proof0() (gas: 179199) +testFail_receive_messages_delivery_proof0() (gas: 60853) +test_constructor_args() (gas: 8686) +testFail_receive_messages_delivery_proof4() (gas: 61298) +test_receive_multi_messages_delivery_proof1() (gas: 196861) +testFail_receive_messages_delivery_proof5() (gas: 61204) +test_send_multi_message() (gas: 170327) +testFail_receive_messages_delivery_proof2() (gas: 60899) +testFail_receive_messages_delivery_proof1() (gas: 60921) +testFail_too_many_pending_messages() (gas: 1046165) +test_receive_messages_delivery_proof() (gas: 72090) +testFail_receive_messages_delivery_proof3() (gas: 60969) +test_receive_multi_messages_delivery_proof0() (gas: 174896) +test_encode_message_key() (gas: 1461) +test_contructor_args() (gas: 1627) testFail_add_g1() (gas: 41338) test_encode_next_validator_set() (gas: 8910) test_hash() (gas: 3693) test_fork_data_hash() (gas: 1200) -test_compute_signing_root() (gas: 12623) +test_compute_signing_root() (gas: 12711) test_signing_data_hash() (gas: 1213) test_is_valid_merkle_branch_case1() (gas: 6243) test_is_valid_merkle_branch_case0() (gas: 6265) -test_beacon_block_header_hash() (gas: 61765) +test_beacon_block_header_hash() (gas: 62293) test_compute_signing_root2() (gas: 1101) -test_to_little_endian_64() (gas: 479) -test_default_hash() (gas: 10511) -test_sync_committee_hash() (gas: 5072713) +test_to_little_endian_64() (gas: 523) +test_default_hash() (gas: 10599) +test_sync_committee_hash() (gas: 5072712) test_hash_rlp_with_chain_id_block_header() (gas: 304413) test_hash_block_header() (gas: 51685) test_hash_rlp_block_header() (gas: 294495) @@ -100,30 +99,30 @@ test_constants() (gas: 1281) test_decode_message_key() (gas: 876) test_hash2() (gas: 4791) test_hash() (gas: 11935) -test_verify_single_storage_proof() (gas: 344601) -test_storage_proof() (gas: 932453) -test_message_proof() (gas: 720342) +test_verify_single_storage_proof() (gas: 344586) +test_storage_proof() (gas: 932393) +test_message_proof() (gas: 720297) test_default_hash() (gas: 1643) test_constants() (gas: 1203) test_hash() (gas: 4504) -test_recover_creator() (gas: 73397) -test_constructor_args() (gas: 126059) -testFail_import_finalized_epoch_header() (gas: 6349) -test_extract_authorities() (gas: 33958) -test_import_finalized_epoch_header() (gas: 2469969) -test_recover_creator() (gas: 69938) -test_constructor_args() (gas: 93532) -testFail_import_finalized_epoch_header() (gas: 5726) -test_extract_authorities() (gas: 18552) -test_import_finalized_epoch_header() (gas: 1306709) +test_recover_creator() (gas: 73442) +test_constructor_args() (gas: 125509) +testFail_import_finalized_epoch_header() (gas: 6351) +test_extract_authorities() (gas: 33936) +test_import_finalized_epoch_header() (gas: 2469421) +test_recover_creator() (gas: 69983) +test_constructor_args() (gas: 93224) +testFail_import_finalized_epoch_header() (gas: 5728) +test_extract_authorities() (gas: 18530) +test_import_finalized_epoch_header() (gas: 1306403) test_fast_aggregate_verify() (gas: 122) test_verify_single_storage_proof() (gas: 122) test_verify_multi_storage_proof() (gas: 144) -test_prove_one_leave() (gas: 135349) -testFail_prove_empty() (gas: 4318) -testFail_prove_three_leaves_with_wrong_pos() (gas: 344673) -test_prove_two_leaves() (gas: 255970) -test_prove_three_leaves() (gas: 387492) +test_prove_one_leave() (gas: 135395) +testFail_prove_empty() (gas: 4364) +testFail_prove_three_leaves_with_wrong_pos() (gas: 344719) +test_prove_two_leaves() (gas: 256062) +test_prove_three_leaves() (gas: 387630) test_empty_commitment() (gas: 3786) testFail_complete_signature_commitment() (gas: 119466) test_new_signature_commitment() (gas: 85539) @@ -139,18 +138,29 @@ testFail_add_relayer_with_wrong_threshold() (gas: 55828) test_swap_relayer_hash() (gas: 1589) test_remove_relayer() (gas: 73383) test_swap_relayer() (gas: 60120) -test_import_message_commitment() (gas: 58567) +test_import_message_commitment() (gas: 58624) test_message_commitment_hash2() (gas: 978) test_message_commitment_hash() (gas: 977) +test_hash_body() (gas: 43274) test_constructor_args() (gas: 211) -test_import_finalized_header() (gas: 3184718) -test_import_next_sync_committee() (gas: 8551265) -test_import_latest_execution_payload_state_root() (gas: 3220512) -test_constructor_args() (gas: 189) -test_import_finalized_header() (gas: 2976093) -test_sum_sync_committee_bits() (gas: 1029) -test_import_next_sync_committee() (gas: 8342599) -test_import_latest_execution_payload_state_root() (gas: 3011871) +test_import_finalized_header() (gas: 3100337) +test_sum_sync_committee_bits() (gas: 1047) +test_hash_execution_payload() (gas: 22834) +test_import_next_sync_committee() (gas: 8985305) +test_import_latest_execution_payload_state_root() (gas: 3168744) +test_hash_body() (gas: 43274) +test_constructor_args() (gas: 211) +test_import_finalized_header() (gas: 2910756) +test_sum_sync_committee_bits() (gas: 1047) +test_hash_execution_payload() (gas: 22834) +test_import_next_sync_committee() (gas: 8800997) +test_import_latest_execution_payload_state_root() (gas: 2979163) +test_hash_body() (gas: 43263) +test_constructor_args() (gas: 233) +test_import_finalized_header() (gas: 3143100) +test_hash_execution_payload() (gas: 22846) +testFail_import_next_sync_committee() (gas: 5395376) +test_import_latest_execution_payload_state_root() (gas: 3211411) test_random_n_bits_with_prior_check() (gas: 13872) test_clean() (gas: 1118) test_count_set_bits() (gas: 1344) @@ -182,8 +192,8 @@ testFail_bits_highest_bit_set_throws_bit_field_is_zero() (gas: 244) test_bits_lowest_bit_set_all_higher_set() (gas: 1236) test_recover() (gas: 4119) testFail_recover() (gas: 10030) -test_get_power_of_two_ceil() (gas: 4001) -test_log_2() (gas: 8463) +test_get_power_of_two_ceil() (gas: 4079) +test_log_2() (gas: 9037) test_decode_uint_compact() (gas: 3423) test_decode_uint256() (gas: 10942) testFail_decode_uint_compact() (gas: 663) diff --git a/contracts/bridge/Makefile b/contracts/bridge/Makefile index ec08d90ab..cb10ec652 100644 --- a/contracts/bridge/Makefile +++ b/contracts/bridge/Makefile @@ -27,6 +27,7 @@ test :; dapp test # --ffi # enable if you need the `ffi` cheat code on HEVM clean :; dapp clean lint :; yarn run lint doc :; yarn run docs +cov :; @./bin/coverage.sh audit :; @./bin/audit.sh flatten :; @./bin/flatten.sh estimate :; @./bin/estimate-gas.sh ${contract} @@ -47,4 +48,4 @@ migrate-test :; @./bin/migrate-test.sh migrate-prod :; @./bin/migrate-prod.sh # verify on Etherscan -verify:; ETH_RPC_URL=$(call network,$(network_name)) dapp verify-contract src/Greeter.sol:Greeter $(contract_address) +verify :; ETH_RPC_URL=$(call network,$(network_name)) dapp verify-contract src/Greeter.sol:Greeter $(contract_address) diff --git a/contracts/bridge/bin/coverage.sh b/contracts/bridge/bin/coverage.sh new file mode 100755 index 000000000..109095363 --- /dev/null +++ b/contracts/bridge/bin/coverage.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e +MAX_UNCOVERED=0 # Maximum number of uncovered lines allowed + +echo "Running coverage..." +uncovered=$(dapp test -v --coverage --cov-match "src\/spec" | grep "\[31m" | wc -l) +echo "Uncovered lines: $uncovered" + +if [[ $uncovered -gt $MAX_UNCOVERED ]]; then + echo "Insufficient coverage (max $MAX_UNCOVERED uncovered lines allowed)" + exit 1 +fi + +echo "Satisfying coverage!" diff --git a/contracts/bridge/nix/sources.json b/contracts/bridge/nix/sources.json index ecb3cf40b..f25494dfe 100644 --- a/contracts/bridge/nix/sources.json +++ b/contracts/bridge/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "https://dapp.tools", "owner": "hujw77", "repo": "dapptools", - "rev": "3409d395163dbd82121408945349f97d59001372", - "sha256": "05rn9kj4drc23rkhjca90r76zbs3siqy4vihy50zcygqc3gh871d", + "rev": "ab02d89f6b20945dcc608aa3a9b907690097438d", + "sha256": "140apalhrp4njcyh2r79393jfzisivi8vjxirgcp0x792kavbkyr", "type": "tarball", - "url": "https://github.com/hujw77/dapptools/archive/3409d395163dbd82121408945349f97d59001372.tar.gz", + "url": "https://github.com/hujw77/dapptools/archive/ab02d89f6b20945dcc608aa3a9b907690097438d.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/contracts/bridge/nix/sources.nix b/contracts/bridge/nix/sources.nix index 1938409dd..9a01c8acf 100644 --- a/contracts/bridge/nix/sources.nix +++ b/contracts/bridge/nix/sources.nix @@ -31,8 +31,28 @@ let if spec ? branch then "refs/heads/${spec.branch}" else if spec ? tag then "refs/tags/${spec.tag}" else abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; + submodules = if spec ? submodules then spec.submodules else false; + submoduleArg = + let + nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0; + emptyArgWithWarning = + if submodules == true + then + builtins.trace + ( + "The niv input \"${name}\" uses submodules " + + "but your nix's (${builtins.nixVersion}) builtins.fetchGit " + + "does not support them" + ) + {} + else {}; + in + if nixSupportsSubmodules + then { inherit submodules; } + else emptyArgWithWarning; in - builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; + builtins.fetchGit + ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg); fetch_local = spec: spec.path; diff --git a/contracts/bridge/shell.nix b/contracts/bridge/shell.nix index 10058ea21..1782c2994 100644 --- a/contracts/bridge/shell.nix +++ b/contracts/bridge/shell.nix @@ -10,6 +10,6 @@ in pkgs.seth pkgs.go-ethereum-unlimited pkgs.hevm - pkgs.nodejs ]; + LANG="en_US.UTF-8"; } diff --git a/contracts/bridge/src/test/fee-market/FeeMarket.t.sol b/contracts/bridge/src/test/fee-market/FeeMarket.t.sol index 646269b1f..c7e231f57 100644 --- a/contracts/bridge/src/test/fee-market/FeeMarket.t.sol +++ b/contracts/bridge/src/test/fee-market/FeeMarket.t.sol @@ -60,6 +60,14 @@ contract FeeMarketTest is DSTest { c = new Guy(market); } + function invariant_setter() public { + assertEq(market.setter(), self); + } + + function invariant_totalSupply() public { + assert_market_balances(); + } + function test_constructor_args() public { assertEq(market.setter(), self); assertEq(market.VAULT(), vault); diff --git a/contracts/bridge/src/test/fee-market/SimpleFeeMarket.t.sol b/contracts/bridge/src/test/fee-market/SimpleFeeMarket.t.sol index 678835048..96506a59c 100644 --- a/contracts/bridge/src/test/fee-market/SimpleFeeMarket.t.sol +++ b/contracts/bridge/src/test/fee-market/SimpleFeeMarket.t.sol @@ -41,495 +41,502 @@ contract SimpleFeeMarketTest is DSTest { Guy public b; Guy public c; + function setUp() public { + market = new SimpleFeeMarket( + COLLATERAL_PERORDER, + SLASH_TIME, + RELAY_TIME, + PRICE_RATIO, + DUTY_RATIO + ); + self = address(this); + market.initialize(); + a = new Guy(market); + b = new Guy(market); + c = new Guy(market); + } + + function invariant_setter() public { + assertEq(market.setter(), self); + } + + function invariant_totalSupply() public { + assert_market_balances(); + } + + function test_constructor_args() public { + assertEq(market.setter(), self); + assertEq(market.COLLATERAL_PER_ORDER(), COLLATERAL_PERORDER); + assertEq(market.SLASH_TIME(), uint(SLASH_TIME)); + assertEq(market.RELAY_TIME(), uint(RELAY_TIME)); + assertEq(market.PRICE_RATIO_NUMERATOR(), uint(PRICE_RATIO)); + } + + function test_set_setter_fuzz(address setter) public { + market.setSetter(setter); + assertEq(market.setter(), setter); + } + + function test_set_outbound_fuzz(uint flag) public { + market.setOutbound(self, flag); + assertEq(market.outbounds(self), flag); + } + + function test_initial_state() public { + assert_eth_balance (a, 0 ether); + assert_market_balance (a, 0 ether); + assert_eth_balance (b, 0 ether); + assert_market_balance (b, 0 ether); + assert_eth_balance (c, 0 ether); + assert_market_balance (c, 0 ether); + + assert_market_supply (0 ether); + } + + function test_join() public { + perform_join (a, 3 ether); + assert_market_balance (a, 3 ether); + assert_market_balance (b, 0 ether); + assert_eth_balance (a, 0 ether); + assert_market_supply (3 ether); + + perform_join (a, 4 ether); + assert_market_balance (a, 7 ether); + assert_market_balance (b, 0 ether); + assert_eth_balance (a, 0 ether); + assert_market_supply (7 ether); + + perform_join (b, 5 ether); + assert_market_balance (b, 5 ether); + assert_market_balance (a, 7 ether); + assert_market_supply (12 ether); + } + + function testFail_exit_1() public { + perform_exit (a, 1 wei); + } + + function testFail_exit_2() public { + perform_join (a, 1 ether); + perform_exit (b, 1 wei); + } + + function testFail_exit_3() public { + perform_join (a, 1 ether); + perform_join (b, 1 ether); + perform_exit (b, 1 ether); + perform_exit (b, 1 wei); + } + + function test_exit() public { + perform_join (a, 7 ether); + assert_market_balance (a, 7 ether); + assert_eth_balance (a, 0 ether); + + perform_exit (a, 3 ether); + assert_market_balance (a, 4 ether); + assert_eth_balance (a, 3 ether); + + perform_exit (a, 4 ether); + assert_market_balance (a, 0 ether); + assert_eth_balance (a, 7 ether); + } + + function test_enroll() public { + perform_enroll (a, address(1), 1 ether, 1 ether); + assert_market_is_relayer (a); + assert_market_fee_of (a, 1 ether); + assert_market_balance (a, 1 ether); + assert_market_supply (1 ether); + + perform_enroll (b, address(a), 1 ether, 1 ether); + assert_market_is_relayer (b); + assert_market_fee_of (b, 1 ether); + assert_market_balance (b, 1 ether); + assert_market_supply (2 ether); + + perform_enroll (c, address(b), 1 ether, 1.1 ether); + assert_market_is_relayer (c); + assert_market_fee_of (c, 1.1 ether); + assert_market_balance (c, 1 ether); + assert_market_supply (3 ether); + } + + function testFail_enroll_1() public { + perform_enroll (a, address(1), 1 ether, 1.1 ether); + perform_enroll (b, address(a), 1 ether, 1 ether); + } + + function testFail_enroll_2() public { + perform_enroll (a, address(1), 0.9 ether, 1 ether); + } + + function test_leave() public { + perform_enroll (a, address(1), 7 ether, 1 ether); + assert_market_is_relayer (a); + assert_market_fee_of (a, 1 ether); + assert_market_balance (a, 7 ether); + assert_market_supply (7 ether); + assert_eth_balance (a, 0 ether); + + perform_leave (a, address(1)); + assert_market_is_not_relayer (a); + assert_market_fee_of (a, 0 ether); + assert_market_balance (a, 0 ether); + assert_market_supply (0 ether); + assert_eth_balance (a, 7 ether); + } + + function test_add_relayer() public { + perform_join (a, 3 ether); + perform_join (b, 4 ether); + perform_join (c, 5 ether); + + perform_enrol (a, address ( 1), 1 ether); + assert_market_is_relayer (a); + assert_market_fee_of (a, 1 ether); + + perform_enrol (b, address ( a), 1 ether); + assert_market_is_relayer (b); + assert_market_fee_of (b, 1 ether); + perform_enrol (c, address ( b), 1.1 ether); + assert_market_is_relayer (c); + assert_market_fee_of (c, 1.1 ether); + } + + function test_remove_relayer() public { + perform_enroll (a, address(1), 1 ether, 1 ether); + perform_enroll (b, address(a), 1 ether, 1 ether); + perform_enroll (c, address(b), 1 ether, 1.1 ether); + + perform_delist (a, address(1)); + assert_market_is_not_relayer (a); + assert_market_fee_of (a, 0 ether); + perform_delist (b, address(1)); + assert_market_is_not_relayer (b); + assert_market_fee_of (b, 0 ether); + perform_delist (c, address(1)); + assert_market_is_not_relayer (c); + assert_market_fee_of (c, 0 ether); + } + + function test_move_relayer() public { + perform_enroll (a, address(1), 1 ether, 1 ether); + perform_enroll (b, address(a), 1 ether, 1 ether); + perform_enroll (c, address(b), 1 ether, 1.1 ether); + + perform_move (a, address(1), address(c), 1.2 ether); + assert_market_is_relayer (a); + assert_market_fee_of (a, 1.2 ether); + } + + function test_market_status() public { + perform_enroll (a, address(1), 1 ether, 1 ether); + perform_enroll (b, address(a), 1 ether, 1 ether); + perform_enroll (c, address(b), 1 ether, 1.1 ether); + + address top = market.getTopRelayer(); + assertEq(top, address(a)); + + ( + uint index, + address[] memory relayers, + uint[] memory fees, + uint[] memory balances, + uint[] memory locks + ) = market.getOrderBook(3, true); + assertEq(index, 3); + assertEq(relayers[0], address(a)); + assertEq(relayers[1], address(b)); + assertEq(relayers[2], address(c)); + assertEq(fees[0], 1 ether); + assertEq(fees[1], 1 ether); + assertEq(fees[2], 1.1 ether); + assertEq(balances[0], 1 ether); + assertEq(balances[1], 1 ether); + assertEq(balances[2], 1 ether); + assertEq(locks[0], 0 ether); + assertEq(locks[1], 0 ether); + assertEq(locks[2], 0 ether); + } + + function test_assign() public { + uint key = 1; + init(key); + ( + uint index, + address[] memory relayers, + uint[] memory fees, + uint[] memory balances, + uint[] memory locks + ) = market.getOrderBook(1, false); + assertEq(index, 1); + assertEq(relayers[0], address(b)); + assertEq(fees[0], market.feeOf(address(b))); + assertEq(balances[0], 1 ether); + assertEq(locks[0], 0 ether); + + assert_market_locked(a, 1 ether); + assert_market_locked(b, 0 ether); + assert_market_locked(c, 0 ether); + + assert_market_order(a, key); + } + + function test_settle_when_a_relay_and_confirm_at_a_slot() public { + hevm.warp(1); + uint key = 1; + init(key); + + assert_market_balance(a, 0 ether); + assert_market_balance(b, 1 ether); + assert_market_balance(c, 1 ether); + assert_market_locked(a, 1 ether); + assert_market_locked(b, 0 ether); + assert_market_locked(c, 0 ether); + assert_market_supply(4 ether); + + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); + assertTrue(market.settle(deliveredRelayers, address(a))); + + assert_market_order_clean(key); + + assert_market_balance(a, 2 ether); + assert_market_balance(b, 1 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_a_relay_and_b_confirm_at_a_slot() public { + hevm.warp(1); + uint key = 1; + init(key); + + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); + assertTrue(market.settle(deliveredRelayers, address(b))); + + assert_market_order_clean(key); + + assert_market_balance(a, 1.92 ether); + assert_market_balance(b, 1.08 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_b_relay_and_c_confirm_at_a_slot() public { + hevm.warp(1); + uint key = 1; + init(key); + + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); + assertTrue(market.settle(deliveredRelayers, address(c))); + + assert_market_order_clean(key); + + assert_market_balance(a, 1.6 ether); + assert_market_balance(b, 1.32 ether); + assert_market_balance(c, 1.08 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_a_relay_and_a_confirm_at_a_slot() public { + hevm.warp(1); + uint key = 1; + init(key); + + hevm.warp(1 + RELAY_TIME); + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); + assertTrue(market.settle(deliveredRelayers, address(a))); + + assert_market_order_clean(key); + + assert_market_balance(a, 2 ether); + assert_market_balance(b, 1 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } - function setUp() public { - market = new SimpleFeeMarket( - COLLATERAL_PERORDER, - SLASH_TIME, - RELAY_TIME, - PRICE_RATIO, - DUTY_RATIO - ); - self = address(this); - market.initialize(); - a = new Guy(market); - b = new Guy(market); - c = new Guy(market); - } - - function test_constructor_args() public { - assertEq(market.setter(), self); - assertEq(market.COLLATERAL_PER_ORDER(), COLLATERAL_PERORDER); - assertEq(market.SLASH_TIME(), uint(SLASH_TIME)); - assertEq(market.RELAY_TIME(), uint(RELAY_TIME)); - assertEq(market.PRICE_RATIO_NUMERATOR(), uint(PRICE_RATIO)); - } - - function test_set_setter() public { - market.setSetter(address(0)); - assertEq(market.setter(), address(0)); - } - - function test_set_outbound() public { - market.setOutbound(self, 1); - assertEq(market.outbounds(self), 1); - } - - function test_initial_state() public { - assert_eth_balance (a, 0 ether); - assert_market_balance (a, 0 ether); - assert_eth_balance (b, 0 ether); - assert_market_balance (b, 0 ether); - assert_eth_balance (c, 0 ether); - assert_market_balance (c, 0 ether); - - assert_market_supply (0 ether); - } - - function test_join() public { - perform_join (a, 3 ether); - assert_market_balance (a, 3 ether); - assert_market_balance (b, 0 ether); - assert_eth_balance (a, 0 ether); - assert_market_supply (3 ether); - - perform_join (a, 4 ether); - assert_market_balance (a, 7 ether); - assert_market_balance (b, 0 ether); - assert_eth_balance (a, 0 ether); - assert_market_supply (7 ether); - - perform_join (b, 5 ether); - assert_market_balance (b, 5 ether); - assert_market_balance (a, 7 ether); - assert_market_supply (12 ether); - } - - function testFail_exit_1() public { - perform_exit (a, 1 wei); - } - - function testFail_exit_2() public { - perform_join (a, 1 ether); - perform_exit (b, 1 wei); - } - - function testFail_exit_3() public { - perform_join (a, 1 ether); - perform_join (b, 1 ether); - perform_exit (b, 1 ether); - perform_exit (b, 1 wei); - } - - function test_exit() public { - perform_join (a, 7 ether); - assert_market_balance (a, 7 ether); - assert_eth_balance (a, 0 ether); - - perform_exit (a, 3 ether); - assert_market_balance (a, 4 ether); - assert_eth_balance (a, 3 ether); - - perform_exit (a, 4 ether); - assert_market_balance (a, 0 ether); - assert_eth_balance (a, 7 ether); - } - - function test_enroll() public { - perform_enroll (a, address(1), 1 ether, 1 ether); - assert_market_is_relayer (a); - assert_market_fee_of (a, 1 ether); - assert_market_balance (a, 1 ether); - assert_market_supply (1 ether); - - perform_enroll (b, address(a), 1 ether, 1 ether); - assert_market_is_relayer (b); - assert_market_fee_of (b, 1 ether); - assert_market_balance (b, 1 ether); - assert_market_supply (2 ether); - - perform_enroll (c, address(b), 1 ether, 1.1 ether); - assert_market_is_relayer (c); - assert_market_fee_of (c, 1.1 ether); - assert_market_balance (c, 1 ether); - assert_market_supply (3 ether); - } - - function testFail_enroll_1() public { - perform_enroll (a, address(1), 1 ether, 1.1 ether); - perform_enroll (b, address(a), 1 ether, 1 ether); - } - - function testFail_enroll_2() public { - perform_enroll (a, address(1), 0.9 ether, 1 ether); - } - - function test_leave() public { - perform_enroll (a, address(1), 7 ether, 1 ether); - assert_market_is_relayer (a); - assert_market_fee_of (a, 1 ether); - assert_market_balance (a, 7 ether); - assert_market_supply (7 ether); - assert_eth_balance (a, 0 ether); - - perform_leave (a, address(1)); - assert_market_is_not_relayer (a); - assert_market_fee_of (a, 0 ether); - assert_market_balance (a, 0 ether); - assert_market_supply (0 ether); - assert_eth_balance (a, 7 ether); - } - - function test_add_relayer() public { - perform_join (a, 3 ether); - perform_join (b, 4 ether); - perform_join (c, 5 ether); - - perform_enrol (a, address ( 1), 1 ether); - assert_market_is_relayer (a); - assert_market_fee_of (a, 1 ether); - - perform_enrol (b, address ( a), 1 ether); - assert_market_is_relayer (b); - assert_market_fee_of (b, 1 ether); - perform_enrol (c, address ( b), 1.1 ether); - assert_market_is_relayer (c); - assert_market_fee_of (c, 1.1 ether); - } - - function test_remove_relayer() public { - perform_enroll (a, address(1), 1 ether, 1 ether); - perform_enroll (b, address(a), 1 ether, 1 ether); - perform_enroll (c, address(b), 1 ether, 1.1 ether); - - perform_delist (a, address(1)); - assert_market_is_not_relayer (a); - assert_market_fee_of (a, 0 ether); - perform_delist (b, address(1)); - assert_market_is_not_relayer (b); - assert_market_fee_of (b, 0 ether); - perform_delist (c, address(1)); - assert_market_is_not_relayer (c); - assert_market_fee_of (c, 0 ether); - } - - function test_move_relayer() public { - perform_enroll (a, address(1), 1 ether, 1 ether); - perform_enroll (b, address(a), 1 ether, 1 ether); - perform_enroll (c, address(b), 1 ether, 1.1 ether); - - perform_move (a, address(1), address(c), 1.2 ether); - assert_market_is_relayer (a); - assert_market_fee_of (a, 1.2 ether); - } - - function test_market_status() public { - perform_enroll (a, address(1), 1 ether, 1 ether); - perform_enroll (b, address(a), 1 ether, 1 ether); - perform_enroll (c, address(b), 1 ether, 1.1 ether); - - address top = market.getTopRelayer(); - assertEq(top, address(a)); - - ( - uint index, - address[] memory relayers, - uint[] memory fees, - uint[] memory balances, - uint[] memory locks - ) = market.getOrderBook(3, true); - assertEq(index, 3); - assertEq(relayers[0], address(a)); - assertEq(relayers[1], address(b)); - assertEq(relayers[2], address(c)); - assertEq(fees[0], 1 ether); - assertEq(fees[1], 1 ether); - assertEq(fees[2], 1.1 ether); - assertEq(balances[0], 1 ether); - assertEq(balances[1], 1 ether); - assertEq(balances[2], 1 ether); - assertEq(locks[0], 0 ether); - assertEq(locks[1], 0 ether); - assertEq(locks[2], 0 ether); - } - - function test_assign() public { - uint key = 1; - init(key); - ( - uint index, - address[] memory relayers, - uint[] memory fees, - uint[] memory balances, - uint[] memory locks - ) = market.getOrderBook(1, false); - assertEq(index, 1); - assertEq(relayers[0], address(b)); - assertEq(fees[0], market.feeOf(address(b))); - assertEq(balances[0], 1 ether); - assertEq(locks[0], 0 ether); - - assert_market_locked(a, 1 ether); - assert_market_locked(b, 0 ether); - assert_market_locked(c, 0 ether); - - assert_market_order(a, key); - } - - function test_settle_when_a_relay_and_confirm_at_a_slot() public { - hevm.warp(1); - uint key = 1; - init(key); - - assert_market_balance(a, 0 ether); - assert_market_balance(b, 1 ether); - assert_market_balance(c, 1 ether); - assert_market_locked(a, 1 ether); - assert_market_locked(b, 0 ether); - assert_market_locked(c, 0 ether); - assert_market_supply(4 ether); - - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); - assertTrue(market.settle(deliveredRelayers, address(a))); - - assert_market_order_clean(key); - - assert_market_balance(a, 2 ether); - assert_market_balance(b, 1 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_a_relay_and_b_confirm_at_a_slot() public { - hevm.warp(1); - uint key = 1; - init(key); - - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); - assertTrue(market.settle(deliveredRelayers, address(b))); - - assert_market_order_clean(key); - - assert_market_balance(a, 1.92 ether); - assert_market_balance(b, 1.08 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_b_relay_and_c_confirm_at_a_slot() public { - hevm.warp(1); - uint key = 1; - init(key); - - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); - assertTrue(market.settle(deliveredRelayers, address(c))); - - assert_market_order_clean(key); - - assert_market_balance(a, 1.6 ether); - assert_market_balance(b, 1.32 ether); - assert_market_balance(c, 1.08 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_a_relay_and_a_confirm_at_a_slot() public { - hevm.warp(1); - uint key = 1; - init(key); - - hevm.warp(1 + RELAY_TIME); - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); - assertTrue(market.settle(deliveredRelayers, address(a))); - - assert_market_order_clean(key); - - assert_market_balance(a, 2 ether); - assert_market_balance(b, 1 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_b_relay_and_b_confirm_at_a_slot_late() public { - hevm.warp(1); - uint key = 1; - init(key); - - hevm.warp(1 + RELAY_TIME); - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); - assertTrue(market.settle(deliveredRelayers, address(b))); - - assert_market_order_clean(key); - - assert_market_balance(a, 1 ether); - assert_market_balance(b, 2 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_a_relay_and_b_confirm_late_half_slash() public { - hevm.warp(1); - uint key = 1; - init(key); - - hevm.warp(1 + RELAY_TIME + SLASH_TIME / 2); - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); - assertTrue(market.settle(deliveredRelayers, address(b))); - - assert_market_order_clean(key); - - assert_market_balance(a, 1.7 ether); - assert_market_balance(b, 1.3 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_a_relay_and_b_confirm_late_all_slash() public { - hevm.warp(1); - uint key = 1; - init(key); - - hevm.warp(1 + RELAY_TIME + SLASH_TIME); - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); - assertTrue(market.settle(deliveredRelayers, address(b))); - - assert_market_order_clean(key); - - assert_market_balance(a, 1.6 ether); - assert_market_balance(b, 1.4 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - function test_settle_when_b_relay_and_b_confirm_late_all_slash() public { - hevm.warp(1); - uint key = 1; - init(key); - - hevm.warp(1 + RELAY_TIME + SLASH_TIME); - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); - assertTrue(market.settle(deliveredRelayers, address(b))); - - assert_market_order_clean(key); - - assert_market_balance(a, 0 ether); - assert_market_balance(b, 3 ether); - assert_market_balance(c, 1 ether); - assert_market_balances(); - assert_market_supply(4 ether); - } - - //------------------------------------------------------------------ - // Helper functions - //------------------------------------------------------------------ - - function init(uint key) public { - market.setOutbound(self, 1); - perform_enroll (a, address(1), 1 ether, 1 ether); - perform_enroll (b, address(a), 1 ether, 1 ether); - perform_enroll (c, address(b), 1 ether, 1.1 ether); - - perform_assign(key, 1 ether); - } - - function newDeliveredRelayers(Guy relayer, uint key) public pure returns (IFeeMarket.DeliveredRelayer[] memory) { - IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = new IFeeMarket.DeliveredRelayer[](1); - deliveredRelayers[0] = IFeeMarket.DeliveredRelayer(address(relayer), key, key); - return deliveredRelayers; - } - - function assert_eth_balance(Guy guy, uint balance) public { - assertEq(address(guy).balance, balance); - } - - function assert_market_balance(Guy guy, uint balance) public { - assertEq(market.balanceOf(address(guy)), balance); - } - - function assert_market_balances() public { - uint ba = market.balanceOf(address(a)); - uint bb = market.balanceOf(address(b)); - uint bc = market.balanceOf(address(c)); - assertEq(ba + bb + bc, market.totalSupply()); - } - - function assert_market_locked(Guy guy, uint locked) public { - assertEq(market.lockedOf(address(guy)), locked); - } - - function assert_market_order(Guy guy, uint key) public { - (uint32 assignedTime, address assignedRelayer, uint collateral, uint fee) = market.orderOf(key); - assertEq(assignedTime, block.timestamp); - assertEq(collateral, COLLATERAL_PERORDER); - assertEq(assignedRelayer, address(guy)); - assertEq(fee, market.feeOf(assignedRelayer)); - } - - function assert_market_order_clean(uint key) public { - (uint32 assignedTime, address assignedRelayer, uint collateral, uint fee) = market.orderOf(key); - assertEq(uint(assignedTime), 0); - assertEq(assignedRelayer, address(0)); - assertEq(collateral, 0); - assertEq(fee, 0); - - assert_market_locked(a, 0 ether); - assert_market_locked(b, 0 ether); - assert_market_locked(c, 0 ether); - } - - function assert_market_supply(uint supply) public { - assertEq(market.totalSupply(), supply); - } - - function assert_market_is_relayer(Guy guy) public { - assertTrue(market.isRelayer(address(guy))); - } - - function assert_market_is_not_relayer(Guy guy) public { - assertTrue(!market.isRelayer(address(guy))); - } - - function assert_market_fee_of(Guy guy, uint fee) public { - assertEq(market.feeOf(address(guy)), fee); - } - - function perform_join(Guy guy, uint wad) public { - guy.join{value: wad}(); - } - - function perform_exit(Guy guy, uint wad) public { - guy.exit(wad); - } - - function perform_enroll(Guy guy, address prev, uint wad, uint fee) public { - guy.enroll{value: wad}(prev, fee); - } - - function perform_leave(Guy guy, address prev) public { - guy.leave(prev); - } - - function perform_enrol(Guy guy, address prev, uint fee) public { - guy.enrol(prev, fee); - } - - function perform_delist(Guy guy, address prev) public { - guy.delist(prev); - } - - function perform_move(Guy guy, address old_prev, address new_prev, uint new_fee) public { - guy.move(old_prev, new_prev, new_fee); - } - - function perform_assign(uint key, uint wad) public { - market.assign{value: wad}(key); - } + function test_settle_when_b_relay_and_b_confirm_at_a_slot_late() public { + hevm.warp(1); + uint key = 1; + init(key); + + hevm.warp(1 + RELAY_TIME); + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); + assertTrue(market.settle(deliveredRelayers, address(b))); + + assert_market_order_clean(key); + + assert_market_balance(a, 1 ether); + assert_market_balance(b, 2 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_a_relay_and_b_confirm_late_half_slash() public { + hevm.warp(1); + uint key = 1; + init(key); + + hevm.warp(1 + RELAY_TIME + SLASH_TIME / 2); + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); + assertTrue(market.settle(deliveredRelayers, address(b))); + + assert_market_order_clean(key); + + assert_market_balance(a, 1.7 ether); + assert_market_balance(b, 1.3 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_a_relay_and_b_confirm_late_all_slash() public { + hevm.warp(1); + uint key = 1; + init(key); + + hevm.warp(1 + RELAY_TIME + SLASH_TIME); + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(a, key); + assertTrue(market.settle(deliveredRelayers, address(b))); + + assert_market_order_clean(key); + + assert_market_balance(a, 1.6 ether); + assert_market_balance(b, 1.4 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + function test_settle_when_b_relay_and_b_confirm_late_all_slash() public { + hevm.warp(1); + uint key = 1; + init(key); + + hevm.warp(1 + RELAY_TIME + SLASH_TIME); + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = newDeliveredRelayers(b, key); + assertTrue(market.settle(deliveredRelayers, address(b))); + + assert_market_order_clean(key); + + assert_market_balance(a, 0 ether); + assert_market_balance(b, 3 ether); + assert_market_balance(c, 1 ether); + assert_market_balances(); + assert_market_supply(4 ether); + } + + //------------------------------------------------------------------ + // Helper functions + //------------------------------------------------------------------ + + function init(uint key) public { + market.setOutbound(self, 1); + perform_enroll (a, address(1), 1 ether, 1 ether); + perform_enroll (b, address(a), 1 ether, 1 ether); + perform_enroll (c, address(b), 1 ether, 1.1 ether); + + perform_assign(key, 1 ether); + } + + function newDeliveredRelayers(Guy relayer, uint key) public pure returns (IFeeMarket.DeliveredRelayer[] memory) { + IFeeMarket.DeliveredRelayer[] memory deliveredRelayers = new IFeeMarket.DeliveredRelayer[](1); + deliveredRelayers[0] = IFeeMarket.DeliveredRelayer(address(relayer), key, key); + return deliveredRelayers; + } + + function assert_eth_balance(Guy guy, uint balance) public { + assertEq(address(guy).balance, balance); + } + + function assert_market_balance(Guy guy, uint balance) public { + assertEq(market.balanceOf(address(guy)), balance); + } + + function assert_market_balances() public { + uint ba = market.balanceOf(address(a)); + uint bb = market.balanceOf(address(b)); + uint bc = market.balanceOf(address(c)); + assertEq(ba + bb + bc, market.totalSupply()); + } + + function assert_market_locked(Guy guy, uint locked) public { + assertEq(market.lockedOf(address(guy)), locked); + } + + function assert_market_order(Guy guy, uint key) public { + (uint32 assignedTime, address assignedRelayer, uint collateral, uint fee) = market.orderOf(key); + assertEq(assignedTime, block.timestamp); + assertEq(collateral, COLLATERAL_PERORDER); + assertEq(assignedRelayer, address(guy)); + assertEq(fee, market.feeOf(assignedRelayer)); + } + + function assert_market_order_clean(uint key) public { + (uint32 assignedTime, address assignedRelayer, uint collateral, uint fee) = market.orderOf(key); + assertEq(uint(assignedTime), 0); + assertEq(assignedRelayer, address(0)); + assertEq(collateral, 0); + assertEq(fee, 0); + + assert_market_locked(a, 0 ether); + assert_market_locked(b, 0 ether); + assert_market_locked(c, 0 ether); + } + + function assert_market_supply(uint supply) public { + assertEq(market.totalSupply(), supply); + } + + function assert_market_is_relayer(Guy guy) public { + assertTrue(market.isRelayer(address(guy))); + } + + function assert_market_is_not_relayer(Guy guy) public { + assertTrue(!market.isRelayer(address(guy))); + } + + function assert_market_fee_of(Guy guy, uint fee) public { + assertEq(market.feeOf(address(guy)), fee); + } + + function perform_join(Guy guy, uint wad) public { + guy.join{value: wad}(); + } + + function perform_exit(Guy guy, uint wad) public { + guy.exit(wad); + } + + function perform_enroll(Guy guy, address prev, uint wad, uint fee) public { + guy.enroll{value: wad}(prev, fee); + } + + function perform_leave(Guy guy, address prev) public { + guy.leave(prev); + } + + function perform_enrol(Guy guy, address prev, uint fee) public { + guy.enrol(prev, fee); + } + + function perform_delist(Guy guy, address prev) public { + guy.delist(prev); + } + + function perform_move(Guy guy, address old_prev, address new_prev, uint new_fee) public { + guy.move(old_prev, new_prev, new_fee); + } + + function perform_assign(uint key, uint wad) public { + market.assign{value: wad}(key); + } } contract Guy { diff --git a/contracts/bridge/src/test/utils/Math.p.sol b/contracts/bridge/src/test/utils/Math.p.sol new file mode 100644 index 000000000..f54388463 --- /dev/null +++ b/contracts/bridge/src/test/utils/Math.p.sol @@ -0,0 +1,44 @@ +// This file is part of Darwinia. +// Copyright (C) 2018-2022 Darwinia Network +// SPDX-License-Identifier: GPL-3.0 +// +// Darwinia is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Darwinia is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Darwinia. If not, see . + +pragma solidity 0.7.6; + +import "../../utils/Math.sol"; +pragma experimental SMTChecker; + +contract MathTest is Math { + function property_get_power_of_two_ceil(uint x) public pure { + if (x == 0 || x == type(uint).max) return; + uint y = get_power_of_two_ceil(x); + assert(y / 2 < x && x <= y); + } + + function property_log_2(uint x) public pure { + if (x <= 1) return; + uint y = log_2(x); + assert(2**(y-1) < x && x <= 2**y); + } + + function property_max(uint x, uint y) public pure { + uint z = _max(x, y); + if (z == x) { + assert(z >= y); + } { + assert(z >= x); + } + } +} diff --git a/contracts/bridge/src/test/utils/Math.t.sol b/contracts/bridge/src/test/utils/Math.t.sol index 64a471012..4658517b2 100644 --- a/contracts/bridge/src/test/utils/Math.t.sol +++ b/contracts/bridge/src/test/utils/Math.t.sol @@ -59,5 +59,30 @@ contract MathTest is DSTest, Math { assertEq(log_2(256), 8); assertEq(log_2(512), 9); assertEq(log_2(1024), 10); + assertEq(log_2(0xffffffffffffffffffffffffffffffff), 128); + assertEq(log_2(0x8000000000000000000000000000000000000000000000000000000000000000), 255); + } + + function prove_get_power_of_two_ceil(uint x) public { + if (x == 0 || x == type(uint).max) return; + uint y = get_power_of_two_ceil(x); + assertTrue(y / 2 < x && x <= y); + } + + function prove_log_2(uint x) public { + if (x == 0 || x >= 0x8000000000000000000000000000000000000000000000000000000000000000) return; + uint y = log_2(x); + assertTrue(2**(y-1) < x && x <= 2**y); + } + + function prove_max(uint x, uint y) public { + uint z = _max(x, y); + if (z == x) { + assertGe(z, y); + } else if (z == y) { + assertGe(z, x); + } else { + fail(); + } } } diff --git a/contracts/bridge/src/utils/Math.sol b/contracts/bridge/src/utils/Math.sol index 5552ebe4f..40c8ba2d6 100644 --- a/contracts/bridge/src/utils/Math.sol +++ b/contracts/bridge/src/utils/Math.sol @@ -28,6 +28,7 @@ contract Math { } function log_2(uint256 x) internal pure returns (uint256 pow) { + require(0 < x && x < 0x8000000000000000000000000000000000000000000000000000000000000001, "invalid"); uint256 a = 1; while (a < x) { a <<= 1;