diff --git a/pykwasm/src/pykwasm/call.py b/pykwasm/src/pykwasm/call.py index b853cc6ac..c9f5cc357 100644 --- a/pykwasm/src/pykwasm/call.py +++ b/pykwasm/src/pykwasm/call.py @@ -5,7 +5,7 @@ from eth_account import Account from requests.exceptions import ConnectionError from web3 import Web3 -from web3.exceptions import BadFunctionCallOutput, Web3RPCError +from web3.exceptions import BadFunctionCallOutput, ContractLogicError, Web3RPCError from web3.middleware import SignAndSendRawMiddlewareBuilder ABI_MAP = { @@ -98,11 +98,13 @@ def run_method(w3, contract, sender, eth, method, params): else: tx_hash = func.transact({'from': sender.address, 'value': eth}) result_or_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - except (ConnectionError, BadFunctionCallOutput, Web3RPCError) as e: + except (ConnectionError, ContractLogicError, BadFunctionCallOutput, Web3RPCError) as e: if isinstance(e, (ConnectionError, ConnectionRefusedError)): msg = f'Failed to connect to node: {e.message}' elif isinstance(e, BadFunctionCallOutput): msg = f'Could not interpret function output: {",".join(e.args)}' + elif isinstance(e, ContractLogicError): + msg = f'Contract logic error: {e.message}' else: msg = f'Node RPC encountered an error: {e.message}' print(msg, file=sys.stderr) diff --git a/pykwasm/src/pykwasm/kdist/wasm-semantics/ulm-wasm.md b/pykwasm/src/pykwasm/kdist/wasm-semantics/ulm-wasm.md index 9582f9cc6..db01a95ab 100644 --- a/pykwasm/src/pykwasm/kdist/wasm-semantics/ulm-wasm.md +++ b/pykwasm/src/pykwasm/kdist/wasm-semantics/ulm-wasm.md @@ -337,7 +337,7 @@ These rules define various integration points between the ULM and our Wasm inter ... - ) => #if OutVal ==K NO_OUTPUT #then EVMC_INTERNAL_ERROR #else Status #fi + ) => #if Status ==Int EVMC_SUCCESS andBool OutVal ==K NO_OUTPUT #then EVMC_INTERNAL_ERROR #else Status #fi ``` Hooks implementation @@ -629,7 +629,7 @@ Handle the actual hook calls. rule - #fail(ulmBytes(BYTES:Bytes)) => #throwException(EVMC_FAILURE, Bytes2String(BYTES)) + #fail(ulmBytes(BYTES:Bytes)) => #throwException(EVMC_REVERT, Bytes2String(BYTES)) ... diff --git a/tests/ulm/erc20/erc20_negative_test.sh b/tests/ulm/erc20/erc20_negative_test.sh new file mode 100755 index 000000000..9d04ead9b --- /dev/null +++ b/tests/ulm/erc20/erc20_negative_test.sh @@ -0,0 +1,473 @@ +#!/bin/bash + +set -e + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +ROOT_DIR="$SCRIPT_DIR/../../.." + +# change to root directory +cd "$ROOT_DIR" + +# re-execute script with poetry to get poetry script aliases +if [ -z ${IN_POETRY_SHELL:+x} ]; then + export IN_POETRY_SHELL=1 + exec poetry -C pykwasm run bash "$SCRIPT_DIR/$(basename -- $0)" +fi + + +RED='\033[0;31m' +NC='\033[0m' +GREEN="\033[0;32m" +BLUE="\033[0;34m" +WHITE="\033[0;00m" + +function assert_eq { + if [ ! "$1" == "$2" ]; then + >&2 echo -e "${RED}$3${NC}: Expected $1 to be equal to $2" + exit 1 + fi +} + +function to_hex { + printf "0x%x" $1 +} + +function erc20_deploy { + local key=$1 + deploy build/erc20/erc20.bin http://localhost:8545 /dev/stdin <<< $key +} + +function erc20_decimals { + local key=$1 + local contract=$2 + call http://localhost:8545 erc20 $contract /dev/stdin 0 decimals <<< $key +} + +function erc20_totalSupply { + local key=$1 + local contract=$2 + call http://localhost:8545 erc20 $contract /dev/stdin 0 totalSupply <<< $key +} + +function erc20_balanceOf { + local key=$1 + local contract=$2 + local account=$3 + call http://localhost:8545 erc20 $contract /dev/stdin 0 balanceOf $account <<< $key +} + +function erc20_allowance { + local key=$1 + local contract=$2 + local from_account=$3 + local allowed_account=$4 + call http://localhost:8545 erc20 $contract /dev/stdin 0 allowance $from_account $allowed_account <<< $key +} + +function erc20_mint { + local key=$1 + local contract=$2 + local account=$3 + local amount=$4 + call http://localhost:8545 erc20 $contract /dev/stdin 0 mint $account $amount <<< $key +} + +function erc20_transfer { + local key=$1 + local contract=$2 + local account=$3 + local amount=$4 + call http://localhost:8545 erc20 $contract /dev/stdin 0 transfer $account $amount <<< $key +} + +function erc20_approve { + local key=$1 + local contract=$2 + local account=$3 + local amount=$4 + call http://localhost:8545 erc20 $contract /dev/stdin 0 approve $account $amount <<< $key +} + +function erc20_transfer_from { + local key=$1 + local contract=$2 + local from_account=$3 + local to_account=$4 + local amount=$5 + call http://localhost:8545 erc20 $contract /dev/stdin 0 transferFrom $from_account $to_account $amount <<< $key +} + +function test_transfer_too_much { + echo -n "Transfer test " + + # generate some accounts + account1=($(mkacct)) + a1=${account1[0]} + k1=${account1[1]} + echo -n "." + + account2=($(mkacct)) + a2=${account2[0]} + k2=${account2[1]} + echo -n "." + + account3=($(mkacct)) + a3=${account3[0]} + k3=${account3[1]} + echo -n "." + + # fund accounts + fund /dev/stdin <<< $k1 + echo -n "." + fund /dev/stdin <<< $k2 + echo -n "." + fund /dev/stdin <<< $k3 + echo -n "." + + # deploy contract + contract=$(erc20_deploy $k1) + echo -n "." + + balance2=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "0" "$balance2" "Balance of Account 2" + + balance3=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance3" "Balance of Account 3" + + # check total supply + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "0" "$supply" "Total Supply" + + erc20_mint $k1 $contract $a2 $(to_hex 1000) + echo -n "." + + balance2=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "1000" "$balance2" "Balance of Account 2" + + balance3=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance3" "Balance of Account 3" + + # check total supply + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + erc20_transfer $k2 $contract $a3 $(to_hex 600) + echo -n "." + + balance2=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "400" "$balance2" "Balance of Account 2" + + balance3=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "600" "$balance3" "Balance of Account 3" + + # check total supply + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + echo -e -n "\nTransfer too much test (should fail): " + (! erc20_transfer $k2 $contract $a3 $(to_hex 600)) # this should fail + + echo -n "Transfer test (continue): " + balance2=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "400" "$balance2" "Balance of Account 2" + + balance3=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "600" "$balance3" "Balance of Account 3" + + # check total supply + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + + echo -e " ${GREEN}passed${NC}" +} + +function test_transfer_too_much_from { + echo -n "Transfer from test " + + # generate some accounts + account1=($(mkacct)) + a1=${account1[0]} + k1=${account1[1]} + echo -n "." + + account2=($(mkacct)) + a2=${account2[0]} + k2=${account2[1]} + echo -n "." + + account3=($(mkacct)) + a3=${account3[0]} + k3=${account3[1]} + echo -n "." + + account4=($(mkacct)) + a4=${account4[0]} + k4=${account4[1]} + echo -n "." + + # fund accounts + fund /dev/stdin <<< $k1 + echo -n "." + fund /dev/stdin <<< $k2 + echo -n "." + fund /dev/stdin <<< $k3 + echo -n "." + fund /dev/stdin <<< $k4 + echo -n "." + + # deploy contract + contract=$(erc20_deploy $k1) + echo -n "." + + # ***** Check initial state + + balance=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 2" + + balance=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 3" + + balance=$(erc20_balanceOf $k1 $contract $a4) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 4" + + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "0" "$supply" "Total Supply" + + allowance=$(erc20_allowance $k1 $contract $a2 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 3" + + allowance=$(erc20_allowance $k1 $contract $a2 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a3 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a3 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a4 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a4 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 3" + + # ***** Mint tokens + + erc20_mint $k1 $contract $a2 $(to_hex 1000) + echo -n "." + + # ***** Check state after mint + + balance=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "1000" "$balance" "Balance of Account 2" + + balance=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 3" + + balance=$(erc20_balanceOf $k1 $contract $a4) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 4" + + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + allowance=$(erc20_allowance $k1 $contract $a2 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 3" + + allowance=$(erc20_allowance $k1 $contract $a2 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a3 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a3 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a4 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a4 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 3" + + # ***** Account 2 approves Account 3 to transfer 300 tokens + + erc20_approve $k2 $contract $a3 $(to_hex 300) + echo -n "." + + # ***** Check state after approve + + balance=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "1000" "$balance" "Balance of Account 2" + + balance=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 3" + + balance=$(erc20_balanceOf $k1 $contract $a4) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 4" + + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + allowance=$(erc20_allowance $k1 $contract $a2 $a3) + echo -n "." + assert_eq "300" "$allowance" "Allowance from Account 2 to Account 3" + + allowance=$(erc20_allowance $k1 $contract $a2 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a3 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a3 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a4 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a4 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 3" + + # ***** Account 3 transfers 200 tokens from Account 2 to Account 4 + + erc20_transfer_from $k3 $contract $a2 $a4 $(to_hex 200) + echo -n "." + + # ***** Check state after transfer + + balance=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "800" "$balance" "Balance of Account 2" + + balance=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 3" + + balance=$(erc20_balanceOf $k1 $contract $a4) + echo -n "." + assert_eq "200" "$balance" "Balance of Account 4" + + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + allowance=$(erc20_allowance $k1 $contract $a2 $a3) + echo -n "." + assert_eq "100" "$allowance" "Allowance from Account 2 to Account 3" + + allowance=$(erc20_allowance $k1 $contract $a2 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a3 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a3 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a4 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a4 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 3" + + # ***** Account 3 transfers 100 tokens from Account 2 to Account 3 + + echo -e -n "\nTransfer more than allowance test (should fail): " + (! erc20_transfer_from $k3 $contract $a2 $a3 $(to_hex 200)) # this should fail + + # ***** Check state after transfer + + echo -n "Transfer test (continue): " + balance=$(erc20_balanceOf $k1 $contract $a2) + echo -n "." + assert_eq "800" "$balance" "Balance of Account 2" + + balance=$(erc20_balanceOf $k1 $contract $a3) + echo -n "." + assert_eq "0" "$balance" "Balance of Account 3" + + balance=$(erc20_balanceOf $k1 $contract $a4) + echo -n "." + assert_eq "200" "$balance" "Balance of Account 4" + + supply=$(erc20_totalSupply $k1 $contract) + echo -n "." + assert_eq "1000" "$supply" "Total Supply" + + allowance=$(erc20_allowance $k1 $contract $a2 $a3) + echo -n "." + assert_eq "100" "$allowance" "Allowance from Account 2 to Account 3" + + allowance=$(erc20_allowance $k1 $contract $a2 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 2 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a3 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a3 $a4) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 3 to Account 4" + + allowance=$(erc20_allowance $k1 $contract $a4 $a2) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 2" + + allowance=$(erc20_allowance $k1 $contract $a4 $a3) + echo -n "." + assert_eq "0" "$allowance" "Allowance from Account 4 to Account 3" + + echo -e " ${GREEN}passed${NC}" +} + + +test_transfer_too_much +test_transfer_too_much_from + +echo -e "${GREEN}All tests passed${NC}"