diff --git a/CHANGELOG.md b/CHANGELOG.md index cfabc00a82..926de01670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,17 +38,38 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### State Machine Breaking + +* (deps) [#1159](https://github.com/evmos/ethermint/pull/1159) Bump Geth version to `v1.10.19`. +* (ante) [#1176](https://github.com/evmos/ethermint/pull/1176) Fix invalid tx hashes; Remove `Size_` field and validate `Hash`/`From` fields in ante handler, + recompute eth tx hashes in JSON-RPC APIs to fix old blocks. +* (ante) [#1173](https://github.com/evmos/ethermint/pull/1173) Make `NewAnteHandler` return error if input is invalid + +### API Breaking + +* (rpc) [#1121](https://github.com/tharsis/ethermint/pull/1121) Implement Ethereum tx indexer + +### Bug Fixes + +* (rpc) [#1179](https://github.com/evmos/ethermint/pull/1179) Fix gas used in `debug_traceTransaction` response. + ### Improvements -* (cli) [#1230](https://github.com/evmos/ethermint/pull/1230) Remove redundant positional height parameter from feemarket's query cli. + +* (test) [#1196](https://github.com/evmos/ethermint/pull/1196) Integration tests setup +* (test) [#1199](https://github.com/evmos/ethermint/pull/1199) Add backend test suite with mock gRPC query client +* (test) [#1189](https://github.com/evmos/ethermint/pull/1189) JSON-RPC unit tests +* (test) [#1212](https://github.com/evmos/ethermint/pull/1212) Prune node integration tests +* (test) [#1207](https://github.com/evmos/ethermint/pull/1207) JSON-RPC types integration tests +* (test) [#1218](https://github.com/evmos/ethermint/pull/1218) Restructure JSON-RPC API * (rpc) [#1229](https://github.com/evmos/ethermint/pull/1229) Add support for configuring RPC `MaxOpenConnections` +* (cli) [#1230](https://github.com/evmos/ethermint/pull/1230) Remove redundant positional height parameter from feemarket's query cli. +* (test)[#1233](https://github.com/evmos/ethermint/pull/1233) Add filters integration tests ## [v0.18.0] - 2022-08-04 ### State Machine Breaking * (evm) [\#1174](https://github.com/evmos/ethermint/pull/1174) Don't allow eth txs with 0 in mempool. -* (ante) [#1176](https://github.com/evmos/ethermint/pull/1176) Fix invalid tx hashes; Remove `Size_` field and validate `Hash`/`From` fields in ante handler, - recompute eth tx hashes in JSON-RPC APIs to fix old blocks. ### Improvements @@ -63,17 +84,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [v0.17.1] - 2022-07-13 -### State Machine Breaking -* (deps) [\#1159](https://github.com/evmos/ethermint/pull/1159) Bump Geth version to `v1.10.19`. - ### Improvements * (rpc) [\#1169](https://github.com/evmos/ethermint/pull/1169) Remove unnecessary queries from `getBlockNumber` function -### Bug Fixes - -* (rpc) [#1179](https://github.com/evmos/ethermint/pull/1179) Fix gas used in traceTransaction response. - ## [v0.17.0] - 2022-06-27 ### State Machine Breaking @@ -87,10 +101,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (rpc) [\#1143](https://github.com/evmos/ethermint/pull/1143) Restrict unprotected txs on the node JSON-RPC configuration. * (all) [\#1137](https://github.com/evmos/ethermint/pull/1137) Rename go module to `evmos/ethermint` -### API Breaking - -- (json-rpc) [tharsis#1121](https://github.com/tharsis/ethermint/pull/1121) Store eth tx index separately - ### Improvements * (deps) [\#1147](https://github.com/evmos/ethermint/pull/1147) Bump Go version to `1.18`. diff --git a/gomod2nix.toml b/gomod2nix.toml index fe5f2a7a24..48324e3af5 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -406,8 +406,8 @@ schema = 3 version = "v1.3.6" hash = "sha256-DenVAmyN22xUiivk6fdJp4C9ZnUJXCMDUf8E0goRRV4=" [mod."golang.org/x/crypto"] - version = "v0.0.0-20220622213112-05595931fe9d" - hash = "sha256-2c4wvwiQ0DJSJsApfmNoxCxx1siRCiJvW9hhrGlcvds=" + version = "v0.0.0-20220722155217-630584e8d5aa" + hash = "sha256-gLzdWnyJtT1z2lw/GGmJEFBZ7AS1qVAo8M9Fqi0xFuE=" [mod."golang.org/x/net"] version = "v0.0.0-20220520000938-2e3eb7b945c2" hash = "sha256-uCMgd3uUlLREUdD9CYRbYlbJgRQyOts0jC1Okjk8Al8=" @@ -424,15 +424,15 @@ schema = 3 version = "v0.3.7" hash = "sha256-XH2pUzzQx95O0rak00grQvfACfL+EmZiV7ZzJBkX+XY=" [mod."google.golang.org/genproto"] - version = "v0.0.0-20220628213854-d9e0b6570c03" - hash = "sha256-sM4ik5KAdxxNYqhinnE/baQzjKgzrZ8fJURhZarcDO8=" + version = "v0.0.0-20220810155839-1856144b1d9c" + hash = "sha256-veGcsuGWHmWHLnnq+z1SJrY+//j2kxzVwVpdXeFpSZ0=" [mod."google.golang.org/grpc"] version = "v1.33.2" hash = "sha256-0vOeuHDvRviofJmr4dOzUh4Y1y2X6ujGCcLcICVnQ0Q=" replaced = "google.golang.org/grpc" [mod."google.golang.org/protobuf"] - version = "v1.28.0" - hash = "sha256-p1cVM3OeEErh1WeNRAg4n3zYm/0qPTPmIiWNjRmJiMQ=" + version = "v1.28.1" + hash = "sha256-sTJYgvlv5is7vHNxcuigF2lNASp0QonhUgnrguhfHSU=" [mod."gopkg.in/ini.v1"] version = "v1.66.4" hash = "sha256-PZ5vwf47Pjv7lzaUlm/mIdkIfnwEpmSNYG1UIfGh3M4=" diff --git a/indexer/kv_indexer_test.go b/indexer/kv_indexer_test.go index e183e3db92..22a633fddf 100644 --- a/indexer/kv_indexer_test.go +++ b/indexer/kv_indexer_test.go @@ -37,7 +37,7 @@ func TestKVIndexer(t *testing.T) { txHash := tx.AsTransaction().Hash() encodingConfig := MakeEncodingConfig() - clientCtx := client.Context{}.WithTxConfig(encodingConfig.TxConfig).WithCodec(encodingConfig.Codec) + clientCtx := client.Context{}.WithTxConfig(encodingConfig.TxConfig).WithCodec(encodingConfig.Marshaler) // build cosmos-sdk wrapper tx tmTx, err := tx.BuildTx(clientCtx.TxConfig.NewTxBuilder(), "aphoton") @@ -62,7 +62,7 @@ func TestKVIndexer(t *testing.T) { "success, format 1", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 0, Events: []abci.Event{ {Type: types.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ @@ -82,7 +82,7 @@ func TestKVIndexer(t *testing.T) { "success, format 2", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 0, Events: []abci.Event{ {Type: types.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ @@ -104,7 +104,7 @@ func TestKVIndexer(t *testing.T) { "success, exceed block gas limit", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 11, Log: "out of gas in location: block gas meter; gasWanted: 21000", Events: []abci.Event{}, @@ -116,7 +116,7 @@ func TestKVIndexer(t *testing.T) { "fail, failed eth tx", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 15, Log: "nonce mismatch", Events: []abci.Event{}, @@ -128,7 +128,7 @@ func TestKVIndexer(t *testing.T) { "fail, invalid events", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 0, Events: []abci.Event{}, }, @@ -139,7 +139,7 @@ func TestKVIndexer(t *testing.T) { "fail, not eth tx", &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{txBz2}}}, []*abci.ResponseDeliverTx{ - &abci.ResponseDeliverTx{ + { Code: 0, Events: []abci.Event{}, }, diff --git a/rpc/backend/account_info.go b/rpc/backend/account_info.go index b9a8916a00..3b3103ce40 100644 --- a/rpc/backend/account_info.go +++ b/rpc/backend/account_info.go @@ -44,9 +44,8 @@ func (b *Backend) GetProof(address common.Address, storageKeys []string, blockNr height := blockNum.Int64() _, err = b.GetTendermintBlockByNumber(blockNum) if err != nil { - // Get 'latest' proof if query is in the future - // this imitates geth behavior - height = 0 + // the error message imitates geth behavior + return nil, errors.New("header not found") } ctx := rpctypes.ContextWithHeight(height) diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index a2c0c51967..dfa61a84d3 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -49,6 +49,7 @@ type EVMBackend interface { Accounts() ([]common.Address, error) Syncing() (interface{}, error) SetEtherbase(etherbase common.Address) bool + SetGasPrice(gasPrice hexutil.Big) bool ImportRawKey(privkey, password string) (common.Address, error) ListAccounts() ([]common.Address, error) NewMnemonic(uid string, language keyring.Language, hdPath, bip39Passphrase string, algo keyring.SignatureAlgo) (keyring.Info, error) diff --git a/rpc/backend/node_info.go b/rpc/backend/node_info.go index a120ee224a..a9dae0fa14 100644 --- a/rpc/backend/node_info.go +++ b/rpc/backend/node_info.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" sdkcrypto "github.com/cosmos/cosmos-sdk/crypto" "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdkconfig "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -18,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/evmos/ethermint/crypto/ethsecp256k1" rpctypes "github.com/evmos/ethermint/rpc/types" + "github.com/evmos/ethermint/server/config" ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" tmtypes "github.com/tendermint/tendermint/types" @@ -230,6 +232,35 @@ func (b *Backend) NewMnemonic(uid string, language keyring.Language, hdPath, bip return info, err } +// SetGasPrice sets the minimum accepted gas price for the miner. +// NOTE: this function accepts only integers to have the same interface than go-eth +// to use float values, the gas prices must be configured using the configuration file +func (b *Backend) SetGasPrice(gasPrice hexutil.Big) bool { + appConf := config.GetConfig(b.clientCtx.Viper) + + var unit string + minGasPrices := appConf.GetMinGasPrices() + + // fetch the base denom from the sdk Config in case it's not currently defined on the node config + if len(minGasPrices) == 0 || minGasPrices.Empty() { + var err error + unit, err = sdk.GetBaseDenom() + if err != nil { + b.logger.Debug("could not get the denom of smallest unit registered", "error", err.Error()) + return false + } + } else { + unit = minGasPrices[0].Denom + } + + c := sdk.NewDecCoin(unit, sdk.NewIntFromBigInt(gasPrice.ToInt())) + + appConf.SetMinGasPrices(sdk.DecCoins{c}) + sdkconfig.WriteConfigFile(b.clientCtx.Viper.ConfigFileUsed(), appConf) + b.logger.Info("Your configuration file was modified. Please RESTART your node.", "gas-price", c.String()) + return true +} + // UnprotectedAllowed returns the node configuration value for allowing // unprotected transactions (i.e not replay-protected) func (b Backend) UnprotectedAllowed() bool { diff --git a/rpc/namespaces/ethereum/eth/filters/api.go b/rpc/namespaces/ethereum/eth/filters/api.go index 80705e0b2e..11d97be7c5 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -25,13 +25,13 @@ import ( // FilterAPI gathers type FilterAPI interface { - GetLogs(ctx context.Context, crit filters.FilterCriteria) ([]*ethtypes.Log, error) - GetFilterChanges(id rpc.ID) (interface{}, error) - GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ethtypes.Log, error) + NewPendingTransactionFilter() rpc.ID NewBlockFilter() rpc.ID NewFilter(criteria filters.FilterCriteria) (rpc.ID, error) - NewPendingTransactionFilter() rpc.ID + GetFilterChanges(id rpc.ID) (interface{}, error) + GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ethtypes.Log, error) UninstallFilter(id rpc.ID) bool + GetLogs(ctx context.Context, crit filters.FilterCriteria) ([]*ethtypes.Log, error) } // Backend defines the methods requided by the PublicFilterAPI backend diff --git a/rpc/namespaces/ethereum/miner/api.go b/rpc/namespaces/ethereum/miner/api.go index 98313b3be7..23f688605e 100644 --- a/rpc/namespaces/ethereum/miner/api.go +++ b/rpc/namespaces/ethereum/miner/api.go @@ -4,6 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/tendermint/tendermint/libs/log" @@ -34,3 +35,9 @@ func (api *API) SetEtherbase(etherbase common.Address) bool { api.logger.Debug("miner_setEtherbase") return api.backend.SetEtherbase(etherbase) } + +// SetGasPrice sets the minimum accepted gas price for the miner. +func (api *API) SetGasPrice(gasPrice hexutil.Big) bool { + api.logger.Info(api.ctx.Viper.ConfigFileUsed()) + return api.backend.SetGasPrice(gasPrice) +} diff --git a/tests/integration_tests/README.md b/tests/integration_tests/README.md new file mode 100644 index 0000000000..d321b7fb72 --- /dev/null +++ b/tests/integration_tests/README.md @@ -0,0 +1,55 @@ +# RPC Integration tests + +The RPC integration test suite uses nix for reproducible and configurable +builds allowing to run integration tests using python web3 library against +different Ethermint and [Geth](https://github.com/ethereum/go-ethereum) clients with multiple configurations. + +## Installation + +Nix Multi-user installation: + +``` +sh <(curl -L https://nixos.org/nix/install) --daemon +``` + +Make sure the following line has been added to your shell profile (e.g. ~/.profile): + +``` +source ~/.nix-profile/etc/profile.d/nix.sh +``` + +Then re-login shell, the nix installation is completed. + +For linux: + +``` +sh <(curl -L https://nixos.org/nix/install) --no-daemon +``` + +## Run Local + +First time run (can take a while): + +``` +make run-integration-tests +``` + +Once you've run them once and, you can run: + +``` +nix-shell tests/integration_tests/shell.nix +cd tests/integration_tests +pytest -s -vv +``` + +If you're changing anything on the ethermint rpc, rerun the first command. + + +## Caching + +You can enable Binary Cache to speed up the tests: + +``` +$ nix-env -iA cachix -f https://cachix.org/api/v1/install +$ cachix use ethermint +``` \ No newline at end of file diff --git a/tests/integration_tests/contracts/contracts/Greeter.sol b/tests/integration_tests/contracts/contracts/Greeter.sol new file mode 100644 index 0000000000..2f5dbda54f --- /dev/null +++ b/tests/integration_tests/contracts/contracts/Greeter.sol @@ -0,0 +1,20 @@ +pragma solidity >0.5.0; + +contract Greeter { + string public greeting; + + event ChangeGreeting(address from, string value); + + constructor() public { + greeting = "Hello"; + } + + function setGreeting(string memory _greeting) public { + greeting = _greeting; + emit ChangeGreeting(msg.sender, _greeting); + } + + function greet() public view returns (string memory) { + return greeting; + } +} diff --git a/tests/integration_tests/network.py b/tests/integration_tests/network.py index e2a816ae02..a843a340b7 100644 --- a/tests/integration_tests/network.py +++ b/tests/integration_tests/network.py @@ -63,6 +63,7 @@ def setup_ethermint(path, base_port): cfg = Path(__file__).parent / "configs/default.jsonnet" yield from setup_custom_ethermint(path, base_port, cfg) + def setup_geth(path, base_port): with (path / "geth.log").open("w") as logfile: cmd = [ diff --git a/tests/integration_tests/test_filters.py b/tests/integration_tests/test_filters.py new file mode 100644 index 0000000000..1f871eeebd --- /dev/null +++ b/tests/integration_tests/test_filters.py @@ -0,0 +1,118 @@ +import pytest +from web3 import Web3 + +from .utils import ( + ADDRS, + CONTRACTS, + deploy_contract, + send_successful_transaction, + send_transaction, +) + + +def test_pending_transaction_filter(cluster): + w3: Web3 = cluster.w3 + flt = w3.eth.filter("pending") + + # without tx + assert flt.get_new_entries() == [] # GetFilterChanges + + # with tx + txhash = send_successful_transaction(w3) + assert txhash in flt.get_new_entries() + + # without new txs since last call + assert flt.get_new_entries() == [] + + +def test_block_filter(cluster): + w3: Web3 = cluster.w3 + flt = w3.eth.filter("latest") + + # without tx + assert flt.get_new_entries() == [] + + # with tx + send_successful_transaction(w3) + blocks = flt.get_new_entries() + assert len(blocks) >= 1 + + # without new txs since last call + assert flt.get_new_entries() == [] + + +def test_event_log_filter_by_contract(cluster): + w3: Web3 = cluster.w3 + contract = deploy_contract(w3, CONTRACTS["Greeter"]) + assert contract.caller.greet() == "Hello" + + # Create new filter from contract + current_height = hex(w3.eth.get_block_number()) + flt = contract.events.ChangeGreeting.createFilter(fromBlock=current_height) + + # without tx + assert flt.get_new_entries() == [] # GetFilterChanges + assert flt.get_all_entries() == [] # GetFilterLogs + + # with tx + tx = contract.functions.setGreeting("world").buildTransaction() + tx_receipt = send_transaction(w3, tx) + assert tx_receipt.status == 1 + + log = contract.events.ChangeGreeting().processReceipt(tx_receipt)[0] + assert log["event"] == "ChangeGreeting" + + new_entries = flt.get_new_entries() + assert len(new_entries) == 1 + assert new_entries[0] == log + assert contract.caller.greet() == "world" + + # without new txs since last call + assert flt.get_new_entries() == [] + assert flt.get_all_entries() == new_entries + + # Uninstall + assert w3.eth.uninstall_filter(flt.filter_id) + assert not w3.eth.uninstall_filter(flt.filter_id) + with pytest.raises(Exception): + flt.get_all_entries() + + +def test_event_log_filter_by_address(cluster): + w3: Web3 = cluster.w3 + + contract = deploy_contract(w3, CONTRACTS["Greeter"]) + assert contract.caller.greet() == "Hello" + + flt = w3.eth.filter({"address": contract.address}) + flt2 = w3.eth.filter({"address": ADDRS["validator"]}) + + # without tx + assert flt.get_new_entries() == [] # GetFilterChanges + assert flt.get_all_entries() == [] # GetFilterLogs + + # with tx + tx = contract.functions.setGreeting("world").buildTransaction() + receipt = send_transaction(w3, tx) + assert receipt.status == 1 + + assert len(flt.get_new_entries()) == 1 + assert len(flt2.get_new_entries()) == 0 + + +def test_get_logs(cluster): + w3: Web3 = cluster.w3 + + contract = deploy_contract(w3, CONTRACTS["Greeter"]) + + # without tx + assert w3.eth.get_logs({"address": contract.address}) == [] + assert w3.eth.get_logs({"address": ADDRS["validator"]}) == [] + + # with tx + tx = contract.functions.setGreeting("world").buildTransaction() + receipt = send_transaction(w3, tx) + assert receipt.status == 1 + + assert len(w3.eth.get_logs({"address": contract.address})) == 1 + assert len(w3.eth.get_logs({"address": ADDRS["validator"]})) == 0 diff --git a/tests/integration_tests/test_types.py b/tests/integration_tests/test_types.py index a9f53b31d7..8126e44850 100644 --- a/tests/integration_tests/test_types.py +++ b/tests/integration_tests/test_types.py @@ -11,6 +11,7 @@ KEYS, deploy_contract, send_transaction, + w3_wait_for_block, w3_wait_for_new_blocks, ) @@ -177,6 +178,9 @@ def send_and_get_hash(w3, tx_value=10): def test_get_proof(ethermint, geth): + # on ethermint the proof query will fail for block numbers <= 2 + # so we must wait for several blocks + w3_wait_for_block(ethermint.w3, 3) eth_rpc = ethermint.w3.provider geth_rpc = geth.w3.provider make_same_rpc_calls( @@ -190,7 +194,7 @@ def test_get_proof(ethermint, geth): eth_rpc, geth_rpc, "eth_getProof", - ["0x57f96e6b86cdefdb3d412547816a82e3e0ebf9d2", ["0x0"], "0x32"], + ["0x57f96e6b86cdefdb3d412547816a82e3e0ebf9d2", ["0x0"], "0x1024"], ) _ = send_and_get_hash(ethermint.w3) diff --git a/tests/integration_tests/utils.py b/tests/integration_tests/utils.py index ed15b35cdd..16548fae98 100644 --- a/tests/integration_tests/utils.py +++ b/tests/integration_tests/utils.py @@ -2,6 +2,7 @@ import os import socket import time +import sys from pathlib import Path from dotenv import load_dotenv @@ -21,6 +22,7 @@ ETHERMINT_ADDRESS_PREFIX = "ethm" TEST_CONTRACTS = { "TestERC20A": "TestERC20A.sol", + "Greeter": "Greeter.sol", } @@ -64,6 +66,46 @@ def w3_wait_for_new_blocks(w3, n): break +def wait_for_new_blocks(cli, n): + begin_height = int((cli.status())["SyncInfo"]["latest_block_height"]) + while True: + time.sleep(0.5) + cur_height = int((cli.status())["SyncInfo"]["latest_block_height"]) + if cur_height - begin_height >= n: + break + + +def wait_for_block(cli, height, timeout=240): + for i in range(timeout * 2): + try: + status = cli.status() + except AssertionError as e: + print(f"get sync status failed: {e}", file=sys.stderr) + else: + current_height = int(status["SyncInfo"]["latest_block_height"]) + if current_height >= height: + break + print("current block height", current_height) + time.sleep(0.5) + else: + raise TimeoutError(f"wait for block {height} timeout") + + +def w3_wait_for_block(w3, height, timeout=240): + for i in range(timeout * 2): + try: + current_height = w3.eth.block_number + except Exception as e: + print(f"get json-rpc block number failed: {e}", file=sys.stderr) + else: + if current_height >= height: + break + print("current block height", current_height) + time.sleep(0.5) + else: + raise TimeoutError(f"wait for block {height} timeout") + + def deploy_contract(w3, jsonfile, args=(), key=KEYS["validator"]): """ deploy contract and return the deployed contract instance @@ -95,3 +137,11 @@ def send_transaction(w3, tx, key=KEYS["validator"]): signed = sign_transaction(w3, tx, key) txhash = w3.eth.send_raw_transaction(signed.rawTransaction) return w3.eth.wait_for_transaction_receipt(txhash) + + +def send_successful_transaction(w3): + signed = sign_transaction(w3, {"to": ADDRS["community"], "value": 1000}) + txhash = w3.eth.send_raw_transaction(signed.rawTransaction) + receipt = w3.eth.wait_for_transaction_receipt(txhash) + assert receipt.status == 1 + return txhash