diff --git a/src/dapp-tests/.gitignore b/src/dapp-tests/.gitignore new file mode 100644 index 000000000..ba6d5135e --- /dev/null +++ b/src/dapp-tests/.gitignore @@ -0,0 +1,4 @@ +gethout +hevmout +__pycache__/ +out diff --git a/src/dapp-tests/Makefile b/src/dapp-tests/Makefile index edd3c150e..ad11312dc 100644 --- a/src/dapp-tests/Makefile +++ b/src/dapp-tests/Makefile @@ -1,5 +1,5 @@ test: pytest --hypothesis-show-statistics integration/diff-fuzz.py - integration/tests.sh + bash_unit integration/tests.sh .PHONY: test diff --git a/src/dapp-tests/AB.sol b/src/dapp-tests/integration/contracts/AB.sol similarity index 100% rename from src/dapp-tests/AB.sol rename to src/dapp-tests/integration/contracts/AB.sol diff --git a/src/dapp-tests/dstoken.bin-runtime b/src/dapp-tests/integration/contracts/dstoken.bin-runtime similarity index 100% rename from src/dapp-tests/dstoken.bin-runtime rename to src/dapp-tests/integration/contracts/dstoken.bin-runtime diff --git a/src/dapp-tests/factor.sol b/src/dapp-tests/integration/contracts/factor.sol similarity index 100% rename from src/dapp-tests/factor.sol rename to src/dapp-tests/integration/contracts/factor.sol diff --git a/src/dapp-tests/stateful.sol b/src/dapp-tests/integration/contracts/stateful.sol similarity index 100% rename from src/dapp-tests/stateful.sol rename to src/dapp-tests/integration/contracts/stateful.sol diff --git a/src/dapp-tests/token.sol b/src/dapp-tests/integration/contracts/token.sol similarity index 100% rename from src/dapp-tests/token.sol rename to src/dapp-tests/integration/contracts/token.sol diff --git a/src/dapp-tests/integration/tests.sh b/src/dapp-tests/integration/tests.sh index a729ea5e5..65441d63f 100755 --- a/src/dapp-tests/integration/tests.sh +++ b/src/dapp-tests/integration/tests.sh @@ -1,133 +1,218 @@ -#!/usr/bin/env bash +#! /usr/bin/env bash -set -ex +set -euo pipefail -# clean up -trap 'killall geth && rm -rf "$TMPDIR"' EXIT -trap "exit 1" SIGINT SIGTERM +# ------------------------------------------------ +# CONFIGURATION +# ------------------------------------------------ -error() { - printf 1>&2 "fail: function '%s' at line %d.\n" "${FUNCNAME[1]}" "${BASH_LINENO[0]}" - printf 1>&2 "got: %s" "$output" - exit 1 +FUZZ_RUNS=100 +TESTNET_SLEEP=5 + +# ------------------------------------------------ +# SHARED SETUP +# ------------------------------------------------ + +# we spin up a new testnet instance and share it between all tests +setup_suite() { + if [[ -z "$SKIP_SETUP" ]]; then + TMPDIR=$(mktemp -d) + + dapp testnet --dir "$TMPDIR" & + # give it a few secs to start up + sleep "$TESTNET_SLEEP" + + export ETH_RPC_URL="http://127.0.0.1:8545" + export ETH_KEYSTORE="$TMPDIR/8545/keystore" + export ETH_PASSWORD=/dev/null + read -r ROOT _ <<< "$(seth ls --keystore "$TMPDIR/8545/keystore")" + fi } -# tests some of the behaviour of -# `dapp testnet` -# `seth ls` -# `seth send` -# `seth run-tx` -# `hevm exec` -dapp_testnet() { - TMPDIR=$(mktemp -d) +# cleanup the testnet +teardown_suite() { + if [[ -z "$SKIP_SETUP" ]]; then + killall geth + rm -rf "$TMPDIR" + fi +} + +# ------------------------------------------------ +# TEST HELPERS +# ------------------------------------------------ + +# Tests for resolve-name and lookup-address use a Rinkeby name that's been registered for 100 years and will not be changed +# Infura ID source: https://github.com/ethers-io/ethers.js/blob/0d40156fcba5be155aa5def71bcdb95b9c11d889/packages/providers/src.ts/infura-provider.ts#L17 +RINKEBY_RPC_URL=https://rinkeby.infura.io/v3/84842078b09946638c03157f83405213 + +# generates a new account and gives it some eth, returns the address +fresh_account() { + wei_amount=${1:-$(seth --to-wei 42069 ether)} + output=$(geth account new --password /dev/null --keystore "$ETH_KEYSTORE") + account=$(echo "$output" | grep "Public address of the key" | awk 'NF>1{print $NF}') + seth send -F "$ROOT" -V "$wei_amount" "$account" 1>&2 + echo "$account" +} - dapp testnet --dir "$TMPDIR" & - # give it a few secs to start up - sleep 180 - read -r ACC BAL <<< "$(seth ls --keystore "$TMPDIR/8545/keystore")" - # The account has maximum balance - [[ $(seth --to-hex "$BAL") = $(seth --to-int256 -1) ]] || error +# ensure that fresh_account does what it should +test_funding() { + # shellcheck disable=SC2119 + acc=$(fresh_account) + assert_equals "$(seth --to-wei 42069 ether)" "$(seth balance "$acc")" - # Deploy a simple contract: - solc --bin --bin-runtime stateful.sol -o "$TMPDIR" + acc=$(fresh_account "$(seth --to-wei 100 ether)") + assert_equals "$(seth --to-wei 100 ether)" "$(seth balance "$acc")" +} - A_ADDR=$(seth send --create "$(<"$TMPDIR"/A.bin)" "constructor(uint y)" 1 --from "$ACC" --keystore "$TMPDIR"/8545/keystore --password /dev/null --gas 0xffffff) +# a few useful addresses +VITALIK=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 +ZERO=0x0000000000000000000000000000000000000000 - # Compare deployed code with what solc gives us - [[ $(seth code "$A_ADDR") = 0x"$(cat "$TMPDIR"/A.bin-runtime)" ]] || error +# location of the test contracts +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +CONTRACTS="$SCRIPT_DIR/contracts" - # And with what hevm gives us - EXTRA_CALLDATA=$(seth --to-uint256 1) - HEVM_RET=$(hevm exec --code "$(<"$TMPDIR"/A.bin)""${EXTRA_CALLDATA/0x/}" --gas 0xffffff) +# ------------------------------------------------ +# GENERATORS +# ------------------------------------------------ - [[ $(seth code "$A_ADDR") = "$HEVM_RET" ]] || error +mod() { + bc <<< "$1%$2" +} - TX=$(seth send "$A_ADDR" "off()" --gas 0xffff --password /dev/null --from "$ACC" --keystore "$TMPDIR"/8545/keystore --async) +hex2dec() { + echo "ibase=16;${1^^}" | bc | tr -d '\\\n' +} - # since we have one tx per block, seth run-tx and seth debug are equivalent - [[ $(seth run-tx "$TX") = 0x ]] || error +byte() { + hexdump -n 1 -e '1/1 "%08X" 1 "\n"' /dev/urandom +} - # dynamic fee transaction (EIP-1559) - seth send "$A_ADDR" "on()" --gas 0xffff --password /dev/null --from "$ACC" --keystore "$TMPDIR"/8545/keystore --prio-fee 2gwei --gas-price 10gwei +bytes32() { + hexdump -n 32 -e '4/8 "%08X" 1 "\n"' /dev/urandom +} - B_ADDR=$(seth send --create 0x647175696e6550383480393834f3 --gas 0xffff --password /dev/null --from "$ACC" --keystore "$TMPDIR"/8545/keystore --prio-fee 2gwei --gas-price 10gwei) +uint8() { + hex2dec "$(byte)" +} - [[ $(seth code "$B_ADDR") = 0x647175696e6550383480393834f3 ]] || error +uint256() { + hex2dec "$(bytes32)" +} - # clean up - killall geth +alpha() { + local size + size=$1 + tr -dc '[:alpha:]' < /dev/urandom | fold -w "${1:-$size}" | head -n 1 } -dapp_testnet +# ------------------------------------------------ +# TESTS +# ------------------------------------------------ -# checks that seth send works with both checksummed and unchecksummed addresses -seth_send_address_formats() { - TMPDIR=$(mktemp -d) +# tests some of the behaviour of +# `seth send` +# `seth run-tx` +# `hevm exec` +test_smoke() { + local account + account=$(fresh_account) + + # Deploy a simple contract: + solc --bin --bin-runtime "$CONTRACTS/stateful.sol" -o "$TMPDIR" + + A_ADDR=$(seth send --create "$(<"$TMPDIR"/A.bin)" "constructor(uint y)" 1 --from "$account" --keystore "$TMPDIR"/8545/keystore --password /dev/null --gas 0xffffff) + + # Compare deployed code with what solc gives us + assert_equals 0x"$(cat "$TMPDIR"/A.bin-runtime)" "$(seth code "$A_ADDR")" - dapp testnet --dir "$TMPDIR" & - # give it a few secs to start up - sleep 180 - read -r ACC BAL <<< "$(seth ls --keystore "$TMPDIR/8545/keystore")" + # And with what hevm gives us + EXTRA_CALLDATA=$(seth --to-uint256 1) + HEVM_RET=$(hevm exec --code "$(<"$TMPDIR"/A.bin)""${EXTRA_CALLDATA/0x/}" --gas 0xffffff) - lower=$(echo "$ACC" | tr '[:upper:]' '[:lower:]') - export ETH_GAS=0xffff + assert_equals "$HEVM_RET" "$(seth code "$A_ADDR")" - zero=0x0000000000000000000000000000000000000000 + TX=$(seth send "$A_ADDR" "off()" --gas 0xffff --password /dev/null --from "$account" --keystore "$TMPDIR"/8545/keystore --async) - # with checksummed - tx=$(seth send "$zero" --from "$ACC" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) - [[ $(seth tx "$tx" from) = "$lower" ]] + # since we have one tx per block, seth run-tx and seth debug are equivalent + assert_equals 0x "$(seth run-tx "$TX")" - # without checksum - tx=$(seth send "$zero" --from "$lower" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) - [[ $(seth tx "$tx" from) = "$lower" ]] + # dynamic fee transactions (EIP-1559) + seth send "$A_ADDR" "on()" \ + --gas 0xffff \ + --password /dev/null \ + --from "$account" \ + --keystore "$TMPDIR"/8545/keystore \ + --prio-fee 2gwei \ + --gas-price 10gwei - # try again with eth_rpc_accounts - export ETH_RPC_ACCOUNTS=true + B_ADDR=$(seth send \ + --create 0x647175696e6550383480393834f3 \ + --gas 0xffff \ + --password /dev/null \ + --from "$ACC" \ + --keystore "$TMPDIR"/8545/keystore \ + --prio-fee 2gwei \ + --gas-price 10gwei) - # with checksummed - tx=$(seth send "$zero" --from "$ACC" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) - [[ $(seth tx "$tx" from) = "$lower" ]] + assert_equals 0x647175696e6550383480393834f3 "$(seth code "$B_ADDR")" +} + +# checks that seth send works with both checksummed and unchecksummed addresses +test_seth_send_address_formats() { + local account + account=$(fresh_account) + + lower=$(echo "$account" | tr '[:upper:]' '[:lower:]') + export ETH_GAS=0xffff - # without checksum - tx=$(seth send "$zero" --from "$lower" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) - [[ $(seth tx "$tx" from) = "$lower" ]] + # with checksummed + tx=$(seth send "$ZERO" --from "$ACC" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) + assert_equals "$lower" "$(seth tx "$tx" from)" - # clean up - killall geth + # without checksum + tx=$(seth send "$ZERO" --from "$lower" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) + assert_equals "$lower" "$(seth tx "$tx" from)" + + # try again with eth_rpc_accounts + export ETH_RPC_ACCOUNTS=true + + # with checksummed + tx=$(seth send "$ZERO" --from "$ACC" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) + assert_equals "$lower" "$(seth tx "$tx" from)" + + # without checksum + tx=$(seth send "$ZERO" --from "$lower" --password /dev/null --value "$(seth --to-wei 1 ether)" --keystore "$TMPDIR"/8545/keystore --async) + assert_equals "$lower" "$(seth tx "$tx" from)" } -seth_send_address_formats test_hevm_symbolic() { - solc --bin-runtime -o . --overwrite factor.sol + cd "$(mktemp -d)" || exit + + solc --bin-runtime -o . --overwrite "$CONTRACTS/factor.sol" # should find counterexample hevm symbolic --code "$(