From dc0340404513e2e7abcf8505838ca6d47546e764 Mon Sep 17 00:00:00 2001 From: y19818 Date: Mon, 2 Dec 2019 12:48:37 +0800 Subject: [PATCH] vns --- ARCHITECTURE.md | 110 +- CONTRIBUTING.md | 68 +- Dockerfile | 39 +- LICENSE | 42 +- MANIFEST.in | 16 +- Makefile | 131 +- README.md | 314 +- conftest.py | 221 +- docker-compose.yml | 20 +- docs/Makefile | 354 +- docs/README-freebsd.md | 156 +- docs/README-linux.md | 98 +- docs/README-osx.md | 53 +- docs/README-windows.md | 14 +- docs/abi_types.rst | 43 - docs/conf.py | 603 ++-- docs/contracts.rst | 2007 +++++------ docs/conventions.rst | 26 + docs/ens.rst | 42 +- docs/ens_overview.rst | 322 +- docs/ethpm.rst | 860 ----- docs/examples.rst | 993 +++--- docs/filters.rst | 610 ++-- docs/gas_price.rst | 234 +- docs/index.rst | 99 +- docs/internals.rst | 420 +-- docs/middleware.rst | 836 +++-- docs/node.rst | 236 +- docs/overview.rst | 728 ++-- docs/providers.rst | 657 ++-- docs/quickstart.rst | 207 +- docs/releases.rst | 2865 +++++++-------- docs/troubleshooting.rst | 144 +- docs/v4_migration.rst | 328 +- docs/v5_migration.rst | 272 +- docs/web3.eth.account.rst | 322 -- docs/web3.eth.rst | 1071 ------ docs/web3.geth.rst | 1555 ++++----- docs/web3.main.rst | 194 +- docs/web3.miner.rst | 186 +- docs/web3.net.rst | 62 +- docs/web3.parity.rst | 569 ++- docs/web3.pm.rst | 120 +- docs/web3.testing.rst | 110 +- ens/__init__.py | 30 +- ens/abis.py | 2788 +++++++-------- ens/auto.py | 6 +- ens/constants.py | 29 +- ens/contract_data.py | 38 +- ens/exceptions.py | 158 +- ens/main.py | 679 ++-- ens/utils.py | 429 +-- pytest.ini | 14 +- replace.py | 40 + requirements-docs.txt | 2 +- setup.py | 206 +- tests/conftest.py | 47 +- tests/core/admin-module/test_admin_addPeer.py | 27 +- .../core/admin-module/test_admin_nodeInfo.py | 27 +- tests/core/admin-module/test_admin_peers.py | 8 +- tests/core/admin-module/test_admin_setSolc.py | 20 + .../caching-utils/test_generate_cache_key.py | 102 +- tests/core/contracts/conftest.py | 1701 +++++---- .../contracts/contract_sources/Emitter.sol | 192 +- .../contracts/test_args_and_kwargs_merger.py | 204 +- tests/core/contracts/test_concise_contract.py | 285 +- .../test_contract_ambiguous_functions.py | 387 ++- .../test_contract_buildTransaction.py | 449 ++- .../contracts/test_contract_call_interface.py | 1571 ++++----- .../test_contract_caller_interface.py | 341 +- .../test_contract_class_construction.py | 112 +- .../contracts/test_contract_constructor.py | 468 +-- .../test_contract_constructor_encoding.py | 183 +- .../contracts/test_contract_deployment.py | 174 +- .../contracts/test_contract_estimateGas.py | 278 +- .../test_contract_events_buildFilter.py | 140 +- tests/core/contracts/test_contract_example.py | 206 +- tests/core/contracts/test_contract_init.py | 152 +- .../test_contract_method_abi_decoding.py | 224 +- .../test_contract_method_abi_encoding.py | 222 +- ...st_contract_method_to_argument_matching.py | 337 +- .../test_contract_transact_interface.py | 646 ++-- .../contracts/test_contract_util_functions.py | 26 +- .../contracts/test_extracting_event_data.py | 955 ++--- .../test_extracting_event_data_old.py | 282 +- .../core/contracts/test_implicit_contract.py | 225 +- ...test_select_method_for_block_identifier.py | 85 +- .../test_empty_object_is_falsy.py | 16 +- tests/core/eth-module/conftest.py | 82 +- tests/core/eth-module/test_accounts.py | 821 +++-- .../eth-module/test_defaultAccount_api.py | 72 +- tests/core/eth-module/test_eth_properties.py | 6 - tests/core/eth-module/test_gas_pricing.py | 50 +- tests/core/eth-module/test_iban.py | 324 +- tests/core/eth-module/test_poa.py | 78 +- tests/core/eth-module/test_transactions.py | 122 +- ...t_eth_contract.py => test_vns_contract.py} | 86 +- ...{test_eth_mining.py => test_vns_mining.py} | 18 +- tests/core/eth-module/test_vns_properties.py | 6 + tests/core/filtering/conftest.py | 325 +- .../core/filtering/test_basic_filter_tests.py | 80 +- ...est_contract_createFilter_topic_merging.py | 38 +- .../filtering/test_contract_data_filters.py | 416 +-- tests/core/filtering/test_contract_getLogs.py | 194 +- .../test_contract_on_event_filtering.py | 432 +-- .../test_contract_past_event_filtering.py | 176 +- .../filtering/test_contract_topic_filters.py | 410 +-- .../test_existing_filter_instance.py | 90 +- .../test_filter_against_latest_blocks.py | 60 +- ...est_filter_against_pending_transactions.py | 164 +- .../test_filter_against_transaction_logs.py | 134 +- .../test_filters_against_many_blocks.py | 312 +- tests/core/filtering/test_utils_functions.py | 337 +- .../test_rpc_gas_pricing_strategies.py | 28 +- .../test_time_based_gas_price_strategy.py | 412 +-- tests/core/manager/conftest.py | 54 +- .../test_middleware_add_and_clear_api.py | 340 +- .../test_middleware_can_be_stateful.py | 60 +- tests/core/manager/test_provider_property.py | 40 +- .../manager/test_provider_request_wrapping.py | 62 +- tests/core/method-class/test_method.py | 562 +-- .../method-class/test_result_formatters.py | 54 - .../core/middleware/test_filter_middleware.py | 297 +- .../middleware/test_fixture_middleware.py | 178 +- .../middleware/test_gas_price_strategy.py | 138 +- .../middleware/test_http_request_retry.py | 168 +- ...est_latest_block_based_cache_middleware.py | 484 +-- .../test_name_to_address_middleware.py | 108 +- .../test_request_param_normalizer.py | 70 +- .../test_simple_cache_middleware.py | 230 +- tests/core/middleware/test_stalecheck.py | 236 +- .../test_time_based_cache_middleware.py | 306 +- .../middleware/test_transaction_signing.py | 636 ++-- tests/core/mining-module/conftest.py | 26 +- .../core/mining-module/test_miner_hashrate.py | 22 +- .../core/mining-module/test_miner_setExtra.py | 74 +- .../mining-module/test_miner_setGasPrice.py | 56 +- tests/core/mining-module/test_miner_start.py | 68 +- tests/core/mining-module/test_miner_stop.py | 54 +- tests/core/mining-module/test_setEtherBase.py | 14 +- tests/core/pm-module/conftest.py | 374 +- tests/core/pm-module/test_ens_integration.py | 286 +- tests/core/pm-module/test_pm_init.py | 214 +- tests/core/pm-module/test_registry.py | 223 +- .../pm-module/test_registry_integration.py | 378 +- tests/core/providers/test_auto_provider.py | 310 +- tests/core/providers/test_ipc_provider.py | 194 +- tests/core/providers/test_provider.py | 106 +- .../core/providers/test_websocket_provider.py | 135 +- tests/core/shh-module/test_shh_filter.py | 390 +-- tests/core/shh-module/test_shh_key_pair.py | 147 +- tests/core/shh-module/test_shh_post.py | 39 +- tests/core/shh-module/test_shh_properties.py | 49 +- .../core/testing-module/test_testing_mine.py | 44 +- .../test_testing_snapshot_and_revert.py | 94 +- .../testing-module/test_testing_timeTravel.py | 18 +- .../tools/pytest_ethereum/assets/greeter.json | 1 - tests/core/tools/pytest_ethereum/conftest.py | 27 - .../tools/pytest_ethereum/test_deployer.py | 83 - .../core/tools/pytest_ethereum/test_linker.py | 82 - .../pytest_ethereum/test_linker_utils.py | 90 - tests/core/txpool-module/conftest.py | 26 +- .../core/txpool-module/test_txpool_content.py | 84 +- .../core/txpool-module/test_txpool_inspect.py | 92 +- tests/core/utilities/test_abi.py | 554 +-- .../utilities/test_abi_filter_by_abi_name.py | 142 +- .../test_abi_filtering_by_argument_name.py | 122 +- tests/core/utilities/test_abi_is_encodable.py | 194 +- tests/core/utilities/test_attributedict.py | 214 +- .../test_construct_event_data_set.py | 225 +- .../test_construct_event_filter_params.py | 148 +- .../utilities/test_construct_event_topics.py | 244 +- tests/core/utilities/test_datatypes.py | 70 +- tests/core/utilities/test_decorators.py | 44 +- tests/core/utilities/test_encoding.py | 474 +-- .../utilities/test_event_filter_builder.py | 135 +- tests/core/utilities/test_event_interface.py | 68 +- tests/core/utilities/test_formatters.py | 105 +- .../test_is_predefined_block_number.py | 40 +- tests/core/utilities/test_is_probably_enum.py | 46 +- .../core/utilities/test_is_recognized_type.py | 366 +- tests/core/utilities/test_math.py | 112 +- .../test_prepare_transaction_replacement.py | 244 +- tests/core/utilities/test_threads.py | 214 +- .../test_valid_transaction_params.py | 188 +- tests/core/utilities/test_validation.py | 294 +- .../version-module/test_version_module.py | 112 +- tests/core/web3-module/test_api.py | 4 +- tests/core/web3-module/test_clientVersion.py | 4 +- tests/core/web3-module/test_conversions.py | 500 +-- tests/core/web3-module/test_keccak.py | 205 +- tests/core/web3-module/test_providers.py | 50 +- tests/ens/conftest.py | 458 +-- tests/ens/test_get_registry.py | 100 +- tests/ens/test_nameprep.py | 59 +- tests/ens/test_setup_address.py | 330 +- tests/ens/test_setup_name.py | 266 +- tests/ens/test_utils.py | 20 +- tests/ethpm/_utils/test_backend_utils.py | 74 - tests/ethpm/_utils/test_chain_utils.py | 62 - tests/ethpm/_utils/test_contract_utils.py | 93 - tests/ethpm/_utils/test_ipfs_utils.py | 122 - tests/ethpm/_utils/test_registry_utils.py | 81 - tests/ethpm/backends/test_http_backends.py | 39 - tests/ethpm/backends/test_ipfs_backends.py | 126 - tests/ethpm/backends/test_registry_backend.py | 46 - tests/ethpm/conftest.py | 257 -- .../ethpm/integration/test_escrow_manifest.py | 59 - .../integration/test_ipfs_integration.py | 64 - tests/ethpm/test_contract.py | 181 - tests/ethpm/test_dependencies.py | 69 - tests/ethpm/test_deployments.py | 218 -- tests/ethpm/test_get_build_dependencies.py | 48 - tests/ethpm/test_get_deployments.py | 45 - tests/ethpm/test_package.py | 89 - tests/ethpm/test_package_init.py | 131 - tests/ethpm/test_uri.py | 96 - tests/ethpm/tools/test_builder.py | 735 ---- tests/ethpm/tools/test_checker.py | 150 - tests/ethpm/validation/test_manifest.py | 253 -- .../test_manifest_assets_are_valid.py | 42 - tests/ethpm/validation/test_misc.py | 35 - tests/generate_go_ethereum_fixture.py | 816 ++--- tests/integration/README.md | 188 +- tests/integration/common.py | 34 +- tests/integration/conftest.py | 60 +- tests/integration/generate_fixtures/common.py | 478 +-- .../generate_fixtures/go_ethereum.py | 306 +- tests/integration/generate_fixtures/parity.py | 576 +-- tests/integration/go_ethereum/common.py | 223 +- tests/integration/go_ethereum/conftest.py | 434 +-- .../go_ethereum/test_goethereum_http.py | 167 +- .../go_ethereum/test_goethereum_ipc.py | 157 +- .../go_ethereum/test_goethereum_ws.py | 187 +- tests/integration/go_ethereum/utils.py | 104 +- tests/integration/parity/common.py | 389 +-- tests/integration/parity/conftest.py | 412 +-- tests/integration/parity/install_parity.py | 232 +- tests/integration/parity/test_parity_http.py | 214 +- tests/integration/parity/test_parity_ipc.py | 220 +- tests/integration/parity/test_parity_ws.py | 238 +- tests/integration/parity/utils.py | 164 +- tests/integration/test_ethereum_tester.py | 646 ++-- tests/utils.py | 50 +- tox.ini | 127 +- web3/__init__.py | 90 +- web3/_utils/abi.py | 1669 ++++----- web3/_utils/admin.py | 129 +- web3/_utils/blocks.py | 120 +- web3/_utils/caching.py | 84 +- web3/_utils/compat/__init__.py | 7 - web3/_utils/contracts.py | 563 ++- web3/_utils/datatypes.py | 76 +- web3/_utils/decorators.py | 117 +- web3/_utils/empty.py | 18 +- web3/_utils/encoding.py | 666 ++-- web3/_utils/ens.py | 124 +- web3/_utils/events.py | 957 +++-- web3/_utils/filters.py | 531 ++- web3/_utils/formatters.py | 274 +- web3/_utils/function_identifiers.py | 4 +- web3/_utils/http.py | 16 +- web3/_utils/hypothesis.py | 14 +- web3/_utils/math.py | 64 +- web3/_utils/method_formatters.py | 479 --- web3/_utils/miner.py | 102 +- web3/_utils/module.py | 32 +- web3/_utils/module_testing/__init__.py | 51 +- .../_utils/module_testing/emitter_contract.py | 898 ++--- web3/_utils/module_testing/event_contract.py | 64 - .../go_ethereum_admin_module.py | 108 - .../module_testing/indexed_event_contract.py | 64 - web3/_utils/module_testing/math_contract.py | 182 +- web3/_utils/module_testing/net_module.py | 58 +- web3/_utils/module_testing/parity_module.py | 223 +- web3/_utils/module_testing/personal_module.py | 512 +-- web3/_utils/module_testing/shh_module.py | 989 +++--- web3/_utils/module_testing/version_module.py | 22 +- .../{eth_module.py => vns_module.py} | 1793 +++++----- web3/_utils/module_testing/web3_module.py | 434 +-- web3/_utils/normalizers.py | 438 +-- web3/_utils/personal.py | 108 +- web3/_utils/request.py | 58 +- web3/_utils/rpc_abi.py | 180 +- web3/_utils/shh.py | 342 +- web3/_utils/threads.py | 236 +- web3/_utils/toolz/__init__.py | 44 + web3/_utils/toolz/curried.py | 10 + web3/_utils/transactions.py | 349 +- web3/_utils/txpool.py | 40 +- web3/_utils/validation.py | 368 +- web3/_utils/windows.py | 58 +- web3/auto/__init__.py | 6 +- web3/auto/gethdev.py | 26 +- web3/auto/http.py | 12 +- web3/auto/infura/__init__.py | 30 +- web3/auto/infura/endpoints.py | 119 +- web3/auto/infura/goerli.py | 38 +- web3/auto/infura/kovan.py | 30 +- web3/auto/infura/mainnet.py | 30 +- web3/auto/infura/rinkeby.py | 38 +- web3/auto/infura/ropsten.py | 30 +- web3/auto/ipc.py | 12 +- web3/auto/websocket.py | 12 +- web3/contract.py | 3090 ++++++++--------- web3/datastructures.py | 451 ++- web3/exceptions.py | 322 +- web3/gas_strategies/rpc.py | 16 +- web3/gas_strategies/time_based.py | 405 +-- web3/geth.py | 289 +- web3/iban.py | 461 ++- web3/logs.py | 8 - web3/main.py | 529 ++- web3/manager.py | 327 +- web3/method.py | 306 +- web3/middleware/__init__.py | 166 +- web3/middleware/abi.py | 37 +- web3/middleware/attrdict.py | 71 +- web3/middleware/cache.py | 831 +++-- web3/middleware/exception_handling.py | 66 +- web3/middleware/exception_retry_request.py | 220 +- web3/middleware/filter.py | 685 ++-- web3/middleware/fixture.py | 127 +- web3/middleware/formatting.py | 163 +- web3/middleware/gas_price_strategy.py | 54 +- web3/middleware/geth_poa.py | 65 +- web3/middleware/names.py | 50 +- web3/middleware/normalize_errors.py | 70 +- .../normalize_request_parameters.py | 36 +- web3/middleware/pythonic.py | 384 +- web3/middleware/signing.py | 305 +- .../simulate_unmined_transaction.py | 60 +- web3/middleware/stalecheck.py | 114 +- web3/middleware/validation.py | 238 +- web3/module.py | 166 +- web3/net.py | 46 +- web3/parity.py | 347 +- web3/pm.py | 1218 ++++--- web3/providers/__init__.py | 25 +- web3/providers/auto.py | 223 +- web3/providers/base.py | 193 +- web3/providers/ipc.py | 512 ++- web3/providers/rpc.py | 163 +- .../{eth_tester => vns_tester}/__init__.py | 8 +- .../{eth_tester => vns_tester}/defaults.py | 771 ++-- .../{eth_tester => vns_tester}/main.py | 184 +- .../{eth_tester => vns_tester}/middleware.py | 650 ++-- web3/providers/websocket.py | 256 +- web3/replace.py | 40 + web3/testing.py | 50 +- web3/tools/__init__.py | 4 - web3/tools/pytest_ethereum/__init__.py | 0 web3/tools/pytest_ethereum/_utils.py | 142 - web3/tools/pytest_ethereum/deployer.py | 51 - web3/tools/pytest_ethereum/exceptions.py | 22 - web3/tools/pytest_ethereum/linker.py | 124 - web3/tools/pytest_ethereum/plugins.py | 31 - web3/types.py | 289 -- web3/version.py | 126 +- web3/{eth.py => vns.py} | 982 +++--- 360 files changed, 40499 insertions(+), 52070 deletions(-) delete mode 100644 docs/abi_types.rst create mode 100644 docs/conventions.rst delete mode 100644 docs/ethpm.rst delete mode 100644 docs/web3.eth.account.rst delete mode 100644 docs/web3.eth.rst create mode 100755 replace.py mode change 100644 => 100755 setup.py create mode 100644 tests/core/admin-module/test_admin_setSolc.py delete mode 100644 tests/core/eth-module/test_eth_properties.py rename tests/core/eth-module/{test_eth_contract.py => test_vns_contract.py} (81%) rename tests/core/eth-module/{test_eth_mining.py => test_vns_mining.py} (69%) create mode 100644 tests/core/eth-module/test_vns_properties.py delete mode 100644 tests/core/method-class/test_result_formatters.py delete mode 100644 tests/core/tools/pytest_ethereum/assets/greeter.json delete mode 100644 tests/core/tools/pytest_ethereum/conftest.py delete mode 100644 tests/core/tools/pytest_ethereum/test_deployer.py delete mode 100644 tests/core/tools/pytest_ethereum/test_linker.py delete mode 100644 tests/core/tools/pytest_ethereum/test_linker_utils.py delete mode 100644 tests/ethpm/_utils/test_backend_utils.py delete mode 100644 tests/ethpm/_utils/test_chain_utils.py delete mode 100644 tests/ethpm/_utils/test_contract_utils.py delete mode 100644 tests/ethpm/_utils/test_ipfs_utils.py delete mode 100644 tests/ethpm/_utils/test_registry_utils.py delete mode 100644 tests/ethpm/backends/test_http_backends.py delete mode 100644 tests/ethpm/backends/test_ipfs_backends.py delete mode 100644 tests/ethpm/backends/test_registry_backend.py delete mode 100644 tests/ethpm/conftest.py delete mode 100644 tests/ethpm/integration/test_escrow_manifest.py delete mode 100644 tests/ethpm/integration/test_ipfs_integration.py delete mode 100644 tests/ethpm/test_contract.py delete mode 100644 tests/ethpm/test_dependencies.py delete mode 100644 tests/ethpm/test_deployments.py delete mode 100644 tests/ethpm/test_get_build_dependencies.py delete mode 100644 tests/ethpm/test_get_deployments.py delete mode 100644 tests/ethpm/test_package.py delete mode 100644 tests/ethpm/test_package_init.py delete mode 100644 tests/ethpm/test_uri.py delete mode 100644 tests/ethpm/tools/test_builder.py delete mode 100644 tests/ethpm/tools/test_checker.py delete mode 100644 tests/ethpm/validation/test_manifest.py delete mode 100644 tests/ethpm/validation/test_manifest_assets_are_valid.py delete mode 100644 tests/ethpm/validation/test_misc.py delete mode 100644 web3/_utils/compat/__init__.py delete mode 100644 web3/_utils/method_formatters.py delete mode 100644 web3/_utils/module_testing/event_contract.py delete mode 100644 web3/_utils/module_testing/go_ethereum_admin_module.py delete mode 100644 web3/_utils/module_testing/indexed_event_contract.py rename web3/_utils/module_testing/{eth_module.py => vns_module.py} (58%) create mode 100644 web3/_utils/toolz/__init__.py create mode 100644 web3/_utils/toolz/curried.py delete mode 100644 web3/logs.py rename web3/providers/{eth_tester => vns_tester}/__init__.py (94%) rename web3/providers/{eth_tester => vns_tester}/defaults.py (53%) rename web3/providers/{eth_tester => vns_tester}/main.py (57%) rename web3/providers/{eth_tester => vns_tester}/middleware.py (62%) create mode 100755 web3/replace.py delete mode 100644 web3/tools/__init__.py delete mode 100644 web3/tools/pytest_ethereum/__init__.py delete mode 100644 web3/tools/pytest_ethereum/_utils.py delete mode 100644 web3/tools/pytest_ethereum/deployer.py delete mode 100644 web3/tools/pytest_ethereum/exceptions.py delete mode 100644 web3/tools/pytest_ethereum/linker.py delete mode 100644 web3/tools/pytest_ethereum/plugins.py delete mode 100644 web3/types.py rename web3/{eth.py => vns.py} (52%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 5350081efb..275fd0be5e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,55 +1,55 @@ -methods/* - These files define all methods callable under - web3.db.* - web3.eth.* - web3.net.* - web3.personal.* - web3.shh.* - - Methods and properties are first defined as a dictionary - and then are converted to real functions and attached to the - respective objects. This will make it easier to mirror the - next migrations of the web3.js library. Both types are defined - in method.py and property.py respectively. - -RequestManager - Is supplied with one Provider at initialisation which - can be replaced by calling setProvider(). It uses Jsonrpc - to convert the methods into raw payloads and to validate - responses attained with receive(). - - Has three other methods: - - - send(data, timeout=None) - If timeout is None, send blocks until the result is - available, which it then returns. - - If the timeout is 0, send returns immediately, only - returning the id of the request, which can be used - to poll with receive() later. - - If the timeout is greater than 0, it blocks until - either the result is available or the timeout is - reached, at which point a ValueError is thrown. - - send() makes use of the other two functions: - - - forward(data) - Forwards the data to the provider and returns the - request id. - - - receive(requestid, timeout=0) - Implements the timeout functionality described in send. - If timeout is 0, it returns None if the response was - not available. - -Provider - On initialisation, is started in a separate thread. - Continuously fetches incoming requests from a queue - and appends the responses to another queue. Providers - only receives and returns "raw" requests, JSON validation - and decoding happens in the Request Manager. - - As of now there are two implementations: - - RPCProvider - - IPCProvider +methods/* + These files define all methods callable under + web3.db.* + web3.vns.* + web3.net.* + web3.personal.* + web3.shh.* + + Methods and properties are first defined as a dictionary + and then are converted to real functions and attached to the + respective objects. This will make it easier to mirror the + next migrations of the web3.js library. Both types are defined + in method.py and property.py respectively. + +RequestManager + Is supplied with one Provider at initialisation which + can be replaced by calling setProvider(). It uses Jsonrpc + to convert the methods into raw payloads and to validate + responses attained with receive(). + + Has three other methods: + + - send(data, timeout=None) + If timeout is None, send blocks until the result is + available, which it then returns. + + If the timeout is 0, send returns immediately, only + returning the id of the request, which can be used + to poll with receive() later. + + If the timeout is greater than 0, it blocks until + either the result is available or the timeout is + reached, at which point a ValueError is thrown. + + send() makes use of the other two functions: + + - forward(data) + Forwards the data to the provider and returns the + request id. + + - receive(requestid, timeout=0) + Implements the timeout functionality described in send. + If timeout is 0, it returns None if the response was + not available. + +Provider + On initialisation, is started in a separate thread. + Continuously fetches incoming requests from a queue + and appends the responses to another queue. Providers + only receives and returns "raw" requests, JSON validation + and decoding happens in the Request Manager. + + As of now there are two implementations: + - RPCProvider + - IPCProvider diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c581b48e9..8ebec775d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,34 +1,34 @@ -# Development - -To start development you should begin by cloning the repo. - -```bash -$ git clone git@github.com/ethereum/web3.py.git -``` - - -# Cute Animal Pictures - -All pull requests need to have a cute animal picture. This is a very important -part of the development process. - - -# Pull Requests - -In general, pull requests are welcome. Please try to adhere to the following. - -- code should conform to PEP8 and as well as the linting done by `flake8 web3/ tests/` -- include tests. -- include any relevant documentation updates. - -It's a good idea to make pull requests early on. A pull request represents the -start of a discussion, and doesn't necessarily need to be the final, finished -submission. - -GitHub's documentation for working on pull requests is [available here](https://help.github.com/articles/about-pull-requests/). - -Always run the tests before submitting pull requests, and ideally run `tox` in -order to check that your modifications don't break anything. - -Once you've made a pull request take a look at the travis build status in the -GitHub interface and make sure the tests are runnning as you'd expect. +# Development + +To start development you should begin by cloning the repo. + +```bash +$ git clone git@github.com/ethereum/web3.py.git +``` + + +# Cute Animal Pictures + +All pull requests need to have a cute animal picture. This is a very important +part of the development process. + + +# Pull Requests + +In general, pull requests are welcome. Please try to adhere to the following. + +- code should conform to PEP8 and as well as the linting done by `flake8 web3/ tests/` +- include tests. +- include any relevant documentation updates. + +It's a good idea to make pull requests early on. A pull request represents the +start of a discussion, and doesn't necessarily need to be the final, finished +submission. + +GitHub's documentation for working on pull requests is [available here](https://help.github.com/articles/about-pull-requests/). + +Always run the tests before submitting pull requests, and ideally run `tox` in +order to check that your modifications don't break anything. + +Once you've made a pull request take a look at the travis build status in the +GitHub interface and make sure the tests are runnning as you'd expect. diff --git a/Dockerfile b/Dockerfile index 912a2961e2..97513acd9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,19 @@ -FROM python:3.6 - -# Set up code directory -RUN mkdir -p /usr/src/app -WORKDIR /usr/src/app - -# Install Linux dependencies -RUN apt-get update && apt-get install -y libssl-dev - -COPY web3 ./web3/ -COPY tests ./tests/ -COPY ens ./ens/ -COPY ethpm ./ethpm/ - -COPY setup.py . -COPY README.md . - -RUN pip install -e .[dev] - -WORKDIR /code +FROM python:3.6 + +# Set up code directory +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +# Install Linux dependencies +RUN apt-get update && apt-get install -y libssl-dev + +COPY web3 ./web3/ +COPY tests ./tests/ +COPY ens ./ens/ + +COPY setup.py . +COPY README.md . + +RUN pip install -e .[dev] + +WORKDIR /code diff --git a/LICENSE b/LICENSE index 8c67f6d079..9970c28cc6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -The MIT License (MIT) - -Copyright (c) 2016 Piper Merriam - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +The MIT License (MIT) + +Copyright (c) 2016 Piper Merriam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in index 8a5a54c30a..a37f3eba43 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,7 @@ -include LICENSE -include VERSION -include README.md -include requirements.txt - -recursive-exclude * __pycache__ -recursive-exclude * *.py[co] - -recursive-include ethpm/assets/ * +include LICENSE +include VERSION +include README.md +include requirements.txt + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] diff --git a/Makefile b/Makefile index e7d05f83c1..35515ab06d 100644 --- a/Makefile +++ b/Makefile @@ -1,71 +1,60 @@ -.PHONY: clean-pyc clean-build docs - -help: - @echo "clean-build - remove build artifacts" - @echo "clean-pyc - remove Python file artifacts" - @echo "lint - check style with flake8" - @echo "test - run tests quickly with the default Python" - @echo "testall - run tests on every Python version with tox" - @echo "release - package and upload a release" - @echo "dist - package" - -clean: clean-build clean-pyc - -clean-build: - rm -fr build/ - rm -fr dist/ - rm -fr *.egg-info - -clean-pyc: - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - -lint: - tox -elint - -lint-roll: - isort --recursive web3 ens tests - $(MAKE) lint - -test: - pytest tests - -test-all: - tox - -build-docs: - sphinx-apidoc -o docs/ . setup.py "web3/utils/*" "*conftest*" "tests" "ethpm" - $(MAKE) -C docs clean - $(MAKE) -C docs html - $(MAKE) -C docs doctest - -docs: build-docs validate-docs - open docs/_build/html/index.html - -validate-docs: build-docs - python newsfragments/validate_files.py - towncrier --draft - -linux-docs: build-docs - readlink -f docs/_build/html/index.html - -release: clean - CURRENT_SIGN_SETTING=$(git config commit.gpgSign) - git config commit.gpgSign true - # Let UPCOMING_VERSION be the version that is used for the current bump - $(eval UPCOMING_VERSION=$(shell bumpversion $(bump) --dry-run --list | grep new_version= | sed 's/new_version=//g')) - # Now generate the release notes to have them included in the release commit - towncrier --yes --version $(UPCOMING_VERSION) - # We need --allow-dirty because of the generated release_notes file but it is safe because the - # previous dry-run runs *without* --allow-dirty which ensures it's really just the release notes - # file that we are allowing to sit here dirty, waiting to get included in the release commit. - bumpversion --allow-dirty $(bump) - git push upstream && git push upstream --tags - python setup.py sdist bdist_wheel - twine upload dist/* - git config commit.gpgSign "$(CURRENT_SIGN_SETTING)" - -dist: clean - python setup.py sdist bdist_wheel - ls -l dist +.PHONY: clean-pyc clean-build docs + +help: + @echo "clean-build - remove build artifacts" + @echo "clean-pyc - remove Python file artifacts" + @echo "lint - check style with flake8" + @echo "test - run tests quickly with the default Python" + @echo "testall - run tests on every Python version with tox" + @echo "release - package and upload a release" + @echo "dist - package" + +clean: clean-build clean-pyc + +clean-build: + rm -fr build/ + rm -fr dist/ + rm -fr *.egg-info + +clean-pyc: + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + +lint: + tox -elint + +lint-roll: + isort --recursive web3 ens tests + $(MAKE) lint + +test: + pytest tests + +test-all: + tox + +build-docs: + sphinx-apidoc -o docs/ . setup.py "web3/utils/*" "*conftest*" "tests" + $(MAKE) -C docs clean + $(MAKE) -C docs html + $(MAKE) -C docs doctest + +docs: build-docs + open docs/_build/html/index.html + +linux-docs: build-docs + readlink -f docs/_build/html/index.html + +release: clean + CURRENT_SIGN_SETTING=$(git config commit.gpgSign) + git config commit.gpgSign true + bumpversion $(bump) + git push upstream && git push upstream --tags + python setup.py sdist bdist_wheel + twine upload dist/* + git config commit.gpgSign "$(CURRENT_SIGN_SETTING)" + +dist: clean + python setup.py sdist bdist_wheel + ls -l dist diff --git a/README.md b/README.md index 0139db1038..20564f6701 100644 --- a/README.md +++ b/README.md @@ -1,160 +1,154 @@ -# Web3.py - -[![Join the chat at https://gitter.im/ethereum/web3.py](https://badges.gitter.im/ethereum/web3.py.svg)](https://gitter.im/ethereum/web3.py?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -[![Build Status](https://circleci.com/gh/ethereum/web3.py.svg?style=shield)](https://circleci.com/gh/ethereum/web3.py) - - -A Python implementation of [web3.js](https://github.com/ethereum/web3.js) - -* Python 3.6+ support - -Read more in the [documentation on ReadTheDocs](http://web3py.readthedocs.io/). [View the change log on Github](docs/releases.rst). - -## Developer Setup - -```sh -git clone git@github.com:ethereum/web3.py.git -cd web3.py -``` - -Please see OS-specific instructions for: - -- [Linux](docs/README-linux.md#Developer-Setup) -- [Mac](docs/README-osx.md#Developer-Setup) -- [Windows](docs/README-windows.md#Developer-Setup) -- [FreeBSD](docs/README-freebsd.md#Developer-Setup) - -Then run these install commands: - -```sh -virtualenv venv -. venv/bin/activate -pip install -e .[dev] -``` - -For different environments, you can set up multiple `virtualenv`. For example, if you want to create a `venvdocs`, then you do the following: - -```sh -virtualenv venvdocs -. venvdocs/bin/activate -pip install -e .[docs] -pip install -e . -``` - -## Using Docker - -If you would like to develop and test inside a Docker environment, use the *sandbox* container provided in the **docker-compose.yml** file. - -To start up the test environment, run: - -``` -docker-compose up -d -``` - -This will build a Docker container set up with an environment to run the Python test code. - -**Note: This container does not have `go-ethereum` installed, so you cannot run the go-ethereum test suite.** - -To run the Python tests from your local machine: - -``` -docker-compose exec sandbox bash -c 'pytest -n 4 -f -k "not goethereum"' -``` - -You can run arbitrary commands inside the Docker container by using the `bash -c` prefix. - -``` -docker-compose exec sandbox bash -c '' -``` - -Or, if you would like to just open a session to the container, run: - -``` -docker-compose exec sandbox bash -``` - -### Testing Setup - -During development, you might like to have tests run on every file save. - -Show flake8 errors on file change: - -```sh -# Test flake8 -when-changed -v -s -r -1 web3/ tests/ ens/ -c "clear; flake8 web3 tests ens && echo 'flake8 success' || echo 'error'" -``` - -You can use `pytest-watch`, running one for every Python environment: - -```sh -pip install pytest-watch - -cd venv -ptw --onfail "notify-send -t 5000 'Test failure ⚠⚠⚠⚠⚠' 'python 3 test on web3.py failed'" ../tests ../web3 -``` - -Or, you can run multi-process tests in one command, but without color: - -```sh -# in the project root: -pytest --numprocesses=4 --looponfail --maxfail=1 -# the same thing, succinctly: -pytest -n 4 -f --maxfail=1 -``` - -#### How to Execute the Tests? - -1. [Setup your development environment](https://github.com/ethereum/web3.py/#developer-setup). - -2. Execute `tox` for the tests - -There are multiple [components](https://github.com/ethereum/web3.py/blob/master/.circleci/config.yml#L144) of the tests. You can run test to against specific component. For example: - -```sh -# Run Tests for the Core component (for Python 3.6): -tox -e py36-core - -# Run Tests for the Core component (for Python 3.7): -tox -e py37-core -``` - -If for some reason it is not working, add `--recreate` params. - -`tox` is good for testing against the full set of build targets. But if you want to run the tests individually, `pytest` is better for development workflow. For example, to run only the tests in one file: - -```sh -pytest tests/core/gas-strategies/test_time_based_gas_price_strategy.py -``` - -### Release setup - -For Debian-like systems: -``` -apt install pandoc -``` - -To release a new version: - -```sh -make release bump=$$VERSION_PART_TO_BUMP$$ -``` - -To preview the upcoming release notes: - -```sh -towncrier --draft -``` - -#### How to bumpversion - -The version format for this repo is `{major}.{minor}.{patch}` for stable, and -`{major}.{minor}.{patch}-{stage}.{devnum}` for unstable (`stage` can be alpha or beta). - -To issue the next version in line, specify which part to bump, -like `make release bump=minor` or `make release bump=devnum`. - -If you are in a beta version, `make release bump=stage` will switch to a stable. - -To issue an unstable version when the current version is stable, specify the -new version explicitly, like `make release bump="--new-version 4.0.0-alpha.1 devnum"` +# Web3.py + +[![Join the chat at https://gitter.im/ethereum/web3.py](https://badges.gitter.im/ethereum/web3.py.svg)](https://gitter.im/ethereum/web3.py?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[![Build Status](https://circleci.com/gh/ethereum/web3.py.svg?style=shield)](https://circleci.com/gh/ethereum/web3.py) + + +A Python implementation of [web3.js](https://github.com/ethereum/web3.js) + +* Python 3.6+ support + +Read more in the [documentation on ReadTheDocs](http://web3py.readthedocs.io/). [View the change log on Github](docs/releases.rst). + +## Developer Setup + +```sh +git clone git@github.com:ethereum/web3.py.git +cd web3.py +``` + +Please see OS-specific instructions for: + +- [Linux](docs/README-linux.md#Developer-Setup) +- [Mac](docs/README-osx.md#Developer-Setup) +- [Windows](docs/README-windows.md#Developer-Setup) +- [FreeBSD](docs/README-freebsd.md#Developer-Setup) + +Then run these install commands: + +```sh +virtualenv venv +. venv/bin/activate +pip install -e .[dev] +``` + +For different environments, you can set up multiple `virtualenv`. For example, if you want to create a `venvdocs`, then you do the following: + +```sh +virtualenv venvdocs +. venvdocs/bin/activate +pip install -e .[docs] +pip install -e . +``` + +## Using Docker + +If you would like to develop and test inside a Docker environment, use the *sandbox* container provided in the **docker-compose.yml** file. + +To start up the test environment, run: + +``` +docker-compose up -d +``` + +This will build a Docker container set up with an environment to run the Python test code. + +**Note: This container does not have `go-ethereum` installed, so you cannot run the go-ethereum test suite.** + +To run the Python tests from your local machine: + +``` +docker-compose exec sandbox bash -c 'pytest -n 4 -f -k "not goethereum"' +``` + +You can run arbitrary commands inside the Docker container by using the `bash -c` prefix. + +``` +docker-compose exec sandbox bash -c '' +``` + +Or, if you would like to just open a session to the container, run: + +``` +docker-compose exec sandbox bash +``` + +### Testing Setup + +During development, you might like to have tests run on every file save. + +Show flake8 errors on file change: + +```sh +# Test flake8 +when-changed -v -s -r -1 web3/ tests/ ens/ -c "clear; flake8 web3 tests ens && echo 'flake8 success' || echo 'error'" +``` + +You can use `pytest-watch`, running one for every Python environment: + +```sh +pip install pytest-watch + +cd venv +ptw --onfail "notify-send -t 5000 'Test failure ⚠⚠⚠⚠⚠' 'python 3 test on web3.py failed'" ../tests ../web3 +``` + +Or, you can run multi-process tests in one command, but without color: + +```sh +# in the project root: +pytest --numprocesses=4 --looponfail --maxfail=1 +# the same thing, succinctly: +pytest -n 4 -f --maxfail=1 +``` + +#### How to Execute the Tests? + +1. [Setup your development environment](https://github.com/ethereum/web3.py/#developer-setup). + +2. Execute `tox` for the tests + +There are multiple [components](https://github.com/ethereum/web3.py/blob/master/.travis.yml#L53) of the tests. You can run test to against specific component. For example: + +```sh +# Run Tests for the Core component (for Python 3.5): +tox -e py35-core + +# Run Tests for the Core component (for Python 3.6): +tox -e py36-core +``` + +If for some reason it is not working, add `--recreate` params. + +`tox` is good for testing against the full set of build targets. But if you want to run the tests individually, `py.test` is better for development workflow. For example, to run only the tests in one file: + +```sh +py.test tests/core/gas-strategies/test_time_based_gas_price_strategy.py +``` + +### Release setup + +For Debian-like systems: +``` +apt install pandoc +``` + +To release a new version: + +```sh +make release bump=$$VERSION_PART_TO_BUMP$$ +``` + +#### How to bumpversion + +The version format for this repo is `{major}.{minor}.{patch}` for stable, and +`{major}.{minor}.{patch}-{stage}.{devnum}` for unstable (`stage` can be alpha or beta). + +To issue the next version in line, specify which part to bump, +like `make release bump=minor` or `make release bump=devnum`. + +If you are in a beta version, `make release bump=stage` will switch to a stable. + +To issue an unstable version when the current version is stable, specify the +new version explicitly, like `make release bump="--new-version 4.0.0-alpha.1 devnum"` diff --git a/conftest.py b/conftest.py index 69373ff634..afdcad80ad 100644 --- a/conftest.py +++ b/conftest.py @@ -1,114 +1,107 @@ -import os -import pytest -import time -import warnings - -from web3._utils.threads import ( - Timeout, -) -from web3.main import ( - Web3, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - - -class PollDelayCounter: - def __init__(self, initial_delay=0, max_delay=1, initial_step=0.01): - self.initial_delay = initial_delay - self.initial_step = initial_step - self.max_delay = max_delay - self.current_delay = initial_delay - - def __call__(self): - delay = self.current_delay - - if self.current_delay == 0: - self.current_delay += self.initial_step - else: - self.current_delay *= 2 - self.current_delay = min(self.current_delay, self.max_delay) - - return delay - - def reset(self): - self.current_delay = self.initial_delay - - -@pytest.fixture() -def sleep_interval(): - return PollDelayCounter() - - -def is_testrpc_provider(provider): - return isinstance(provider, EthereumTesterProvider) - - -@pytest.fixture() -def skip_if_testrpc(): - - def _skip_if_testrpc(web3): - if is_testrpc_provider(web3.provider): - pytest.skip() - return _skip_if_testrpc - - -@pytest.fixture() -def wait_for_miner_start(): - def _wait_for_miner_start(web3, timeout=60): - poll_delay_counter = PollDelayCounter() - with Timeout(timeout) as timeout: - while not web3.eth.mining or not web3.eth.hashrate: - time.sleep(poll_delay_counter()) - timeout.check() - return _wait_for_miner_start - - -@pytest.fixture() -def wait_for_block(): - def _wait_for_block(web3, block_number=1, timeout=None): - if not timeout: - timeout = (block_number - web3.eth.blockNumber) * 3 - poll_delay_counter = PollDelayCounter() - with Timeout(timeout) as timeout: - while True: - if web3.eth.blockNumber >= block_number: - break - web3.manager.request_blocking("evm_mine", []) - timeout.sleep(poll_delay_counter()) - return _wait_for_block - - -@pytest.fixture() -def wait_for_transaction(): - def _wait_for_transaction(web3, txn_hash, timeout=120): - poll_delay_counter = PollDelayCounter() - with Timeout(timeout) as timeout: - while True: - txn_receipt = web3.eth.getTransactionReceipt(txn_hash) - if txn_receipt is not None: - break - time.sleep(poll_delay_counter()) - timeout.check() - - return txn_receipt - return _wait_for_transaction - - -@pytest.fixture() -def web3(): - provider = EthereumTesterProvider() - return Web3(provider) - - -@pytest.fixture -def w3_strict_abi(): - w3 = Web3(EthereumTesterProvider()) - w3.enable_strict_bytes_type_checking() - return w3 - - -@pytest.fixture(autouse=True) -def print_warnings(): - warnings.simplefilter('always') +import os +import pytest +import time +import warnings + +from web3._utils.threads import ( + Timeout, +) +from web3.main import ( + Web3, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) + + +class PollDelayCounter: + def __init__(self, initial_delay=0, max_delay=1, initial_step=0.01): + self.initial_delay = initial_delay + self.initial_step = initial_step + self.max_delay = max_delay + self.current_delay = initial_delay + + def __call__(self): + delay = self.current_delay + + if self.current_delay == 0: + self.current_delay += self.initial_step + else: + self.current_delay *= 2 + self.current_delay = min(self.current_delay, self.max_delay) + + return delay + + def reset(self): + self.current_delay = self.initial_delay + + +@pytest.fixture() +def sleep_interval(): + return PollDelayCounter() + + +def is_testrpc_provider(provider): + return isinstance(provider, EthereumTesterProvider) + + +@pytest.fixture() +def skip_if_testrpc(): + + def _skip_if_testrpc(web3): + if is_testrpc_provider(web3.provider): + pytest.skip() + return _skip_if_testrpc + + +@pytest.fixture() +def wait_for_miner_start(): + def _wait_for_miner_start(web3, timeout=60): + poll_delay_counter = PollDelayCounter() + with Timeout(timeout) as timeout: + while not web3.vns.mining or not web3.vns.hashrate: + time.sleep(poll_delay_counter()) + timeout.check() + return _wait_for_miner_start + + +@pytest.fixture() +def wait_for_block(): + def _wait_for_block(web3, block_number=1, timeout=None): + if not timeout: + timeout = (block_number - web3.vns.blockNumber) * 3 + poll_delay_counter = PollDelayCounter() + with Timeout(timeout) as timeout: + while True: + if web3.vns.blockNumber >= block_number: + break + web3.manager.request_blocking("evm_mine", []) + timeout.sleep(poll_delay_counter()) + return _wait_for_block + + +@pytest.fixture() +def wait_for_transaction(): + def _wait_for_transaction(web3, txn_hash, timeout=120): + poll_delay_counter = PollDelayCounter() + with Timeout(timeout) as timeout: + while True: + txn_receipt = web3.vns.getTransactionReceipt(txn_hash) + if txn_receipt is not None: + break + time.sleep(poll_delay_counter()) + timeout.check() + + return txn_receipt + return _wait_for_transaction + + +@pytest.fixture() +def web3(): + provider = EthereumTesterProvider() + return web3(provider) + + +@pytest.fixture(autouse=True) +def print_warnings(): + warnings.simplefilter('always') diff --git a/docker-compose.yml b/docker-compose.yml index c130de201a..195c520b89 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,10 @@ -version: '3' -services: - sandbox: - build: - context: . - environment: - PARITY_VERSION: v2.3.5 - volumes: - - .:/code - command: tail -f /dev/null +version: '3' +services: + sandbox: + build: + context: . + environment: + PARITY_VERSION: v2.3.5 + volumes: + - .:/code + command: tail -f /dev/null diff --git a/docs/Makefile b/docs/Makefile index 3f0ed96698..c049e713d4 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,177 +1,177 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -W -T -E -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/web3.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/web3.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/web3" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/web3" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/web3.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/web3.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/web3" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/web3" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/README-freebsd.md b/docs/README-freebsd.md index 63f61009d3..941ecf35bd 100644 --- a/docs/README-freebsd.md +++ b/docs/README-freebsd.md @@ -1,78 +1,78 @@ -# Web3.py on FreeBSD (11.2) - -## Developer Setup - -### Prerequisites - -Make sure you've UTF-8 defined for charset and lang in your [~/.login_conf](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/using-localization.html), -otherwise almost every Python 3 module will fail to install. - -`~/.login_conf`: -``` -me:\ - :charset=UTF-8:\ - :lang=en_US.UTF-8: -``` - -Also make sure you've defined valid include and library paths in `~/.pydistutils.cfg`, otherwise native compilations fail. - -`~/.pydistutils.cfg`: -``` -[build_ext] -include_dirs=/usr/local/include -library_dirs=/usr/local/lib -``` - -``` -sudo pkg install python3 py36-virtualenv git leveldb libxml2 libxslt pkgconf gmake secp256k1 - -# hack around https://github.com/ethereum/ethash/pull/107#issuecomment-445072692 -sudo touch /usr/local/include/alloca.h - -mkdir -p /tmp/venv_python -virtualenv-3.6 /tmp/venv_python/python3 -source /tmp/venv_python/python3/bin/activate.csh - -pip install coincurve - -cd /tmp -git clone https://github.com/ethereum/web3.py.git -cd web3.py - -# assuming you're using tcsh -pip install -e .\[dev\] -``` - -### Test - -#### Prerequisites for integration tests: - -##### geth (https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-FreeBSD) -``` -pkg install go -cd /tmp -git clone https://github.com/ethereum/go-ethereum -cd go-ethereum -make geth -cp build/bin/geth /usr/local/bin/ -``` - -##### parity (https://github.com/paukstis/freebsd_parity/blob/v1.6/README.md) -``` -BROKEN (build crashes on FreeBSD 11.2) -``` - -``` -cd web3.py -tox -e py36-core -tox -e py36-ens -tox -e py36-integration -etc - -or - -py.test tests/core -py.test tests/ens -py.test tests/integration -etc -``` +# Web3.py on FreeBSD (11.2) + +## Developer Setup + +### Prerequisites + +Make sure you've UTF-8 defined for charset and lang in your [~/.login_conf](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/using-localization.html), +otherwise almost every Python 3 module will fail to install. + +`~/.login_conf`: +``` +me:\ + :charset=UTF-8:\ + :lang=en_US.UTF-8: +``` + +Also make sure you've defined valid include and library paths in `~/.pydistutils.cfg`, otherwise native compilations fail. + +`~/.pydistutils.cfg`: +``` +[build_ext] +include_dirs=/usr/local/include +library_dirs=/usr/local/lib +``` + +``` +sudo pkg install python3 py36-virtualenv git leveldb libxml2 libxslt pkgconf gmake secp256k1 + +# hack around https://github.com/ethereum/ethash/pull/107#issuecomment-445072692 +sudo touch /usr/local/include/alloca.h + +mkdir -p /tmp/venv_python +virtualenv-3.6 /tmp/venv_python/python3 +source /tmp/venv_python/python3/bin/activate.csh + +pip install coincurve + +cd /tmp +git clone https://github.com/ethereum/web3.py.git +cd web3.py + +# assuming you're using tcsh +pip install -e .\[dev\] +``` + +### Test + +#### Prerequisites for integration tests: + +##### geth (https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-FreeBSD) +``` +pkg install go +cd /tmp +git clone https://github.com/ethereum/go-ethereum +cd go-ethereum +make geth +cp build/bin/geth /usr/local/bin/ +``` + +##### parity (https://github.com/paukstis/freebsd_parity/blob/v1.6/README.md) +``` +BROKEN (build crashes on FreeBSD 11.2) +``` + +``` +cd web3.py +tox -e py36-core +tox -e py36-ens +tox -e py36-integration +etc + +or + +py.test tests/core +py.test tests/ens +py.test tests/integration +etc +``` diff --git a/docs/README-linux.md b/docs/README-linux.md index ac3fb85395..fe898b9e77 100644 --- a/docs/README-linux.md +++ b/docs/README-linux.md @@ -1,49 +1,49 @@ -# Web3.py on Linux - -## Developer Setup - -1. Install all of the package dependencies - -```sh -sudo apt-get install libssl-dev libffi-dev autoconf automake libtool -# ^ This is for Debian-like systems. TODO: Add more platforms - -sudo pacman -Sy libsecp256k1 -# ^ This is for ArchLinux system - -sudo dnf install openssl-devel libffi-devel autoconf automake libtool -# ^ This is for Fedora. -``` - -2. Install `leveldb` (TODO) - - -## Examples for specific operating systems - -### Ubuntu 16.04 - -```sh -#!/bin/bash - -sudo apt-get update -sudo apt-get -y upgrade - -sudo apt-get -y install build-essential -#RESOLVES ERROR: unable to execute 'x86_64-linux-gnu-gcc': No such file or directory error: command 'x86_64-linux-gnu-gcc' failed with exit status 1 - -sudo apt-get -y install python3-dev -#RESOLVES ERROR: cytoolz/dicttoolz.c:17:20: fatal error: Python.h: No such file or directory compilation terminated. error: command 'x86_64-linux-gnu-gcc' failed with exit status 1 - -sudo apt-get -y install python3-venv -#RESOLVES ERROR: The virtual environment was not created successfully because ensurepip is not available. On Debian/Ubuntu systems, you need to install the python3-venv package using the following command. - -cd ~ -git clone https://github.com/ethereum/web3.py.git -cd web3.py -python3 -m venv venv -. venv/bin/activate -pip install --upgrade pip -pip install -e .[dev] - -``` - +# Web3.py on Linux + +## Developer Setup + +1. Install all of the package dependencies + +```sh +sudo apt-get install libssl-dev libffi-dev autoconf automake libtool +# ^ This is for Debian-like systems. TODO: Add more platforms + +sudo pacman -Sy libsecp256k1 +# ^ This is for ArchLinux system + +sudo dnf install openssl-devel libffi-devel autoconf automake libtool +# ^ This is for Fedora. +``` + +2. Install `leveldb` (TODO) + + +## Examples for specific operating systems + +### Ubuntu 16.04 + +```sh +#!/bin/bash + +sudo apt-get update +sudo apt-get -y upgrade + +sudo apt-get -y install build-essential +#RESOLVES ERROR: unable to execute 'x86_64-linux-gnu-gcc': No such file or directory error: command 'x86_64-linux-gnu-gcc' failed with exit status 1 + +sudo apt-get -y install python3-dev +#RESOLVES ERROR: cytoolz/dicttoolz.c:17:20: fatal error: Python.h: No such file or directory compilation terminated. error: command 'x86_64-linux-gnu-gcc' failed with exit status 1 + +sudo apt-get -y install python3-venv +#RESOLVES ERROR: The virtual environment was not created successfully because ensurepip is not available. On Debian/Ubuntu systems, you need to install the python3-venv package using the following command. + +cd ~ +git clone https://github.com/ethereum/web3.py.git +cd web3.py +python3 -m venv venv +. venv/bin/activate +pip install --upgrade pip +pip install -e .[dev] + +``` + diff --git a/docs/README-osx.md b/docs/README-osx.md index 12908c3164..0b08e56049 100644 --- a/docs/README-osx.md +++ b/docs/README-osx.md @@ -1,32 +1,21 @@ -# Web3.py on OSX - -## Developer Setup - -1. Install XCode command line tools - -```sh -xcode-select --install -``` - -2. Install all of the package dependencies - -```sh -brew install openssl libffi autoconf automake libtool -``` - -3. Install `leveldb` - -```sh -brew install leveldb -``` - -> If you are on `>=OSX 10.15 Catalina` you may encounter the following error with the default `ZSH` shell. This can be fixed by wrapping the `[dev]` part in quotes. -```sh -pip install -e .[dev] -zsh: no matches found: .[dev] -``` - -Run install commands as follows: -```sh -pip install -e .'[dev]' -``` +# Web3.py on OSX + +## Developer Setup + +1. Install XCode command line tools + +```sh +xcode-select --install +``` + +2. Install all of the package dependencies + +```sh +brew install openssl libffi autoconf automake libtool +``` + +3. Install `leveldb` + +```sh +brew install leveldb +``` diff --git a/docs/README-windows.md b/docs/README-windows.md index cae0be60ba..200bab1094 100644 --- a/docs/README-windows.md +++ b/docs/README-windows.md @@ -1,7 +1,7 @@ -# Web3.py on Windows - -## Developer Setup - -1. Install all of the package dependencies (TODO) - -2. Install `leveldb` (TODO) +# Web3.py on Windows + +## Developer Setup + +1. Install all of the package dependencies (TODO) + +2. Install `leveldb` (TODO) diff --git a/docs/abi_types.rst b/docs/abi_types.rst deleted file mode 100644 index 4d7172c8a9..0000000000 --- a/docs/abi_types.rst +++ /dev/null @@ -1,43 +0,0 @@ -ABI Types -========= - -The Web3 library follows the following conventions. - -Bytes vs Text -------------- - -* The term *bytes* is used to refer to the binary representation of a string. -* The term *text* is used to refer to unicode representations of strings. - -Hexadecimal Representations ---------------------------- - -* All hexadecimal values will be returned as text. -* All hexadecimal values will be ``0x`` prefixed. - -Addresses ---------- - -All addresses must be supplied in one of three ways: - -* While connected to mainnet, an Ethereum Name Service name (often in the form ``myname.eth``) -* A 20-byte hexadecimal that is checksummed using the `EIP-55 - `_ spec. -* A 20-byte binary address. - -Strict Bytes Type Checking --------------------------- - -.. note :: - - In version 6, this will be the default behavior - -There is a method on web3 that will enable stricter bytes type checking. -The default is to allow Python strings, and to allow bytestrings less -than the specified byte size. To enable stricter checks, use -``w3.enable_strict_bytes_type_checking()``. This method will cause the web3 -instance to raise an error if a Python string is passed in without a "0x" -prefix. It will also raise an error if the byte string or hex string is not -the exact number of bytes specified by the ABI type. See the -:ref:`enable-strict-byte-check` section -for an example and more details. diff --git a/docs/conf.py b/docs/conf.py index 1aaf119408..b32d5f66c0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,302 +1,301 @@ -# -*- coding: utf-8 -*- -# -# Web3.py documentation build configuration file, created by -# sphinx-quickstart on Thu Oct 16 20:43:24 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -import os - -DIR = os.path.dirname('__file__') -with open (os.path.join(DIR, '../setup.py'), 'r') as f: - for line in f: - if 'version=' in line: - setup_version = line.split('\'')[1] - break - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Web3.py' -copyright = u'2018, Piper Merriam, Jason Carver' - -__version__ = setup_version -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '.'.join(__version__.split('.')[:2]) -# The full version, including alpha/beta/rc tags. -release = __version__ - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [ - '_build', - 'web3.rst', - 'modules.rst', - 'web3.auto.rst', - 'web3.auto.infura.rst', - 'web3.gas_strategies.rst', - 'web3.middleware.rst', - 'web3.providers.rst', - 'web3.providers.eth_tester.rst', - 'web3.testing.rst', - 'web3.tools.*', -] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Populusdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'Populus.tex', u'Populus Documentation', - u'Piper Merriam', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'web3', u'Web3.py Documentation', - [u'Piper Merriam'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'Web3.py', u'Web3.py Documentation', - u'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False - -# -- Intersphinx configuration ------------------------------------------------ - -intersphinx_mapping = { - 'python': ('https://docs.python.org/3.5', None), - 'eth-account': ('http://eth-account.readthedocs.io/en/latest/', None), - # does not exist yet: 'eth-tester': ('http://eth-tester.readthedocs.io/en/latest/', None), - 'hexbytes': ('http://hexbytes.readthedocs.io/en/latest/', None), -} - -autodoc_member_order = "bysource" - -# -- Doctest configuration ---------------------------------------- - -import doctest - -doctest_default_flags = (0 - | doctest.DONT_ACCEPT_TRUE_FOR_1 - | doctest.ELLIPSIS - | doctest.IGNORE_EXCEPTION_DETAIL - | doctest.NORMALIZE_WHITESPACE -) +# -*- coding: utf-8 -*- +# +# Web3.py documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 16 20:43:24 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +import os + +DIR = os.path.dirname('__file__') +with open (os.path.join(DIR, '../setup.py'), 'r') as f: + for line in f: + if 'version=' in line: + setup_version = line.split('\'')[1] + break + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Web3.py' +copyright = u'2018, Piper Merriam, Jason Carver' + +__version__ = setup_version +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '.'.join(__version__.split('.')[:2]) +# The full version, including alpha/beta/rc tags. +release = __version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [ + '_build', + 'web3.rst', + 'modules.rst', + 'web3.auto.rst', + 'web3.auto.infura.rst', + 'web3.gas_strategies.rst', + 'web3.middleware.rst', + 'web3.providers.rst', + 'web3.providers.vns_tester.rst', + 'web3.testing.rst', +] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Populusdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'Populus.tex', u'Populus Documentation', + u'Piper Merriam', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'web3', u'Web3.py Documentation', + [u'Piper Merriam'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Web3.py', u'Web3.py Documentation', + u'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + +# -- Intersphinx configuration ------------------------------------------------ + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3.5', None), + 'vns-account': ('http://eth-account.readthedocs.io/en/latest/', None), + # does not exist yet: 'vns-tester': ('http://eth-tester.readthedocs.io/en/latest/', None), + 'hexbytes': ('http://hexbytes.readthedocs.io/en/latest/', None), +} + +autodoc_member_order = "bysource" + +# -- Doctest configuration ---------------------------------------- + +import doctest + +doctest_default_flags = (0 + | doctest.DONT_ACCEPT_TRUE_FOR_1 + | doctest.ELLIPSIS + | doctest.IGNORE_EXCEPTION_DETAIL + | doctest.NORMALIZE_WHITESPACE +) diff --git a/docs/contracts.rst b/docs/contracts.rst index cabbd55d9a..f8d61d124c 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1,1201 +1,806 @@ -.. _contracts: - -Contracts -========= - -.. py:module:: web3.contract - -It is worth taking your time to understand all about contracts. To get started, -check out this example: - -.. _contract_example: - -Contract Deployment Example ----------------------------------------------- - -To run this example, you will need to install a few extra features: - -- The sandbox node provided by eth-tester. You can install it with ``pip install -U web3[tester]``. -- The ``solc`` solidity compiler. See `Installing the Solidity Compiler - `_ - -.. code-block:: python - - >>> import json - - >>> from web3 import Web3 - >>> from solc import compile_standard - - # Solidity source code - >>> compiled_sol = compile_standard({ - ... "language": "Solidity", - ... "sources": { - ... "Greeter.sol": { - ... "content": ''' - ... pragma solidity ^0.5.0; - ... - ... contract Greeter { - ... string public greeting; - ... - ... constructor() public { - ... greeting = 'Hello'; - ... } - ... - ... function setGreeting(string memory _greeting) public { - ... greeting = _greeting; - ... } - ... - ... function greet() view public returns (string memory) { - ... return greeting; - ... } - ... } - ... ''' - ... } - ... }, - ... "settings": - ... { - ... "outputSelection": { - ... "*": { - ... "*": [ - ... "metadata", "evm.bytecode" - ... , "evm.bytecode.sourceMap" - ... ] - ... } - ... } - ... } - ... }) - - # web3.py instance - >>> w3 = Web3(Web3.EthereumTesterProvider()) - - # set pre-funded account as sender - >>> w3.eth.defaultAccount = w3.eth.accounts[0] - - # get bytecode - >>> bytecode = compiled_sol['contracts']['Greeter.sol']['Greeter']['evm']['bytecode']['object'] - - # get abi - >>> abi = json.loads(compiled_sol['contracts']['Greeter.sol']['Greeter']['metadata'])['output']['abi'] - - >>> Greeter = w3.eth.contract(abi=abi, bytecode=bytecode) - - # Submit the transaction that deploys the contract - >>> tx_hash = Greeter.constructor().transact() - - # Wait for the transaction to be mined, and get the transaction receipt - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - - >>> greeter = w3.eth.contract( - ... address=tx_receipt.contractAddress, - ... abi=abi - ... ) - - >>> greeter.functions.greet().call() - 'Hello' - - >>> tx_hash = greeter.functions.setGreeting('Nihao').transact() - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - >>> greeter.functions.greet().call() - 'Nihao' - - -Contract Factories ------------------- - -These factories are not intended to be initialized directly. -Instead, create contract objects using the :meth:`w3.eth.contract() ` -method. By default, the contract factory is :class:`Contract`. See the -example in :class:`ConciseContract` for specifying an alternate factory. - -.. py:class:: Contract(address) - - Contract provides a default interface for deploying and interacting with - Ethereum smart contracts. - - The address parameter can be a hex address or an ENS name, like ``mycontract.eth``. - -.. py:class:: ConciseContract(Contract()) - - .. warning:: Deprecated: This method is deprecated in favor of the :class:`~ContractCaller` API - or the verbose syntax - - This variation of :class:`Contract` is designed for more succinct read access, - without making write access more wordy. This comes at a cost of losing - access to features like ``deploy()`` and properties like ``address``. It is - recommended to use the classic ``Contract`` for those use cases. - Just to be be clear, `ConciseContract` only exposes contract functions and all - other `Contract` class methods and properties are not available with the `ConciseContract` - API. This includes but is not limited to ``contract.address``, ``contract.abi``, and - ``contract.deploy()``. - - Create this type of contract by passing a :py:class:`Contract` instance to - :class:`ConciseContract`: - - - .. code-block:: python - - >>> concise = ConciseContract(myContract) - - - This variation invokes all methods as a call, so if the classic contract had a method like - ``contract.functions.owner().call()``, you could call it with ``concise.owner()`` instead. - - For access to send a transaction or estimate gas, you can add a keyword argument like so: - - - .. code-block:: python - - >>> concise.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...}) - - >>> # which is equivalent to this transaction in the classic contract: - - >>> contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...}) - -.. py:class:: ImplicitContract(Contract()) - - .. warning:: Deprecated: This method is deprecated in favor of the verbose syntax - - This variation mirrors :py:class:`ConciseContract`, but it invokes all methods as a - transaction rather than a call, so if the classic contract had a method like - ``contract.functions.owner.transact()``, you could call it with ``implicit.owner()`` instead. - - Create this type of contract by passing a :py:class:`Contract` instance to - :class:`ImplicitContract`: - - - .. code-block:: python - - >>> concise = ImplicitContract(myContract) - - -Properties ----------- - -Each Contract Factory exposes the following properties. - - -.. py:attribute:: Contract.address - - The hexadecimal encoded 20-byte address of the contract, or an ENS name. - May be ``None`` if not provided during factory creation. - - -.. py:attribute:: Contract.abi - - The contract ABI array. - - -.. py:attribute:: Contract.bytecode - - The contract bytecode string. May be ``None`` if not provided during - factory creation. - - -.. py:attribute:: Contract.bytecode_runtime - - The runtime part of the contract bytecode string. May be ``None`` if not - provided during factory creation. - -.. py:attribute:: Contract.functions - - This provides access to contract functions as attributes. For example: - ``myContract.functions.MyMethod()``. The exposed contract functions are classes of the - type :py:class:`ContractFunction`. - -.. py:attribute:: Contract.events - - This provides access to contract events as attributes. For example: - ``myContract.events.MyEvent()``. The exposed contract events are classes of the - type :py:class:`ContractEvent`. - -Methods -------- - -Each Contract Factory exposes the following methods. - -.. py:classmethod:: Contract.constructor(*args, **kwargs).transact(transaction=None) - - Construct and deploy a contract by sending a new public transaction. - - If provided ``transaction`` should be a dictionary conforming to the - ``web3.eth.sendTransaction(transaction)`` method. This value may not - contain the keys ``data`` or ``to``. - - If the contract takes constructor parameters they should be provided as - positional arguments or keyword arguments. - - If any of the arguments specified in the ABI are an ``address`` type, they - will accept ENS names. - - If a ``gas`` value is not provided, then the ``gas`` value for the - deployment transaction will be created using the ``web3.eth.estimateGas()`` - method. - - Returns the transaction hash for the deploy transaction. - - .. code-block:: python - - >>> deploy_txn = token_contract.constructor(web3.eth.coinbase, 12345).transact() - >>> txn_receipt = web3.eth.getTransactionReceipt(deploy_txn) - >>> txn_receipt['contractAddress'] - '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' - -.. py:classmethod:: Contract.constructor(*args, **kwargs).estimateGas(transaction=None) - - Estimate gas for constructing and deploying the contract. - - This method behaves the same as the - :py:meth:`Contract.constructor(*args, **kwargs).transact` method, - with transaction details being passed into the end portion of the - function call, and function arguments being passed into the first portion. - - Returns the amount of gas consumed which can be used as a gas estimate for - executing this transaction publicly. - - Returns the gas needed to deploy the contract. - - .. code-block:: python - - >>> token_contract.constructor(web3.eth.coinbase, 12345).estimateGas() - 12563 - -.. py:classmethod:: Contract.constructor(*args, **kwargs).buildTransaction(transaction=None) - - Construct the contract deploy transaction bytecode data. - - If the contract takes constructor parameters they should be provided as - positional arguments or keyword arguments. - - If any of the ``args`` specified in the ABI are an ``address`` type, they - will accept ENS names. - - Returns the transaction dictionary that you can pass to sendTransaction method. - - .. code-block:: python - - >>> transaction = { - 'gasPrice': w3.eth.gasPrice, - 'chainId': None - } - >>> contract_data = token_contract.constructor(web3.eth.coinbase, 12345).buildTransaction(transaction) - >>> web3.eth.sendTransaction(contract_data) - -.. _contract_createFilter: - -.. py:classmethod:: Contract.events.your_event_name.createFilter(fromBlock=block, toBlock=block, \ - argument_filters={"arg1": "value"}, topics=[]) - - Creates a new event filter, an instance of :py:class:`web3.utils.filters.LogFilter`. - - - ``fromBlock`` is a mandatory field. Defines the starting block (exclusive) filter block range. It can be either the starting block number, or 'latest' for the last mined block, or 'pending' for unmined transactions. In the case of ``fromBlock``, 'latest' and 'pending' set the 'latest' or 'pending' block as a static value for the starting filter block. - - ``toBlock`` optional. Defaults to 'latest'. Defines the ending block (inclusive) in the filter block range. Special values 'latest' and 'pending' set a dynamic range that always includes the 'latest' or 'pending' blocks for the filter's upper block range. - - ``address`` optional. Defaults to the contract address. The filter matches the event logs emanating from ``address``. - - ``argument_filters``, optional. Expects a dictionary of argument names and values. When provided event logs are filtered for the event argument values. Event arguments can be both indexed or unindexed. Indexed values with be translated to their corresponding topic arguments. Unindexed arguments will be filtered using a regular expression. - - ``topics`` optional, accepts the standard JSON-RPC topics argument. See the JSON-RPC documentation for `eth_newFilter `_ more information on the ``topics`` parameters. - -.. py:classmethod:: Contract.events.your_event_name.build_filter() - - Creates a EventFilterBuilder instance with the event abi, and the contract address if called from a deployed contract instance. The EventFilterBuilder provides a convenient way to construct the filter parameters with value checking against the event abi. It allows for defining multiple match values or of single values through the match_any and match_single methods. - - .. code-block:: python - - filter_builder = myContract.events.myEvent.build_filter() - filter_builder.fromBlock = "latest" - filter_builder.args.clientID.match_any(1, 2, 3, 4) - filter_builder.args.region.match_single("UK") - filter_instance = filter_builder.deploy() - - The ``deploy`` method returns a :py:class:`web3.utils.filters.LogFilter` instance from the filter parameters generated by the filter builder. Defining multiple match values for array arguments can be accomplished easily with the filter builder: - - .. code-block:: python - - filter_builder = myContract.events.myEvent.build_filter() - filter_builder.args.clientGroups.match_any((1, 3, 5,), (2, 3, 5), (1, 2, 3)) - - The filter builder blocks already defined filter parameters from being changed. - - .. code-block:: python - - filter_builder = myContract.events.myEvent.build_filter() - filter_builder.fromBlock = "latest" - filter_builder.fromBlock = 0 # raises a ValueError - - -.. py:classmethod:: Contract.deploy(transaction=None, args=None) - - .. warning:: Deprecated: this method is deprecated in favor of - :meth:`~Contract.constructor`, which provides more flexibility. - - Construct and send a transaction to deploy the contract. - - If provided ``transaction`` should be a dictionary conforming to the - ``web3.eth.sendTransaction(transaction)`` method. This value may not - contain the keys ``data`` or ``to``. - - If the contract takes constructor arguments they should be provided as a - list via the ``args`` parameter. - - If any of the ``args`` specified in the ABI are an ``address`` type, they - will accept ENS names. - - If a ``gas`` value is not provided, then the ``gas`` value for the - deployment transaction will be created using the ``web3.eth.estimateGas()`` - method. - - Returns the transaction hash for the deploy transaction. - - -.. py:classmethod:: Contract.encodeABI(fn_name, args=None, kwargs=None, data=None) - - Encodes the arguments using the Ethereum ABI for the contract function that - matches the given `fn_name` and arguements `args`. The `data` parameter - defaults to the function selector. - - .. code-block:: python - - >>> contract.encodeABI(fn_name="register", args=["rainbows", 10]) - "0xea87152b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000087261696e626f7773000000000000000000000000000000000000000000000000" - -.. py:classmethod:: Contract.all_functions() - - Returns a list of all the functions present in a Contract where every function is - an instance of :py:class:`ContractFunction`. - - .. code-block:: python - - >>> contract.all_functions() - [, ] - - -.. py:classmethod:: Contract.get_function_by_signature(signature) - - Searches for a distinct function with matching signature. Returns an instance of - :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no - match is found. - - .. code-block:: python - - >>> contract.get_function_by_signature('identity(uint256,bool)') - - - -.. py:classmethod:: Contract.find_functions_by_name(name) - - Searches for all function with matching name. Returns a list of matching functions - where every function is an instance of :py:class:`ContractFunction`. Returns an empty - list when no match is found. - - .. code-block:: python - - >>> contract.find_functions_by_name('identity') - [, ] - - -.. py:classmethod:: Contract.get_function_by_name(name) - - Searches for a distinct function with matching name. Returns an instance of - :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no - match is found or if multiple matches are found. - - .. code-block:: python - - >>> contract.get_function_by_name('unique_name') - - - -.. py:classmethod:: Contract.get_function_by_selector(selector) - - Searches for a distinct function with matching selector. - The selector can be a hexadecimal string, bytes or int. - Returns an instance of :py:class:`ContractFunction` upon finding a match. - Raises `ValueError` if no match is found. - - .. code-block:: python - - >>> contract.get_function_by_selector('0xac37eebb') - - >>> contract.get_function_by_selector(b'\xac7\xee\xbb') - - >>> contract.get_function_by_selector(0xac37eebb) - - - -.. py:classmethod:: Contract.find_functions_by_args(*args) - - Searches for all function with matching args. Returns a list of matching functions - where every function is an instance of :py:class:`ContractFunction`. Returns an empty - list when no match is found. - - .. code-block:: python - - >>> contract.find_functions_by_args(1, True) - [, ] - - -.. py:classmethod:: Contract.get_function_by_args(*args) - - Searches for a distinct function with matching args. Returns an instance of - :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no - match is found or if multiple matches are found. - - .. code-block:: python - - >>> contract.get_function_by_args(1) - - - -.. note:: - `Contract` methods `all_functions`, `get_function_by_signature`, `find_functions_by_name`, - `get_function_by_name`, `get_function_by_selector`, `find_functions_by_args` and - `get_function_by_args` can only be used when abi is provided to the contract. - - -.. note:: - `Web3.py` rejects the initialization of contracts that have more than one function - with the same selector or signature. - eg. `blockHashAddendsInexpansible(uint256)` and `blockHashAskewLimitary(uint256)` have the - same selector value equal to `0x00000000`. A contract containing both of these functions - will be rejected. - - -.. _ambiguous-contract-functions: - -Invoke Ambiguous Contract Functions Example -------------------------------------------- - -Below is an example of a contract that has multiple functions of the same name, -and the arguments are ambiguous. - -.. code-block:: python - - >>> contract_source_code = """ - pragma solidity ^0.4.21; - contract AmbiguousDuo { - function identity(uint256 input, bool uselessFlag) returns (uint256) { - return input; - } - function identity(int256 input, bool uselessFlag) returns (int256) { - return input; - } - } - """ - # fast forward all the steps of compiling and deploying the contract. - >>> ambiguous_contract.functions.identity(1, True) # raises ValidationError - - >>> identity_func = ambiguous_contract.get_function_by_signature('identity(uint256,bool)') - >>> identity_func(1, True) - - >>> identity_func(1, True).call() - 1 - - -.. _enable-strict-byte-check: - -Enabling Strict Checks for Bytes Types --------------------------------------- - -By default, web3 is not very strict when it comes to hex and bytes values. -A bytes type will take a hex string, a bytestring, or a regular python -string that can be decoded as a hex. -Additionally, if an abi specifies a byte size, but the value that gets -passed in is less than the specified size, web3 will automatically pad the value. -For example, if an abi specifies a type of ``bytes4``, web3 will handle all of the following values: - -.. list-table:: Valid byte and hex strings for a bytes4 type - :widths: 25 75 - :header-rows: 1 - - * - Input - - Normalizes to - * - ``''`` - - ``b'\x00\x00\x00\x00'`` - * - ``'0x'`` - - ``b'\x00\x00\x00\x00'`` - * - ``b''`` - - ``b'\x00\x00\x00\x00'`` - * - ``b'ab'`` - - ``b'ab\x00\x00'`` - * - ``'0xab'`` - - ``b'\xab\x00\x00\x00'`` - * - ``'1234'`` - - ``b'\x124\x00\x00'`` - * - ``'0x61626364'`` - - ``b'abcd'`` - * - ``'1234'`` - - ``b'1234'`` - - -The following values will raise an error by default: - -.. list-table:: Invalid byte and hex strings for a bytes4 type - :widths: 25 75 - :header-rows: 1 - - * - Input - - Reason - * - ``b'abcde'`` - - Bytestring with more than 4 bytes - * - ``'0x6162636423'`` - - Hex string with more than 4 bytes - * - ``2`` - - Wrong type - * - ``'ah'`` - - String is not valid hex - -However, you may want to be stricter with acceptable values for bytes types. -For this you can use the :meth:`w3.enable_strict_bytes_type_checking()` method, -which is available on the web3 instance. A web3 instance which has had this method -invoked will enforce a stricter set of rules on which values are accepted. - - - A Python string that is not prefixed with ``0x`` will throw an error. - - A bytestring whose length not exactly the specified byte size - will raise an error. - -.. list-table:: Valid byte and hex strings for a strict bytes4 type - :widths: 25 75 - :header-rows: 1 - - * - Input - - Normalizes to - * - ``'0x'`` - - ``b'\x00\x00\x00\x00'`` - * - ``'0x61626364'`` - - ``b'abcd'`` - * - ``'1234'`` - - ``b'1234'`` - -.. list-table:: Invalid byte and hex strings with strict bytes4 type checking - :widths: 25 75 - :header-rows: 1 - - * - Input - - Reason - * - ``''`` - - Needs to be prefixed with a "0x" to be interpreted as an empty hex string - * - ``'1234'`` - - Needs to either be a bytestring (b'1234') or be a hex value of the right size, prefixed with 0x (in this case: '0x31323334') - * - ``b''`` - - Needs to have exactly 4 bytes - * - ``b'ab'`` - - Needs to have exactly 4 bytes - * - ``'0xab'`` - - Needs to have exactly 4 bytes - * - ``'0x6162636464'`` - - Needs to have exactly 4 bytes - - -Taking the following contract code as an example: - -.. testsetup:: arrayscontract - - from web3 import Web3 - w3 = Web3(Web3.EthereumTesterProvider()) - bytecode = "608060405234801561001057600080fd5b506040516106103803806106108339810180604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185602082028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b505061019c565b82805482825590600052602060002090600f0160109004810192821561015a5791602002820160005b8382111561012a57835183826101000a81548161ffff02191690837e010000000000000000000000000000000000000000000000000000000000009004021790555092602001926002016020816001010492830192600103026100cc565b80156101585782816101000a81549061ffff021916905560020160208160010104928301926001030261012a565b505b509050610167919061016b565b5090565b61019991905b8082111561019557600081816101000a81549061ffff021916905550600101610171565b5090565b90565b610465806101ab6000396000f3fe608060405260043610610051576000357c0100000000000000000000000000000000000000000000000000000000900480633b3230ee14610056578063d7c8a410146100e7578063dfe3136814610153575b600080fd5b34801561006257600080fd5b5061008f6004803603602081101561007957600080fd5b8101908080359060200190929190505050610218565b60405180827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b3480156100f357600080fd5b506100fc61026c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561013f578082015181840152602081019050610124565b505050509050019250505060405180910390f35b34801561015f57600080fd5b506102166004803603602081101561017657600080fd5b810190808035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460208302840111640100000000831117156101c757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610326565b005b60008181548110151561022757fe5b9060005260206000209060109182820401919006600202915054906101000a90047e010000000000000000000000000000000000000000000000000000000000000281565b6060600080548060200260200160405190810160405280929190818152602001828054801561031c57602002820191906000526020600020906000905b82829054906101000a90047e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190600201906020826001010492830192600103820291508084116102a95790505b5050505050905090565b806000908051906020019061033c929190610340565b5050565b82805482825590600052602060002090600f016010900481019282156103f75791602002820160005b838211156103c757835183826101000a81548161ffff02191690837e01000000000000000000000000000000000000000000000000000000000000900402179055509260200192600201602081600101049283019260010302610369565b80156103f55782816101000a81549061ffff02191690556002016020816001010492830192600103026103c7565b505b5090506104049190610408565b5090565b61043691905b8082111561043257600081816101000a81549061ffff02191690555060010161040e565b5090565b9056fea165627a7a72305820a8f9f1f4815c1eedfb8df31298a5cd13b198895de878871328b5d96296b69b4e0029" - abi = ''' - [ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "bytes2Value", - "outputs": [ - { - "name": "", - "type": "bytes2" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getBytes2Value", - "outputs": [ - { - "name": "", - "type": "bytes2[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_bytes2Value", - "type": "bytes2[]" - } - ], - "name": "setBytes2Value", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "name": "_bytes2Value", - "type": "bytes2[]" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - } - ] - '''.strip() - -.. code-block:: python - - >>> # pragma solidity >=0.4.22 <0.6.0; - ... - ... # contract ArraysContract { - ... # bytes2[] public bytes2Value; - - ... # constructor(bytes2[] memory _bytes2Value) public { - ... # bytes2Value = _bytes2Value; - ... # } - - ... # function setBytes2Value(bytes2[] memory _bytes2Value) public { - ... # bytes2Value = _bytes2Value; - ... # } - - ... # function getBytes2Value() public view returns (bytes2[] memory) { - ... # return bytes2Value; - ... # } - ... # } - - >>> # abi = "..." - >>> # bytecode = "6080..." - -.. doctest:: arrayscontract - - >>> ArraysContract = w3.eth.contract(abi=abi, bytecode=bytecode) - - >>> tx_hash = ArraysContract.constructor([b'b']).transact() - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - - >>> array_contract = w3.eth.contract( - ... address=tx_receipt.contractAddress, - ... abi=abi - ... ) - - >>> array_contract.functions.getBytes2Value().call() - [b'b\x00'] - >>> array_contract.functions.setBytes2Value([b'a']).transact() - HexBytes('0x39bc9a0bf5b8ec8e8115ccb20bf02f5570351a20a8fd774da91353f38535bec1') - >>> array_contract.functions.getBytes2Value().call() - [b'a\x00'] - >>> w3.enable_strict_bytes_type_checking() - >>> array_contract.functions.setBytes2Value([b'a']).transact() - Traceback (most recent call last): - ... - ValidationError: - Could not identify the intended function with name `setBytes2Value` - -Contract Functions ------------------- - -.. py:class:: ContractFunction - -The named functions exposed through the :py:attr:`Contract.functions` property are -of the ContractFunction type. This class is not to be used directly, -but instead through :py:attr:`Contract.functions`. - -For example: - - .. code-block:: python - - myContract = web3.eth.contract(address=contract_address, abi=contract_abi) - twentyone = myContract.functions.multiply7(3).call() - -If you have the function name in a variable, you might prefer this alternative: - - .. code-block:: python - - func_to_call = 'multiply7' - contract_func = myContract.functions[func_to_call] - twentyone = contract_func(3).call() - -:py:class:`ContractFunction` provides methods to interact with contract functions. -Positional and keyword arguments supplied to the contract function subclass -will be used to find the contract function by signature, -and forwarded to the contract function when applicable. - -Methods -~~~~~~~~~~ - -.. py:method:: ContractFunction.transact(transaction) - - Execute the specified function by sending a new public transaction. - - Refer to the following invocation: - - .. code-block:: python - - myContract.functions.myMethod(*args, **kwargs).transact(transaction) - - The first portion of the function call ``myMethod(*args, **kwargs)`` - selects the appropriate contract function based on the name and provided - argument. Arguments can be provided as positional arguments, keyword - arguments, or a mix of the two. - - The end portion of this function call ``transact(transaction)`` takes a - single parameter which should be a python dictionary conforming to - the same format as the ``web3.eth.sendTransaction(transaction)`` method. - This dictionary may not contain the keys ``data``. - - If any of the ``args`` or ``kwargs`` specified in the ABI are an ``address`` type, they - will accept ENS names. - - If a ``gas`` value is not provided, then the ``gas`` value for the - method transaction will be created using the ``web3.eth.estimateGas()`` - method. - - Returns the transaction hash. - - .. code-block:: python - - >>> token_contract.functions.transfer(web3.eth.accounts[1], 12345).transact() - "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd" - - -.. py:method:: ContractFunction.call(transaction, block_identifier='latest') - - Call a contract function, executing the transaction locally using the - ``eth_call`` API. This will not create a new public transaction. - - Refer to the following invocation: - - .. code-block:: python - - myContract.functions.myMethod(*args, **kwargs).call(transaction) - - This method behaves the same as the :py:meth:`ContractFunction.transact` method, - with transaction details being passed into the end portion of the - function call, and function arguments being passed into the first portion. - - Returns the return value of the executed function. - - .. code-block:: python - - >>> my_contract.functions.multiply7(3).call() - 21 - >>> token_contract.functions.myBalance().call({'from': web3.eth.coinbase}) - 12345 # the token balance for `web3.eth.coinbase` - >>> token_contract.functions.myBalance().call({'from': web3.eth.accounts[1]}) - 54321 # the token balance for the account `web3.eth.accounts[1]` - - You can call the method at a historical block using ``block_identifier``. Some examples: - - .. code-block:: python - - # You can call your contract method at a block number: - >>> token_contract.functions.myBalance().call(block_identifier=10) - - # or a number of blocks back from pending, - # in this case, the block just before the latest block: - >>> token_contract.functions.myBalance().call(block_identifier=-2) - - # or a block hash: - >>> token_contract.functions.myBalance().call(block_identifier='0x4ff4a38b278ab49f7739d3a4ed4e12714386a9fdf72192f2e8f7da7822f10b4d') - >>> token_contract.functions.myBalance().call(block_identifier=b'O\xf4\xa3\x8b\'\x8a\xb4\x9fw9\xd3\xa4\xedN\x12qC\x86\xa9\xfd\xf7!\x92\xf2\xe8\xf7\xdax"\xf1\x0bM') - - # Latest is the default, so this is redundant: - >>> token_contract.functions.myBalance().call(block_identifier='latest') - - # You can check the state after your pending transactions (if supported by your node): - >>> token_contract.functions.myBalance().call(block_identifier='pending') - -.. py:method:: ContractFunction.estimateGas(transaction) - - Call a contract function, executing the transaction locally using the - ``eth_call`` API. This will not create a new public transaction. - - Refer to the following invocation: - - .. code-block:: python - - myContract.functions.myMethod(*args, **kwargs).estimateGas(transaction) - - This method behaves the same as the :py:meth:`ContractFunction.transact` method, - with transaction details being passed into the end portion of the - function call, and function arguments being passed into the first portion. - - Returns the amount of gas consumed which can be used as a gas estimate for - executing this transaction publicly. - - .. code-block:: python - - >>> my_contract.functions.multiply7(3).estimateGas() - 42650 - -.. py:method:: ContractFunction.buildTransaction(transaction) - - Builds a transaction dictionary based on the contract function call specified. - - Refer to the following invocation: - - .. code-block:: python - - myContract.functions.myMethod(*args, **kwargs).buildTransaction(transaction) - - This method behaves the same as the :py:meth:`Contract.transact` method, - with transaction details being passed into the end portion of the - function call, and function arguments being passed into the first portion. - - .. note:: - `nonce` is not returned as part of the transaction dictionary unless it is - specified in the first portion of the function call: - - .. code-block:: python - - >>> math_contract.functions.increment(5).buildTransaction({'nonce': 10}) - - You may use :meth:`~web3.eth.Eth.getTransactionCount` to get the current nonce - for an account. Therefore a shortcut for producing a transaction dictionary with - nonce included looks like: - - .. code-block:: python - - >>> math_contract.functions.increment(5).buildTransaction({'nonce': web3.eth.getTransactionCount('0xF5...')}) - - Returns a transaction dictionary. This transaction dictionary can then be sent using - :meth:`~web3.eth.Eth.sendTransaction`. - - Additionally, the dictionary may be used for offline transaction signing using - :meth:`~web3.eth.account.Account.signTransaction`. - - .. code-block:: python - - >>> math_contract.functions.increment(5).buildTransaction({'gasPrice': 21000000000}) - { - 'to': '0x6Bc272FCFcf89C14cebFC57B8f1543F5137F97dE', - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', - 'value': 0, - 'gas': 43242, - 'gasPrice': 21000000000, - 'chainId': 1 - } - -.. _fallback-function: - -Fallback Function -~~~~~~~~~~~~~~~~~ - - The Contract Factory also offers an API to interact with the fallback function, which supports four methods like - normal functions: - -.. py:method:: Contract.fallback.call(transaction) - - Call fallback function, executing the transaction locally using the - ``eth_call`` API. This will not create a new public transaction. - -.. py:method:: Contract.fallback.estimateGas(transaction) - - Call fallback function and return the gas estimation. - -.. py:method:: Contract.fallback.transact(transaction) - - Execute fallback function by sending a new public transaction. - -.. py:method:: Contract.fallback.buildTransaction(transaction) - - Builds a transaction dictionary based on the contract fallback function call. - -Events ------- - -.. py:class:: ContractEvents - -The named events exposed through the :py:attr:`Contract.events` property are of the ContractEvents type. This class is not to be used directly, but instead through :py:attr:`Contract.events`. - -For example: - - .. code-block:: python - - myContract = web3.eth.contract(address=contract_address, abi=contract_abi) - tx_hash = myContract.functions.myFunction().transact() - receipt = web3.eth.getTransactionReceipt(tx_hash) - myContract.events.myEvent().processReceipt(receipt) - -:py:class:`ContractEvent` provides methods to interact with contract events. Positional and keyword arguments supplied to the contract event subclass will be used to find the contract event by signature. - -.. _processReceipt: - -.. py:method:: ContractEvents.myEvent(*args, **kwargs).processReceipt(transaction_receipt, errors=WARN) - - Extracts the pertinent logs from a transaction receipt. - - If there are no errors, ``processReceipt`` returns a tuple of :ref:`Event Log Objects `, emitted from the event (e.g. ``myEvent``), - with decoded ouput. - - .. code-block:: python - - >>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address}) - >>> tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - >>> rich_logs = contract.events.myEvent().processReceipt(tx_receipt) - >>> rich_logs[0]['args'] - {'myArg': 12345} - - If there are errors, the logs will be handled differently depending on the flag that is passed in: - - - ``WARN`` (default) - logs a warning to the console for the log that has an error, and discards the log. Returns any logs that are able to be processed. - - ``STRICT`` - stops all processing and raises the error encountered. - - ``IGNORE`` - returns any raw logs that raised an error with an added "errors" field, along with any other logs were able to be processed. - - ``DISCARD`` - silently discards any logs that have errors, and returns processed logs that don't have errors. - - An event log error flag needs to be imported from ``web3/logs.py``. - - .. code-block:: python - - >>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address}) - >>> tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - >>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt) - >>> processed_logs - ( - AttributeDict({ - 'args': AttributeDict({}), - 'event': 'myEvent', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xfb95ccb6ab39e19821fb339dee33e7afe2545527725b61c64490a5613f8d11fa'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('0xd74c3e8bdb19337987b987aee0fa48ed43f8f2318edfc84e3a8643e009592a68'), - 'blockNumber': 3 - }) - ) - - - # Or, if there were errors encountered during processing: - >>> from web3.logs import STRICT, IGNORE, DISCARD, WARN - >>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt, errors=IGNORE) - >>> processed_logs - ( - AttributeDict({ - 'type': 'mined', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0x01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529'), - 'blockHash': HexBytes('0x92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a'), - 'blockNumber': 5, - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'data': '0x0000000000000000000000000000000000000000000000000000000000003039', - 'topics': [ - HexBytes('0xf70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb15') - ], - 'errors': LogTopicError('Expected 1 log topics. Got 0')}) - }) - ) - >>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt, errors=DISCARD) - >>> assert processed_logs == () - True - -.. py:method:: ContractEvents.myEvent(*args, **kwargs).processLog(log) - - Similar to processReceipt_, but only processes one log at a time, instead of a whole transaction receipt. - Will return a single :ref:`Event Log Object ` if there are no errors encountered during processing. If an error is encountered during processing, it will be raised. - - .. code-block:: python - - >>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address}) - >>> tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - >>> log_to_process = tx_receipt['logs'][0] - >>> processed_log = contract.events.myEvent().processLog(log_to_process) - >>> processed_log - AttributeDict({ - 'args': AttributeDict({}), - 'event': 'myEvent', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xfb95ccb6ab39e19821fb339dee33e7afe2545527725b61c64490a5613f8d11fa'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('0xd74c3e8bdb19337987b987aee0fa48ed43f8f2318edfc84e3a8643e009592a68'), - 'blockNumber': 3 - }) - - -.. _event-log-object: - -Event Log Object -~~~~~~~~~~~~~~~~ - - The Event Log Object is a python dictionary with the following keys: - - * ``args``: Dictionary - The arguments coming from the event. - * ``event``: String - The event name. - * ``logIndex``: Number - integer of the log index position in the block. - * ``transactionIndex``: Number - integer of the transactions index position - log was created from. - * ``transactionHash``: String, 32 Bytes - hash of the transactions this log - was created from. - * ``address``: String, 32 Bytes - address from which this log originated. - * ``blockHash``: String, 32 Bytes - hash of the block where this log was - in. null when it's pending. - * ``blockNumber``: Number - the block number where this log was in. null - when it's pending. - -.. testsetup:: createFilter - - from web3 import Web3 - w3 = Web3(Web3.EthereumTesterProvider()) - bytecode = '6060604052341561000c57fe5b604051602080610acb833981016040528080519060200190919050505b620f42408114151561003b5760006000fd5b670de0b6b3a76400008102600281905550600254600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b505b610a27806100a46000396000f30060606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610099578063095ea7b31461013257806318160ddd1461018957806323b872dd146101af578063313ce5671461022557806370a082311461025157806395d89b411461029b578063a9059cbb14610334578063dd62ed3e1461038b575bfe5b34156100a157fe5b6100a96103f4565b60405180806020018281038252838181518152602001915080519060200190808383600083146100f8575b8051825260208311156100f8576020820191506020810190506020830392506100d4565b505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013a57fe5b61016f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061042e565b604051808215151515815260200191505060405180910390f35b341561019157fe5b610199610521565b6040518082815260200191505060405180910390f35b34156101b757fe5b61020b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610527565b604051808215151515815260200191505060405180910390f35b341561022d57fe5b610235610791565b604051808260ff1660ff16815260200191505060405180910390f35b341561025957fe5b610285600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610796565b6040518082815260200191505060405180910390f35b34156102a357fe5b6102ab6107e0565b60405180806020018281038252838181518152602001915080519060200190808383600083146102fa575b8051825260208311156102fa576020820191506020810190506020830392506102d6565b505050905090810190601f1680156103265780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561033c57fe5b610371600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061081a565b604051808215151515815260200191505060405180910390f35b341561039357fe5b6103de600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610973565b6040518082815260200191505060405180910390f35b604060405190810160405280600981526020017f54657374546f6b656e000000000000000000000000000000000000000000000081525081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a3600190505b92915050565b60025481565b600081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410806105f1575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054105b156105fc5760006000fd5b81600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b9392505050565b601281565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b604060405190810160405280600481526020017f544553540000000000000000000000000000000000000000000000000000000081525081565b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156108695760006000fd5b81600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b929150505600a165627a7a723058205071371ee2a4a1be3c96e77d939cdc26161a256fdd638efc08bd33dfc65d3b850029' - ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"inputs":[{"name":"_totalSupply","type":"uint256"}],"payable":false,"type":"constructor","stateMutability":"nonpayable"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]' - my_token_contract = w3.eth.contract(abi=ABI, bytecode=bytecode) - alice, bob = w3.eth.accounts[0], w3.eth.accounts[1] - assert alice == '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', alice - assert bob == '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', bob - tx_hash = my_token_contract.constructor(1000000).transact({'from': alice}) - assert tx_hash == b'h9\xeb\xdb4\x07\x03y\x92RP`X\xf6\xf7\x9f\xfaT\xed&e\xee*\xc2\rx\xb3\xab\x8c4\xc9\x1f', tx_hash - txn_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - assert txn_receipt['contractAddress'] == '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - contract = w3.eth.contract(contract_address, abi=ABI) - total_supply = contract.functions.totalSupply().call() - decimals = 10 ** 18 - assert total_supply == 1000000 * decimals, total_supply - tx_hash = contract.functions.transfer(alice, 10).transact() - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - -.. doctest:: createFilter - - >>> transfer_filter = my_token_contract.events.Transfer.createFilter(fromBlock="0x0", argument_filters={'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf'}) - >>> transfer_filter.get_new_entries() - [AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'value': 10}), - 'event': 'Transfer', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xc7b96b166506c5a6edf6bccd22195e9f1aac025421b4a3eac159b878eef5e6e7'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('...'), - 'blockNumber': 2})] - >>> transfer_filter.get_new_entries() - [] - >>> tx_hash = contract.functions.transfer(alice, 10).transact() - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - >>> transfer_filter.get_new_entries() - [AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'value': 10}), - 'event': 'Transfer', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xb1cf8541708184daf8c1ea59ce494bbafe0bb45208c09a81bff184907e88e9b9'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('...'), - 'blockNumber': 3})] - >>> transfer_filter.get_all_entries() - [AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'value': 10}), - 'event': 'Transfer', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xc7b96b166506c5a6edf6bccd22195e9f1aac025421b4a3eac159b878eef5e6e7'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('...'), - 'blockNumber': 2}), - AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'value': 10}), - 'event': 'Transfer', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('0xb1cf8541708184daf8c1ea59ce494bbafe0bb45208c09a81bff184907e88e9b9'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('...'), - 'blockNumber': 3})] - -Utils ------ - -.. py:classmethod:: Contract.decode_function_input(data) - - Decodes the transaction data used to invoke a smart contract function, and returns - :py:class:`ContractFunction` and decoded parameters as :py:class:`dict`. - - .. code-block:: python - - >>> transaction = w3.eth.getTransaction('0x5798fbc45e3b63832abc4984b0f3574a13545f415dd672cd8540cd71f735db56') - >>> transaction.input - '0x612e45a3000000000000000000000000b656b2a9c3b2416437a811e07466ca712f5a5b5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000116c6f6e656c792c20736f206c6f6e656c7900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - >>> contract.decode_function_input(transaction.input) - (, - {'_recipient': '0xB656b2a9c3b2416437A811e07466cA712F5a5b5a', - '_amount': 0, - '_description': b'lonely, so lonely', - '_transactionData': b'', - '_debatingPeriod': 604800, - '_newCurator': True}) - -ContractCaller --------------- - -.. py:class:: ContractCaller - -The ``ContractCaller`` class provides an API to call functions in a contract. This class -is not to be used directly, but instead through ``Contract.caller``. - -There are a number of different ways to invoke the ``ContractCaller``. - -For example: - -.. testsetup:: contractcaller - - import json - from web3 import Web3 - w3 = Web3(Web3.EthereumTesterProvider()) - bytecode = "0x606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" - ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"increased","type":"event"}]') - contract = w3.eth.contract(abi=ABI, bytecode=bytecode) - deploy_txn = contract.constructor().transact() - deploy_receipt = w3.eth.waitForTransactionReceipt(deploy_txn) - address = deploy_receipt.contractAddress - -.. doctest:: contractcaller - - >>> myContract = w3.eth.contract(address=address, abi=ABI) - >>> twentyone = myContract.caller.multiply7(3) - >>> twentyone - 21 - -It can also be invoked using parentheses: - -.. doctest:: contractcaller - - >>> twentyone = myContract.caller().multiply7(3) - >>> twentyone - 21 - -And a transaction dictionary, with or without the ``transaction`` keyword. -You can also optionally include a block identifier. For example: - -.. doctest:: contractcaller - - >>> from_address = w3.eth.accounts[1] - >>> twentyone = myContract.caller({'from': from_address}).multiply7(3) - >>> twentyone - 21 - >>> twentyone = myContract.caller(transaction={'from': from_address}).multiply7(3) - >>> twentyone - 21 - >>> twentyone = myContract.caller(block_identifier='latest').multiply7(3) - >>> twentyone - 21 - -Like :py:class:`ContractFunction`, :py:class:`ContractCaller` -provides methods to interact with contract functions. -Positional and keyword arguments supplied to the contract caller subclass -will be used to find the contract function by signature, -and forwarded to the contract function when applicable. +.. _contracts: + +Contracts +========= + +.. py:module:: web3.contract + +It is worth taking your time to understand all about contracts. To get started, +check out this example: + +.. _contract_example: + +Contract Deployment Example +---------------------------------------------- + +To run this example, you will need to install a few extra features: + +- The sandbox node provided by vns-tester. You can install it with ``pip install -U web3[tester]``. +- The ``solc`` solidity compiler. See `Installing the Solidity Compiler + `_ + +.. code-block:: python + + import json + import web3 + + from web3 import Web3 + from solc import compile_source + + # Solidity source code + contract_source_code = """ + pragma solidity ^0.4.21; + + contract Greeter { + string public greeting; + + function Greeter() public { + greeting = 'Hello'; + } + + function setGreeting(string _greeting) public { + greeting = _greeting; + } + + function greet() view public returns (string) { + return greeting; + } + } + """ + + compiled_sol = compile_source(contract_source_code) # Compiled source code + contract_interface = compiled_sol[':Greeter'] + + # web3.py instance + w3 = Web3(Web3.EthereumTesterProvider()) + + # set pre-funded account as sender + w3.vns.defaultAccount = w3.vns.accounts[0] + + # Instantiate and deploy contract + Greeter = w3.vns.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + + # Submit the transaction that deploys the contract + tx_hash = Greeter.constructor().transact() + + # Wait for the transaction to be mined, and get the transaction receipt + tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + + # Create the contract instance with the newly-deployed address + greeter = w3.vns.contract( + address=tx_receipt.contractAddress, + abi=contract_interface['abi'], + ) + + # Display the default greeting from the contract + print('Default contract greeting: {}'.format( + greeter.functions.greet().call() + )) + + print('Setting the greeting to Nihao...') + tx_hash = greeter.functions.setGreeting('Nihao').transact() + + # Wait for transaction to be mined... + w3.vns.waitForTransactionReceipt(tx_hash) + + # Display the new greeting value + print('Updated contract greeting: {}'.format( + greeter.functions.greet().call() + )) + +Contract Factories +------------------ + +These factories are not intended to be initialized directly. +Instead, create contract objects using the :meth:`w3.vns.contract() ` +method. By default, the contract factory is :class:`Contract`. See the +example in :class:`ConciseContract` for specifying an alternate factory. + +.. py:class:: Contract(address) + + Contract provides a default interface for deploying and interacting with + Ethereum smart contracts. + + The address parameter can be a hex address or an ENS name, like ``mycontract.vns``. + +.. py:class:: ConciseContract(Contract()) + + .. warning:: Deprecated: This method is deprecated in favor of the :class:`~ContractCaller` API + or the verbose syntax + + This variation of :class:`Contract` is designed for more succinct read access, + without making write access more wordy. This comes at a cost of losing + access to features like ``deploy()`` and properties like ``address``. It is + recommended to use the classic ``Contract`` for those use cases. + Just to be be clear, `ConciseContract` only exposes contract functions and all + other `Contract` class methods and properties are not available with the `ConciseContract` + API. This includes but is not limited to ``contract.address``, ``contract.abi``, and + ``contract.deploy()``. + + Create this type of contract by passing a :py:class:`Contract` instance to + :class:`ConciseContract`: + + + .. code-block:: python + + >>> concise = ConciseContract(myContract) + + + This variation invokes all methods as a call, so if the classic contract had a method like + ``contract.functions.owner().call()``, you could call it with ``concise.owner()`` instead. + + For access to send a transaction or estimate gas, you can add a keyword argument like so: + + + .. code-block:: python + + >>> concise.withdraw(amount, transact={'from': vns.accounts[1], 'gas': 100000, ...}) + + >>> # which is equivalent to this transaction in the classic contract: + + >>> contract.functions.withdraw(amount).transact({'from': vns.accounts[1], 'gas': 100000, ...}) + +.. py:class:: ImplicitContract(Contract()) + + .. warning:: Deprecated: This method is deprecated in favor of the verbose syntax + + This variation mirrors :py:class:`ConciseContract`, but it invokes all methods as a + transaction rather than a call, so if the classic contract had a method like + ``contract.functions.owner.transact()``, you could call it with ``implicit.owner()`` instead. + + Create this type of contract by passing a :py:class:`Contract` instance to + :class:`ImplicitContract`: + + + .. code-block:: python + + >>> concise = ImplicitContract(myContract) + + +Properties +---------- + +Each Contract Factory exposes the following properties. + + +.. py:attribute:: Contract.address + + The hexadecimal encoded 20-byte address of the contract, or an ENS name. + May be ``None`` if not provided during factory creation. + + +.. py:attribute:: Contract.abi + + The contract ABI array. + + +.. py:attribute:: Contract.bytecode + + The contract bytecode string. May be ``None`` if not provided during + factory creation. + + +.. py:attribute:: Contract.bytecode_runtime + + The runtime part of the contract bytecode string. May be ``None`` if not + provided during factory creation. + +.. py:attribute:: Contract.functions + + This provides access to contract functions as attributes. For example: + ``myContract.functions.MyMethod()``. The exposed contract functions are classes of the + type :py:class:`ContractFunction`. + +.. py:attribute:: Contract.events + + This provides access to contract events as attributes. For example: + ``myContract.events.MyEvent()``. The exposed contract events are classes of the + type :py:class:`ContractEvent`. + +Methods +------- + +Each Contract Factory exposes the following methods. + +.. py:classmethod:: Contract.constructor(*args, **kwargs).transact(transaction=None) + + Construct and deploy a contract by sending a new public transaction. + + If provided ``transaction`` should be a dictionary conforming to the + ``web3.vns.sendTransaction(transaction)`` method. This value may not + contain the keys ``data`` or ``to``. + + If the contract takes constructor parameters they should be provided as + positional arguments or keyword arguments. + + If any of the arguments specified in the ABI are an ``address`` type, they + will accept ENS names. + + If a ``gas`` value is not provided, then the ``gas`` value for the + deployment transaction will be created using the ``web3.vns.estimateGas()`` + method. + + Returns the transaction hash for the deploy transaction. + + .. code-block:: python + + >>> deploy_txn = token_contract.constructor(web3.vns.coinbase, 12345).transact() + >>> txn_receipt = web3.vns.getTransactionReceipt(deploy_txn) + >>> txn_receipt['contractAddress'] + '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' + +.. py:classmethod:: Contract.constructor(*args, **kwargs).estimateGas(transaction=None) + + Estimate gas for constructing and deploying the contract. + + This method behaves the same as the + :py:meth:`Contract.constructor(*args, **kwargs).transact` method, + with transaction details being passed into the end portion of the + function call, and function arguments being passed into the first portion. + + Returns the amount of gas consumed which can be used as a gas estimate for + executing this transaction publicly. + + Returns the gas needed to deploy the contract. + + .. code-block:: python + + >>> token_contract.constructor(web3.vns.coinbase, 12345).estimateGas() + 12563 + +.. py:classmethod:: Contract.constructor(*args, **kwargs).buildTransaction(transaction=None) + + Construct the contract deploy transaction bytecode data. + + If the contract takes constructor parameters they should be provided as + positional arguments or keyword arguments. + + If any of the ``args`` specified in the ABI are an ``address`` type, they + will accept ENS names. + + Returns the transaction dictionary that you can pass to sendTransaction method. + + .. code-block:: python + + >>> transaction = { + 'gasPrice': w3.vns.gasPrice, + 'chainId': None + } + >>> contract_data = token_contract.constructor(web3.vns.coinbase, 12345).buildTransaction(transaction) + >>> web3.vns.sendTransaction(contract_data) + +.. _contract_createFilter: + +.. py:classmethod:: Contract.events..createFilter(fromBlock=block, toBlock=block, argument_filters={"arg1": "value"}, topics=[]) + + Creates a new event filter, an instance of :py:class:`web3.utils.filters.LogFilter`. + + ``fromBlock`` is a mandatory field. Defines the starting block (exclusive) filter block range. It can be either the starting block number, or 'latest' for the last mined block, or 'pending' for unmined transactions. In the case of ``fromBlock``, 'latest' and 'pending' set the 'latest' or 'pending' block as a static value for the starting filter block. + ``toBlock`` optional. Defaults to 'latest'. Defines the ending block (inclusive) in the filter block range. Special values 'latest' and 'pending' set a dynamic range that always includes the 'latest' or 'pending' blocks for the filter's upper block range. + ``address`` optional. Defaults to the contract address. The filter matches the event logs emanating from ``address``. + ``argument_filters``, optional. Expects a dictionary of argument names and values. When provided event logs are filtered for the event argument values. Event arguments can be both indexed or unindexed. Indexed values with be translated to their corresponding topic arguments. Unindexed arguments will be filtered using a regular expression. + ``topics`` optional, accepts the standard JSON-RPC topics argument. See the JSON-RPC documentation for `vns_newFilter `_ more information on the ``topics`` parameters. + +.. _contract_build_filter: + +.. py:classmethod:: Contract.events..build_filter() + + Creates a EventFilterBuilder instance with the event abi, and the contract address if called from a deployed contract instance. The EventFilterBuilder provides a convenient way to construct the filter parameters with value checking against the event abi. It allows for defining multiple match values or of single values through the match_any and match_single methods. + + .. code-block:: python + + filter_builder = myContract.events.myEvent.build_filter() + filter_builder.fromBlock = "latest" + filter_builder.args.clientID.match_any(1, 2, 3, 4) + filter_builder.args.region.match_single("UK") + filter_instance = filter_builder.deploy() + + The ``deploy`` method returns a :py:class:`web3.utils.filters.LogFilter` instance from the filter parameters generated by the filter builder. Defining multiple match values for array arguments can be accomplished easily with the filter builder: + + .. code-block:: python + + filter_builder = myContract.events.myEvent.build_filter() + filter_builder.args.clientGroups.match_any((1, 3, 5,), (2, 3, 5), (1, 2, 3)) + + The filter builder blocks already defined filter parameters from being changed. + + .. code-block:: python + + filter_builder = myContract.events.myEvent.build_filter() + filter_builder.fromBlock = "latest" + filter_builder.fromBlock = 0 # raises a ValueError + + +.. py:classmethod:: Contract.deploy(transaction=None, args=None) + + .. warning:: Deprecated: this method is deprecated in favor of + :meth:`~Contract.constructor`, which provides more flexibility. + + Construct and send a transaction to deploy the contract. + + If provided ``transaction`` should be a dictionary conforming to the + ``web3.vns.sendTransaction(transaction)`` method. This value may not + contain the keys ``data`` or ``to``. + + If the contract takes constructor arguments they should be provided as a + list via the ``args`` parameter. + + If any of the ``args`` specified in the ABI are an ``address`` type, they + will accept ENS names. + + If a ``gas`` value is not provided, then the ``gas`` value for the + deployment transaction will be created using the ``web3.vns.estimateGas()`` + method. + + Returns the transaction hash for the deploy transaction. + + +.. py:classmethod:: Contract.encodeABI(fn_name, args=None, kwargs=None, data=None) + + Encodes the arguments using the Ethereum ABI for the contract function that + matches the given `fn_name` and arguements `args`. The `data` parameter + defaults to the function selector. + + .. code-block:: python + + >>> contract.encodeABI(fn_name="register", args=["rainbows", 10]) + "0xea87152b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000087261696e626f7773000000000000000000000000000000000000000000000000" + +.. py:classmethod:: Contract.all_functions() + + Returns a list of all the functions present in a Contract where every function is + an instance of :py:class:`ContractFunction`. + + .. code-block:: python + + >>> contract.all_functions() + [, ] + + +.. py:classmethod:: Contract.get_function_by_signature(signature) + + Searches for a distinct function with matching signature. Returns an instance of + :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no + match is found. + + .. code-block:: python + + >>> contract.get_function_by_signature('identity(uint256,bool)') + + + +.. py:classmethod:: Contract.find_functions_by_name(name) + + Searches for all function with matching name. Returns a list of matching functions + where every function is an instance of :py:class:`ContractFunction`. Returns an empty + list when no match is found. + + .. code-block:: python + + >>> contract.find_functions_by_name('identity') + [, ] + + +.. py:classmethod:: Contract.get_function_by_name(name) + + Searches for a distinct function with matching name. Returns an instance of + :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no + match is found or if multiple matches are found. + + .. code-block:: python + + >>> contract.get_function_by_name('unique_name') + + + +.. py:classmethod:: Contract.get_function_by_selector(selector) + + Searches for a distinct function with matching selector. + The selector can be a hexadecimal string, bytes or int. + Returns an instance of :py:class:`ContractFunction` upon finding a match. + Raises `ValueError` if no match is found. + + .. code-block:: python + + >>> contract.get_function_by_selector('0xac37eebb') + + >>> contract.get_function_by_selector(b'\xac7\xee\xbb') + + >>> contract.get_function_by_selector(0xac37eebb) + + + +.. py:classmethod:: Contract.find_functions_by_args(*args) + + Searches for all function with matching args. Returns a list of matching functions + where every function is an instance of :py:class:`ContractFunction`. Returns an empty + list when no match is found. + + .. code-block:: python + + >>> contract.find_functions_by_args(1, True) + [, ] + + +.. py:classmethod:: Contract.get_function_by_args(*args) + + Searches for a distinct function with matching args. Returns an instance of + :py:class:`ContractFunction` upon finding a match. Raises `ValueError` if no + match is found or if multiple matches are found. + + .. code-block:: python + + >>> contract.get_function_by_args(1) + + + +.. note:: + `Contract` methods `all_functions`, `get_function_by_signature`, `find_functions_by_name`, + `get_function_by_name`, `get_function_by_selector`, `find_functions_by_args` and + `get_function_by_args` can only be used when abi is provided to the contract. + + +.. note:: + `Web3.py` rejects the initialization of contracts that have more than one function + with the same selector or signature. + eg. `blockHashAddendsInexpansible(uint256)` and `blockHashAskewLimitary(uint256)` have the + same selector value equal to `0x00000000`. A contract containing both of these functions + will be rejected. + + +.. _ambiguous-contract-functions: + +Invoke Ambiguous Contract Functions Example +------------------------------------------- + +Below is an example of a contract that has multiple functions of the same name, +and the arguments are ambiguous. + +.. code-block:: python + + >>> contract_source_code = """ + pragma solidity ^0.4.21; + contract AmbiguousDuo { + function identity(uint256 input, bool uselessFlag) returns (uint256) { + return input; + } + function identity(int256 input, bool uselessFlag) returns (int256) { + return input; + } + } + """ + # fast forward all the steps of compiling and deploying the contract. + >>> ambiguous_contract.functions.identity(1, True) # raises ValidationError + + >>> identity_func = ambiguous_contract.get_function_by_signature('identity(uint256,bool)') + >>> identity_func(1, True) + + >>> identity_func(1, True).call() + 1 + + +Contract Functions +------------------ + +.. py:class:: ContractFunction + +The named functions exposed through the :py:attr:`Contract.functions` property are +of the ContractFunction type. This class is not to be used directly, +but instead through :py:attr:`Contract.functions`. + +For example: + + .. code-block:: python + + myContract = web3.vns.contract(address=contract_address, abi=contract_abi) + twentyone = myContract.functions.multiply7(3).call() + +If you have the function name in a variable, you might prefer this alternative: + + .. code-block:: python + + func_to_call = 'multiply7' + contract_func = myContract.functions[func_to_call] + twentyone = contract_func(3).call() + +:py:class:`ContractFunction` provides methods to interact with contract functions. +Positional and keyword arguments supplied to the contract function subclass +will be used to find the contract function by signature, +and forwarded to the contract function when applicable. + +Methods +~~~~~~~~~~ + +.. py:method:: ContractFunction.transact(transaction) + + Execute the specified function by sending a new public transaction. + + Refer to the following invocation: + + .. code-block:: python + + myContract.functions.myMethod(*args, **kwargs).transact(transaction) + + The first portion of the function call ``myMethod(*args, **kwargs)`` + selects the appropriate contract function based on the name and provided + argument. Arguments can be provided as positional arguments, keyword + arguments, or a mix of the two. + + The end portion of this function call ``transact(transaction)`` takes a + single parameter which should be a python dictionary conforming to + the same format as the ``web3.vns.sendTransaction(transaction)`` method. + This dictionary may not contain the keys ``data``. + + If any of the ``args`` or ``kwargs`` specified in the ABI are an ``address`` type, they + will accept ENS names. + + If a ``gas`` value is not provided, then the ``gas`` value for the + method transaction will be created using the ``web3.vns.estimateGas()`` + method. + + Returns the transaction hash. + + .. code-block:: python + + >>> token_contract.functions.transfer(web3.vns.accounts[1], 12345).transact() + "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd" + + +.. py:method:: ContractFunction.call(transaction, block_identifier='latest') + + Call a contract function, executing the transaction locally using the + ``vns_call`` API. This will not create a new public transaction. + + Refer to the following invocation: + + .. code-block:: python + + myContract.functions.myMethod(*args, **kwargs).call(transaction) + + This method behaves the same as the :py:meth:`ContractFunction.transact` method, + with transaction details being passed into the end portion of the + function call, and function arguments being passed into the first portion. + + Returns the return value of the executed function. + + .. code-block:: python + + >>> my_contract.functions.multiply7(3).call() + 21 + >>> token_contract.functions.myBalance().call({'from': web3.vns.coinbase}) + 12345 # the token balance for `web3.vns.coinbase` + >>> token_contract.functions.myBalance().call({'from': web3.vns.accounts[1]}) + 54321 # the token balance for the account `web3.vns.accounts[1]` + + You can call the method at a historical block using ``block_identifier``. Some examples: + + .. code-block:: python + + # You can call your contract method at a block number: + >>> token_contract.functions.myBalance().call(block_identifier=10) + + # or a number of blocks back from pending, + # in this case, the block just before the latest block: + >>> token_contract.functions.myBalance().call(block_identifier=-2) + + # or a block hash: + >>> token_contract.functions.myBalance().call(block_identifier='0x4ff4a38b278ab49f7739d3a4ed4e12714386a9fdf72192f2e8f7da7822f10b4d') + >>> token_contract.functions.myBalance().call(block_identifier=b'O\xf4\xa3\x8b\'\x8a\xb4\x9fw9\xd3\xa4\xedN\x12qC\x86\xa9\xfd\xf7!\x92\xf2\xe8\xf7\xdax"\xf1\x0bM') + + # Latest is the default, so this is redundant: + >>> token_contract.functions.myBalance().call(block_identifier='latest') + + # You can check the state after your pending transactions (if supported by your node): + >>> token_contract.functions.myBalance().call(block_identifier='pending') + +.. py:method:: ContractFunction.estimateGas(transaction) + + Call a contract function, executing the transaction locally using the + ``vns_call`` API. This will not create a new public transaction. + + Refer to the following invocation: + + .. code-block:: python + + myContract.functions.myMethod(*args, **kwargs).estimateGas(transaction) + + This method behaves the same as the :py:meth:`ContractFunction.transact` method, + with transaction details being passed into the end portion of the + function call, and function arguments being passed into the first portion. + + Returns the amount of gas consumed which can be used as a gas estimate for + executing this transaction publicly. + + .. code-block:: python + + >>> my_contract.functions.multiply7(3).estimateGas() + 42650 + +.. py:method:: ContractFunction.buildTransaction(transaction) + + Builds a transaction dictionary based on the contract function call specified. + + Refer to the following invocation: + + .. code-block:: python + + myContract.functions.myMethod(*args, **kwargs).buildTransaction(transaction) + + This method behaves the same as the :py:meth:`Contract.transact` method, + with transaction details being passed into the end portion of the + function call, and function arguments being passed into the first portion. + + .. note:: + `nonce` is not returned as part of the transaction dictionary unless it is + specified in the first portion of the function call: + + .. code-block:: python + + >>> math_contract.functions.increment(5).buildTransaction({'nonce': 10}) + + You may use :meth:`~web3.vns.Bbbbbbbb.getTransactionCount` to get the current nonce + for an account. Therefore a shortcut for producing a transaction dictionary with + nonce included looks like: + + .. code-block:: python + + >>> math_contract.functions.increment(5).buildTransaction({'nonce': web3.vns.getTransactionCount('0xF5...')}) + + Returns a transaction dictionary. This transaction dictionary can then be sent using + :meth:`~web3.vns.Bbbbbbbb.sendTransaction`. + + Additionally, the dictionary may be used for offline transaction signing using + :meth:`~web3.vns.account.Account.signTransaction`. + + .. code-block:: python + + >>> math_contract.functions.increment(5).buildTransaction({'gasPrice': 21000000000}) + { + 'to': '0x6Bc272FCFcf89C14cebFC57B8f1543F5137F97dE', + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', + 'value': 0, + 'gas': 43242, + 'gasPrice': 21000000000, + 'chainId': 1 + } + +.. _fallback-function: + +Fallback Function +~~~~~~~~~~~~~~~~~ + + The Contract Factory also offers an API to interact with the fallback function, which supports four methods like + normal functions: + +.. py:method:: Contract.fallback.call(transaction) + + Call fallback function, executing the transaction locally using the + ``vns_call`` API. This will not create a new public transaction. + +.. py:method:: Contract.fallback.estimateGas(transaction) + + Call fallback function and return the gas estimation. + +.. py:method:: Contract.fallback.transact(transaction) + + Execute fallback function by sending a new public transaction. + +.. py:method:: Contract.fallback.buildTransaction(transaction) + + Builds a transaction dictionary based on the contract fallback function call. + +Events +------ + +.. py:class:: ContractEvents + +The named events exposed through the :py:attr:`Contract.events` property are of the ContractEvents type. This class is not to be used directly, but instead through :py:attr:`Contract.events`. + +For example: + + .. code-block:: python + + myContract = web3.vns.contract(address=contract_address, abi=contract_abi) + tx_hash = myContract.functions.myFunction().transact() + receipt = web3.vns.getTransactionReceipt(tx_hash) + myContract.events.myEvent().processReceipt(receipt) + +:py:class:`ContractEvent` provides methods to interact with contract events. Positional and keyword arguments supplied to the contract event subclass will be used to find the contract event by signature. + +.. py:method:: ContractEvents.myEvent(*args, **kwargs).processReceipt(transaction_receipt) + + Extracts the pertinent logs from a transaction receipt. + + Returns a tuple of :ref:`Event Log Objects `, emitted from the event (e.g. ``myEvent``), + with decoded ouput. + + .. code-block:: python + + >>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address}) + >>> tx_receipt = w3.vns.getTransactionReceipt(tx_hash) + >>> rich_logs = contract.events.myEvent().processReceipt(tx_receipt) + >>> rich_logs[0]['args'] + {'myArg': 12345} + +Utils +----- + +.. py:classmethod:: Contract.decode_function_input(data) + + Decodes the transaction data used to invoke a smart contract function, and returns + :py:class:`ContractFunction` and decoded parameters as :py:class:`dict`. + + .. code-block:: python + + >>> transaction = w3.vns.getTransaction('0x5798fbc45e3b63832abc4984b0f3574a13545f415dd672cd8540cd71f735db56') + >>> transaction.input + '0x612e45a3000000000000000000000000b656b2a9c3b2416437a811e07466ca712f5a5b5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000116c6f6e656c792c20736f206c6f6e656c7900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + >>> contract.decode_function_input(transaction.input) + (, + {'_recipient': '0xb656b2a9c3b2416437a811e07466ca712f5a5b5a', + '_amount': 0, + '_description': b'lonely, so lonely', + '_transactionData': b'', + '_debatingPeriod': 604800, + '_newCurator': True}) + +ContractCaller +-------------- + +.. py:class:: ContractCaller + +The :py:class:``ContractCaller`` class provides an API to call functions in a contract. This class +is not to be used directly, but instead through ``Contract.caller``. + +There are a number of different ways to invoke the ``ContractCaller``. + +For example: + +.. testsetup:: + + import json + from web3 import Web3 + w3 = Web3(Web3.EthereumTesterProvider()) + bytecode = "0x606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" + ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') + contract = w3.vns.contract(abi=ABI, bytecode=bytecode) + deploy_txn = contract.constructor().transact() + deploy_receipt = w3.vns.waitForTransactionReceipt(deploy_txn) + address = deploy_receipt.contractAddress + +.. doctest:: + + >>> myContract = w3.vns.contract(address=address, abi=ABI) + >>> twentyone = myContract.caller.multiply7(3) + >>> twentyone + 21 + +It can also be invoked using parentheses: + +.. doctest:: + + >>> twentyone = myContract.caller().multiply7(3) + >>> twentyone + 21 + +And a transaction dictionary, with or without the ``transaction`` keyword. +You can also optionally include a block identifier. For example: + +.. doctest:: + + >>> from_address = w3.vns.accounts[1] + >>> twentyone = myContract.caller({'from': from_address}).multiply7(3) + >>> twentyone + 21 + >>> twentyone = myContract.caller(transaction={'from': from_address}).multiply7(3) + >>> twentyone + 21 + >>> twentyone = myContract.caller(block_identifier='latest').multiply7(3) + >>> twentyone + 21 + +Like :py:class:`ContractFunction`, :py:class:`ContractCaller` +provides methods to interact with contract functions. +Positional and keyword arguments supplied to the contract caller subclass +will be used to find the contract function by signature, +and forwarded to the contract function when applicable. diff --git a/docs/conventions.rst b/docs/conventions.rst new file mode 100644 index 0000000000..e97a2d6d5f --- /dev/null +++ b/docs/conventions.rst @@ -0,0 +1,26 @@ +Conventions +=========== + +The Web3 library follows the following conventions. + +Bytes vs Text +------------- + +* The term *bytes* is used to refer to the binary representation of a string. +* The term *text* is used to refer to unicode representations of strings. + +Hexadecimal Representations +--------------------------- + +* All hexadecimal values will be returned as text. +* All hexadecimal values will be ``0x`` prefixed. + +Addresses +--------- + +All addresses must be supplied in one of three ways: + +* While connected to mainnet, an Ethereum Name Service name (often in the form ``myname.vns``) +* A 20-byte hexadecimal that is checksummed using the `EIP-55 + `_ spec. +* A 20-byte binary address. diff --git a/docs/ens.rst b/docs/ens.rst index d82e125f13..0065852229 100644 --- a/docs/ens.rst +++ b/docs/ens.rst @@ -1,21 +1,21 @@ -.. py:module:: ens - -ENS API -=========== - -:doc:`ens_overview` has a friendly overview. - -Continue below for the detailed specs on each method and class in the ens module. - -ens\.main module ----------------- - -.. automodule:: ens.main - :members: - -ens\.exceptions module ----------------------- - -.. automodule:: ens.exceptions - :members: - :show-inheritance: +.. py:module:: ens + +ENS API +=========== + +:doc:`ens_overview` has a friendly overview. + +Continue below for the detailed specs on each method and class in the ens module. + +ens\.main module +---------------- + +.. automodule:: ens.main + :members: + +ens\.exceptions module +---------------------- + +.. automodule:: ens.exceptions + :members: + :show-inheritance: diff --git a/docs/ens_overview.rst b/docs/ens_overview.rst index 89ae116f16..2a0c6dbf80 100644 --- a/docs/ens_overview.rst +++ b/docs/ens_overview.rst @@ -1,161 +1,161 @@ -Ethereum Name Service -================================ - -The Ethereum Name Service is analogous to the Domain Name Service. It -enables users and developers to use human-friendly names in place of error-prone -hexadecimal addresses, content hashes, and more. - -The :mod:`ens` module is included with web3.py. It provides an interface to look up -an address from a name, set up your own address, and more. - -Setup ------ - -Create an :class:`~ens.main.ENS` object (named ``ns`` below) in one of three ways: - -1. Automatic detection -2. Specify an instance or list of :ref:`providers` -3. From an existing :class:`web3.Web3` object - -:: - - # automatic detection - from ens.auto import ns - - - # or, with a provider - from web3 import IPCProvider - from ens import ENS - - provider = IPCProvider(...) - ns = ENS(provider) - - - # or, with a w3 instance - from ens import ENS - - w3 = Web3(...) - ns = ENS.fromWeb3(w3) - - -Usage ------ - -Name info -~~~~~~~~~ - -.. _ens_get_address: - -Look up the address for an ENS name -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: - - from ens.auto import ns - - - # look up the hex representation of the address for a name - - eth_address = ns.address('jasoncarver.eth') - - assert eth_address == '0x5B2063246F2191f18F2675ceDB8b28102e957458' - - -The ``ENS`` module has no opinion as to which TLD you can use, -but will not infer a TLD if it is not provided with the name. - - -Get name from address -^^^^^^^^^^^^^^^^^^^^^ - -:: - - domain = ns.name('0x5B2063246F2191f18F2675ceDB8b28102e957458') - - - # name() also accepts the bytes version of the address - - assert ns.name(b'[ c$o!\x91\xf1\x8f&u\xce\xdb\x8b(\x10.\x95tX') == domain - - - # confirm that the name resolves back to the address that you looked up: - - assert ns.address(domain) == '0x5B2063246F2191f18F2675ceDB8b28102e957458' - -Get owner of name -^^^^^^^^^^^^^^^^^ - -:: - - eth_address = ns.owner('exchange.eth') - -Set up your name -~~~~~~~~~~~~~~~~ - -Point your name to your address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Do you want to set up your name so that :meth:`~ens.main.ENS.address` will show the -address it points to? - -:: - - ns.setup_address('jasoncarver.eth', '0x5B2063246F2191f18F2675ceDB8b28102e957458') - -You must already be the owner of the domain (or its parent). - -In the common case where you want to point the name to the owning -address, you can skip the address - -:: - - ns.setup_address('jasoncarver.eth') - -You can claim arbitrarily deep subdomains. *Gas costs scale up with the -number of subdomains!* - -:: - - ns.setup_address('supreme.executive.power.derives.from.a.mandate.from.the.masses.jasoncarver.eth') - -Wait for the transaction to be mined, then: - -:: - - assert ns.address('supreme.executive.power.derives.from.a.mandate.from.the.masses.jasoncarver.eth') == \ - '0x5B2063246F2191f18F2675ceDB8b28102e957458' - -Allow people to find your name using your address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Do you want to set up your address so that :meth:`~ens.main.ENS.name` will show the -name that points to it? - -This is like Caller ID. It enables you and others to take an account and -determine what name points to it. Sometimes this is referred to as -"reverse" resolution. - -:: - - ns.setup_name('jasoncarver.eth', '0x5B2063246F2191f18F2675ceDB8b28102e957458') - -.. note:: Do not rely on reverse resolution for security. - - Anyone can claim any "caller ID". Only forward resolution implies that - the owner of the name gave their stamp of approval. - -If you don't supply the address, :meth:`~ens.main.ENS.setup_name` will assume you want the -address returned by :meth:`~ens.main.ENS.address`. - -:: - - ns.setup_name('jasoncarver.eth') - -If the name doesn't already point to an address, :meth:`~ens.main.ENS.setup_name` will -call :meth:`~ens.main.ENS.setup_address` for you. - -Wait for the transaction to be mined, then: - -:: - - assert ns.name('0x5B2063246F2191f18F2675ceDB8b28102e957458') == 'jasoncarver.eth' +Ethereum Name Service +================================ + +The Ethereum Name Service is analogous to the Domain Name Service. It +enables users and developers to use human-friendly names in place of error-prone +hexadecimal addresses, content hashes, and more. + +The :mod:`ens` module is included with web3.py. It provides an interface to look up +an address from a name, set up your own address, and more. + +Setup +----- + +Create an :class:`~ens.main.ENS` object (named ``ns`` below) in one of three ways: + +1. Automatic detection +2. Specify an instance or list of :ref:`providers` +3. From an existing :class:`web3.Web3` object + +:: + + # automatic detection + from ens.auto import ns + + + # or, with a provider + from web3 import IPCProvider + from ens import ENS + + provider = IPCProvider(...) + ns = ENS(provider) + + + # or, with a w3 instance + from ens import ENS + + w3 = Web3(...) + ns = ENS.fromWeb3(w3) + + +Usage +----- + +Name info +~~~~~~~~~ + +.. _ens_get_address: + +Look up the address for an ENS name +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + from ens.auto import ns + + + # look up the hex representation of the address for a name + + vns_address = ns.address('jasoncarver.vns') + + assert vns_address == '0x5B2063246F2191f18F2675ceDB8b28102e957458' + + +The ``ENS`` module has no opinion as to which TLD you can use, +but will not infer a TLD if it is not provided with the name. + + +Get name from address +^^^^^^^^^^^^^^^^^^^^^ + +:: + + domain = ns.name('0x5B2063246F2191f18F2675ceDB8b28102e957458') + + + # name() also accepts the bytes version of the address + + assert ns.name(b'[ c$o!\x91\xf1\x8f&u\xce\xdb\x8b(\x10.\x95tX') == domain + + + # confirm that the name resolves back to the address that you looked up: + + assert ns.address(domain) == '0x5B2063246F2191f18F2675ceDB8b28102e957458' + +Get owner of name +^^^^^^^^^^^^^^^^^ + +:: + + vns_address = ns.owner('exchange.vns') + +Set up your name +~~~~~~~~~~~~~~~~ + +Point your name to your address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Do you want to set up your name so that :meth:`~ens.main.ENS.address` will show the +address it points to? + +:: + + ns.setup_address('jasoncarver.vns', '0x5B2063246F2191f18F2675ceDB8b28102e957458') + +You must already be the owner of the domain (or its parent). + +In the common case where you want to point the name to the owning +address, you can skip the address + +:: + + ns.setup_address('jasoncarver.vns') + +You can claim arbitrarily deep subdomains. *Gas costs scale up with the +number of subdomains!* + +:: + + ns.setup_address('supreme.executive.power.derives.from.a.mandate.from.the.masses.jasoncarver.vns') + +Wait for the transaction to be mined, then: + +:: + + assert ns.address('supreme.executive.power.derives.from.a.mandate.from.the.masses.jasoncarver.vns') == \ + '0x5B2063246F2191f18F2675ceDB8b28102e957458' + +Allow people to find your name using your address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Do you want to set up your address so that :meth:`~ens.main.ENS.name` will show the +name that points to it? + +This is like Caller ID. It enables you and others to take an account and +determine what name points to it. Sometimes this is referred to as +"reverse" resolution. + +:: + + ns.setup_name('jasoncarver.vns', '0x5B2063246F2191f18F2675ceDB8b28102e957458') + +.. note:: Do not rely on reverse resolution for security. + + Anyone can claim any "caller ID". Only forward resolution implies that + the owner of the name gave their stamp of approval. + +If you don't supply the address, :meth:`~ens.main.ENS.setup_name` will assume you want the +address returned by :meth:`~ens.main.ENS.address`. + +:: + + ns.setup_name('jasoncarver.vns') + +If the name doesn't already point to an address, :meth:`~ens.main.ENS.setup_name` will +call :meth:`~ens.main.ENS.setup_address` for you. + +Wait for the transaction to be mined, then: + +:: + + assert ns.name('0x5B2063246F2191f18F2675ceDB8b28102e957458') == 'jasoncarver.vns' diff --git a/docs/ethpm.rst b/docs/ethpm.rst deleted file mode 100644 index b9275c849c..0000000000 --- a/docs/ethpm.rst +++ /dev/null @@ -1,860 +0,0 @@ -ethPM -===== - -Overview --------- - -This is a Python implementation of the `Ethereum Smart Contract -Packaging -Specification `__, -driven by discussions in `ERC -190 `__ and `ERC -1123 `__. - -``Py-EthPM`` is being built as a low-level library to help developers leverage the ethPM spec. Including ... - -- Parse and validate packages. -- Construct and publish new packages. -- Provide access to contract factory classes. -- Provide access to all of a package's deployments. -- Validate package bytecode matches compilation output. -- Validate deployed bytecode matches compilation output. -- Access to package’s dependencies. - -Package -------- - -The ``Package`` object will function much like the ``Contract`` class -provided by ``web3``. Rather than instantiating the base class provided -by ``ethpm``, you will instead use a ``classmethod`` which generates a -new ``Package`` class for a given package. - -``Package`` objects *must* be instantiated with a valid ``web3`` object. - -.. doctest:: - - >>> from ethpm import Package, ASSETS_DIR - >>> from web3 import Web3 - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> owned_manifest_path = ASSETS_DIR / 'owned' / '1.0.0.json' - >>> OwnedPackage = Package.from_file(owned_manifest_path, w3) - >>> assert isinstance(OwnedPackage, Package) - -Properties -~~~~~~~~~~ - -Each ``Package`` exposes the following properties. - -.. autoclass:: ethpm.Package - :members: name, version, manifest_version, uri, __repr__, contract_types, build_dependencies, deployments - -.. py:attribute:: Package.w3 - - The ``Web3`` instance currently set on this ``Package``. The deployments available on a package are automatically filtered to only contain those belonging to the currently set ``w3`` instance. - -.. py:attribute:: Package.manifest - - The manifest dict used to instantiate a ``Package``. - - -Methods -~~~~~~~ - -Each ``Package`` exposes the following methods. - -.. autoclass:: ethpm.Package - :members: from_file, from_uri, update_w3, get_contract_factory, get_contract_instance - - -Validation -~~~~~~~~~~ - -The ``Package`` class currently verifies the following things. - -- Manifests used to instantiate a ``Package`` object conform to the `EthPM V2 Manifest Specification `__ and are tightly packed, with keys sorted alphabetically. - - -LinkableContract ----------------- - -`Py-EthPM` uses a custom subclass of ``Web3.contract.Contract`` to manage contract factories and instances which might require bytecode linking. To create a deployable contract factory, both the contract type's `abi` and `deployment_bytecode` must be available in the Package's manifest. - -.. doctest:: - - >>> from eth_utils import is_address - >>> from web3 import Web3 - >>> from ethpm import Package, ASSETS_DIR - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> escrow_manifest_path = ASSETS_DIR / 'escrow' / '1.0.3.json' - - >>> # Try to deploy from unlinked factory - >>> EscrowPackage = Package.from_file(escrow_manifest_path, w3) - >>> EscrowFactory = EscrowPackage.get_contract_factory("Escrow") - >>> assert EscrowFactory.needs_bytecode_linking - >>> escrow_instance = EscrowFactory.constructor(w3.eth.accounts[0]).transact() - Traceback (most recent call last): - ... - ethpm.exceptions.BytecodeLinkingError: Contract cannot be deployed until its bytecode is linked. - - >>> # Deploy SafeSendLib - >>> SafeSendFactory = EscrowPackage.get_contract_factory("SafeSendLib") - >>> safe_send_tx_hash = SafeSendFactory.constructor().transact() - >>> safe_send_tx_receipt = w3.eth.waitForTransactionReceipt(safe_send_tx_hash) - - >>> # Link Escrow factory to deployed SafeSendLib instance - >>> LinkedEscrowFactory = EscrowFactory.link_bytecode({"SafeSendLib": safe_send_tx_receipt.contractAddress}) - >>> assert LinkedEscrowFactory.needs_bytecode_linking is False - >>> escrow_tx_hash = LinkedEscrowFactory.constructor(w3.eth.accounts[0]).transact() - >>> escrow_tx_receipt = w3.eth.waitForTransactionReceipt(escrow_tx_hash) - >>> assert is_address(escrow_tx_receipt.contractAddress) - - -Properties -~~~~~~~~~~ - -.. py:attribute:: LinkableContract.unlinked_references - - A list of link reference data for the deployment bytecode, if present in the manifest data used to generate a ``LinkableContract`` factory. Deployment bytecode link reference data must be present in a manifest in order to generate a factory for a contract which requires bytecode linking. - -.. py:attribute:: LinkableContract.linked_references - - A list of link reference data for the runtime bytecode, if present in the manifest data used to generate a ``LinkableContract`` factory. If you want to use the `web3` `Deployer` tool for a contract, then runtime bytecode link reference data must be present in a manifest. - -.. py:attribute:: LinkableContract.needs_bytecode_linking - - A boolean attribute used to indicate whether a contract factory has unresolved link references, which must be resolved before a new contract instance can be deployed or instantiated at a given address. - - -Methods -~~~~~~~ - -.. py:classmethod:: LinkableContract.link_bytecode(attr_dict) - - This method returns a newly created contract factory with the applied link references defined in the `attr_dict`. This method expects `attr_dict` to be of the type ``Dict[`contract_name`: `address`]`` for all link references that are unlinked. - -URI Schemes and Backends ------------------------- - -BaseURIBackend -~~~~~~~~~~~~~~ - -``Py-EthPM`` uses the ``BaseURIBackend`` as the parent class for all of its URI backends. To write your own backend, it must implement the following methods. - -.. py:method:: BaseURIBackend.can_resolve_uri(uri) - - Return a bool indicating whether or not this backend is capable of resolving the given URI to a manifest. - A content-addressed URI pointing to valid manifest is said to be capable of "resolving". - -.. py:method:: BaseURIBackend.can_translate_uri(uri) - - Return a bool indicating whether this backend class can translate the given URI to a corresponding content-addressed URI. - A registry URI is said to be capable of "transalating" if it points to another content-addressed URI in its respective on-chain registry. - -.. py:method:: BaseURIBackend.fetch_uri_contents(uri) - - Fetch the contents stored at the provided uri, if an available backend is capable of resolving the URI. Validates that contents stored at uri match the content hash suffixing the uri. - - -IPFS -~~~~ - -``Py-EthPM`` has multiple backends available to fetch/pin files to IPFS. The desired backend can be set via the environment variable: ``ETHPM_IPFS_BACKEND_CLASS``. - -- ``InfuraIPFSBackend`` (default) - - `https://ipfs.infura.io` -- ``IPFSGatewayBackend`` (temporarily deprecated) - - `https://ipfs.io/ipfs/` -- ``LocalIPFSBacked`` - - Connect to a local IPFS API gateway running on port 5001. -- ``DummyIPFSBackend`` - - Won't pin/fetch files to an actual IPFS node, but mocks out this behavior. - -.. py:method:: BaseIPFSBackend.pin_assets(file_or_directory_path) - - Pin asset(s) found at the given path and returns the pinned asset data. - - -HTTPS -~~~~~ - -``Py-EthPM`` offers a backend to fetch files from Github, ``GithubOverHTTPSBackend``. - -A valid content-addressed Github URI *must* conform to the following scheme, as described in `ERC1319 `__, to be used with this backend. - -.. code:: python - - https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha - - -.. py:method:: create_content_addressed_github_uri(uri) - - This util function will return a content-addressed URI, as defined by Github's `blob `__ scheme. To generate a content-addressed URI for any manifest stored on github, this function requires accepts a Github API uri that follows the following scheme. - -:: - - https://api.github.com/repos/:owner/:repo/contents/:path/:to/manifest.json - -.. doctest:: - - >>> from ethpm.uri import create_content_addressed_github_uri - - >>> owned_github_api_uri = "https://api.github.com/repos/ethpm/py-ethpm/contents/ethpm/assets/owned/1.0.1.json" - >>> content_addressed_uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480" - - >>> actual_blob_uri = create_content_addressed_github_uri(owned_github_api_uri) - >>> assert actual_blob_uri == content_addressed_uri - - -Registry URIs -~~~~~~~~~~~~~ - -The URI to lookup a package from a registry should follow the following -format. (subject to change as the Registry Contract Standard makes it’s -way through the EIP process) - -:: - - scheme://address:chain_id/package-name?version=x.x.x - -- URI must be a string type -- ``scheme``: ``erc1319`` -- ``address``: Must be a valid ENS domain or a valid checksum address - pointing towards a registry contract. -- ``chain_id``: Chain ID of the chain on which the registry lives. Supported chains include... - - - 1: Mainnet - - 3: Ropsten - - 4: Rinkeby - - 5: Goerli - - 42: Kovan - -- ``package-name``: Must conform to the package-name as specified in - the - `EthPM-Spec `__. -- ``version``: The URI escaped version string, *should* conform to the - `semver `__ version numbering specification. - -i.e. ``erc1319://packages.zeppelinos.eth:1/owned?version=1.0.0`` - -Builder -------- - -The manifest Builder is a tool designed to help construct custom manifests. The builder is still under active development, and can only handle simple use-cases for now. - -To create a simple manifest -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For all manifests, the following ingredients are *required*. - -.. code:: python - - build( - {}, - package_name(str), - version(str), - manifest_version(str), ..., - ) - # Or - build( - init_manifest(package_name: str, version: str, manifest_version: str="2") - ..., - ) - - -The builder (i.e. ``build()``) expects a dict as the first argument. This dict can be empty, or populated if you want to extend an existing manifest. - -.. doctest:: - - >>> from ethpm.tools.builder import * - - >>> expected_manifest = { - ... "package_name": "owned", - ... "version": "1.0.0", - ... "manifest_version": "2" - ... } - >>> base_manifest = {"package_name": "owned"} - >>> built_manifest = build( - ... {}, - ... package_name("owned"), - ... manifest_version("2"), - ... version("1.0.0"), - ... ) - >>> extended_manifest = build( - ... base_manifest, - ... manifest_version("2"), - ... version("1.0.0"), - ... ) - >>> assert built_manifest == expected_manifest - >>> assert extended_manifest == expected_manifest - -With ``init_manifest()``, which populates "version" with "2" (the only supported EthPM specification version), unless provided with an alternative "version". - -.. doctest:: - - >>> build( - ... init_manifest("owned", "1.0.0"), - ... ) - {'package_name': 'owned', 'version': '1.0.0', 'manifest_version': '2'} - - - -To return a ``Package`` -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - as_package(w3: Web3), - ) - -By default, the manifest builder returns a dict representing the manifest. To return a ``Package`` instance (instantiated with the generated manifest) from the builder, add the ``as_package()`` builder function with a valid ``web3`` instance to the end of the builder. - -.. doctest:: - - >>> from ethpm import Package - >>> from web3 import Web3 - - >>> w3 = Web3(Web3.EthereumTesterProvider()) - >>> built_package = build( - ... {}, - ... package_name("owned"), - ... manifest_version("2"), - ... version("1.0.0"), - ... as_package(w3), - ... ) - >>> assert isinstance(built_package, Package) - - -To validate a manifest -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - validate(), - ) - -By default, the manifest builder does *not* perform any validation that the generated fields are correctly formatted. There are two ways to validate that the built manifest conforms to the EthPM V2 Specification. - - Return a Package, which automatically runs validation. - - Add the ``validate()`` function to the end of the manifest builder. - -.. doctest:: - - >>> valid_manifest = build( - ... {}, - ... package_name("owned"), - ... manifest_version("2"), - ... version("1.0.0"), - ... validate(), - ... ) - >>> assert valid_manifest == {"package_name": "owned", "manifest_version": "2", "version": "1.0.0"} - >>> invalid_manifest = build( - ... {}, - ... package_name("_InvalidPkgName"), - ... manifest_version("2"), - ... version("1.0.0"), - ... validate(), - ... ) - Traceback (most recent call last): - ethpm.exceptions.EthPMValidationError: Manifest invalid for schema version 2. Reason: '_InvalidPkgName' does not match '^[a-z][-a-z0-9]{0,255}$' - - -To write a manifest to disk -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - write_to_disk( - manifest_root_dir: Optional[Path], - manifest_name: Optional[str], - prettify: Optional[bool], - ), - ) - - -Writes the active manifest to disk. Will not overwrite an existing manifest with the same name and root directory. - -Defaults -- Writes manifest to current working directory (as returned by `os.getcwd()`) unless a ``Path`` is provided as manifest_root_dir. -- Writes manifest with a filename of ".json" unless desired manifest name (which must end in ".json") is provided as manifest_name. -- Writes the minified manifest version to disk unless prettify is set to True - -.. doctest:: - - >>> from pathlib import Path - >>> import tempfile - >>> p = Path(tempfile.mkdtemp("temp")) - >>> build( - ... {}, - ... package_name("owned"), - ... manifest_version("2"), - ... version("1.0.0"), - ... write_to_disk(manifest_root_dir=p, manifest_name="manifest.json", prettify=True), - ... ) - {'package_name': 'owned', 'manifest_version': '2', 'version': '1.0.0'} - >>> with open(str(p / "manifest.json")) as f: - ... actual_manifest = f.read() - >>> print(actual_manifest) - { - "manifest_version": "2", - "package_name": "owned", - "version": "1.0.0" - } - - -To pin a manifest to IPFS -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - pin_to_ipfs( - backend: BaseIPFSBackend, - prettify: Optional[bool], - ), - ) - -Pins the active manfiest to disk. Must be the concluding function in a builder set since it returns the IPFS pin data rather than returning the manifest for further processing. - - -To add meta fields -~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - description(str), - license(str), - authors(*args: str), - keywords(*args: str), - links(*kwargs: str), - ..., - ) - -.. doctest:: - - >>> BASE_MANIFEST = {"package_name": "owned", "manifest_version": "2", "version": "1.0.0"} - >>> expected_manifest = { - ... "package_name": "owned", - ... "manifest_version": "2", - ... "version": "1.0.0", - ... "meta": { - ... "authors": ["Satoshi", "Nakamoto"], - ... "description": "An awesome package.", - ... "keywords": ["auth"], - ... "license": "MIT", - ... "links": { - ... "documentation": "www.readthedocs.com/...", - ... "repo": "www.github/...", - ... "website": "www.website.com", - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... authors("Satoshi", "Nakamoto"), - ... description("An awesome package."), - ... keywords("auth"), - ... license("MIT"), - ... links(documentation="www.readthedocs.com/...", repo="www.github/...", website="www.website.com"), - ... ) - >>> assert expected_manifest == built_manifest - - -Compiler Output -~~~~~~~~~~~~~~~ - -To build a more complex manifest for solidity contracts, it is required that you provide standard-json output from the solidity compiler. - -Here is an example of how to compile the contracts and generate the standard-json output. More information can be found in the `Solidity Compiler `__ docs. - -.. code:: sh - - solc --allow-paths --standard-json < standard-json-input.json > owned_compiler_output.json - -Sample standard-json-input.json - -.. code:: json - - { - "language": "Solidity", - "sources": { - "Contract.sol": { - "urls": [""] - } - }, - "settings": { - "outputSelection": { - "*": { - "*": ["abi", "evm.bytecode.object"] - } - } - } - } - - -The ``compiler_output`` as used in the following examples is the entire value of the ``contracts`` key of the solc output, which contains compilation data for all compiled contracts. - - -To add a source -~~~~~~~~~~~~~~~ - -.. code:: python - - # To inline a source - build( - ..., - inline_source( - contract_name: str, - compiler_output: Dict[str, Any], - package_root_dir: Optional[Path] - ), - ..., - ) - # To pin a source - build( - ..., - pin_source( - contract_name: str, - compiler_output: Dict[str, Any], - ipfs_backend: BaseIPFSBackend, - package_root_dir: Optional[Path] - ), - ..., - ) - -There are two ways to include a contract source in your manifest. - -Both strategies require that either . . . - - The current working directory is set to the package root directory - or - - The package root directory is provided as an argument (``package_root_dir``) - - -To inline the source code directly in the manifest, use ``inline_source()`` or ``source_inliner()`` (to inline multiple sources from the same compiler_output), which requires the contract name and compiler output as args. - -.. note:: - - `owned_compiler_output.json` below is expected to be the standard-json output generated by the solidity compiler as described `here `. The output must contain the `abi` and `bytecode` objects from compilation. - -.. doctest:: - - >>> import json - >>> from ethpm import ASSETS_DIR - >>> owned_dir = ASSETS_DIR / "owned" / "contracts" - >>> owned_contract_source = owned_dir / "Owned.sol" - >>> compiler_output = json.loads((ASSETS_DIR / "owned" / "owned_compiler_output.json").read_text())['contracts'] - >>> expected_manifest = { - ... "package_name": "owned", - ... "version": "1.0.0", - ... "manifest_version": "2", - ... "sources": { - ... "./Owned.sol": """pragma solidity ^0.4.24;\n\ncontract Owned {\n address""" - ... """ owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }\n\n """ - ... """constructor() public {\n owner = msg.sender;\n }\n}""" - ... } - ... } - >>> # With `inline_source()` - >>> built_manifest = build( - ... BASE_MANIFEST, - ... inline_source("Owned", compiler_output, package_root_dir=owned_dir), - ... ) - >>> assert expected_manifest == built_manifest - >>> # With `source_inliner()` for multiple sources from the same compiler output - >>> inliner = source_inliner(compiler_output, package_root_dir=owned_dir) - >>> built_manifest = build( - ... BASE_MANIFEST, - ... inliner("Owned"), - ... # inliner("other_source"), etc... - ... ) - >>> assert expected_manifest == built_manifest - - -To include the source as a content-addressed URI, ``Py-EthPM`` can pin your source via the Infura IPFS API. As well as the contract name and compiler output, this function requires that you provide the desired IPFS backend to pin the contract sources. - -.. doctest:: - - >>> from ethpm.backends.ipfs import get_ipfs_backend - >>> ipfs_backend = get_ipfs_backend() - >>> expected_manifest = { - ... "package_name": "owned", - ... "version": "1.0.0", - ... "manifest_version": "2", - ... "sources": { - ... "./Owned.sol": "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - ... } - ... } - >>> # With `pin_source()` - >>> built_manifest = build( - ... BASE_MANIFEST, - ... pin_source("Owned", compiler_output, ipfs_backend, package_root_dir=owned_dir), - ... ) - >>> assert expected_manifest == built_manifest - >>> # With `source_pinner()` for multiple sources from the same compiler output - >>> pinner = source_pinner(compiler_output, ipfs_backend, package_root_dir=owned_dir) - >>> built_manifest = build( - ... BASE_MANIFEST, - ... pinner("Owned"), - ... # pinner("other_source"), etc - ... ) - >>> assert expected_manifest == built_manifest - - - -To add a contract type -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - contract_type( - contract_name: str, - compiler_output: Dict[str, Any], - alias: Optional[str], - abi: Optional[bool], - compiler: Optional[bool], - contract_type: Optional[bool], - deployment_bytecode: Optional[bool], - natspec: Optional[bool], - runtime_bytecode: Optional[bool] - ), - ..., - ) - -The default behavior of the manifest builder's ``contract_type()`` function is to populate the manifest with all of the contract type data found in the ``compiler_output``. - -.. doctest:: - - >>> expected_manifest = { - ... 'package_name': 'owned', - ... 'manifest_version': '2', - ... 'version': '1.0.0', - ... 'contract_types': { - ... 'Owned': { - ... 'abi': [{'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... 'deployment_bytecode': { - ... 'bytecode': '0x6080604052348015600f57600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550603580605d6000396000f3006080604052600080fd00a165627a7a72305820d6ab9e295aa1d1adb0fca69ce42c2c73e991afe290852e8247a208a78b352ff00029' - ... } - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output) - ... ) - >>> assert expected_manifest == built_manifest - - -To select only certain contract type data to be included in your manifest, provide the desired fields as ``True`` keyword arguments. The following fields can be specified for inclusion in the manifest . . . - - ``abi`` - - ``compiler`` - - ``deployment_bytecode`` - - ``natspec`` - - ``runtime_bytecode`` - -.. doctest:: - - >>> expected_manifest = { - ... 'package_name': 'owned', - ... 'manifest_version': '2', - ... 'version': '1.0.0', - ... 'contract_types': { - ... 'Owned': { - ... 'abi': [{'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output, abi=True) - ... ) - >>> assert expected_manifest == built_manifest - -If you would like to alias your contract type, provide the desired alias as a kwarg. This will automatically include the original contract type in a ``contract_type`` field. Unless specific contract type fields are provided as kwargs, ``contract_type`` will stil default to including all availabe contract type data found in the compiler output. - -.. doctest:: - - >>> expected_manifest = { - ... 'package_name': 'owned', - ... 'manifest_version': '2', - ... 'version': '1.0.0', - ... 'contract_types': { - ... 'OwnedAlias': { - ... 'abi': [{'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}], - ... 'contract_type': 'Owned' - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... contract_type("Owned", compiler_output, alias="OwnedAlias", abi=True) - ... ) - >>> assert expected_manifest == built_manifest - - -To add a deployment -~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - deployment( - block_uri, - contract_instance, - contract_type, - address, - transaction=None, - block=None, - deployment_bytecode=None, - runtime_bytecode=None, - compiler=None, - ), - ..., - ) - -There are two strategies for adding a deployment to your manifest. - -.. py:function:: deployment(block_uri, contract_instance, contract_type, address, transaction=None, block=None, deployment_bytecode=None, runtime_bytecode=None, compiler=None) - -This is the simplest builder function for adding a deployment to a manifest. All arguments require keywords. This builder function requires a valid ``block_uri``, it's up to the user to be sure that multiple chain URIs representing the same blockchain are not included in the "deployments" object keys. - -``runtime_bytecode``, ``deployment_bytecode`` and ``compiler`` must all be validly formatted dicts according to the `EthPM Spec `__. If your contract has link dependencies, be sure to include them in the bytecode objects. - - -.. doctest:: - - >>> expected_manifest = { - ... 'package_name': 'owned', - ... 'manifest_version': '2', - ... 'version': '1.0.0', - ... 'deployments': { - ... 'blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef': { - ... 'Owned': { - ... 'contract_type': 'Owned', - ... 'address': '0x4F5B11C860B37B68De6d14FB7e7b5f18A9a1BD00', - ... } - ... } - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... deployment( - ... block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - ... contract_instance='Owned', - ... contract_type='Owned', - ... address='0x4F5B11C860B37B68De6d14FB7e7b5f18A9a1BD00', - ... ), - ... ) - >>> assert expected_manifest == built_manifest - -.. py:function:: deployment_type(contract_instance, contract_type, deployment_bytecode=None, runtime_bytecode=None, compiler=None) - -This builder function simplifies adding the same contract type deployment across multiple chains. It requires both a ``contract_instance`` and ``contract_type`` argument (in many cases these are the same, though ``contract_type`` *must* always match its correspondent in the manifest's "contract_types") and all arguments require keywords. - -``runtime_bytecode``, ``deployment_bytecode`` and ``compiler`` must all be validly formatted dicts according to the `EthPM Spec `__. If your contract has link dependencies, be sure to include them in the bytecode objects. - -.. code:: python - - owned_type = deployment_type(contract_instance="Owned", contract_type="Owned") - escrow_type = deployment_type( - contract_instance = "Escrow", - contract_type = "Escrow", - deployment_bytecode = { - "bytecode": "0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001" - }, - runtime_bytecode = { - "bytecode": "0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029" - }, - compiler = { - "name": "solc", - "version": "0.4.24+commit.e67f0147.Emscripten.clang", - "settings": { - "optimize": True - } - } - ) - manifest = build( - package_name("escrow"), - version("1.0.0"), - manifest_version("2"), - owned_type( - block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=owned_testnet_address, - ), - owned_type( - block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=owned_mainnet_address, - transaction=owned_mainnet_transaction, - block=owned_mainnet_block, - ), - escrow_type( - block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=escrow_testnet_address, - ), - escrow_type( - block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - address=escrow_mainnet_address, - transaction=escrow_mainnet_transaction, - ), - ) - -To add a build dependency -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - build( - ..., - build_dependency( - package_name, - uri, - ), - ..., - ) - -.. py:function:: build_dependency(package_name, uri) - -To add a build dependency to your manifest, just provide the package's name and a supported, content-addressed URI. - -.. doctest:: - - >>> expected_manifest = { - ... 'package_name': 'owned', - ... 'manifest_version': '2', - ... 'version': '1.0.0', - ... 'build_dependencies': { - ... 'owned': 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW', - ... } - ... } - >>> built_manifest = build( - ... BASE_MANIFEST, - ... build_dependency('owned', 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW'), - ... ) - >>> assert expected_manifest == built_manifest - - -Checker -------- - -The manifest Checker is a tool designed to help validate manifests according to the natural language spec (link). - -To validate a manifest -~~~~~~~~~~~~~~~~~~~~~~ - -.. doctest:: - - >>> from ethpm.tools.checker import check_manifest - - >>> basic_manifest = {"package_name": "example", "version": "1.0.0", "manifest_version": "2"} - >>> check_manifest(basic_manifest) - {'meta': "Manifest missing a suggested 'meta' field.", 'sources': 'Manifest is missing a sources field, which defines a source tree that should comprise the full source tree necessary to recompile the contracts contained in this release.', 'contract_types': "Manifest does not contain any 'contract_types'. Packages should only include contract types that can be found in the source files for this package. Packages should not include contract types from dependencies. Packages should not include abstract contracts in the contract types section of a release."} diff --git a/docs/examples.rst b/docs/examples.rst index dde92106ca..88a4d8a530 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,520 +1,473 @@ -Examples -======== - -.. contents:: :local: - -Here are some common things you might want to do with web3. - -Looking up blocks ------------------ - -Blocks can be looked up by either their number or hash using the -``web3.eth.getBlock`` API. Block hashes should be in their hexadecimal -representation. Block numbers - -.. code-block:: python - - # get a block by number - >>> web3.eth.getBlock(12345) - { - 'author': '0xad5C1768e5974C231b2148169da064e61910f31a', - 'difficulty': 735512610763, - 'extraData': '0x476574682f76312e302e302f6c696e75782f676f312e342e32', - 'gasLimit': 5000, - 'gasUsed': 0, - 'hash': '0x767c2bfb3bdee3f78676c1285cd757bcd5d8c272cef2eb30d9733800a78c0b6d', - 'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - 'miner': '0xad5c1768e5974c231b2148169da064e61910f31a', - 'mixHash': '0x31d9ec7e3855aeba37fd92aa1639845e70b360a60f77f12eff530429ef8cfcba', - 'nonce': '0x549f882c5f356f85', - 'number': 12345, - 'parentHash': '0x4b3c1d7e65a507b62734feca1ee9f27a5379e318bd52ae62de7ba67dbeac66a3', - 'receiptsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - 'sealFields': ['0x31d9ec7e3855aeba37fd92aa1639845e70b360a60f77f12eff530429ef8cfcba', - '0x549f882c5f356f85'], - 'sha3Uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - 'size': 539, - 'stateRoot': '0xca495e22ed6b88c61714d129dbc8c94f5bf966ac581c09a57c0a72d0e55e7286', - 'timestamp': 1438367030, - 'totalDifficulty': 3862140487204603, - 'transactions': [], - 'transactionsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - 'uncles': [], - } - # get a block by it's hash - >>> web3.eth.getBlock('0x767c2bfb3bdee3f78676c1285cd757bcd5d8c272cef2eb30d9733800a78c0b6d') - {...} - - -Getting the latest block ------------------------- - -You can also retrieve the latest block using the string ``'latest'`` in the -``web3.eth.getBlock`` API. - -.. code-block:: python - - >>> web3.eth.getBlock('latest') - {...} - - -If you want to know the latest block number you can use the -``web3.eth.blockNumber`` property. - -.. code-block:: python - - >>> web3.eth.blockNumber - 4194803 - - -Currency conversions --------------------- - -Web3 can help you convert between denominations. The following denominations are supported. - -+--------------+---------------------------------+ -| denomination | amount in wei | -+--------------+---------------------------------+ -| wei | 1 | -+--------------+---------------------------------+ -| kwei | 1000 | -+--------------+---------------------------------+ -| babbage | 1000 | -+--------------+---------------------------------+ -| femtoether | 1000 | -+--------------+---------------------------------+ -| mwei | 1000000 | -+--------------+---------------------------------+ -| lovelace | 1000000 | -+--------------+---------------------------------+ -| picoether | 1000000 | -+--------------+---------------------------------+ -| gwei | 1000000000 | -+--------------+---------------------------------+ -| shannon | 1000000000 | -+--------------+---------------------------------+ -| nanoether | 1000000000 | -+--------------+---------------------------------+ -| nano | 1000000000 | -+--------------+---------------------------------+ -| szabo | 1000000000000 | -+--------------+---------------------------------+ -| microether | 1000000000000 | -+--------------+---------------------------------+ -| micro | 1000000000000 | -+--------------+---------------------------------+ -| finney | 1000000000000000 | -+--------------+---------------------------------+ -| milliether | 1000000000000000 | -+--------------+---------------------------------+ -| milli | 1000000000000000 | -+--------------+---------------------------------+ -| ether | 1000000000000000000 | -+--------------+---------------------------------+ -| kether | 1000000000000000000000 | -+--------------+---------------------------------+ -| grand | 1000000000000000000000 | -+--------------+---------------------------------+ -| mether | 1000000000000000000000000 | -+--------------+---------------------------------+ -| gether | 1000000000000000000000000000 | -+--------------+---------------------------------+ -| tether | 1000000000000000000000000000000 | -+--------------+---------------------------------+ - - -.. code-block:: python - - >>> web3.toWei('1', 'ether') - 1000000000000000000 - >>> web3.fromWei('1000000000000000000', 'ether') - Decimal('1') - >>> from_wei(123456789, 'ether') - Decimal('1.23456789E-10') - - -Looking up transactions ------------------------ - -You can look up transactions using the ``web3.eth.getTransaction`` function. - -.. code-block:: python - - >>> web3.eth.getTransaction('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') - { - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'condition': None, - 'creates': None, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gas': 21000, - 'gasPrice': 50000000000000, - 'hash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'input': '0x', - 'networkId': None, - 'nonce': 0, - 'publicKey': '0x376fc429acc35e610f75b14bc96242b13623833569a5bb3d72c17be7e51da0bb58e48e2462a59897cead8ab88e78709f9d24fd6ec24d1456f43aae407a8970e4', - 'r': '0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0', - 'raw': '0xf86780862d79883d2000825208945df9b87991262f6ba471f09758cde1c0fc1de734827a69801ca088ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0a045e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a', - 's': '0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a', - 'standardV': '0x1', - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionIndex': 0, - 'v': '0x1c', - 'value': 31337, - } - -If no transaction for the given hash can be found, then this function will -instead return ``None``. - - -Looking up receipts -------------------- - -Transaction receipts can be retrieved using the ``web3.eth.getTransactionReceipt`` API. - - -.. code-block:: python - - >>> web3.eth.getTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') - { - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'contractAddress': None, - 'cumulativeGasUsed': 21000, - 'gasUsed': 21000, - 'logs': [], - 'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - 'root': '0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957', - 'transactionHash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'transactionIndex': 0, - } - - -If the transaction has not yet been mined then this method will return ``None``. - - -Working with Contracts ----------------------- - -Given the following solidity source file stored at ``contract.sol``. - -.. code-block:: javascript - - contract StoreVar { - - uint8 public _myVar; - event MyEvent(uint indexed _var); - - function setVar(uint8 _var) public { - _myVar = _var; - MyEvent(_var); - } - - function getVar() public view returns (uint8) { - return _myVar; - } - - } - -The following example demonstrates a few things: - -* Compiling a contract from a sol file. -* Estimating gas costs of a transaction. -* Transacting with a contract function. -* Waiting for a transaction receipt to be mined. - -.. code-block:: python - - import sys - import time - import pprint - - from web3.providers.eth_tester import EthereumTesterProvider - from web3 import Web3 - from solc import compile_source - - - def compile_source_file(file_path): - with open(file_path, 'r') as f: - source = f.read() - - return compile_source(source) - - - def deploy_contract(w3, contract_interface): - tx_hash = w3.eth.contract( - abi=contract_interface['abi'], - bytecode=contract_interface['bin']).deploy() - - address = w3.eth.getTransactionReceipt(tx_hash)['contractAddress'] - return address - - - w3 = Web3(EthereumTesterProvider()) - - contract_source_path = 'contract.sol' - compiled_sol = compile_source_file('contract.sol') - - contract_id, contract_interface = compiled_sol.popitem() - - address = deploy_contract(w3, contract_interface) - print("Deployed {0} to: {1}\n".format(contract_id, address)) - - store_var_contract = w3.eth.contract( - address=address, - abi=contract_interface['abi']) - - gas_estimate = store_var_contract.functions.setVar(255).estimateGas() - print("Gas estimate to transact with setVar: {0}\n".format(gas_estimate)) - - if gas_estimate < 100000: - print("Sending transaction to setVar(255)\n") - tx_hash = store_var_contract.functions.setVar(255).transact() - receipt = w3.eth.waitForTransactionReceipt(tx_hash) - print("Transaction receipt mined: \n") - pprint.pprint(dict(receipt)) - print("Was transaction successful? \n") - pprint.pprint(receipt['status']) - else: - print("Gas cost exceeds 100000") - - -Output: - -.. code-block:: none - - Deployed :StoreVar to: 0xF2E246BB76DF876Cef8b38ae84130F4F55De395b - - Gas estimate to transact with setVar: 32463 - - Sending transaction to setVar(255) - - Transaction receipt mined: - - {'blockHash': HexBytes('0x94e07b0b88667da284e914fa44b87d4e7fec39761be51245ef94632a3b5ab9f0'), - 'blockNumber': 2, - 'contractAddress': None, - 'cumulativeGasUsed': 43106, - 'gasUsed': 43106, - 'logs': [AttributeDict({'type': 'mined', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x3ac3518cc59d1698aa03a0bab7fb8191a4ef017aeda7429b11e8c6462b20a62a'), 'blockHash': HexBytes('0x94e07b0b88667da284e914fa44b87d4e7fec39761be51245ef94632a3b5ab9f0'), 'blockNumber': 2, 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', 'data': '0x', 'topics': [HexBytes('0x6c2b4666ba8da5a95717621d879a77de725f3d816709b9cbe9f059b8f875e284'), HexBytes('0x00000000000000000000000000000000000000000000000000000000000000ff')]})], - 'transactionHash': HexBytes('0x3ac3518cc59d1698aa03a0bab7fb8191a4ef017aeda7429b11e8c6462b20a62a'), - 'transactionIndex': 0} - - -Working with an ERC20 Token Contract ------------------------------------- - -Most fungible tokens on the Ethereum blockchain conform to the `ERC20`_ -standard. This section of the guide covers interacting with an existing token -contract which conforms to this standard. - -.. testsetup:: - - from web3 import Web3 - w3 = Web3(Web3.EthereumTesterProvider()) - bytecode = '6060604052341561000c57fe5b604051602080610acb833981016040528080519060200190919050505b620f42408114151561003b5760006000fd5b670de0b6b3a76400008102600281905550600254600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b505b610a27806100a46000396000f30060606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610099578063095ea7b31461013257806318160ddd1461018957806323b872dd146101af578063313ce5671461022557806370a082311461025157806395d89b411461029b578063a9059cbb14610334578063dd62ed3e1461038b575bfe5b34156100a157fe5b6100a96103f4565b60405180806020018281038252838181518152602001915080519060200190808383600083146100f8575b8051825260208311156100f8576020820191506020810190506020830392506100d4565b505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013a57fe5b61016f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061042e565b604051808215151515815260200191505060405180910390f35b341561019157fe5b610199610521565b6040518082815260200191505060405180910390f35b34156101b757fe5b61020b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610527565b604051808215151515815260200191505060405180910390f35b341561022d57fe5b610235610791565b604051808260ff1660ff16815260200191505060405180910390f35b341561025957fe5b610285600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610796565b6040518082815260200191505060405180910390f35b34156102a357fe5b6102ab6107e0565b60405180806020018281038252838181518152602001915080519060200190808383600083146102fa575b8051825260208311156102fa576020820191506020810190506020830392506102d6565b505050905090810190601f1680156103265780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561033c57fe5b610371600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061081a565b604051808215151515815260200191505060405180910390f35b341561039357fe5b6103de600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610973565b6040518082815260200191505060405180910390f35b604060405190810160405280600981526020017f54657374546f6b656e000000000000000000000000000000000000000000000081525081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a3600190505b92915050565b60025481565b600081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410806105f1575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054105b156105fc5760006000fd5b81600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b9392505050565b601281565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b604060405190810160405280600481526020017f544553540000000000000000000000000000000000000000000000000000000081525081565b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156108695760006000fd5b81600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b929150505600a165627a7a723058205071371ee2a4a1be3c96e77d939cdc26161a256fdd638efc08bd33dfc65d3b850029' - ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"inputs":[{"name":"_totalSupply","type":"uint256"}],"payable":false,"type":"constructor","stateMutability":"nonpayable"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]' - factory = w3.eth.contract(abi=ABI, bytecode=bytecode) - alice, bob = w3.eth.accounts[0], w3.eth.accounts[1] - assert alice == '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', alice - assert bob == '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', bob - tx_hash = factory.constructor(1000000).transact({'from': alice}) - assert tx_hash == b'h9\xeb\xdb4\x07\x03y\x92RP`X\xf6\xf7\x9f\xfaT\xed&e\xee*\xc2\rx\xb3\xab\x8c4\xc9\x1f', tx_hash - txn_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - assert txn_receipt['contractAddress'] == '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - contract = w3.eth.contract(contract_address, abi=ABI) - total_supply = contract.functions.totalSupply().call() - decimals = 10 ** 18 - assert total_supply == 1000000 * decimals, total_supply - - -In this guide we will interact with an existing token contract that we have -already deployed to a local testing chain. This guide assumes: - -1. An existing token contract at a known address. -1. Access to the proper ``ABI`` for the given contract. -1. A `~web3.main.Web3` instance connected to a provider with an unlocked account which can send transactions. - - -Creating the contract factory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First we need to create a contract instance with the address of our token -contract and the ``ERC20`` ABI. - -.. doctest:: - - >>> contract = w3.eth.contract(contract_address, abi=ABI) - >>> contract.address - '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b' - - -Querying token metadata -~~~~~~~~~~~~~~~~~~~~~~~ - -Each token will have a total supply which represents the total number of tokens -in circulation. In this example we've initialized the token contract to have 1 -million tokens. Since this token contract is setup to have 18 decimal places, -the raw total supply returned by the contract is going to have 18 additional -decimal places. - -.. doctest:: - - >>> contract.functions.name().call() - 'TestToken' - >>> contract.functions.symbol().call() - 'TEST' - >>> decimals = contract.functions.decimals().call() - >>> decimals - 18 - >>> DECIMALS = 10 ** decimals - >>> contract.functions.totalSupply().call() // DECIMALS - 1000000 - - -Query account balances -~~~~~~~~~~~~~~~~~~~~~~ - -Next we can query some account balances using the contract's ``balanceOf`` -function. The token contract we are using starts with a single account which -we'll refer to as ``alice`` holding all of the tokens. - -.. doctest:: - - >>> alice = '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf' - >>> bob = '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF' - >>> raw_balance = contract.functions.balanceOf(alice).call() - >>> raw_balance - 1000000000000000000000000 - >>> raw_balance // DECIMALS - 1000000 - >>> contract.functions.balanceOf(bob).call() - 0 - - -Sending tokens -~~~~~~~~~~~~~~ - -Next we can transfer some tokens from ``alice`` to ``bob`` using the contract's -``transfer`` function. - - -.. doctest:: - - >>> tx_hash = contract.functions.transfer(bob, 100).transact({'from': alice}) - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - >>> contract.functions.balanceOf(alice).call() - 999999999999999999999900 - >>> contract.functions.balanceOf(bob).call() - 100 - - -Creating an approval for external transfers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Alice could also *approve* someone else to spend tokens from her account using -the ``approve`` function. We can also query how many tokens we're approved to -spend using the ``allowance`` function. - -.. doctest:: - - >>> contract.functions.allowance(alice, bob).call() - 0 - >>> tx_hash = contract.functions.approve(bob, 200).transact({'from': alice}) - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - >>> contract.functions.allowance(alice, bob).call() - 200 - - -Performing an external transfer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When someone has an allowance they can transfer those tokens using the -``transferFrom`` function. - -.. doctest:: - - >>> contract.functions.allowance(alice, bob).call() - 200 - >>> contract.functions.balanceOf(bob).call() - 100 - >>> tx_hash = contract.functions.transferFrom(alice, bob, 75).transact({'from': bob}) - >>> tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - >>> contract.functions.allowance(alice, bob).call() - 125 - >>> contract.functions.balanceOf(bob).call() - 175 - - -.. _ERC20: https://github.com/ethereum/EIPs/blob/7f4f0377730f5fc266824084188cc17cf246932e/EIPS/eip-20.md - - -Contract Unit Tests in Python ------------------------------ - -Here is an example of how one can use the `pytest`_ framework in python, Web3.py, -eth-tester, and PyEVM to perform unit tests entirely in python without any -additional need for a full featured ethereum node/client. To install needed -dependencies you can use the pinned extra for eth_tester in web3 and pytest: - -.. _pytest: https://docs.pytest.org/en/latest/ - -.. code-block:: bash - - $ pip install web3[tester] pytest - -Once you have an environment set up for testing, you can then write your tests -like so: - -.. include:: ../tests/core/contracts/test_contract_example.py - :code: python - :start-line: 1 - -Using Infura Rinkeby Node -------------------------- -Import your required libraries - -.. code-block:: python - - from web3 import Web3, HTTPProvider - -Initialize a web3 instance with an Infura node - -.. code-block:: python - - w3 = Web3(Web3.HTTPProvider("https://rinkeby.infura.io/v3/YOUR_INFURA_KEY")) - - -Inject the middleware into the middleware onion - -.. code-block:: python - - from web3.middleware import geth_poa_middleware - w3.middleware_onion.inject(geth_poa_middleware, layer=0) - -Just remember that you have to sign all transactions locally, as infura does not handle any keys from your wallet ( refer to `this`_ ) - - -.. _this: https://web3py.readthedocs.io/en/stable/web3.eth.account.html#local-vs-hosted-nodes - -.. code-block:: python - - transaction = contract.functions.function_Name(params).buildTransaction() - transaction.update({ 'gas' : appropriate_gas_amount }) - transaction.update({ 'nonce' : web3.eth.getTransactionCount('Your_Wallet_Address') }) - signed_tx = w3.eth.account.signTransaction(transaction, private_key) - -P.S : the two updates are done to the transaction dictionary, since a raw transaction might not contain gas & nonce amounts, so you have to add them manually. - -And finally, send the transaction - -.. code-block:: python - - txn_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction) - txn_receipt = w3.eth.waitForTransactionReceipt(txn_hash) - -Tip : afterwards you can use the value stored in ``txn_hash``, in an explorer like `etherscan`_ to view the transaction's details - -.. _etherscan: https://rinkeby.etherscan.io +Examples +======== + +.. contents:: :local: + +Here are some common things you might want to do with web3. + +Looking up blocks +----------------- + +Blocks can be looked up by either their number or hash using the +``web3.vns.getBlock`` API. Block hashes should be in their hexadecimal +representation. Block numbers + +.. code-block:: python + + # get a block by number + >>> web3.vns.getBlock(12345) + { + 'author': '0xad5c1768e5974c231b2148169da064e61910f31a', + 'difficulty': 735512610763, + 'extraData': '0x476574682f76312e302e302f6c696e75782f676f312e342e32', + 'gasLimit': 5000, + 'gasUsed': 0, + 'hash': '0x767c2bfb3bdee3f78676c1285cd757bcd5d8c272cef2eb30d9733800a78c0b6d', + 'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + 'miner': '0xad5c1768e5974c231b2148169da064e61910f31a', + 'mixHash': '0x31d9ec7e3855aeba37fd92aa1639845e70b360a60f77f12eff530429ef8cfcba', + 'nonce': '0x549f882c5f356f85', + 'number': 12345, + 'parentHash': '0x4b3c1d7e65a507b62734feca1ee9f27a5379e318bd52ae62de7ba67dbeac66a3', + 'receiptsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'sealFields': ['0x31d9ec7e3855aeba37fd92aa1639845e70b360a60f77f12eff530429ef8cfcba', + '0x549f882c5f356f85'], + 'sha3Uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + 'size': 539, + 'stateRoot': '0xca495e22ed6b88c61714d129dbc8c94f5bf966ac581c09a57c0a72d0e55e7286', + 'timestamp': 1438367030, + 'totalDifficulty': 3862140487204603, + 'transactions': [], + 'transactionsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'uncles': [], + } + # get a block by it's hash + >>> web3.vns.getBlock('0x767c2bfb3bdee3f78676c1285cd757bcd5d8c272cef2eb30d9733800a78c0b6d') + {...} + + +Getting the latest block +------------------------ + +You can also retrieve the latest block using the string ``'latest'`` in the +``web3.vns.getBlock`` API. + +.. code-block:: python + + >>> web3.vns.getBlock('latest') + {...} + + +If you want to know the latest block number you can use the +``web3.vns.blockNumber`` property. + +.. code-block:: python + + >>> web3.vns.blockNumber + 4194803 + + +Currency conversions +-------------------- + +Web3 can help you convert between denominations. The following denominations are supported. + ++--------------+---------------------------------+ +| denomination | amount in wei | ++--------------+---------------------------------+ +| wei | 1 | ++--------------+---------------------------------+ +| kwei | 1000 | ++--------------+---------------------------------+ +| babbage | 1000 | ++--------------+---------------------------------+ +| femtoether | 1000 | ++--------------+---------------------------------+ +| mwei | 1000000 | ++--------------+---------------------------------+ +| lovelace | 1000000 | ++--------------+---------------------------------+ +| picoether | 1000000 | ++--------------+---------------------------------+ +| gwei | 1000000000 | ++--------------+---------------------------------+ +| shannon | 1000000000 | ++--------------+---------------------------------+ +| nanoether | 1000000000 | ++--------------+---------------------------------+ +| nano | 1000000000 | ++--------------+---------------------------------+ +| szabo | 1000000000000 | ++--------------+---------------------------------+ +| microether | 1000000000000 | ++--------------+---------------------------------+ +| micro | 1000000000000 | ++--------------+---------------------------------+ +| finney | 1000000000000000 | ++--------------+---------------------------------+ +| milliether | 1000000000000000 | ++--------------+---------------------------------+ +| milli | 1000000000000000 | ++--------------+---------------------------------+ +| ether | 1000000000000000000 | ++--------------+---------------------------------+ +| kether | 1000000000000000000000 | ++--------------+---------------------------------+ +| grand | 1000000000000000000000 | ++--------------+---------------------------------+ +| mether | 1000000000000000000000000 | ++--------------+---------------------------------+ +| gether | 1000000000000000000000000000 | ++--------------+---------------------------------+ +| tether | 1000000000000000000000000000000 | ++--------------+---------------------------------+ + + +.. code-block:: python + + >>> web3.toWei('1', 'ether') + 1000000000000000000 + >>> web3.fromWei('1000000000000000000', 'ether') + Decimal('1') + >>> from_wei(123456789, 'ether') + Decimal('1.23456789E-10') + + +Looking up transactions +----------------------- + +You can look up transactions using the ``web3.vns.getTransaction`` function. + +.. code-block:: python + + >>> web3.vns.getTransaction('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') + { + 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', + 'blockNumber': 46147, + 'condition': None, + 'creates': None, + 'from': '0xa1e4380a3b1f749673e270229993ee55f35663b4', + 'gas': 21000, + 'gasPrice': 50000000000000, + 'hash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', + 'input': '0x', + 'networkId': None, + 'nonce': 0, + 'publicKey': '0x376fc429acc35e610f75b14bc96242b13623833569a5bb3d72c17be7e51da0bb58e48e2462a59897cead8ab88e78709f9d24fd6ec24d1456f43aae407a8970e4', + 'r': '0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0', + 'raw': '0xf86780862d79883d2000825208945df9b87991262f6ba471f09758cde1c0fc1de734827a69801ca088ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0a045e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a', + 's': '0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a', + 'standardV': '0x1', + 'to': '0x5df9b87991262f6ba471f09758cde1c0fc1de734', + 'transactionIndex': 0, + 'v': '0x1c', + 'value': 31337, + } + +If no transaction for the given hash can be found, then this function will +instead return ``None``. + + +Looking up receipts +------------------- + +Transaction receipts can be retrieved using the ``web3.vns.getTransactionReceipt`` API. + + +.. code-block:: python + + >>> web3.vns.getTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') + { + 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', + 'blockNumber': 46147, + 'contractAddress': None, + 'cumulativeGasUsed': 21000, + 'gasUsed': 21000, + 'logs': [], + 'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + 'root': '0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957', + 'transactionHash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', + 'transactionIndex': 0, + } + + +If the transaction has not yet been mined then this method will return ``None``. + + +Working with Contracts +---------------------- + +Given the following solidity source file stored at ``contract.sol``. + +.. code-block:: javascript + + contract StoreVar { + + uint8 public _myVar; + event MyEvent(uint indexed _var); + + function setVar(uint8 _var) public { + _myVar = _var; + MyEvent(_var); + } + + function getVar() public view returns (uint8) { + return _myVar; + } + + } + +The following example demonstrates a few things: + +* Compiling a contract from a sol file. +* Estimating gas costs of a transaction. +* Transacting with a contract function. +* Waiting for a transaction receipt to be mined. + +.. code-block:: python + + import sys + import time + import pprint + + from web3.providers.vns_tester import EthereumTesterProvider + from web3 import Web3 + from solc import compile_source + + + def compile_source_file(file_path): + with open(file_path, 'r') as f: + source = f.read() + + return compile_source(source) + + + def deploy_contract(w3, contract_interface): + tx_hash = w3.vns.contract( + abi=contract_interface['abi'], + bytecode=contract_interface['bin']).deploy() + + address = w3.vns.getTransactionReceipt(tx_hash)['contractAddress'] + return address + + + w3 = Web3(EthereumTesterProvider()) + + contract_source_path = 'contract.sol' + compiled_sol = compile_source_file('contract.sol') + + contract_id, contract_interface = compiled_sol.popitem() + + address = deploy_contract(w3, contract_interface) + print("Deployed {0} to: {1}\n".format(contract_id, address)) + + store_var_contract = w3.vns.contract( + address=address, + abi=contract_interface['abi']) + + gas_estimate = store_var_contract.functions.setVar(255).estimateGas() + print("Gas estimate to transact with setVar: {0}\n".format(gas_estimate)) + + if gas_estimate < 100000: + print("Sending transaction to setVar(255)\n") + tx_hash = store_var_contract.functions.setVar(255).transact() + receipt = w3.vns.waitForTransactionReceipt(tx_hash) + print("Transaction receipt mined: \n") + pprint.pprint(dict(receipt)) + print("Was transaction successful? \n") + pprint.pprint(receipt['status']) + else: + print("Gas cost exceeds 100000") + + +Output: + +.. code-block:: none + + Deployed :StoreVar to: 0xF2E246BB76DF876Cef8b38ae84130F4F55De395b + + Gas estimate to transact with setVar: 32463 + + Sending transaction to setVar(255) + + Transaction receipt mined: + + {'blockHash': HexBytes('0x94e07b0b88667da284e914fa44b87d4e7fec39761be51245ef94632a3b5ab9f0'), + 'blockNumber': 2, + 'contractAddress': None, + 'cumulativeGasUsed': 43106, + 'gasUsed': 43106, + 'logs': [AttributeDict({'type': 'mined', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x3ac3518cc59d1698aa03a0bab7fb8191a4ef017aeda7429b11e8c6462b20a62a'), 'blockHash': HexBytes('0x94e07b0b88667da284e914fa44b87d4e7fec39761be51245ef94632a3b5ab9f0'), 'blockNumber': 2, 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', 'data': '0x', 'topics': [HexBytes('0x6c2b4666ba8da5a95717621d879a77de725f3d816709b9cbe9f059b8f875e284'), HexBytes('0x00000000000000000000000000000000000000000000000000000000000000ff')]})], + 'transactionHash': HexBytes('0x3ac3518cc59d1698aa03a0bab7fb8191a4ef017aeda7429b11e8c6462b20a62a'), + 'transactionIndex': 0} + + +Working with an ERC20 Token Contract +------------------------------------ + +Most fungible tokens on the Ethereum blockchain conform to the `ERC20`_ +standard. This section of the guide covers interacting with an existing token +contract which conforms to this standard. + +.. testsetup:: + + from web3 import Web3 + w3 = Web3 (Web3.EthereumTesterProvider()) + bytecode = '6060604052341561000c57fe5b604051602080610acb833981016040528080519060200190919050505b620f42408114151561003b5760006000fd5b670de0b6b3a76400008102600281905550600254600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b505b610a27806100a46000396000f30060606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610099578063095ea7b31461013257806318160ddd1461018957806323b872dd146101af578063313ce5671461022557806370a082311461025157806395d89b411461029b578063a9059cbb14610334578063dd62ed3e1461038b575bfe5b34156100a157fe5b6100a96103f4565b60405180806020018281038252838181518152602001915080519060200190808383600083146100f8575b8051825260208311156100f8576020820191506020810190506020830392506100d4565b505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013a57fe5b61016f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061042e565b604051808215151515815260200191505060405180910390f35b341561019157fe5b610199610521565b6040518082815260200191505060405180910390f35b34156101b757fe5b61020b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610527565b604051808215151515815260200191505060405180910390f35b341561022d57fe5b610235610791565b604051808260ff1660ff16815260200191505060405180910390f35b341561025957fe5b610285600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610796565b6040518082815260200191505060405180910390f35b34156102a357fe5b6102ab6107e0565b60405180806020018281038252838181518152602001915080519060200190808383600083146102fa575b8051825260208311156102fa576020820191506020810190506020830392506102d6565b505050905090810190601f1680156103265780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561033c57fe5b610371600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061081a565b604051808215151515815260200191505060405180910390f35b341561039357fe5b6103de600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610973565b6040518082815260200191505060405180910390f35b604060405190810160405280600981526020017f54657374546f6b656e000000000000000000000000000000000000000000000081525081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a3600190505b92915050565b60025481565b600081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410806105f1575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054105b156105fc5760006000fd5b81600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555081600060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b9392505050565b601281565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b604060405190810160405280600481526020017f544553540000000000000000000000000000000000000000000000000000000081525081565b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156108695760006000fd5b81600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b929150505600a165627a7a723058205071371ee2a4a1be3c96e77d939cdc26161a256fdd638efc08bd33dfc65d3b850029' + ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","stateMutability":"view"},{"inputs":[{"name":"_totalSupply","type":"uint256"}],"payable":false,"type":"constructor","stateMutability":"nonpayable"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]' + factory = w3.vns.contract(abi=ABI, bytecode=bytecode) + alice, bob = w3.vns.accounts[0], w3.vns.accounts[1] + assert alice == '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', alice + assert bob == '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', bob + tx_hash = factory.constructor(1000000).transact({'from': alice}) + assert tx_hash == b'h9\xeb\xdb4\x07\x03y\x92RP`X\xf6\xf7\x9f\xfaT\xed&e\xee*\xc2\rx\xb3\xab\x8c4\xc9\x1f', tx_hash + txn_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + assert txn_receipt['contractAddress'] == '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', txn_receipt['contractAddress'] + contract_address = txn_receipt['contractAddress'] + contract = w3.vns.contract(contract_address, abi=ABI) + total_supply = contract.functions.totalSupply().call() + decimals = 10 ** 18 + assert total_supply == 1000000 * decimals, total_supply + + +In this guide we will interact with an existing token contract that we have +already deployed to a local testing chain. This guide assumes: + +1. An existing token contract at a known address. +1. Access to the proper ``ABI`` for the given contract. +1. A `~web3.main.Web3` instance connected to a provider with an unlocked account which can send transactions. + + +Creating the contract factory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to create a contract instance with the address of our token +contract and the ``ERC20`` ABI. + +.. doctest:: + + >>> contract = w3.vns.contract(contract_address, abi=ABI) + >>> contract.address + '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b' + + +Querying token metadata +~~~~~~~~~~~~~~~~~~~~~~~ + +Each token will have a total supply which represents the total number of tokens +in circulation. In this example we've initialized the token contract to have 1 +million tokens. Since this token contract is setup to have 18 decimal places, +the raw total supply returned by the contract is going to have 18 additional +decimal places. + +.. doctest:: + + >>> contract.functions.name().call() + 'TestToken' + >>> contract.functions.symbol().call() + 'TEST' + >>> decimals = contract.functions.decimals().call() + >>> decimals + 18 + >>> DECIMALS = 10 ** decimals + >>> contract.functions.totalSupply().call() // DECIMALS + 1000000 + + +Query account balances +~~~~~~~~~~~~~~~~~~~~~~ + +Next we can query some account balances using the contract's ``balanceOf`` +function. The token contract we are using starts with a single account which +we'll refer to as ``alice`` holding all of the tokens. + +.. doctest:: + + >>> alice = '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf' + >>> bob = '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF' + >>> raw_balance = contract.functions.balanceOf(alice).call() + >>> raw_balance + 1000000000000000000000000 + >>> raw_balance // DECIMALS + 1000000 + >>> contract.functions.balanceOf(bob).call() + 0 + + +Sending tokens +~~~~~~~~~~~~~~ + +Next we can transfer some tokens from ``alice`` to ``bob`` using the contract's +``transfer`` function. + + +.. doctest:: + + >>> tx_hash = contract.functions.transfer(bob, 100).transact({'from': alice}) + >>> tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + >>> contract.functions.balanceOf(alice).call() + 999999999999999999999900 + >>> contract.functions.balanceOf(bob).call() + 100 + + +Creating an approval for external transfers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Alice could also *approve* someone else to spend tokens from her account using +the ``approve`` function. We can also query how many tokens we're approved to +spend using the ``allowance`` function. + +.. doctest:: + + >>> contract.functions.allowance(alice, bob).call() + 0 + >>> tx_hash = contract.functions.approve(bob, 200).transact({'from': alice}) + >>> tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + >>> contract.functions.allowance(alice, bob).call() + 200 + + +Performing an external transfer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When someone has an allowance they can transfer those tokens using the +``transferFrom`` function. + +.. doctest:: + + >>> contract.functions.allowance(alice, bob).call() + 200 + >>> contract.functions.balanceOf(bob).call() + 100 + >>> tx_hash = contract.functions.transferFrom(alice, bob, 75).transact({'from': bob}) + >>> tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + >>> contract.functions.allowance(alice, bob).call() + 125 + >>> contract.functions.balanceOf(bob).call() + 175 + + +.. _ERC20: https://github.com/ethereum/EIPs/blob/7f4f0377730f5fc266824084188cc17cf246932e/EIPS/eip-20.md + + +Contract Unit Tests in Python +----------------------------- + +Here is an example of how one can use the `pytest`_ framework in python, Web3.py, +vns-tester, and PyEVM to perform unit tests entirely in python without any +additional need for a full featured ethereum node/client. To install needed +dependencies you can use the pinned extra for vns_tester in web3 and pytest: + +.. _pytest: https://docs.pytest.org/en/latest/ + +.. code-block:: bash + + $ pip install web3[tester] pytest + +Once you have an environment set up for testing, you can then write your tests +like so: + +.. include:: ../tests/core/contracts/test_contract_example.py + :code: python + :start-line: 1 diff --git a/docs/filters.rst b/docs/filters.rst index 61ad211865..58f9f1b032 100644 --- a/docs/filters.rst +++ b/docs/filters.rst @@ -1,305 +1,305 @@ -Filtering -========= - - -.. py:module:: web3.utils.filters - -.. note :: - - Most one-liners below assume ``w3`` to be a :class:`web3.Web3` instance; - obtainable, for example, with: - - .. code-block:: python - - from web3.auto import w3 - -The :meth:`web3.eth.Eth.filter` method can be used to setup filters for: - -* Pending Transactions: ``web3.eth.filter('pending')`` - -* New Blocks ``web3.eth.filter('latest')`` - -* Event Logs - - Through the contract instance api: - - .. code-block:: python - - event_filter = mycontract.events.myEvent.createFilter(fromBlock='latest', argument_filters={'arg1':10}) - - Or built manually by supplying `valid filter params `_: - - .. code-block:: python - - event_filter = w3.eth.filter({"address": contract_address}) - -* Attaching to an existing filter - - .. code-block:: python - - existing_filter = w3.eth.filter(filter_id="0x0") - -.. note :: - - Creating event filters requires that your Ethereum node has an API support enabled for filters. - It does not work with Infura nodes. To get event logs on Infura or other - stateless nodes please see :class:`web3.contract.ContractEvents`. - - -Filter Class ------------- - -.. py:class:: Filter(web3, filter_id) - -.. py:attribute:: Filter.filter_id - - The ``filter_id`` for this filter as returned by the ``eth_newFilter`` RPC - method when this filter was created. - - -.. py:method:: Filter.get_new_entries() - - Retrieve new entries for this filter. - - Logs will be retrieved using the - :func:`web3.eth.Eth.getFilterChanges` which returns only new entries since the last - poll. - - -.. py:method:: Filter.get_all_entries() - - Retrieve all entries for this filter. - - Logs will be retrieved using the - :func:`web3.eth.Eth.getFilterLogs` which returns all entries that match the given - filter. - - -.. py:method:: Filter.format_entry(entry) - - Hook for subclasses to modify the format of the log entries this filter - returns, or passes to it's callback functions. - - By default this returns the ``entry`` parameter umodified. - - -.. py:method:: Filter.is_valid_entry(entry) - - Hook for subclasses to add additional programatic filtering. The default - implementation always returns ``True``. - - -Block and Transaction Filter Classes ------------------------------------- - -.. py:class:: BlockFilter(...) - -``BlockFilter`` is a subclass of :class:`Filter`. - -You can setup a filter for new blocks using ``web3.eth.filter('latest')`` which -will return a new :class:`BlockFilter` object. - - .. code-block:: python - - new_block_filter = w3.eth.filter('latest') - new_block_filter.get_new_entries() - -.. py:class:: TransactionFilter(...) - -``TransactionFilter`` is a subclass of :class:`Filter`. - -You can setup a filter for new blocks using ``web3.eth.filter('pending')`` which -will return a new :class:`BlockFilter` object. - - .. code-block:: python - - new_transaction_filter = w3.eth.filter('pending') - new_transaction_filter.get_new_entries() - - -Event Log Filters ------------------ - -You can set up a filter for event logs using the web3.py contract api: -:meth:`web3.contract.Contract.events.your_event_name.createFilter`, which provides some conveniences for -creating event log filters. Refer to the following example: - - .. code-block:: python - - event_filter = myContract.events..createFilter(fromBlock="latest", argument_filters={'arg1':10}) - event_filter.get_new_entries() - -See :meth:`web3.contract.Contract.events.your_event_name.createFilter()` documentation for more information. - -You can set up an event log filter like the one above with ``web3.eth.filter`` by supplying a -dictionary containing the standard filter parameters. Assuming that ``arg1`` is indexed, the -equivalent filter creation would look like: - - .. code-block:: python - - event_signature_hash = web3.keccak(text="eventName(uint32)").hex() - event_filter = web3.eth.filter({ - "address": myContract_address, - "topics": [event_signature_hash, - "0x000000000000000000000000000000000000000000000000000000000000000a"], - }) - -The ``topics`` argument is order-dependent. For non-anonymous events, the first item in the topic list is always the keccack hash of the event signature. Subsequent topic items are the hex encoded values for indexed event arguments. In the above example, the second item is the ``arg1`` value ``10`` encoded to its hex string representation. - -In addition to being order-dependent, there are a few more points to recognize when specifying topic filters: - - Given a transaction log with topics [A, B], the following topic filters will yield a match: - - - [] "anything" - - [A] "A in first position (and anything after)" - - [None, B] "anything in first position AND B in second position (and anything after)" - - [A, B] "A in first position AND B in second position (and anything after)" - - [[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything after)" - -See the JSON-RPC documentation for `eth_newFilter `_ more information on the standard filter parameters. - -Creating a log filter by either of the above methods will return a :class:`LogFilter` instance. - -.. py:class:: LogFilter(web3, filter_id, log_entry_formatter=None, data_filter_set=None) - -The :py:class:`LogFilter` class is a subclass of :class:`Filter`. See the :class:`Filter` -documentation for inherited methods. - -:class:`LogFilter` provides the following additional -methods: - -.. py:method:: LogFilter.set_data_filters(data_filter_set) - -Provides a means to filter on the log data, in other words the ability to filter on values from -un-indexed event arguments. The parameter ``data_filter_set`` should be a list or set of 32-byte hex encoded values. - -Getting events without setting up a filter ------------------------------------------- - -You can query an Ethereum node for direct fetch of events, without creating a filter first. -This works on all node types, including Infura. - -For examples see :meth:`web3.contract.ContractEvents.getLogs`. - -Examples: Listening For Events ------------------------------- - -Synchronous -^^^^^^^^^^^ - - .. code-block:: python - - from web3.auto import w3 - import time - - def handle_event(event): - print(event) - - def log_loop(event_filter, poll_interval): - while True: - for event in event_filter.get_new_entries(): - handle_event(event) - time.sleep(poll_interval) - - def main(): - block_filter = w3.eth.filter('latest') - log_loop(block_filter, 2) - - if __name__ == '__main__': - main() - -.. _asynchronous_filters: - -Asynchronous Filter Polling -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Starting with web3 version 4, the ``watch`` method was taken out of the web3 filter objects. -There are many decisions to be made when designing a system regarding threading and concurrency. -Rather than force a decision, web3 leaves these choices up to the user. Below are some example -implementations of asynchronous filter-event handling that can serve as starting points. - -Single threaded concurrency with ``async`` and ``await`` -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Beginning in python 3.5, the ``async`` and ``await`` built-in keywords were added. These provide a -shared api for coroutines that can be utilized by modules such as the built-in asyncio_. Below is -an example event loop using asyncio_, that polls multiple web3 filter object, and passes new -entries to a handler. - - .. code-block:: python - - from web3.auto import w3 - import asyncio - - - def handle_event(event): - print(event) - # and whatever - - async def log_loop(event_filter, poll_interval): - while True: - for event in event_filter.get_new_entries(): - handle_event(event) - await asyncio.sleep(poll_interval) - - def main(): - block_filter = w3.eth.filter('latest') - tx_filter = w3.eth.filter('pending') - loop = asyncio.get_event_loop() - try: - loop.run_until_complete( - asyncio.gather( - log_loop(block_filter, 2), - log_loop(tx_filter, 2))) - finally: - loop.close() - - if __name__ == '__main__': - main() - - Read the asyncio_ documentation for more information. - -Running the event loop in a separate thread -""""""""""""""""""""""""""""""""""""""""""" - -Here is an extended version of above example, where the event loop is run in a separate thread, -releasing the ``main`` function for other tasks. - - .. code-block:: python - - from web3.auto import w3 - from threading import Thread - import time - - - def handle_event(event): - print(event) - # and whatever - - - def log_loop(event_filter, poll_interval): - while True: - for event in event_filter.get_new_entries(): - handle_event(event) - time.sleep(poll_interval) - - - def main(): - block_filter = w3.eth.filter('latest') - worker = Thread(target=log_loop, args=(block_filter, 5), daemon=True) - worker.start() - # .. do some other stuff - - if __name__ == '__main__': - main() - -Here are some other libraries that provide frameworks for writing asynchronous python: - - * gevent_ - * twisted_ - * celery_ - -.. _asyncio: https://docs.python.org/3/library/asyncio.html -.. _gevent: https://www.gevent.org/ -.. _twisted: https://twistedmatrix.com/ -.. _celery: https://www.celeryproject.org/ +Filtering +========= + + +.. py:module:: web3.utils.filters + +.. note :: + + Most one-liners below assume ``w3`` to be a :class:`web3.Web3` instance; + obtainable, for example, with: + + .. code-block:: python + + from web3.auto import w3 + +The :meth:`web3.vns.Bbbbbbbb.filter` method can be used to setup filters for: + +* Pending Transactions: ``web3.vns.filter('pending')`` + +* New Blocks ``web3.vns.filter('latest')`` + +* Event Logs + + Through the contract instance api: + + .. code-block:: python + + event_filter = mycontract.events.myEvent.createFilter(fromBlock='latest', argument_filters={'arg1':10}) + + Or built manually by supplying `valid filter params `_: + + .. code-block:: python + + event_filter = w3.vns.filter({"address": contract_address}) + +* Attaching to an existing filter + + .. code-block:: python + + existing_filter = w3.vns.filter(filter_id="0x0") + +.. note :: + + Creating event filters requires that your Ethereum node has an API support enabled for filters. + It does not work with Infura nodes. To get event logs on Infura or other + stateless nodes please see :class:`web3.contract.ContractEvents`. + + +Filter Class +------------ + +.. py:class:: Filter(web3, filter_id) + +.. py:attribute:: Filter.filter_id + + The ``filter_id`` for this filter as returned by the ``vns_newFilter`` RPC + method when this filter was created. + + +.. py:method:: Filter.get_new_entries() + + Retrieve new entries for this filter. + + Logs will be retrieved using the + :func:`web3.vns.Bbbbbbbb.getFilterChanges` which returns only new entries since the last + poll. + + +.. py:method:: Filter.get_all_entries() + + Retrieve all entries for this filter. + + Logs will be retrieved using the + :func:`web3.vns.Bbbbbbbb.getFilterLogs` which returns all entries that match the given + filter. + + +.. py:method:: Filter.format_entry(entry) + + Hook for subclasses to modify the format of the log entries this filter + returns, or passes to it's callback functions. + + By default this returns the ``entry`` parameter umodified. + + +.. py:method:: Filter.is_valid_entry(entry) + + Hook for subclasses to add additional programatic filtering. The default + implementation always returns ``True``. + + +Block and Transaction Filter Classes +------------------------------------ + +.. py:class:: BlockFilter(...) + +``BlockFilter`` is a subclass of :class:`Filter`. + +You can setup a filter for new blocks using ``web3.vns.filter('latest')`` which +will return a new :class:`BlockFilter` object. + + .. code-block:: python + + new_block_filter = w3.vns.filter('latest') + new_block_filter.get_new_entries() + +.. py:class:: TransactionFilter(...) + +``TransactionFilter`` is a subclass of :class:`Filter`. + +You can setup a filter for new blocks using ``web3.vns.filter('pending')`` which +will return a new :class:`BlockFilter` object. + + .. code-block:: python + + new_transaction_filter = w3.vns.filter('pending') + new_transaction_filter.get_new_entries() + + +Event Log Filters +----------------- + +You can set up a filter for event logs using the web3.py contract api: +:func:`web3.contract.Contract.events..createFilter`, which provides some conveniances for +creating event log filters. Refer to the following example: + + .. code-block:: python + + event_filter = myContract.events..createFilter(fromBlock="latest", argument_filters={'arg1':10}) + event_filter.get_new_entries() + +See :meth:`web3.contract.Contract.events..createFilter` documentation for more information. + +You can set up an event log filter like the one above with `web3.vns.filter` by supplying a +dictionary containing the standard filter parameters. Assuming that `arg1` is indexed, the +equivalent filter creation would look like: + + .. code-block:: python + + event_signature_hash = web3.keccak(text="eventName(uint32)").hex() + event_filter = web3.vns.filter({ + "address": myContract_address, + "topics": [event_signature_hash, + "0x000000000000000000000000000000000000000000000000000000000000000a"], + }) + +The ``topics`` argument is order-dependent. For non-anonymous events, the first item in the topic list is always the keccack hash of the event signature. Subsequent topic items are the hex encoded values for indexed event arguments. In the above example, the second item is the ``arg1`` value ``10`` encoded to its hex string representation. + +In addition to being order-dependent, there are a few more points to recognize when specifying topic filters: + + Given a transaction log with topics [A, B], the following topic filters will yield a match: + + - [] "anything" + - [A] "A in first position (and anything after)" + - [None, B] "anything in first position AND B in second position (and anything after)" + - [A, B] "A in first position AND B in second position (and anything after)" + - [[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything after)" + +See the JSON-RPC documentation for `vns_newFilter `_ more information on the standard filter parameters. + +Creating a log filter by either of the above methods will return a :class:`LogFilter` instance. + +.. py:class:: LogFilter(web3, filter_id, log_entry_formatter=None, data_filter_set=None) + +The :py:class:`LogFilter` class is a subclass of :class:`Filter`. See the :class:`Filter` +documentation for inherited methods. + +:class:`LogFilter` provides the following additional +methods: + +.. py:method:: LogFilter.set_data_filters(data_filter_set) + +Provides a means to filter on the log data, in other words the ability to filter on values from +un-indexed event arguments. The parameter ``data_filter_set`` should be a list or set of 32-byte hex encoded values. + +Getting events without setting up a filter +------------------------------------------ + +You can query an Ethereum node for direct fetch of events, without creating a filter first. +This works on all node types, including Infura. + +For examples see :meth:`web3.contract.ContractEvents.getLogs`. + +Examples: Listening For Events +------------------------------ + +Synchronous +^^^^^^^^^^^ + + .. code-block:: python + + from web3.auto import w3 + import time + + def handle_event(event): + print(event) + + def log_loop(event_filter, poll_interval): + while True: + for event in event_filter.get_new_entries(): + handle_event(event) + time.sleep(poll_interval) + + def main(): + block_filter = w3.vns.filter('latest') + log_loop(block_filter, 2) + + if __name__ == '__main__': + main() + +.. _asynchronous_filters: + +Asynchronous Filter Polling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Starting with web3 version 4, the ``watch`` method was taken out of the web3 filter objects. +There are many decisions to be made when designing a system regarding threading and concurrency. +Rather than force a decision, web3 leaves these choices up to the user. Below are some example +implementations of asynchronous filter-event handling that can serve as starting points. + +Single threaded concurrency with ``async`` and ``await`` +"""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Beginning in python 3.5, the ``async`` and ``await`` built-in keywords were added. These provide a +shared api for coroutines that can be utilized by modules such as the built-in asyncio_. Below is +an example event loop using asyncio_, that polls multiple web3 filter object, and passes new +entries to a handler. + + .. code-block:: python + + from web3.auto import w3 + import asyncio + + + def handle_event(event): + print(event) + # and whatever + + async def log_loop(event_filter, poll_interval): + while True: + for event in event_filter.get_new_entries(): + handle_event(event) + await asyncio.sleep(poll_interval) + + def main(): + block_filter = w3.vns.filter('latest') + tx_filter = w3.vns.filter('pending') + loop = asyncio.get_event_loop() + try: + loop.run_until_complete( + asyncio.gather( + log_loop(block_filter, 2), + log_loop(tx_filter, 2))) + finally: + loop.close() + + if __name__ == '__main__': + main() + + Read the asyncio_ documentation for more information. + +Running the event loop in a separate thread +""""""""""""""""""""""""""""""""""""""""""" + +Here is an extended version of above example, where the event loop is run in a separate thread, +releasing the ``main`` function for other tasks. + + .. code-block:: python + + from web3.auto import w3 + from threading import Thread + import time + + + def handle_event(event): + print(event) + # and whatever + + + def log_loop(event_filter, poll_interval): + while True: + for event in event_filter.get_new_entries(): + handle_event(event) + time.sleep(poll_interval) + + + def main(): + block_filter = w3.vns.filter('latest') + worker = Thread(target=log_loop, args=(block_filter, 5), daemon=True) + worker.start() + # .. do some other stuff + + if __name__ == '__main__': + main() + +Here are some other libraries that provide frameworks for writing asynchronous python: + + * gevent_ + * twisted_ + * celery_ + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _gevent: https://www.gevent.org/ +.. _twisted: https://twistedmatrix.com/ +.. _celery: https://www.celeryproject.org/ diff --git a/docs/gas_price.rst b/docs/gas_price.rst index feb901421f..5a733dd275 100644 --- a/docs/gas_price.rst +++ b/docs/gas_price.rst @@ -1,117 +1,117 @@ -.. _Gas_Price: - -Gas Price API -=============== - -For Ethereum transactions, gas price is a delicate property. For this reason, -Web3 includes an API for configuring it. - -By default, Web3 will not include a ``gasPrice`` in the transaction as to relay -this responsibility to the connected node. The Gas Price API allows you to -define Web3's behaviour for populating the gas price. This is done using a -"Gas Price Strategy" - a method which takes the Web3 object and a transaction -dictionary and returns a gas price (denominated in wei). - -Retrieving gas price --------------------- - -To retreive the gas price using the selected strategy simply call -:meth:`~web3.eth.Eth.generateGasPrice` - -.. code-block:: python - - >>> Web3.eth.generateGasPrice() - 20000000000 - -Creating a gas price strategy -------------------------------- - -A gas price strategy is implemented as a python method with the following -signature: - -.. code-block:: python - - def gas_price_strategy(web3, transaction_params=None): - ... - -The method must return a positive integer representing the gas price in wei. - -To demonstrate, here is a rudimentary example of a gas price strategy that -returns a higher gas price when the value of the transaction is higher than -1 Ether. - -.. code-block:: python - - from web3 import Web3 - - def value_based_gas_price_strategy(web3, transaction_params): - if transaction_params['value'] > Web3.toWei(1, 'ether'): - return Web3.toWei(20, 'gwei') - else: - return Web3.toWei(5, 'gwei') - -Selecting the gas price strategy --------------------------------- - -The gas price strategy can be set by calling :meth:`~web3.eth.Eth.setGasPriceStrategy`. - -.. code-block:: python - - from web3 import Web3 - - def value_based_gas_price_strategy(web3, transaction_params): - ... - - w3 = Web3(...) - w3.eth.setGasPriceStrategy(value_based_gas_price_strategy) - -Available gas price strategies ------------------------------- - -.. py:module:: web3.gas_strategies.rpc - -.. py:method:: rpc_gas_price_strategy(web3, transaction_params=None) - - Makes a call to the `JSON-RPC eth_gasPrice - method `_ which returns - the gas price configured by the connected Ethereum node. - -.. py:module:: web3.gas_strategies.time_based - -.. py:method:: construct_time_based_gas_price_strategy(max_wait_seconds, sample_size, probability) - - Constructs a strategy which will compute a gas price such that the - transaction will be mined within a number of seconds defined by - ``max_wait_seconds`` with a probability defined by ``probability``. The - gas price is computed by sampling ``sample_size`` of the most recently - mined blocks. - - * ``max_wait_seconds`` The desired maxiumum number of seconds the - transaction should take to mine. - * ``sample_size`` The number of recent blocks to sample - * ``probability`` An integer representation of the desired probability that - the transaction will be mined within ``max_wait_seconds``. 0 means 0% - and 100 means 100%. - - The following ready to use versions of this strategy are available. - - * ``web3.gas_strategies.time_based.fast_gas_price_strategy``: Transaction mined within 60 seconds. - * ``web3.gas_strategies.time_based.medium_gas_price_strategy``: Transaction mined within 5 minutes. - * ``web3.gas_strategies.time_based.slow_gas_price_strategy``: Transaction mined within 1 hour. - * ``web3.gas_strategies.time_based.glacial_gas_price_strategy``: Transaction mined within 24 hours. - - .. warning:: Due to the overhead of sampling the recent blocks it is - recommended that a caching solution be used to reduce the amount of chain - data that needs to be re-fetched for each request. - - .. code-block:: python - - from web3 import Web3, middleware - from web3.gas_strategies.time_based import medium_gas_price_strategy - - w3 = Web3() - w3.eth.setGasPriceStrategy(medium_gas_price_strategy) - - w3.middleware_onion.add(middleware.time_based_cache_middleware) - w3.middleware_onion.add(middleware.latest_block_based_cache_middleware) - w3.middleware_onion.add(middleware.simple_cache_middleware) +.. _Gas_Price: + +Gas Price API +=============== + +For Ethereum transactions, gas price is a delicate property. For this reason, +Web3 includes an API for configuring it. + +By default, Web3 will not include a ``gasPrice`` in the transaction as to relay +this responsibility to the connected node. The Gas Price API allows you to +define Web3's behaviour for populating the gas price. This is done using a +"Gas Price Strategy" - a method which takes the Web3 object and a transaction +dictionary and returns a gas price (denominated in wei). + +Retrieving gas price +-------------------- + +To retreive the gas price using the selected strategy simply call +:meth:`~web3.vns.Bbbbbbbb.generateGasPrice` + +.. code-block:: python + + >>> Web3.vns.generateGasPrice() + 20000000000 + +Creating a gas price strategy +------------------------------- + +A gas price strategy is implemented as a python method with the following +signature: + +.. code-block:: python + + def gas_price_strategy(web3, transaction_params=None): + ... + +The method must return a positive integer representing the gas price in wei. + +To demonstrate, here is a rudimentary example of a gas price strategy that +returns a higher gas price when the value of the transaction is higher than +1 Ether. + +.. code-block:: python + + from web3 import Web3 + + def value_based_gas_price_strategy(web3, transaction_params): + if transaction_params['value'] > Web3.toWei(1, 'ether'): + return Web3.toWei(20, 'gwei') + else: + return Web3.toWei(5, 'gwei') + +Selecting the gas price strategy +-------------------------------- + +The gas price strategy can be set by calling :meth:`~web3.vns.Bbbbbbbb.setGasPriceStrategy`. + +.. code-block:: python + + from web3 import Web3 + + def value_based_gas_price_strategy(web3, transaction_params): + ... + + w3 = Web3(...) + w3.vns.setGasPriceStrategy(value_based_gas_price_strategy) + +Available gas price strategies +------------------------------ + +.. py:module:: web3.gas_strategies.rpc + +.. py:method:: rpc_gas_price_strategy(web3, transaction_params=None) + + Makes a call to the `JSON-RPC vns_gasPrice + method `_ which returns + the gas price configured by the connected Ethereum node. + +.. py:module:: web3.gas_strategies.time_based + +.. py:method:: construct_time_based_gas_price_strategy(max_wait_seconds, sample_size, probability) + + Constructs a strategy which will compute a gas price such that the + transaction will be mined within a number of seconds defined by + ``max_wait_seconds`` with a probability defined by ``probability``. The + gas price is computed by sampling ``sample_size`` of the most recently + mined blocks. + + * ``max_wait_seconds`` The desired maxiumum number of seconds the + transaction should take to mine. + * ``sample_size`` The number of recent blocks to sample + * ``probability`` An integer representation of the desired probability that + the transaction will be mined within ``max_wait_seconds``. 0 means 0% + and 100 means 100%. + + The following ready to use versions of this strategy are available. + + * ``web3.gas_strategies.time_based.fast_gas_price_strategy``: Transaction mined within 60 seconds. + * ``web3.gas_strategies.time_based.medium_gas_price_strategy``: Transaction mined within 5 minutes. + * ``web3.gas_strategies.time_based.slow_gas_price_strategy``: Transaction mined within 1 hour. + * ``web3.gas_strategies.time_based.glacial_gas_price_strategy``: Transaction mined within 24 hours. + + .. warning:: Due to the overhead of sampling the recent blocks it is + recommended that a caching solution be used to reduce the amount of chain + data that needs to be re-fetched for each request. + + .. code-block:: python + + from web3 import Web3, middleware + from web3.gas_strategies.time_based import medium_gas_price_strategy + + w3 = Web3() + w3.vns.setGasPriceStrategy(medium_gas_price_strategy) + + w3.middleware_onion.add(middleware.time_based_cache_middleware) + w3.middleware_onion.add(middleware.latest_block_based_cache_middleware) + w3.middleware_onion.add(middleware.simple_cache_middleware) diff --git a/docs/index.rst b/docs/index.rst index 70fb31cd7e..94b6b5aa03 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,50 +1,49 @@ -Web3.py -======= - -Web3.py is a python library for interacting with Ethereum. Its API is derived -from the `Web3.js`_ Javascript API and should be familiar to anyone who has -used ``web3.js``. - - -Contents --------- - -.. toctree:: - :maxdepth: 1 - - quickstart - overview - node - v5_migration - v4_migration - filters - contracts - providers - ens_overview - middleware - examples - troubleshooting - web3.main - web3.eth - web3.eth.account - web3.pm - web3.net - web3.miner - web3.geth - web3.parity - gas_price - ens - ethpm - internals - abi_types - releases - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - -.. _Web3.js: https://github.com/ethereum/wiki/wiki/JavaScript-API +Web3.py +======= + +Web3.py is a python library for interacting with Ethereum. Its API is derived +from the `Web3.js`_ Javascript API and should be familiar to anyone who has +used ``web3.js``. + + +Contents +-------- + +.. toctree:: + :maxdepth: 1 + + quickstart + overview + node + v5_migration + v4_migration + filters + contracts + providers + ens_overview + middleware + examples + troubleshooting + web3.main + web3.vns + web3.vns.account + web3.pm + web3.net + web3.miner + web3.geth + web3.parity + gas_price + ens + internals + conventions + releases + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + +.. _Web3.js: https://github.com/ethereum/wiki/wiki/JavaScript-API diff --git a/docs/internals.rst b/docs/internals.rst index fcb950d80e..f590bd6593 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1,210 +1,210 @@ -Web3 Internals -============== - - -.. warning:: This section of the documentation is for advanced users. You should probably stay away from these APIs if you don't know what you are doing. - -The Web3 library has multiple layers of abstraction between the public api -exposed by the web3 object and the backend or node that web3 is connecting to. - -* **Providers** are responsible for the actual communication with the - blockchain such as sending JSON-RPC requests over HTTP or an IPC socket. -* **Middlewares** provide hooks for monitoring and modifying requests and - responses to and from the provider. These can be *global* operating on all - providers or specific to one provider. -* **Managers** provide thread safety and primatives to allow for asyncronous usage of web3. - -Here are some common things you might want to do with these APIs. - -* Redirect certain RPC requests to different providers such as sending all - *read* operations to a provider backed by Infura and all *write* operations - to a go-ethereum node that you control. -* Transparently intercept transactions sent over ``eth_sendTransaction``, sign - them locally, and then send them through ``eth_sendRawTransaction``. -* Modify the response from an RPC request so that it is returned in different - format such as converting all integer values to their hexadecimal - representation. -* Validate the inputs to RPC requests - - -Request Lifecycle ------------------ - -Each web3 RPC call passes through these layers in the following manner. - -.. code-block:: none - - *********** ************ - | Request | | Response | - *********** ************ - | ^ - v | - +-----------------------------+ - | Manager | - +-----------------------------+ - | ^ - v | - +-----------------------------+ - | Global Middlewares | - +-----------------------------+ - | ^ - v | - +-----------------------------+ - | Provider Middlewares | - +-----------------------------+ - | ^ - v | - +-----------------------------+ - | Provider | - +-----------------------------+ - - -You can visualize this relationship like an onion, with the Provider at the -center. The request originates from the Manager, outside of the onion, passing -down through each layer of the onion until it reaches the Provider at the -center. The Provider then handles the request, producing a response which will -then pass back out from the center of the onion, through each layer until it is -finally returned by the Manager. - -In the situation where web3 is operating with multiple providers the same -lifecycle applies. The manager will iterate over each provider, returning the -response from the first provider that returns a response. - - -Providers ---------- - -A provider is responsible for all direct blockchain interactions. In most -cases this means interacting with the JSON-RPC server for an ethereum node over -HTTP or an IPC socket. There is however nothing which requires providers to be -RPC based, allowing for providers designed for testing purposes which use an -in-memory EVM to fulfill requests. - -In most simple cases you will be using a single provider. However, if you -would like to use Web3 with multiple providers, you can simply pass them in as -a list when instantiating your ``Web3`` object. - - -.. code-block:: python - - >>> w3 = Web3([provider_a, provider_b]) - - - -Writing your own Provider -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Writing your own provider requires implementing two required methods as well as -setting the middlewares the provider should use. - - -.. py:method:: BaseProvider.make_request(method, params) - - Each provider class **must** implement this method. This method **should** - return a JSON object with either a ``'result'`` key in the case of success, - or an ``'error'`` key in the case of failure. - - - * ``method`` This will be a string representing the JSON-RPC method that - is being called such as ``'eth_sendTransaction'``. - * ``params`` This will be a list or other iterable of the parameters for - the JSON-RPC method being called. - - -.. py:method:: BaseProvider.isConnected() - - This function should return ``True`` or ``False`` depending on whether the - provider should be considered *connected*. For example, an IPC socket - based provider should return ``True`` if the socket is open and ``False`` - if the socket is closed. - - -If a provider is unable to respond to certain RPC calls it should raise the -``web3.exceptions.CannotHandleRequest`` exception. When this happens, the -request is issued to the next configured provider. If no providers are able to -handle the request then a ``web3.exceptions.UnhandledRequest`` error will be -raised. - -.. py:attribute:: BaseProvider.middlewares - - This should be an iterable of middlewares. - -You can set a new list of middlewares by assigning to ``provider.middlewares``, -with the first middleware that processes the request at the beginning of the list. - - -.. _internals__middlewares: - -Middlewares ------------ - -.. note:: The Middleware API in web3 borrows heavily from the Django middleware API introduced in version 1.10.0 - -Middlewares provide a simple yet powerful api for implementing layers of -business logic for web3 requests. Writing middleware is simple. - -.. code-block:: python - - def simple_middleware(make_request, w3): - # do one-time setup operations here - - def middleware(method, params): - # do pre-processing here - - # perform the RPC request, getting the response - response = make_request(method, params) - - # do post-processing here - - # finally return the response - return response - return middleware - - -It is also possible to implement middlewares as a class. - - -.. code-block:: python - - class SimpleMiddleware: - def __init__(self, make_request, w3): - self.w3 = w3 - self.make_request = make_request - - def __call__(self, method, params): - # do pre-processing here - - # perform the RPC request, getting the response - response = self.make_request(method, params) - - # do post-processing here - - # finally return the response - return response - - -The ``make_request`` parameter is a callable which takes two -positional arguments, ``method`` and ``params`` which correspond to the RPC -method that is being called. There is no requirement that the ``make_request`` -function be called. For example, if you were writing a middleware which cached -responses for certain methods your middleware would likely not call the -``make_request`` method, but instead get the response from some local cache. - -By default, Web3 will use the ``web3.middleware.pythonic_middleware``. This -middleware performs the following translations for requests and responses. - -* Numeric request parameters will be converted to their hexadecimal representation -* Numeric responses will be converted from their hexadecimal representations to - their integer representations. - -The ``RequestManager`` object exposes the ``middleware_onion`` object to manage middlewares. It -is also exposed on the ``Web3`` object for convenience. That API is detailed in -:ref:`Modifying_Middleware`. - - -Managers --------- - -The Manager acts as a gatekeeper for the request/response lifecycle. It is -unlikely that you will need to change the Manager as most functionality can be -implemented in the Middleware layer. +Web3 Internals +============== + + +.. warning:: This section of the documentation is for advanced users. You should probably stay away from these APIs if you don't know what you are doing. + +The Web3 library has multiple layers of abstraction between the public api +exposed by the web3 object and the backend or node that web3 is connecting to. + +* **Providers** are responsible for the actual communication with the + blockchain such as sending JSON-RPC requests over HTTP or an IPC socket. +* **Middlewares** provide hooks for monitoring and modifying requests and + responses to and from the provider. These can be *global* operating on all + providers or specific to one provider. +* **Managers** provide thread safety and primatives to allow for asyncronous usage of web3. + +Here are some common things you might want to do with these APIs. + +* Redirect certain RPC requests to different providers such as sending all + *read* operations to a provider backed by Infura and all *write* operations + to a go-ethereum node that you control. +* Transparently intercept transactions sent over ``vns_sendTransaction``, sign + them locally, and then send them through ``vns_sendRawTransaction``. +* Modify the response from an RPC request so that it is returned in different + format such as converting all integer values to their hexadecimal + representation. +* Validate the inputs to RPC requests + + +Request Lifecycle +----------------- + +Each web3 RPC call passes through these layers in the following manner. + +.. code-block:: none + + *********** ************ + | Request | | Response | + *********** ************ + | ^ + v | + +-----------------------------+ + | Manager | + +-----------------------------+ + | ^ + v | + +-----------------------------+ + | Global Middlewares | + +-----------------------------+ + | ^ + v | + +-----------------------------+ + | Provider Middlewares | + +-----------------------------+ + | ^ + v | + +-----------------------------+ + | Provider | + +-----------------------------+ + + +You can visualize this relationship like an onion, with the Provider at the +center. The request originates from the Manager, outside of the onion, passing +down through each layer of the onion until it reaches the Provider at the +center. The Provider then handles the request, producing a response which will +then pass back out from the center of the onion, through each layer until it is +finally returned by the Manager. + +In the situation where web3 is operating with multiple providers the same +lifecycle applies. The manager will iterate over each provider, returning the +response from the first provider that returns a response. + + +Providers +--------- + +A provider is responsible for all direct blockchain interactions. In most +cases this means interacting with the JSON-RPC server for an ethereum node over +HTTP or an IPC socket. There is however nothing which requires providers to be +RPC based, allowing for providers designed for testing purposes which use an +in-memory EVM to fulfill requests. + +In most simple cases you will be using a single provider. However, if you +would like to use Web3 with multiple providers, you can simply pass them in as +a list when instantiating your ``Web3`` object. + + +.. code-block:: python + + >>> w3 = Web3([provider_a, provider_b]) + + + +Writing your own Provider +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Writing your own provider requires implementing two required methods as well as +setting the middlewares the provider should use. + + +.. py:method:: BaseProvider.make_request(method, params) + + Each provider class **must** implement this method. This method **should** + return a JSON object with either a ``'result'`` key in the case of success, + or an ``'error'`` key in the case of failure. + + + * ``method`` This will be a string representing the JSON-RPC method that + is being called such as ``'vns_sendTransaction'``. + * ``params`` This will be a list or other iterable of the parameters for + the JSON-RPC method being called. + + +.. py:method:: BaseProvider.isConnected() + + This function should return ``True`` or ``False`` depending on whether the + provider should be considered *connected*. For example, an IPC socket + based provider should return ``True`` if the socket is open and ``False`` + if the socket is closed. + + +If a provider is unable to respond to certain RPC calls it should raise the +``web3.exceptions.CannotHandleRequest`` exception. When this happens, the +request is issued to the next configured provider. If no providers are able to +handle the request then a ``web3.exceptions.UnhandledRequest`` error will be +raised. + +.. py:attribute:: BaseProvider.middlewares + + This should be an iterable of middlewares. + +You can set a new list of middlewares by assigning to ``provider.middlewares``, +with the first middleware that processes the request at the beginning of the list. + + +.. _internals__middlewares: + +Middlewares +----------- + +.. note:: The Middleware API in web3 borrows heavily from the Django middleware API introduced in version 1.10.0 + +Middlewares provide a simple yet powerful api for implementing layers of +business logic for web3 requests. Writing middleware is simple. + +.. code-block:: python + + def simple_middleware(make_request, w3): + # do one-time setup operations here + + def middleware(method, params): + # do pre-processing here + + # perform the RPC request, getting the response + response = make_request(method, params) + + # do post-processing here + + # finally return the response + return response + return middleware + + +It is also possible to implement middlewares as a class. + + +.. code-block:: python + + class SimpleMiddleware: + def __init__(self, make_request, w3): + self.w3 = w3 + self.make_request = make_request + + def __call__(self, method, params): + # do pre-processing here + + # perform the RPC request, getting the response + response = self.make_request(method, params) + + # do post-processing here + + # finally return the response + return response + + +The ``make_request`` parameter is a callable which takes two +positional arguments, ``method`` and ``params`` which correspond to the RPC +method that is being called. There is no requirement that the ``make_request`` +function be called. For example, if you were writing a middleware which cached +responses for certain methods your middleware would likely not call the +``make_request`` method, but instead get the response from some local cache. + +By default, Web3 will use the ``web3.middleware.pythonic_middleware``. This +middleware performs the following translations for requests and responses. + +* Numeric request parameters will be converted to their hexadecimal representation +* Numeric responses will be converted from their hexadecimal representations to + their integer representations. + +The ``RequestManager`` object exposes the ``middleware_onion`` object to manage middlewares. It +is also exposed on the ``Web3`` object for convenience. That API is detailed in +:ref:`Modifying_Middleware`. + + +Managers +-------- + +The Manager acts as a gatekeeper for the request/response lifecycle. It is +unlikely that you will need to change the Manager as most functionality can be +implemented in the Middleware layer. diff --git a/docs/middleware.rst b/docs/middleware.rst index 639396ea2b..686bb4e3e8 100644 --- a/docs/middleware.rst +++ b/docs/middleware.rst @@ -1,419 +1,417 @@ -Middleware -========== - -Web3 manages layers of middlewares by default. They sit between the public Web3 methods and the -:doc:`providers`, which handle native communication with the Ethereum client. Each layer -can modify the request and/or response. Some middlewares are enabled by default, and -others are available for optional use. - -Each middleware layer gets invoked before the request reaches the provider, and then -processes the result after the provider returns, in reverse order. However, it is -possible for a middleware to return early from a -call without the request ever getting to the provider (or even reaching the middlewares -that are in deeper layers). - -More information is available in the "Internals: :ref:`internals__middlewares`" section. - - -Default Middleware ------------------- - -Some middlewares are added by default if you do not supply any. The defaults -are likely to change regularly, so this list may not include the latest version's defaults. -You can find the latest defaults in the constructor in `web3/manager.py` - -AttributeDict -~~~~~~~~~~~~~~~~~~ - -.. py:method:: web3.middleware.attrdict_middleware - - This middleware converts the output of a function from a dictionary to an ``AttributeDict`` - which enables dot-syntax access, like ``eth.getBlock('latest').number`` - in addition to ``eth.getBlock('latest')['number']``. - -.eth Name Resolution -~~~~~~~~~~~~~~~~~~~~~ - -.. py:method:: web3.middleware.name_to_address_middleware - - This middleware converts Ethereum Name Service (ENS) names into the - address that the name points to. For example :meth:`~web3.Eth.sendTransaction` will - accept .eth names in the 'from' and 'to' fields. - -.. note:: - This middleware only converts ENS names if invoked with the mainnet - (where the ENS contract is deployed), for all other cases will result in an - ``InvalidAddress`` error - -Pythonic -~~~~~~~~~~~~ - -.. py:method:: web3.middleware.pythonic_middleware - - This converts arguments and returned values to python primitives, - where appropriate. For example, it converts the raw hex string returned by the RPC call - ``eth_blockNumber`` into an ``int``. - -Gas Price Strategy -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. py:method:: web3.middleware.gas_price_strategy_middleware - - This adds a gasPrice to transactions if applicable and when a gas price strategy has - been set. See :ref:`Gas_Price` for information about how gas price is derived. - -HTTPRequestRetry -~~~~~~~~~~~~~~~~~~ - -.. py:method:: web3.middleware.http_retry_request_middleware - - This middleware is a default specifically for HTTPProvider that retries failed - requests that return the following errors: `ConnectionError`, `HTTPError`, `Timeout`, - `TooManyRedirects`. Additionally there is a whitelist that only allows certain - methods to be retried in order to not resend transactions, excluded methods are: - `eth_sendTransaction`, `personal_signAndSendTransaction`, `personal_sendTransaction`. - -.. _Modifying_Middleware: - -Configuring Middleware ------------------------ - -Middleware can be added, removed, replaced, and cleared at runtime. To make that easier, you -can name the middleware for later reference. Alternatively, you can use a reference to the -middleware itself. - -Middleware Order -~~~~~~~~~~~~~~~~~~ - -Think of the middleware as being layered in an onion, where you initiate a web3.py request at -the outermost layer of the onion, and the Ethereum node (like geth or parity) receives and responds -to the request inside the innermost layer of the onion. Here is a (simplified) diagram: - -.. code-block:: none - - New request from web3.py - - | - | - v - - `````Layer 2`````` - ``````` ``````` - ````` | ```` - ```` v ```` - ``` ``` - `. ````````Layer 1``````` `.` - `` ```` ````` .` - `. ``` | ``` `.` - .` ``` v ``` `. - `. `.` ``` .` - `` .` `Layer 0` `` .` - `` `. ````` `````` . .` - `. `` ``` | ``` .` . - . `` `.` | `` . . - . `. `` JSON-RPC call .` . .` - . . `` | . `` . - `` . . v . . . - . .` . . . `` - . . . Ethereum node .` . . - . . . . . . - . `` `. | . . . - . . .` | .` . . - `. .` .` Response .` .` . - . . `.` | `.` `. . - `. . ``` | ```` `. . - . `. ````` v ```` `. `` - . .` ```Layer 0`` `` `. - . `. `.` `. - . `. | `.` `. - .` ``` | ``` .` - `. ``` v ```` `.` - `` `````` ````` .` - `` `````Layer 1````` `.` - ``` ``` - ```` | ``` - ````` v ```` - `````` ````` - `````````Layer 2`````````` - - | - v - - Returned value in Web3.py - - -The middlewares are maintained in ``Web3.middleware_onion``. See -below for the API. - -When specifying middlewares in a list, or retrieving the list of middlewares, they will -be returned in the order of outermost layer first and innermost layer last. In the above -example, that means that ``list(w3.middleware_onion)`` would return the middlewares in -the order of: ``[2, 1, 0]``. - -See "Internals: :ref:`internals__middlewares`" for a deeper dive to how middlewares work. - -Middleware Stack API -~~~~~~~~~~~~~~~~~~~~~ - -To add or remove items in different layers, use the following API: - -.. py:method:: Web3.middleware_onion.add(middleware, name=None) - - Middleware will be added to the outermost layer. That means the new middleware will modify the - request first, and the response last. You can optionally name it with any hashable object, - typically a string. - - .. code-block:: python - - >>> w3 = Web3(...) - >>> w3.middleware_onion.add(web3.middleware.pythonic_middleware) - # or - >>> w3.middleware_onion.add(web3.middleware.pythonic_middleware, 'pythonic') - -.. py:method:: Web3.middleware_onion.inject(middleware, name=None, layer=None) - - Inject a named middleware to an arbitrary layer. - - The current implementation only supports injection at the innermost or - outermost layers. Note that injecting to the outermost layer is equivalent to calling - :meth:`Web3.middleware_onion.add` . - - .. code-block:: python - - # Either of these will put the pythonic middleware at the innermost layer - >>> w3 = Web3(...) - >>> w3.middleware_onion.inject(web3.middleware.pythonic_middleware, layer=0) - # or - >>> w3.middleware_onion.inject(web3.middleware.pythonic_middleware, 'pythonic', layer=0) - -.. py:method:: Web3.middleware_onion.remove(middleware) - - Middleware will be removed from whatever layer it was in. If you added the middleware with - a name, use the name to remove it. If you added the middleware as an object, use the object - again later to remove it: - - .. code-block:: python - - >>> w3 = Web3(...) - >>> w3.middleware_onion.remove(web3.middleware.pythonic_middleware) - # or - >>> w3.middleware_onion.remove('pythonic') - -.. py:method:: Web3.middleware_onion.replace(old_middleware, new_middleware) - - Middleware will be replaced from whatever layer it was in. If the middleware was named, it will - continue to have the same name. If it was un-named, then you will now reference it with the new - middleware object. - - .. code-block:: python - - >>> from web3.middleware import pythonic_middleware, attrdict_middleware - >>> w3 = Web3(...) - - >>> w3.middleware_onion.replace(pythonic_middleware, attrdict_middleware) - # this is now referenced by the new middleware object, so to remove it: - >>> w3.middleware_onion.remove(attrdict_middleware) - - # or, if it was named - - >>> w3.middleware_onion.replace('pythonic', attrdict_middleware) - # this is still referenced by the original name, so to remove it: - >>> w3.middleware_onion.remove('pythonic') - -.. py:method:: Web3.middleware_onion.clear() - - Empty all the middlewares, including the default ones. - - .. code-block:: python - - >>> w3 = Web3(...) - >>> w3.middleware_onion.clear() - >>> assert len(w3.middleware_onion) == 0 - - -Optional Middleware ------------------------ - -Web3 ships with non-default middleware, for your custom use. In addition to the other ways of -:ref:`Modifying_Middleware`, you can specify a list of middleware when initializing Web3, with: - -.. code-block:: python - - Web3(middlewares=[my_middleware1, my_middleware2]) - -.. warning:: - This will - *replace* the default middlewares. To keep the default functionality, - either use ``middleware_onion.add()`` from above, or add the default middlewares to your list of - new middlewares. - -Below is a list of built-in middleware, which is not enabled by default. - -Stalecheck -~~~~~~~~~~~~ - -.. py:method:: web3.middleware.make_stalecheck_middleware(allowable_delay) - - This middleware checks how stale the blockchain is, and interrupts calls with a failure - if the blockchain is too old. - - * ``allowable_delay`` is the length in seconds that the blockchain is allowed to be - behind of ``time.time()`` - - Because this middleware takes an argument, you must create the middleware - with a method call. - - .. code-block:: python - - two_day_stalecheck = make_stalecheck_middleware(60 * 60 * 24 * 2) - web3.middleware_onion.add(two_day_stalecheck) - - If the latest block in the blockchain is older than 2 days in this example, then the - middleware will raise a ``StaleBlockchain`` exception on every call except - ``web3.eth.getBlock()``. - - -Cache -~~~~~~~~~~~ - -All of the caching middlewares accept these common arguments. - -* ``cache_class`` must be a callable which returns an object which implements the dictionary API. -* ``rpc_whitelist`` must be an iterable, preferably a set, of the RPC methods that may be cached. -* ``should_cache_fn`` must be a callable with the signature ``fn(method, params, response)`` which returns whether the response should be cached. - - -.. py:method:: web3.middleware.construct_simple_cache_middleware(cache_class, rpc_whitelist, should_cache_fn) - - Constructs a middleware which will cache the return values for any RPC - method in the ``rpc_whitelist``. - - A ready to use version of this middleware can be found at - ``web3.middlewares.simple_cache_middleware``. - - -.. py:method:: web3.middleware.construct_time_based_cache_middleware(cache_class, cache_expire_seconds, rpc_whitelist, should_cache_fn) - - Constructs a middleware which will cache the return values for any RPC - method in the ``rpc_whitelist`` for an amount of time defined by - ``cache_expire_seconds``. - - * ``cache_expire_seconds`` should be the number of seconds a value may - remain in the cache before being evicted. - - A ready to use version of this middleware can be found at - ``web3.middlewares.time_based_cache_middleware``. - - -.. py:method:: web3.middleware.construct_latest_block_based_cache_middleware(cache_class, average_block_time_sample_size, default_average_block_time, rpc_whitelist, should_cache_fn) - - Constructs a middleware which will cache the return values for any RPC - method in the ``rpc_whitelist`` for an amount of time defined by - ``cache_expire_seconds``. - - * ``average_block_time_sample_size`` The number of blocks which should be - sampled to determine the average block time. - * ``default_average_block_time`` The initial average block time value to - use for cases where there is not enough chain history to determine the - average block time. - - A ready to use version of this middleware can be found at - ``web3.middlewares.latest_block_based_cache_middleware``. - -.. _geth-poa: - -Geth-style Proof of Authority -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This middleware is required to connect to ``geth --dev`` or the Rinkeby public network. - -The easiest way to connect to a default ``geth --dev`` instance which loads the middleware is: - - -.. code-block:: python - - >>> from web3.auto.gethdev import w3 - - # confirm that the connection succeeded - >>> w3.clientVersion - 'Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9' - -This example connects to a local ``geth --dev`` instance on Linux with a -unique IPC location and loads the middleware: - - -.. code-block:: python - - >>> from web3 import Web3, IPCProvider - - # connect to the IPC location started with 'geth --dev --datadir ~/mynode' - >>> w3 = Web3(IPCProvider('~/mynode/geth.ipc')) - - >>> from web3.middleware import geth_poa_middleware - - # inject the poa compatibility middleware to the innermost layer - >>> w3.middleware_onion.inject(geth_poa_middleware, layer=0) - - # confirm that the connection succeeded - >>> w3.clientVersion - 'Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9' - -Why is ``geth_poa_middleware`` necessary? -'''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -There is no strong community consensus on a single Proof-of-Authority (PoA) standard yet. -Some nodes have successful experiments running, though. One is go-ethereum (geth), -which uses a prototype PoA for it's development mode and the Rinkeby test network. - -Unfortunately, it does deviate from the yellow paper specification, which constrains the -``extraData`` field in each block to a maximum of 32-bytes. Geth's PoA uses more than -32 bytes, so this middleware modifies the block data a bit before returning it. - -.. _local-filter: - -Locally Managed Log and Block Filters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This middleware provides an alternative to ethereum node managed filters. When used, Log and -Block filter logic are handled locally while using the same web3 filter api. Filter results are -retrieved using JSON-RPC endpoints that don't rely on server state. - -.. doctest:: - - >>> from web3 import Web3, EthereumTesterProvider - >>> w3 = Web3(EthereumTesterProvider()) - >>> from web3.middleware import local_filter_middleware - >>> w3.middleware_onion.add(local_filter_middleware) - -.. code-block:: python - - # Normal block and log filter apis behave as before. - >>> block_filter = w3.eth.filter("latest") - - >>> log_filter = myContract.events.myEvent.build_filter().deploy() - -Signing -~~~~~~~ - -.. py:method:: web3.middleware.construct_sign_and_send_raw_middleware(private_key_or_account) - -This middleware automatically captures transactions, signs them, and sends them as raw transactions. The from field on the transaction, or ``w3.eth.defaultAccount`` must be set to the address of the private key for this middleware to have any effect. - - * ``private_key_or_account`` A single private key or a tuple, list or set of private keys. - - Keys can be in any of the following formats: - - * An ``eth_account.LocalAccount`` object - * An ``eth_keys.PrivateKey`` object - * A raw private key as a hex string or byte string - -.. code-block:: python - - >>> from web3 import Web3, EthereumTesterProvider - >>> w3 = Web3(EthereumTesterProvider) - >>> from web3.middleware import construct_sign_and_send_raw_middleware - >>> from eth_account import Account - >>> acct = Account.create('KEYSMASH FJAFJKLDSKF7JKFDJ 1530') - >>> w3.middleware_onion.add(construct_sign_and_send_raw_middleware(acct)) - >>> w3.eth.defaultAccount = acct.address - # Now you can send a tx from acct.address without having to build and sign each raw transaction +Middleware +========== + +Web3 manages layers of middlewares by default. They sit between the public Web3 methods and the +:doc:`providers`, which handle native communication with the Ethereum client. Each layer +can modify the request and/or response. Some middlewares are enabled by default, and +others are available for optional use. + +Each middleware layer gets invoked before the request reaches the provider, and then +processes the result after the provider returns, in reverse order. However, it is +possible for a middleware to return early from a +call without the request ever getting to the provider (or even reaching the middlewares +that are in deeper layers). + +More information is available in the "Internals: :ref:`internals__middlewares`" section. + + +Default Middleware +------------------ + +Some middlewares are added by default if you do not supply any. The defaults +are likely to change regularly, so this list may not include the latest version's defaults. +You can find the latest defaults in the constructor in `web3/manager.py` + +AttributeDict +~~~~~~~~~~~~~~~~~~ + +.. py:method:: web3.middleware.attrdict_middleware + + This middleware converts the output of a function from a dictionary to an ``AttributeDict`` + which enables dot-syntax access, like ``vns.getBlock('latest').number`` + in addition to ``vns.getBlock('latest')['number']``. + +.vns Name Resolution +~~~~~~~~~~~~~~~~~~~~~ + +.. py:method:: web3.middleware.name_to_address_middleware + + This middleware converts Ethereum Name Service (ENS) names into the + address that the name points to. For example :meth:`~web3.vns.sendTransaction` will + accept .vns names in the 'from' and 'to' fields. + +.. note:: + This middleware only converts ENS names if invoked with the mainnet + (where the ENS contract is deployed), for all other cases will result in an + ``InvalidAddress`` error + +Pythonic +~~~~~~~~~~~~ + +.. py:method:: web3.middleware.pythonic_middleware + + This converts arguments and returned values to python primitives, + where appropriate. For example, it converts the raw hex string returned by the RPC call + ``vns_blockNumber`` into an ``int``. + +Gas Price Strategy +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:method:: web3.middleware.gas_price_strategy_middleware + + This adds a gasPrice to transactions if applicable and when a gas price strategy has + been set. See :ref:`Gas_Price` for information about how gas price is derived. + +HTTPRequestRetry +~~~~~~~~~~~~~~~~~~ + +.. py:method:: web3.middleware.http_retry_request_middleware + + This middleware is a default specifically for HTTPProvider that retries failed + requests that return the following errors: `ConnectionError`, `HTTPError`, `Timeout`, + `TooManyRedirects`. Additionally there is a whitelist that only allows certain + methods to be retried in order to not resend transactions, excluded methods are: + `vns_sendTransaction`, `personal_signAndSendTransaction`, `personal_sendTransaction`. + +.. _Modifying_Middleware: + +Configuring Middleware +----------------------- + +Middleware can be added, removed, replaced, and cleared at runtime. To make that easier, you +can name the middleware for later reference. Alternatively, you can use a reference to the +middleware itself. + +Middleware Order +~~~~~~~~~~~~~~~~~~ + +Think of the middleware as being layered in an onion, where you initiate a web3.py request at +the outermost layer of the onion, and the Ethereum node (like geth or parity) receives and responds +to the request inside the innermost layer of the onion. Here is a (simplified) diagram: + +.. code-block:: none + + New request from web3.py + + | + | + v + + `````Layer 2`````` + ``````` ``````` + ````` | ```` + ```` v ```` + ``` ``` + `. ````````Layer 1``````` `.` + `` ```` ````` .` + `. ``` | ``` `.` + .` ``` v ``` `. + `. `.` ``` .` + `` .` `Layer 0` `` .` + `` `. ````` `````` . .` + `. `` ``` | ``` .` . + . `` `.` | `` . . + . `. `` JSON-RPC call .` . .` + . . `` | . `` . + `` . . v . . . + . .` . . . `` + . . . Ethereum node .` . . + . . . . . . + . `` `. | . . . + . . .` | .` . . + `. .` .` Response .` .` . + . . `.` | `.` `. . + `. . ``` | ```` `. . + . `. ````` v ```` `. `` + . .` ```Layer 0`` `` `. + . `. `.` `. + . `. | `.` `. + .` ``` | ``` .` + `. ``` v ```` `.` + `` `````` ````` .` + `` `````Layer 1````` `.` + ``` ``` + ```` | ``` + ````` v ```` + `````` ````` + `````````Layer 2`````````` + + | + v + + Returned value in Web3.py + + +The middlewares are maintained in ``Web3.middleware_onion``. See +below for the API. + +When specifying middlewares in a list, or retrieving the list of middlewares, they will +be returned in the order of outermost layer first and innermost layer last. In the above +example, that means that ``list(w3.middleware_onion)`` would return the middlewares in +the order of: ``[2, 1, 0]``. + +See "Internals: :ref:`internals__middlewares`" for a deeper dive to how middlewares work. + +Middleware Stack API +~~~~~~~~~~~~~~~~~~~~~ + +To add or remove items in different layers, use the following API: + +.. py:method:: Web3.middleware_onion.add(middleware, name=None) + + Middleware will be added to the outermost layer. That means the new middleware will modify the + request first, and the response last. You can optionally name it with any hashable object, + typically a string. + + .. code-block:: python + + >>> w3 = Web3(...) + >>> w3.middleware_onion.add(web3.middleware.pythonic_middleware) + # or + >>> w3.middleware_onion.add(web3.middleware.pythonic_middleware, 'pythonic') + +.. py:method:: Web3.middleware_onion.inject(middleware, name=None, layer=None) + + Inject a named middleware to an arbitrary layer. + + The current implementation only supports injection at the innermost or + outermost layers. Note that injecting to the outermost layer is equivalent to calling + :meth:`Web3.middleware_onion.add` . + + .. code-block:: python + + # Either of these will put the pythonic middleware at the innermost layer + >>> w3 = Web3(...) + >>> w3.middleware_onion.inject(web3.middleware.pythonic_middleware, layer=0) + # or + >>> w3.middleware_onion.inject(web3.middleware.pythonic_middleware, 'pythonic', layer=0) + +.. py:method:: Web3.middleware_onion.remove(middleware) + + Middleware will be removed from whatever layer it was in. If you added the middleware with + a name, use the name to remove it. If you added the middleware as an object, use the object + again later to remove it: + + .. code-block:: python + + >>> w3 = Web3(...) + >>> w3.middleware_onion.remove(web3.middleware.pythonic_middleware) + # or + >>> w3.middleware_onion.remove('pythonic') + +.. py:method:: Web3.middleware_onion.replace(old_middleware, new_middleware) + + Middleware will be replaced from whatever layer it was in. If the middleware was named, it will + continue to have the same name. If it was un-named, then you will now reference it with the new + middleware object. + + .. code-block:: python + + >>> from web3.middleware import pythonic_middleware, attrdict_middleware + >>> w3 = Web3(...) + + >>> w3.middleware_onion.replace(pythonic_middleware, attrdict_middleware) + # this is now referenced by the new middleware object, so to remove it: + >>> w3.middleware_onion.remove(attrdict_middleware) + + # or, if it was named + + >>> w3.middleware_onion.replace('pythonic', attrdict_middleware) + # this is still referenced by the original name, so to remove it: + >>> w3.middleware_onion.remove('pythonic') + +.. py:method:: Web3.middleware_onion.clear() + + Empty all the middlewares, including the default ones. + + .. code-block:: python + + >>> w3 = Web3(...) + >>> w3.middleware_onion.clear() + >>> assert len(w3.middleware_onion) == 0 + + +Optional Middleware +----------------------- + +Web3 ships with non-default middleware, for your custom use. In addition to the other ways of +:ref:`Modifying_Middleware`, you can specify a list of middleware when initializing Web3, with: + +.. code-block:: python + + Web3(middlewares=[my_middleware1, my_middleware2]) + +.. warning:: + This will + *replace* the default middlewares. To keep the default functionality, + either use ``middleware_onion.add()`` from above, or add the default middlewares to your list of + new middlewares. + +Below is a list of built-in middleware, which is not enabled by default. + +Stalecheck +~~~~~~~~~~~~ + +.. py:method:: web3.middleware.make_stalecheck_middleware(allowable_delay) + + This middleware checks how stale the blockchain is, and interrupts calls with a failure + if the blockchain is too old. + + * ``allowable_delay`` is the length in seconds that the blockchain is allowed to be + behind of ``time.time()`` + + Because this middleware takes an argument, you must create the middleware + with a method call. + + .. code-block:: python + + two_day_stalecheck = make_stalecheck_middleware(60 * 60 * 24 * 2) + web3.middleware_onion.add(two_day_stalecheck) + + If the latest block in the blockchain is older than 2 days in this example, then the + middleware will raise a ``StaleBlockchain`` exception on every call except + ``web3.vns.getBlock()``. + + +Cache +~~~~~~~~~~~ + +All of the caching middlewares accept these common arguments. + +* ``cache_class`` must be a callable which returns an object which implements the dictionary API. +* ``rpc_whitelist`` must be an iterable, preferably a set, of the RPC methods that may be cached. +* ``should_cache_fn`` must be a callable with the signature ``fn(method, params, response)`` which returns whether the response should be cached. + + +.. py:method:: web3.middleware.construct_simple_cache_middleware(cache_class, rpc_whitelist, should_cache_fn) + + Constructs a middleware which will cache the return values for any RPC + method in the ``rpc_whitelist``. + + A ready to use version of this middleware can be found at + ``web3.middlewares.simple_cache_middleware``. + + +.. py:method:: web3.middleware.construct_time_based_cache_middleware(cache_class, cache_expire_seconds, rpc_whitelist, should_cache_fn) + + Constructs a middleware which will cache the return values for any RPC + method in the ``rpc_whitelist`` for an amount of time defined by + ``cache_expire_seconds``. + + * ``cache_expire_seconds`` should be the number of seconds a value may + remain in the cache before being evicted. + + A ready to use version of this middleware can be found at + ``web3.middlewares.time_based_cache_middleware``. + + +.. py:method:: web3.middleware.construct_latest_block_based_cache_middleware(cache_class, average_block_time_sample_size, default_average_block_time, rpc_whitelist, should_cache_fn) + + Constructs a middleware which will cache the return values for any RPC + method in the ``rpc_whitelist`` for an amount of time defined by + ``cache_expire_seconds``. + + * ``average_block_time_sample_size`` The number of blocks which should be + sampled to determine the average block time. + * ``default_average_block_time`` The initial average block time value to + use for cases where there is not enough chain history to determine the + average block time. + + A ready to use version of this middleware can be found at + ``web3.middlewares.latest_block_based_cache_middleware``. + +.. _geth-poa: + +Geth-style Proof of Authority +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This middleware is required to connect to ``geth --dev`` or the Rinkeby public network. + +The easiest way to connect to a default ``geth --dev`` instance which loads the middleware is: + + +.. code-block:: python + + >>> from web3.auto.gethdev import w3 + + # confirm that the connection succeeded + >>> w3.version.node + 'Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9' + +This example connects to a local ``geth --dev`` instance on Linux with a +unique IPC location and loads the middleware: + + +.. code-block:: python + + >>> from web3 import Web3, IPCProvider + + # connect to the IPC location started with 'geth --dev --datadir ~/mynode' + >>> w3 = Web3(IPCProvider('~/mynode/geth.ipc')) + + >>> from web3.middleware import geth_poa_middleware + + # inject the poa compatibility middleware to the innermost layer + >>> w3.middleware_onion.inject(geth_poa_middleware, layer=0) + + # confirm that the connection succeeded + >>> w3.version.node + 'Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9' + +Why is ``geth_poa_middleware`` necessary? +'''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +There is no strong community consensus on a single Proof-of-Authority (PoA) standard yet. +Some nodes have successful experiments running, though. One is go-ethereum (geth), +which uses a prototype PoA for it's development mode and the Rinkeby test network. + +Unfortunately, it does deviate from the yellow paper specification, which constrains the +``extraData`` field in each block to a maximum of 32-bytes. Geth's PoA uses more than +32 bytes, so this middleware modifies the block data a bit before returning it. + +.. _local-filter: + +Locally Managed Log and Block Filters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This middleware provides an alternative to ethereum node managed filters. When used, Log and +Block filter logic are handled locally while using the same web3 filter api. Filter results are +retrieved using JSON-RPC endpoints that don't rely on server state. + +.. code-block:: python + + >>> from web3 import Web3, EthereumTesterProvider + >>> w3 = Web3(EthereumTesterProvider) + >>> from web3.middleware import local_filter_middleware + >>> w3.middleware_onion.add(local_filter_middleware()) + + # Normal block and log filter apis behave as before. + >>> block_filter = w3.vns.filter("latest") + + >>> log_filter = myContract.events.myEvent.build_filter().deploy() + +Signing +~~~~~~~ + +.. py:method:: web3.middleware.construct_sign_and_send_raw_middleware(private_key_or_account) + +This middleware automatically captures transactions, signs them, and sends them as raw transactions. The from field on the transaction, or ``w3.vns.defaultAccount`` must be set to the address of the private key for this middleware to have any effect. + + * ``private_key_or_account`` A single private key or a tuple, list or set of private keys. + + Keys can be in any of the following formats: + + * An ``vns_account.LocalAccount`` object + * An ``vns_keys.PrivateKey`` object + * A raw private key as a hex string or byte string + +.. code-block:: python + + >>> from web3 import Web3, EthereumTesterProvider + >>> w3 = Web3(EthereumTesterProvider) + >>> from web3.middleware import construct_sign_and_send_raw_middleware + >>> from vns_account import Account + >>> acct = Account.create('KEYSMASH FJAFJKLDSKF7JKFDJ 1530') + >>> w3.middleware_onion.add(construct_sign_and_send_raw_middleware(acct)) + >>> w3.vns.defaultAccount = acct.address + # Now you can send a tx from acct.address without having to build and sign each raw transaction diff --git a/docs/node.rst b/docs/node.rst index db51e14419..8964ffaed6 100644 --- a/docs/node.rst +++ b/docs/node.rst @@ -1,118 +1,118 @@ -Your Ethereum Node -=================== - -.. _why_need_connection: - -Why do I need to connect to a node? --------------------------------------- - -The Ethereum protocol defines a way for people to interact with -smart contracts and each other over a network. -In order to have up-to-date information about the status of contracts, -balances, and new transactions, the protocol requires a connection -to nodes on the network. These nodes are constantly sharing new data -with each other. - -Web3.py is a python library for connecting to these nodes. It does -not run its own node internally. - -.. _choosing_node: - -How do I choose which node to use? --------------------------------------- - -Due to the nature of Ethereum, this is largely a question of personal preference, but -it has significant ramifications on security and usability. Further, node software is -evolving quickly, so please do your own research about the current options. -We won't advocate for any particular node, -but list some popular options and some basic details on each. - -One of the key decisions is whether to use a local node or a hosted -node. A quick summary is at :ref:`local_vs_hosted`. - -A local node requires less trust than a hosted one. -A malicious hosted node can give you incorrect information, log your -sent transactions with your IP address, or simply go offline. Incorrect information -can cause all kinds of problems, including loss of assets. - -On the other hand, with a local node your machine is individually verifying -all the transactions on the network, and providing you with the latest state. -Unfortunately, this means using up a -significant amount of disk space, and sometimes notable -bandwidth and computation. -Additionally, there is a big up-front time cost for downloading the full blockchain history. - -If you want to have your -node manage keys for you (a popular option), you must use a local node. -Note that even if you run a node on your own machine, you are still trusting -the node software with any accounts you create on the node. - -The most popular self-run node options are: - -- `geth (go-ethereum) `_ -- `parity `_ - -You can find a fuller list of node software at `ethdocs.org -`_. - -Some people decide that the time it takes to sync a local node from scratch is too -high, especially if they are just exploring Ethereum for the first time. One way to -work around this issue is to use a hosted node. - -The most popular hosted node option is `Infura `_. -You can connect to it as if it were a local node, -with a few caveats. It cannot (and *should not*) host private keys for -you, meaning that some common methods like :meth:`w3.eth.sendTransaction() -` are not directly available. To send transactions -to a hosted node, read about :ref:`eth-account`. - -Once you decide what node option you want, you need to choose which network to connect to. -Typically, you are choosing between the main network and one of the available test networks. -See :ref:`choosing_network` - -Can I use MetaMask as a node? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -MetaMask is not a node. It is an interface for interacting with a node. -Roughly, it's what you get if you turn Web3.py into a browser extension. - -By default, MetaMask connects to an Infura node. -You can also set up MetaMask to use a node that you run locally. - -If you are trying to use accounts that were already created in MetaMask, see -:ref:`use_metamask_accounts` - -.. _choosing_network: - -Which network should I connect to? ------------------------------------- - -Once you have answered :ref:`choosing_node` you have to pick which network -to connect to. This is easy for some scenarios: if you have ether and you want -to spend it, or you want to interact with any production smart contracts, -then you connect to the main Ethereum network. - -If you want to test these things without using real ether, though, then you -need to connect to a test network. There are several test networks to -choose from. One test network, Ropsten, is the most similar to the production network. -However, spam and mining attacks have happened, -which is disruptive when you want to test out a contract. - -There are some alternative networks that limit the damage of spam attacks, but -they are not standardized across node software. Geth runs their own (Rinkeby), -and Parity runs their own (Kovan). See a full comparison in this `Stackexchange Q&A -`_. - -So roughly, choose this way: - -- If using Parity, connect to Kovan -- If using Geth, connect to Rinkeby -- If using a different node, or testing mining, connect to Ropsten - -Each of their networks has their own version of Ether. Main network ether must -be purchased, naturally, but test network ether is usually available for free. -See :ref:`faucets` - -Once you have decided which network to connect to, and set up your node for that network, -you need to decide how to connect to it. There are a handful of options in most nodes. -See :ref:`choosing_provider`. +Your Ethereum Node +=================== + +.. _why_need_connection: + +Why do I need to connect to a node? +-------------------------------------- + +The Ethereum protocol defines a way for people to interact with +smart contracts and each other over a network. +In order to have up-to-date information about the status of contracts, +balances, and new transactions, the protocol requires a connection +to nodes on the network. These nodes are constantly sharing new data +with each other. + +Web3.py is a python library for connecting to these nodes. It does +not run its own node internally. + +.. _choosing_node: + +How do I choose which node to use? +-------------------------------------- + +Due to the nature of Ethereum, this is largely a question of personal preference, but +it has significant ramifications on security and usability. Further, node software is +evolving quickly, so please do your own research about the current options. +We won't advocate for any particular node, +but list some popular options and some basic details on each. + +One of the key decisions is whether to use a local node or a hosted +node. A quick summary is at :ref:`local_vs_hosted`. + +A local node requires less trust than a hosted one. +A malicious hosted node can give you incorrect information, log your +sent transactions with your IP address, or simply go offline. Incorrect information +can cause all kinds of problems, including loss of assets. + +On the other hand, with a local node your machine is individually verifying +all the transactions on the network, and providing you with the latest state. +Unfortunately, this means using up a +significant amount of disk space, and sometimes notable +bandwidth and computation. +Additionally, there is a big up-front time cost for downloading the full blockchain history. + +If you want to have your +node manage keys for you (a popular option), you must use a local node. +Note that even if you run a node on your own machine, you are still trusting +the node software with any accounts you create on the node. + +The most popular self-run node options are: + +- `geth (go-ethereum) `_ +- `parity `_ + +You can find a fuller list of node software at `ethdocs.org +`_. + +Some people decide that the time it takes to sync a local node from scratch is too +high, especially if they are just exploring Ethereum for the first time. One way to +work around this issue is to use a hosted node. + +The most popular hosted node option is `Infura `_. +You can connect to it as if it were a local node, +with a few caveats. It cannot (and *should not*) host private keys for +you, meaning that some common methods like :meth:`w3.vns.sendTransaction() +` are not directly available. To send transactions +to a hosted node, read about :ref:`vns-account`. + +Once you decide what node option you want, you need to choose which network to connect to. +Typically, you are choosing between the main network and one of the available test networks. +See :ref:`choosing_network` + +Can I use MetaMask as a node? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MetaMask is not a node. It is an interface for interacting with a node. +Roughly, it's what you get if you turn Web3.py into a browser extension. + +By default, MetaMask connects to an Infura node. +You can also set up MetaMask to use a node that you run locally. + +If you are trying to use accounts that were already created in MetaMask, see +:ref:`use_metamask_accounts` + +.. _choosing_network: + +Which network should I connect to? +------------------------------------ + +Once you have answered :ref:`choosing_node` you have to pick which network +to connect to. This is easy for some scenarios: if you have ether and you want +to spend it, or you want to interact with any production smart contracts, +then you connect to the main Ethereum network. + +If you want to test these things without using real ether, though, then you +need to connect to a test network. There are several test networks to +choose from. One test network, Ropsten, is the most similar to the production network. +However, spam and mining attacks have happened, +which is disruptive when you want to test out a contract. + +There are some alternative networks that limit the damage of spam attacks, but +they are not standardized across node software. Geth runs their own (Rinkeby), +and Parity runs their own (Kovan). See a full comparison in this `Stackexchange Q&A +`_. + +So roughly, choose this way: + +- If using Parity, connect to Kovan +- If using Geth, connect to Rinkeby +- If using a different node, or testing mining, connect to Ropsten + +Each of their networks has their own version of Ether. Main network ether must +be purchased, naturally, but test network ether is usually available for free. +See :ref:`faucets` + +Once you have decided which network to connect to, and set up your node for that network, +you need to decide how to connect to it. There are a handful of options in most nodes. +See :ref:`choosing_provider`. diff --git a/docs/overview.rst b/docs/overview.rst index 67b80d14c8..7176323552 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -1,380 +1,348 @@ -Overview -======== - -.. contents:: :local: - -The common entrypoint for interacting with the Web3 library is the ``Web3`` -object. The web3 object provides APIs for interacting with the ethereum -blockchain, typically by connecting to a JSON-RPC server. - - -Providers ---------- - -*Providers* are how web3 connects to the blockchain. The Web3 library comes -with the following built-in providers that should be suitable for most normal -use cases. - -- ``web3.HTTPProvider`` for connecting to http and https based JSON-RPC servers. -- ``web3.IPCProvider`` for connecting to ipc socket based JSON-RPC servers. -- ``web3.WebsocketProvider`` for connecting to ws and wss websocket based JSON-RPC servers. - -The ``HTTPProvider`` takes the full URI where the server can be found. For -local development this would be something like ``http://localhost:8545``. - -The ``IPCProvider`` takes the filesystem path where the IPC socket can be -found. If no argument is provided it will use the *default* path for your -operating system. - -The ``WebsocketProvider`` takes the full URI where the server can be found. For -local development this would be something like ``ws://127.0.0.1:8546``. - -.. code-block:: python - - >>> from web3 import Web3, HTTPProvider, IPCProvider, WebsocketProvider - - # Note that you should create only one RPCProvider per - # process, as it recycles underlying TCP/IP network connections between - # your process and Ethereum node - >>> w3 = Web3(HTTPProvider('http://localhost:8545')) - - # or for an IPC based connection - >>> w3 = Web3(IPCProvider()) - - # or for Websocket based connection - >>> w3 = Web3(WebsocketProvider('ws://127.0.0.1:8546')) - - -Base API --------- - -The ``Web3`` class exposes the following convenient APIs. - - -.. _overview_type_conversions: - -Type Conversions -~~~~~~~~~~~~~~~~ - -.. py:method:: Web3.toHex(primitive=None, hexstr=None, text=None) - - Takes a variety of inputs and returns it in its hexadecimal representation. - It follows the rules for converting to hex in the - `JSON-RPC spec`_ - - .. code-block:: python - - >>> Web3.toHex(0) - '0x0' - >>> Web3.toHex(1) - '0x1' - >>> Web3.toHex(0x0) - '0x0' - >>> Web3.toHex(0x000F) - '0xf' - >>> Web3.toHex(b'') - '0x' - >>> Web3.toHex(b'\x00\x0F') - '0x000f' - >>> Web3.toHex(False) - '0x0' - >>> Web3.toHex(True) - '0x1' - >>> Web3.toHex(hexstr='0x000F') - '0x000f' - >>> Web3.toHex(hexstr='000F') - '0x000f' - >>> Web3.toHex(text='') - '0x' - >>> Web3.toHex(text='cowmö') - '0x636f776dc3b6' - -.. _JSON-RPC spec: https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding - -.. py:method:: Web3.toText(primitive=None, hexstr=None, text=None) - - Takes a variety of inputs and returns its string equivalent. - Text gets decoded as UTF-8. - - - .. code-block:: python - - >>> Web3.toText(0x636f776dc3b6) - 'cowmö' - >>> Web3.toText(b'cowm\xc3\xb6') - 'cowmö' - >>> Web3.toText(hexstr='0x636f776dc3b6') - 'cowmö' - >>> Web3.toText(hexstr='636f776dc3b6') - 'cowmö' - >>> Web3.toText(text='cowmö') - 'cowmö' - - -.. py:method:: Web3.toBytes(primitive=None, hexstr=None, text=None) - - Takes a variety of inputs and returns its bytes equivalent. - Text gets encoded as UTF-8. - - - .. code-block:: python - - >>> Web3.toBytes(0) - b'\x00' - >>> Web3.toBytes(0x000F) - b'\x0f' - >>> Web3.toBytes(b'') - b'' - >>> Web3.toBytes(b'\x00\x0F') - b'\x00\x0f' - >>> Web3.toBytes(False) - b'\x00' - >>> Web3.toBytes(True) - b'\x01' - >>> Web3.toBytes(hexstr='0x000F') - b'\x00\x0f' - >>> Web3.toBytes(hexstr='000F') - b'\x00\x0f' - >>> Web3.toBytes(text='') - b'' - >>> Web3.toBytes(text='cowmö') - b'cowm\xc3\xb6' - - -.. py:method:: Web3.toInt(primitive=None, hexstr=None, text=None) - - Takes a variety of inputs and returns its integer equivalent. - - - .. code-block:: python - - >>> Web3.toInt(0) - 0 - >>> Web3.toInt(0x000F) - 15 - >>> Web3.toInt(b'\x00\x0F') - 15 - >>> Web3.toInt(False) - 0 - >>> Web3.toInt(True) - 1 - >>> Web3.toInt(hexstr='0x000F') - 15 - >>> Web3.toInt(hexstr='000F') - 15 - -.. py:method:: Web3.toJSON(obj) - - Takes a variety of inputs and returns its JSON equivalent. - - - .. code-block:: python - - >>> Web3.toJSON(3) - '3' - >>> Web3.toJSON({'one': 1}) - '{"one": 1}' - -.. _overview_currency_conversions: - -Currency Conversions -~~~~~~~~~~~~~~~~~~~~~ - -.. py:method:: Web3.toWei(value, currency) - - Returns the value in the denomination specified by the ``currency`` argument - converted to wei. - - - .. code-block:: python - - >>> Web3.toWei(1, 'ether') - 1000000000000000000 - - -.. py:method:: Web3.fromWei(value, currency) - - Returns the value in wei converted to the given currency. The value is returned - as a ``Decimal`` to ensure precision down to the wei. - - - .. code-block:: python - - >>> web3.fromWei(1000000000000000000, 'ether') - Decimal('1') - - -.. _overview_addresses: - -Addresses -~~~~~~~~~~~~~~~~ - -.. py:method:: Web3.isAddress(value) - - Returns ``True`` if the value is one of the recognized address formats. - - * Allows for both ``0x`` prefixed and non-prefixed values. - * If the address contains mixed upper and lower cased characters this function also - checks if the address checksum is valid according to `EIP55`_ - - .. code-block:: python - - >>> wch3.isAddress('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - True - - -.. py:method:: Web3.isChecksumAddress(value) - - Returns ``True`` if the value is a valid `EIP55`_ checksummed address - - - .. code-block:: python - - >>> wch3.isChecksumAddress('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - True - >>> wch3.isChecksumAddress('0xd3cda913deb6f67967b99d67acdfa1712c293601') - False - - -.. py:method:: Web3.toChecksumAddress(value) - - Returns the given address with an `EIP55`_ checksum. - - - .. code-block:: python - - >>> Web3.toChecksumAddress('0xd3cda913deb6f67967b99d67acdfa1712c293601') - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - -.. _EIP55: https://github.com/ethereum/EIPs/issues/55 - - -.. _overview_hashing: - -Cryptographic Hashing -~~~~~~~~~~~~~~~~~~~~~ - -.. py:classmethod:: Web3.keccak(primitive=None, hexstr=None, text=None) - - Returns the Keccak-256 of the given value. Text is encoded to UTF-8 before - computing the hash, just like Solidity. Any of the following are - valid and equivalent: - - .. code-block:: python - - >>> Web3.keccak(0x747874) - >>> Web3.keccak(b'\x74\x78\x74') - >>> Web3.keccak(hexstr='0x747874') - >>> Web3.keccak(hexstr='747874') - >>> Web3.keccak(text='txt') - HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e') - -.. py:classmethod:: Web3.solidityKeccak(abi_types, value) - - Returns the Keccak-256 as it would be computed by the solidity ``keccak`` - function on the provided ``value`` and ``abi_types``. The ``abi_types`` - value should be a list of solidity type strings which correspond to each - of the provided values. - - - .. code-block:: python - - >>> Web3.solidityKeccak(['bool'], [True]) - HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2") - - >>> Web3.solidityKeccak(['uint8', 'uint8', 'uint8'], [97, 98, 99]) - HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") - - >>> Web3.solidityKeccak(['uint8[]'], [[97, 98, 99]]) - HexBytes("0x233002c671295529bcc50b76a2ef2b0de2dac2d93945fca745255de1a9e4017e") - - >>> Web3.solidityKeccak(['address'], ["0x49EdDD3769c0712032808D86597B84ac5c2F5614"]) - HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882") - - >>> Web3.solidityKeccak(['address'], ["ethereumfoundation.eth"]) - HexBytes("0x913c99ea930c78868f1535d34cd705ab85929b2eaaf70fcd09677ecd6e5d75e9") - -.. py:classmethod:: Web3.sha3(primitive=None, hexstr=None, text=None) - - .. WARNING:: - This method has been deprecated for :meth:`~Web3.keccak` - - Returns the Keccak SHA256 of the given value. Text is encoded to UTF-8 before - computing the hash, just like Solidity. Any of the following are - valid and equivalent: - - .. code-block:: python - - >>> Web3.sha3(0x747874) - >>> Web3.sha3(b'\x74\x78\x74') - >>> Web3.sha3(hexstr='0x747874') - >>> Web3.sha3(hexstr='747874') - >>> Web3.sha3(text='txt') - HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e') - -.. py:classmethod:: Web3.soliditySha3(abi_types, value) - - .. WARNING:: - This method has been deprecated for :meth:`~Web3.solidityKeccak` - - - Returns the sha3 as it would be computed by the solidity ``sha3`` function - on the provided ``value`` and ``abi_types``. The ``abi_types`` value - should be a list of solidity type strings which correspond to each of the - provided values. - - - .. code-block:: python - - >>> Web3.soliditySha3(['bool'], [True]) - HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2") - - >>> Web3.soliditySha3(['uint8', 'uint8', 'uint8'], [97, 98, 99]) - HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") - - >>> Web3.soliditySha3(['uint8[]'], [[97, 98, 99]]) - HexBytes("0x233002c671295529bcc50b76a2ef2b0de2dac2d93945fca745255de1a9e4017e") - - >>> Web3.soliditySha3(['address'], ["0x49EdDD3769c0712032808D86597B84ac5c2F5614"]) - HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882") - - >>> Web3.soliditySha3(['address'], ["ethereumfoundation.eth"]) - HexBytes("0x913c99ea930c78868f1535d34cd705ab85929b2eaaf70fcd09677ecd6e5d75e9") - -Check Encodability -~~~~~~~~~~~~~~~~~~~~ - -.. py:method:: w3.is_encodable(_type, value) - - Returns ``True`` if a value can be encoded as the given type. Otherwise returns ``False``. - - .. code-block:: python - - >>> from web3.auto.gethdev import w3 - >>> w3.is_encodable('bytes2', b'12') - True - >>> w3.is_encodable('bytes2', b'1') - True - >>> w3.is_encodable('bytes2', '0x1234') - True - >>> w3.is_encodable('bytes2', b'123') - False - -.. py:method:: w3.enable_strict_bytes_type_checking() - - Enables stricter bytes type checking. For more examples see :ref:`enable-strict-byte-check` - - .. doctest:: - - >>> from web3.auto.gethdev import w3 - >>> w3.enable_strict_bytes_type_checking() - >>> w3.is_encodable('bytes2', b'12') - True - >>> w3.is_encodable('bytes2', b'1') - False - -Modules -------- - -The JSON-RPC functionality is split across multiple modules which *loosely* -correspond to the namespaces of the underlying JSON-RPC methods. +Overview +======== + +.. contents:: :local: + +The common entrypoint for interacting with the Web3 library is the ``Web3`` +object. The web3 object provides APIs for interacting with the ethereum +blockchain, typically by connecting to a JSON-RPC server. + + +Providers +--------- + +*Providers* are how web3 connects to the blockchain. The Web3 library comes +with a the following built-in providers that should be suitable for most normal +use cases. + +- ``web3.HTTPProvider`` for connecting to http and https based JSON-RPC servers. +- ``web3.IPCProvider`` for connecting to ipc socket based JSON-RPC servers. +- ``web3.WebsocketProvider`` for connecting to ws and wss websocket based JSON-RPC servers. + +The ``HTTPProvider`` takes the full URI where the server can be found. For +local development this would be something like ``http://localhost:8545``. + +The ``IPCProvider`` takes the filesystem path where the IPC socket can be +found. If no argument is provided it will use the *default* path for your +operating system. + +The ``WebsocketProvider`` takes the full URI where the server can be found. For +local development this would be something like ``ws://127.0.0.1:8546``. + +.. code-block:: python + + >>> from web3 import Web3, HTTPProvider, IPCProvider, WebsocketProvider + + # Note that you should create only one RPCProvider per + # process, as it recycles underlying TCP/IP network connections between + # your process and Ethereum node + >>> w3 = Web3(HTTPProvider('http://localhost:8545')) + + # or for an IPC based connection + >>> w3 = Web3(IPCProvider()) + + # or for Websocket based connection + >>> w3 = Web3(WebsocketProvider('ws://127.0.0.1:8546')) + + +Base API +-------- + +The ``Web3`` class exposes the following convenience APIs. + + +.. _overview_type_conversions: + +Type Conversions +~~~~~~~~~~~~~~~~ + +.. py:method:: Web3.toHex(primitive=None, hexstr=None, text=None) + + Takes a variety of inputs and returns it in its hexadecimal representation. + It follows the rules for converting to hex in the + `JSON-RPC spec`_ + + .. code-block:: python + + >>> Web3.toHex(0) + '0x0' + >>> Web3.toHex(1) + '0x1' + >>> Web3.toHex(0x0) + '0x0' + >>> Web3.toHex(0x000F) + '0xf' + >>> Web3.toHex(b'') + '0x' + >>> Web3.toHex(b'\x00\x0F') + '0x000f' + >>> Web3.toHex(False) + '0x0' + >>> Web3.toHex(True) + '0x1' + >>> Web3.toHex(hexstr='0x000F') + '0x000f' + >>> Web3.toHex(hexstr='000F') + '0x000f' + >>> Web3.toHex(text='') + '0x' + >>> Web3.toHex(text='cowmö') + '0x636f776dc3b6' + +.. _JSON-RPC spec: https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding + +.. py:method:: Web3.toText(primitive=None, hexstr=None, text=None) + + Takes a variety of inputs and returns its string equivalent. + Text gets decoded as UTF-8. + + + .. code-block:: python + + >>> Web3.toText(0x636f776dc3b6) + 'cowmö' + >>> Web3.toText(b'cowm\xc3\xb6') + 'cowmö' + >>> Web3.toText(hexstr='0x636f776dc3b6') + 'cowmö' + >>> Web3.toText(hexstr='636f776dc3b6') + 'cowmö' + >>> Web3.toText(text='cowmö') + 'cowmö' + + +.. py:method:: Web3.toBytes(primitive=None, hexstr=None, text=None) + + Takes a variety of inputs and returns its bytes equivalent. + Text gets encoded as UTF-8. + + + .. code-block:: python + + >>> Web3.toBytes(0) + b'\x00' + >>> Web3.toBytes(0x000F) + b'\x0f' + >>> Web3.toBytes(b'') + b'' + >>> Web3.toBytes(b'\x00\x0F') + b'\x00\x0f' + >>> Web3.toBytes(False) + b'\x00' + >>> Web3.toBytes(True) + b'\x01' + >>> Web3.toBytes(hexstr='0x000F') + b'\x00\x0f' + >>> Web3.toBytes(hexstr='000F') + b'\x00\x0f' + >>> Web3.toBytes(text='') + b'' + >>> Web3.toBytes(text='cowmö') + b'cowm\xc3\xb6' + + +.. py:method:: Web3.toInt(primitive=None, hexstr=None, text=None) + + Takes a variety of inputs and returns its integer equivalent. + + + .. code-block:: python + + >>> Web3.toInt(0) + 0 + >>> Web3.toInt(0x000F) + 15 + >>> Web3.toInt(b'\x00\x0F') + 15 + >>> Web3.toInt(False) + 0 + >>> Web3.toInt(True) + 1 + >>> Web3.toInt(hexstr='0x000F') + 15 + >>> Web3.toInt(hexstr='000F') + 15 + +.. py:method:: Web3.toJSON(obj) + + Takes a variety of inputs and returns its JSON equivalent. + + + .. code-block:: python + + >>> Web3.toJSON(3) + '3' + >>> Web3.toJSON({'one': 1}) + '{"one": 1}' + +.. _overview_currency_conversions: + +Currency Conversions +~~~~~~~~~~~~~~~~~~~~~ + +.. py:method:: Web3.toWei(value, currency) + + Returns the value in the denomination specified by the ``currency`` argument + converted to wei. + + + .. code-block:: python + + >>> Web3.toWei(1, 'ether') + 1000000000000000000 + + +.. py:method:: Web3.fromWei(value, currency) + + Returns the value in wei converted to the given currency. The value is returned + as a ``Decimal`` to ensure precision down to the wei. + + + .. code-block:: python + + >>> web3.fromWei(1000000000000000000, 'ether') + Decimal('1') + + +.. _overview_addresses: + +Addresses +~~~~~~~~~~~~~~~~ + +.. py:method:: Web3.isAddress(value) + + Returns ``True`` if the value is one of the recognized address formats. + + * Allows for both ``0x`` prefixed and non-prefixed values. + * If the address contains mixed upper and lower cased characters this function also + checks if the address checksum is valid according to `EIP55`_ + + .. code-block:: python + + >>> wch3.isAddress('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') + True + + +.. py:method:: Web3.isChecksumAddress(value) + + Returns ``True`` if the value is a valid `EIP55`_ checksummed address + + + .. code-block:: python + + >>> wch3.isChecksumAddress('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') + True + >>> wch3.isChecksumAddress('0xd3cda913deb6f67967b99d67acdfa1712c293601') + False + + +.. py:method:: Web3.toChecksumAddress(value) + + Returns the given address with an `EIP55`_ checksum. + + + .. code-block:: python + + >>> Web3.toChecksumAddress('0xd3cda913deb6f67967b99d67acdfa1712c293601') + '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' + +.. _EIP55: https://github.com/ethereum/EIPs/issues/55 + + +.. _overview_hashing: + +Cryptographic Hashing +~~~~~~~~~~~~~~~~~~~~~ + +.. py:classmethod:: Web3.keccak(primitive=None, hexstr=None, text=None) + + Returns the Keccak-256 of the given value. Text is encoded to UTF-8 before + computing the hash, just like Solidity. Any of the following are + valid and equivalent: + + .. code-block:: python + + >>> Web3.keccak(0x747874) + >>> Web3.keccak(b'\x74\x78\x74') + >>> Web3.keccak(hexstr='0x747874') + >>> Web3.keccak(hexstr='747874') + >>> Web3.keccak(text='txt') + HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e') + +.. py:classmethod:: Web3.solidityKeccak(abi_types, value) + + Returns the Keccak-256 as it would be computed by the solidity ``keccak`` + function on the provided ``value`` and ``abi_types``. The ``abi_types`` + value should be a list of solidity type strings which correspond to each + of the provided values. + + + .. code-block:: python + + >>> Web3.solidityKeccak(['bool'], [True]) + HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2") + + >>> Web3.solidityKeccak(['uint8', 'uint8', 'uint8'], [97, 98, 99]) + HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + + >>> Web3.solidityKeccak(['uint8[]'], [[97, 98, 99]]) + HexBytes("0x233002c671295529bcc50b76a2ef2b0de2dac2d93945fca745255de1a9e4017e") + + >>> Web3.solidityKeccak(['address'], ["0x49eddd3769c0712032808d86597b84ac5c2f5614"]) + HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882") + + >>> Web3.solidityKeccak(['address'], ["ethereumfoundation.vns"]) + HexBytes("0x913c99ea930c78868f1535d34cd705ab85929b2eaaf70fcd09677ecd6e5d75e9") + +.. py:classmethod:: Web3.sha3(primitive=None, hexstr=None, text=None) + + .. WARNING:: + This method has been deprecated for :meth:`~Web3.keccak` + + Returns the Keccak SHA256 of the given value. Text is encoded to UTF-8 before + computing the hash, just like Solidity. Any of the following are + valid and equivalent: + + .. code-block:: python + + >>> Web3.sha3(0x747874) + >>> Web3.sha3(b'\x74\x78\x74') + >>> Web3.sha3(hexstr='0x747874') + >>> Web3.sha3(hexstr='747874') + >>> Web3.sha3(text='txt') + HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e') + +.. py:classmethod:: Web3.soliditySha3(abi_types, value) + + .. WARNING:: + This method has been deprecated for :meth:`~Web3.solidityKeccak` + + + Returns the sha3 as it would be computed by the solidity ``sha3`` function + on the provided ``value`` and ``abi_types``. The ``abi_types`` value + should be a list of solidity type strings which correspond to each of the + provided values. + + + .. code-block:: python + + >>> Web3.soliditySha3(['bool'], [True]) + HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2") + + >>> Web3.soliditySha3(['uint8', 'uint8', 'uint8'], [97, 98, 99]) + HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + + >>> Web3.soliditySha3(['uint8[]'], [[97, 98, 99]]) + HexBytes("0x233002c671295529bcc50b76a2ef2b0de2dac2d93945fca745255de1a9e4017e") + + >>> Web3.soliditySha3(['address'], ["0x49eddd3769c0712032808d86597b84ac5c2f5614"]) + HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882") + + >>> Web3.soliditySha3(['address'], ["ethereumfoundation.vns"]) + HexBytes("0x913c99ea930c78868f1535d34cd705ab85929b2eaaf70fcd09677ecd6e5d75e9") + +Modules +------- + +The JSON-RPC functionality is split across multiple modules which *loosely* +correspond to the namespaces of the underlying JSON-RPC methods. diff --git a/docs/providers.rst b/docs/providers.rst index 38557129d4..e99985efee 100644 --- a/docs/providers.rst +++ b/docs/providers.rst @@ -1,331 +1,326 @@ -.. _providers: - -Providers -========= - -The provider is how web3 talks to the blockchain. Providers take JSON-RPC -requests and return the response. This is normally done by submitting the -request to an HTTP or IPC socket based server. - -If you are already happily connected to your Ethereum node, then you -can skip the rest of the Providers section. - -.. _choosing_provider: - -Choosing How to Connect to Your Node --------------------------------------- - -Most nodes have a variety of ways to connect to them. If you have not -decided what kind of node to use, head on over to :ref:`choosing_node` - -The most common ways to connect to your node are: - -1. IPC (uses local filesystem: fastest and most secure) -2. Websockets (works remotely, faster than HTTP) -3. HTTP (more nodes support it) - -If you're not sure how to decide, choose this way: - -- If you have the option of running Web3.py on the same machine as the node, choose IPC. -- If you must connect to a node on a different computer, use Websockets. -- If your node does not support Websockets, use HTTP. - -Most nodes have a way of "turning off" connection options. -We recommend turning off all connection options that you are not using. -This provides a safer setup: it reduces the -number of ways that malicious hackers can try to steal your ether. - -Once you have decided how to connect, you specify the details using a Provider. -Providers are Web3.py classes that are configured for the kind of connection you want. - -See: - -- :class:`~web3.providers.ipc.IPCProvider` -- :class:`~web3.providers.websocket.WebsocketProvider` -- :class:`~web3.providers.rpc.HTTPProvider` - -Once you have configured your provider, for example: - -.. code-block:: python - - from web3 import Web3 - my_provider = Web3.IPCProvider('/my/node/ipc/path') - -Then you are ready to initialize your Web3 instance, like so: - -.. code-block:: python - - w3 = Web3(my_provider) - -Finally, you are ready to :ref:`get started with Web3.py`. - -Automatic vs Manual Providers ------------------------------ - -The ``Web3`` object will look for the Ethereum node in a few -standard locations if no providers are specified. Auto-detection happens -when you initialize like so: - -.. code-block:: python - - from web3.auto import w3 - - # which is equivalent to: - - from web3 import Web3 - w3 = Web3() - -Sometimes, web3 cannot automatically detect where your node is. - -- If you are not sure which kind of connection method to use, see - :ref:`choosing_provider`. -- If you know the connection method, but not the other information - needed to connect (like the path to the IPC file), you will need to look up - that information in your node's configuration. -- If you're not sure which node you are using, see :ref:`choosing_node` - -For a deeper dive into how automated detection works, see: - -.. _automatic_provider_detection: - -How Automated Detection Works -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Web3 attempts to connect to nodes in the following order, using the first -succesful connection it can make: - -1. The connection specified by an environment variable, see :ref:`provider_uri` -2. :class:`~web3.providers.ipc.IPCProvider`, which looks for several IPC file locations. - `IPCProvider` will not automatically detect a testnet connection, it is suggested that the - user instead uses a `w3` instance from `web3.auto.infura` (e.g. - `from web3.auto.infura.ropsten import w3`) if they want to auto-detect a testnet. -3. :class:`~web3.providers.rpc.HTTPProvider`, which attempts to connect to "http://localhost:8545" -4. None - if no providers are successful, you can still use Web3 APIs - that do not require a connection, like: - - - :ref:`overview_type_conversions` - - :ref:`overview_currency_conversions` - - :ref:`overview_addresses` - - :ref:`eth-account` - - etc. - -.. _automatic_provider_detection_examples: - -Examples Using Automated Detection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some nodes provide APIs beyond the standards. Sometimes the same information is provided -in different ways across nodes. If you want to write code that works -across multiple nodes, you may want to look up the node type you are connected to. - -For example, the following retrieves the client enode endpoint for both geth and parity: - -.. code-block:: python - - from web3.auto import w3 - - connected = w3.isConnected() - - if connected and w3.clientVersion.startswith('Parity'): - enode = w3.parity.enode - - elif connected and w3.clientVersion.startswith('Geth'): - enode = w3.geth.admin.nodeInfo['enode'] - - else: - enode = None - -.. _provider_uri: - -Provider via Environment Variable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Alternatively, you can set the environment variable ``WEB3_PROVIDER_URI`` -before starting your script, and web3 will look for that provider first. - -Valid formats for this environment variable are: - -- ``file:///path/to/node/rpc-json/file.ipc`` -- ``http://192.168.1.2:8545`` -- ``https://node.ontheweb.com`` -- ``ws://127.0.0.1:8546`` - - -.. _custom_auto_providers: - -Auto-initialization Provider Shortcuts --------------------------------------- - -There are a couple auto-initialization shortcuts for common providers. - -Infura Mainnet -~~~~~~~~~~~~~~ - -To easily connect to the Infura Mainnet remote node, first register for a free -project ID if you don't have one at https://infura.io/register . - -Then set the environment variable ``WEB3_INFURA_PROJECT_ID`` with your Project ID:: - - $ export WEB3_INFURA_PROJECT_ID=YourProjectID - -If you have checked the box in the Infura UI indicating that requests need -an optional secret key, set the environment variable ``WEB3_INFURA_API_SECRET``:: - - $ export WEB3_INFURA_API_SECRET=YourProjectSecret - -.. code-block:: python - - >>> from web3.auto.infura import w3 - - # confirm that the connection succeeded - >>> w3.isConnected() - True - -Geth dev Proof of Authority -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To connect to a ``geth --dev`` Proof of Authority instance with defaults: - -.. code-block:: python - - >>> from web3.auto.gethdev import w3 - - # confirm that the connection succeeded - >>> w3.isConnected() - True - -Built In Providers ------------------- - -Web3 ships with the following providers which are appropriate for connecting to -local and remote JSON-RPC servers. - - -HTTPProvider -~~~~~~~~~~~~ - -.. py:class:: web3.providers.rpc.HTTPProvider(endpoint_uri[, request_kwargs]) - - This provider handles interactions with an HTTP or HTTPS based JSON-RPC server. - - * ``endpoint_uri`` should be the full URI to the RPC endpoint such as - ``'https://localhost:8545'``. For RPC servers behind HTTP connections - running on port 80 and HTTPS connections running on port 443 the port can - be omitted from the URI. - * ``request_kwargs`` this should be a dictionary of keyword arguments which - will be passed onto the http/https request. - - .. code-block:: python - - >>> from web3 import Web3 - >>> w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545")) - - Note that you should create only one HTTPProvider per python - process, as the HTTPProvider recycles underlying TCP/IP network connections, - for better performance. - - Under the hood, the ``HTTPProvider`` uses the python requests library for - making requests. If you would like to modify how requests are made, you can - use the ``request_kwargs`` to do so. A common use case for this is increasing - the timeout for each request. - - - .. code-block:: python - - >>> from web3 import Web3 - >>> w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545", request_kwargs={'timeout': 60})) - - -IPCProvider -~~~~~~~~~~~ - -.. py:class:: web3.providers.ipc.IPCProvider(ipc_path=None, testnet=False, timeout=10) - - This provider handles interaction with an IPC Socket based JSON-RPC - server. - - * ``ipc_path`` is the filesystem path to the IPC socket.:56 - - .. code-block:: python - - >>> from web3 import Web3 - >>> w3 = Web3(Web3.IPCProvider("~/Library/Ethereum/geth.ipc")) - - If no ``ipc_path`` is specified, it will use the first IPC file - it can find from this list: - - - On Linux and FreeBSD: - - - ``~/.ethereum/geth.ipc`` - - ``~/.local/share/io.parity.ethereum/jsonrpc.ipc`` - - On Mac OS: - - - ``~/Library/Ethereum/geth.ipc`` - - ``~/Library/Application Support/io.parity.ethereum/jsonrpc.ipc`` - - On Windows: - - - ``\\\.\pipe\geth.ipc`` - - ``\\\.\pipe\jsonrpc.ipc`` - - -WebsocketProvider -~~~~~~~~~~~~~~~~~ - -.. py:class:: web3.providers.websocket.WebsocketProvider(endpoint_uri[, websocket_kwargs]) - - This provider handles interactions with an WS or WSS based JSON-RPC server. - - * ``endpoint_uri`` should be the full URI to the RPC endpoint such as - ``'ws://localhost:8546'``. - * ``websocket_kwargs`` this should be a dictionary of keyword arguments which - will be passed onto the ws/wss websocket connection. - - .. code-block:: python - - >>> from web3 import Web3 - >>> w3 = Web3(Web3.WebsocketProvider("ws://127.0.0.1:8546")) - - Under the hood, the ``WebsocketProvider`` uses the python websockets library for - making requests. If you would like to modify how requests are made, you can - use the ``websocket_kwargs`` to do so. A common use case for this is increasing - the timeout for each request. - - - .. code-block:: python - - >>> from web3 import Web3 - >>> w3 = Web3(Web3.WebsocketProvider("http://127.0.0.1:8546", websocket_kwargs={'timeout': 60})) - -.. py:currentmodule:: web3.providers.eth_tester - -EthereumTesterProvider -~~~~~~~~~~~~~~~~~~~~~~ - -.. warning:: Experimental: This provider is experimental. There are still significant gaps in - functionality. However it is being actively developed and supported. - -.. py:class:: EthereumTesterProvider(eth_tester=None) - - This provider integrates with the ``eth-tester`` library. The ``eth_tester`` constructor - argument should be an instance of the :class:`~eth_tester.EthereumTester` or a subclass of - :class:`~eth_tester.backends.base.BaseChainBackend` class provided by the ``eth-tester`` library. - If you would like a custom eth-tester instance to test with, see the - ``eth-tester`` library `documentation `_ for details. - - .. code-block:: python - - >>> from web3 import Web3, EthereumTesterProvider - >>> w3 = Web3(EthereumTesterProvider()) - -.. NOTE:: To install the needed dependencies to use EthereumTesterProvider, you can install the - pip extras package that has the correct interoperable versions of the ``eth-tester`` - and ``py-evm`` dependencies needed to do testing: e.g. ``pip install web3[tester]`` - - - -AutoProvider -~~~~~~~~~~~~ - -:class:`~web3.providers.auto.AutoProvider` is the default used when initializing -:class:`web3.Web3` without any providers. There's rarely a reason to use it -explicitly. +.. _providers: + +Providers +========= + +The provider is how web3 talks to the blockchain. Providers take JSON-RPC +requests and return the response. This is normally done by submitting the +request to an HTTP or IPC socket based server. + +If you are already happily connected to your Ethereum node, then you +can skip the rest of the Providers section. + +.. _choosing_provider: + +Choosing How to Connect to Your Node +-------------------------------------- + +Most nodes have a variety of ways to connect to them. If you have not +decided what kind of node to use, head on over to :ref:`choosing_node` + +The most common ways to connect to your node are: + +1. IPC (uses local filesystem: fastest and most secure) +2. Websockets (works remotely, faster than HTTP) +3. HTTP (more nodes support it) + +If you're not sure how to decide, choose this way: + +- If you have the option of running Web3.py on the same machine as the node, choose IPC. +- If you must connect to a node on a different computer, use Websockets. +- If your node does not support Websockets, use HTTP. + +Most nodes have a way of "turning off" connection options. +We recommend turning off all connection options that you are not using. +This provides a safer setup: it reduces the +number of ways that malicious hackers can try to steal your ether. + +Once you have decided how to connect, you specify the details using a Provider. +Providers are Web3.py classes that are configured for the kind of connection you want. + +See: + +- :class:`~web3.providers.ipc.IPCProvider` +- :class:`~web3.providers.websocket.WebsocketProvider` +- :class:`~web3.providers.rpc.HTTPProvider` + +Once you have configured your provider, for example: + +.. code-block:: python + + from web3 import Web3 + my_provider = Web3.IPCProvider('/my/node/ipc/path') + +Then you are ready to initialize your Web3 instance, like so: + +.. code-block:: python + + w3 = Web3(my_provider) + +Finally, you are ready to :ref:`get started with Web3.py`. + +Automatic vs Manual Providers +----------------------------- + +The ``Web3`` object will look for the Ethereum node in a few +standard locations if no providers are specified. Auto-detection happens +when you initialize like so: + +.. code-block:: python + + from web3.auto import w3 + + # which is equivalent to: + + from web3 import Web3 + w3 = Web3() + +Sometimes, web3 cannot automatically detect where your node is. + +- If you are not sure which kind of connection method to use, see + :ref:`choosing_provider`. +- If you know the connection method, but not the other information + needed to connect (like the path to the IPC file), you will need to look up + that information in your node's configuration. +- If you're not sure which node you are using, see :ref:`choosing_node` + +For a deeper dive into how automated detection works, see: + +.. _automatic_provider_detection: + +How Automated Detection Works +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Web3 attempts to connect to nodes in the following order, using the first +succesful connection it can make: + +1. The connection specified by an environment variable, see :ref:`provider_uri` +2. :class:`~web3.providers.ipc.IPCProvider`, which looks for several IPC file locations. + `IPCProvider` will not automatically detect a testnet connection, it is suggested that the + user instead uses a `w3` instance from `web3.auto.infura` (e.g. + `from web3.auto.infura.ropsten import w3`) if they want to auto-detect a testnet. +3. :class:`~web3.providers.rpc.HTTPProvider`, which attempts to connect to "http://localhost:8545" +4. None - if no providers are successful, you can still use Web3 APIs + that do not require a connection, like: + + - :ref:`overview_type_conversions` + - :ref:`overview_currency_conversions` + - :ref:`overview_addresses` + - :ref:`vns-account` + - etc. + +.. _automatic_provider_detection_examples: + +Examples Using Automated Detection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some nodes provide APIs beyond the standards. Sometimes the same information is provided +in different ways across nodes. If you want to write code that works +across multiple nodes, you may want to look up the node type you are connected to. + +For example, the following retrieves the client enode endpoint for both geth and parity: + +.. code-block:: python + + from web3.auto import w3 + + connected = w3.isConnected() + + if connected and w3.version.node.startswith('Parity'): + enode = w3.parity.enode + + elif connected and w3.version.node.startswith('Geth'): + enode = w3.geth.admin.nodeInfo['enode'] + + else: + enode = None + +.. _provider_uri: + +Provider via Environment Variable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Alternatively, you can set the environment variable ``WEB3_PROVIDER_URI`` +before starting your script, and web3 will look for that provider first. + +Valid formats for the this environment variable are: + +- ``file:///path/to/node/rpc-json/file.ipc`` +- ``http://192.168.1.2:8545`` +- ``https://node.ontheweb.com`` +- ``ws://127.0.0.1:8546`` + + +.. _custom_auto_providers: + +Auto-initialization Provider Shortcuts +-------------------------------------- + +There are a couple auto-initialization shortcuts for common providers. + +Infura Mainnet +~~~~~~~~~~~~~~ + +To easily connect to the Infura Mainnet remote node, first register for a free +project ID if you don't have one at https://infura.io/register . + +Then set the environment variable ``WEB3_INFURA_PROJECT_ID`` with your Project ID:: + + $ export WEB3_INFURA_PROJECT_ID=YourProjectID + +If you have checked the box in the Infura UI indicating that requests need +an optional secret key, set the environment variable ``WEB3_INFURA_API_SECRET``:: + + $ export WEB3_INFURA_API_SECRET=YourProjectSecret + +.. code-block:: python + + >>> from web3.auto.infura import w3 + + # confirm that the connection succeeded + >>> w3.isConnected() + True + +Geth dev Proof of Authority +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To connect to a ``geth --dev`` Proof of Authority instance with defaults: + +.. code-block:: python + + >>> from web3.auto.gethdev import w3 + + # confirm that the connection succeeded + >>> w3.isConnected() + True + +Built In Providers +------------------ + +Web3 ships with the following providers which are appropriate for connecting to +local and remote JSON-RPC servers. + + +HTTPProvider +~~~~~~~~~~~~ + +.. py:class:: web3.providers.rpc.HTTPProvider(endpoint_uri[, request_kwargs]) + + This provider handles interactions with an HTTP or HTTPS based JSON-RPC server. + + * ``endpoint_uri`` should be the full URI to the RPC endpoint such as + ``'https://localhost:8545'``. For RPC servers behind HTTP connections + running on port 80 and HTTPS connections running on port 443 the port can + be omitted from the URI. + * ``request_kwargs`` this should be a dictionary of keyword arguments which + will be passed onto the http/https request. + + .. code-block:: python + + >>> from web3 import Web3 + >>> w3 = Web3 (Web3.HTTPProvider("http://127.0.0.1:8545")) + + Note that you should create only one HTTPProvider per python + process, as the HTTPProvider recycles underlying TCP/IP network connections, + for better performance. + + Under the hood, the ``HTTPProvider`` uses the python requests library for + making requests. If you would like to modify how requests are made, you can + use the ``request_kwargs`` to do so. A common use case for this is increasing + the timeout for each request. + + + .. code-block:: python + + >>> from web3 import Web3 + >>> w3 = Web3 (Web3.HTTPProvider("http://127.0.0.1:8545", request_kwargs={'timeout': 60})) + + +IPCProvider +~~~~~~~~~~~ + +.. py:class:: web3.providers.ipc.IPCProvider(ipc_path=None, testnet=False, timeout=10) + + This provider handles interaction with an IPC Socket based JSON-RPC + server. + + * ``ipc_path`` is the filesystem path to the IPC socket.:56 + + .. code-block:: python + + >>> from web3 import Web3 + >>> w3 = Web3 (Web3.IPCProvider("~/Library/Ethereum/geth.ipc")) + + If no ``ipc_path`` is specified, it will use the first IPC file + it can find from this list: + + - On Linux and FreeBSD: + + - ``~/.vnsereum/geth.ipc`` + - ``~/.local/share/io.parity.ethreum/jsonrpc.ipc`` + - On Mac OS: + + - ``~/Library/Ethereum/geth.ipc`` + - ``~/Library/Application Support/io.parity.ethreum/jsonrpc.ipc`` + - On Windows: + + - ``\\\.\pipe\geth.ipc`` + - ``\\\.\pipe\jsonrpc.ipc`` + + +WebsocketProvider +~~~~~~~~~~~~~~~~~ + +.. py:class:: web3.providers.websocket.WebsocketProvider(endpoint_uri[, websocket_kwargs]) + + This provider handles interactions with an WS or WSS based JSON-RPC server. + + * ``endpoint_uri`` should be the full URI to the RPC endpoint such as + ``'ws://localhost:8546'``. + * ``websocket_kwargs`` this should be a dictionary of keyword arguments which + will be passed onto the ws/wss websocket connection. + + .. code-block:: python + + >>> from web3 import Web3 + >>> w3 = Web3 (Web3.WebsocketProvider("ws://127.0.0.1:8546")) + + Under the hood, the ``WebsocketProvider`` uses the python websockets library for + making requests. If you would like to modify how requests are made, you can + use the ``websocket_kwargs`` to do so. A common use case for this is increasing + the timeout for each request. + + + .. code-block:: python + + >>> from web3 import Web3 + >>> w3 = Web3 (Web3.WebsocketProvider("http://127.0.0.1:8546", websocket_kwargs={'timeout': 60})) + +.. py:currentmodule:: web3.providers.vns_tester + +EthereumTesterProvider +~~~~~~~~~~~~~~~~~~~~~~ + +.. warning:: Experimental: This provider is experimental. There are still significant gaps in + functionality. However it is being actively developed and supported. + +.. py:class:: EthereumTesterProvider(vns_tester=None) + + This provider integrates with the ``vns-tester`` library. The ``vns_tester`` constructor + argument should be an instance of the :class:`~vns_tester.EthereumTester` or a subclass of + :class:`~vns_tester.backends.base.BaseChainBackend` class provided by the ``vns-tester`` library. + If you would like a custom vns-tester instance to test with, see the + ``vns-tester`` library `documentation `_ for details. + + .. code-block:: python + + >>> from web3 import Web3, EthereumTesterProvider + >>> w3 = Web3(EthereumTesterProvider()) + + +AutoProvider +~~~~~~~~~~~~ + +:class:`~web3.providers.auto.AutoProvider` is the default used when initializing +:class:`web3.Web3` without any providers. There's rarely a reason to use it +explicitly. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 56cc9b3b32..a97de8a7be 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -1,112 +1,95 @@ -Quickstart -========== - -.. contents:: :local: - -.. NOTE:: All code starting with a ``$`` is meant to run on your terminal. - All code starting with a ``>>>`` is meant to run in a python interpreter, - like `ipython `_. - -Installation ------------- - -Web3.py can be installed (preferably in a :ref:`virtualenv `) -using ``pip`` as follows: - -.. code-block:: shell - - $ pip install web3 - - -.. NOTE:: If you run into problems during installation, you might have a - broken environment. See the troubleshooting guide to :ref:`setup_environment`. - - -Installation from source can be done from the root of the project with the -following command. - -.. code-block:: shell - - $ pip install . - - -Using Web3 ----------- - -To use the web3 library you will need to initialize the -:class:`~web3.Web3` class and connect to an Ethereum node. -The quickest way to do so for free is by setting up an account on -`Infura `_. On Infura, create a project and copy -the Project ID. Then set the environment variable ``WEB3_INFURA_PROJECT_ID``. - -.. code-block:: shell - - $ export WEB3_INFURA_PROJECT_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -Use the ``web3.auto.infura`` module to connect to the Infura node. - -.. code-block:: python - - >>> from web3.auto.infura import w3 - >>> w3.eth.blockNumber - 4000000 - -This ``w3`` instance will now allow you to interact with the Ethereum -blockchain. - -.. NOTE:: - If you don't want to use Infura, the ``web3.auto`` module is - available and will :ref:`guess at common node connection - options `. - - .. code-block:: python - - >>> from web3.auto import w3 - >>> w3.eth.blockNumber - 4000000 - -.. NOTE:: If you get the result ``UnhandledRequest: No providers responded to the RPC request`` - then you are not connected to a node. See :ref:`why_need_connection` and - :ref:`choosing_provider` - -.. _first_w3_use: - -Getting Blockchain Info ----------------------------------------- - -It's time to start using Web3.py! Try getting all the information about the latest block. - -.. code-block:: python - - >>> w3.eth.getBlock('latest') - {'difficulty': 1, - 'gasLimit': 6283185, - 'gasUsed': 0, - 'hash': HexBytes('0x53b983fe73e16f6ed8178f6c0e0b91f23dc9dad4cb30d0831f178291ffeb8750'), - 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), - 'miner': '0x0000000000000000000000000000000000000000', - 'mixHash': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), - 'nonce': HexBytes('0x0000000000000000'), - 'number': 0, - 'parentHash': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), - 'proofOfAuthorityData': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000dddc391ab2bf6701c74d0c8698c2e13355b2e4150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), - 'receiptsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'), - 'sha3Uncles': HexBytes('0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'), - 'size': 622, - 'stateRoot': HexBytes('0x1f5e460eb84dc0606ab74189dbcfe617300549f8f4778c3c9081c119b5b5d1c1'), - 'timestamp': 0, - 'totalDifficulty': 1, - 'transactions': [], - 'transactionsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'), - 'uncles': []} - -Many of the typical things you'll want to do will be in the :class:`w3.eth ` API, -so that is a good place to start. - -If you want to dive straight into contracts, check out the section on :ref:`contracts`, -including a :ref:`contract_example`, and how to create a contract instance using -:meth:`w3.eth.contract() `. - -.. NOTE:: It is recommended that your development environment have the ``PYTHONWARNINGS=default`` - environment variable set. Some deprecation warnings will not show up - without this variable being set. +Quickstart +========== + +.. contents:: :local: + +.. NOTE:: All code starting with a ``$`` is meant to run on your terminal. + All code starting with a ``>>>`` is meant to run in a python interpreter, + like `ipython `_. + +Installation +------------ + +Web3.py can be installed (preferably in a :ref:`virtualenv `) +using ``pip`` as follows: + +.. code-block:: shell + + $ pip install web3 + + +.. NOTE:: If you run into problems during installation, you might have a + broken environment. See the troubleshooting guide to :ref:`setup_environment`. + + +Installation from source can be done from the root of the project with the +following command. + +.. code-block:: shell + + $ pip install . + + +Using Web3 +---------- + +To use the web3 library you will need to initialize the +:class:`~web3.Web3` class. + +Use the ``auto`` module to :ref:`guess at common node connection options +`. + +.. code-block:: python + + >>> from web3.auto import w3 + >>> w3.vns.blockNumber + 4000000 + +This ``w3`` instance will now allow you to interact with the Ethereum +blockchain. + +.. NOTE:: If you get the result ``UnhandledRequest: No providers responded to the RPC request`` + then you are not connected to a node. See :ref:`why_need_connection` and + :ref:`choosing_provider` + +.. _first_w3_use: + +Getting Blockchain Info +---------------------------------------- + +It's time to start using Web3.py! Try getting all the information about the latest block. + +.. code-block:: python + + >>> w3.vns.getBlock('latest') + {'difficulty': 1, + 'gasLimit': 6283185, + 'gasUsed': 0, + 'hash': HexBytes('0x53b983fe73e16f6ed8178f6c0e0b91f23dc9dad4cb30d0831f178291ffeb8750'), + 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), + 'miner': '0x0000000000000000000000000000000000000000', + 'mixHash': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), + 'nonce': HexBytes('0x0000000000000000'), + 'number': 0, + 'parentHash': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), + 'proofOfAuthorityData': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000dddc391ab2bf6701c74d0c8698c2e13355b2e4150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), + 'receiptsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'), + 'sha3Uncles': HexBytes('0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'), + 'size': 622, + 'stateRoot': HexBytes('0x1f5e460eb84dc0606ab74189dbcfe617300549f8f4778c3c9081c119b5b5d1c1'), + 'timestamp': 0, + 'totalDifficulty': 1, + 'transactions': [], + 'transactionsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'), + 'uncles': []} + +Many of the typical things you'll want to do will be in the :class:`w3.vns ` API, +so that is a good place to start. + +If you want to dive straight into contracts, check out the section on :ref:`contracts`, +including a :ref:`contract_example`, and how to create a contract instance using +:meth:`w3.vns.contract() `. + +.. NOTE:: It is recommended that your development environment have the ``PYTHONWARNINGS=default`` + environment variable set. Some deprecation warnings will not show up + without this variable being set. diff --git a/docs/releases.rst b/docs/releases.rst index 7cd9f5486e..0ab3a22a66 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -1,1590 +1,1275 @@ -Release Notes -============= - -v5 Breaking Changes Summary - See the :ref:`v5 Migration Guide` - -.. towncrier release notes start - -v5.3.0 (2019-11-14) -------------------- - -Features -~~~~~~~~ - -- Support handling ENS domains in ERC1319 URIs. (`#1489 `__) - - -Bugfixes -~~~~~~~~ - -- Make local block filter return empty list when when no blocks mined (`#1255 `__) -- Google protobuf dependency was updated to `3.10.0` (`#1493 `__) -- Infura websocket provider works when no secret key is present (`#1501 `__) - - -Improved Documentation -~~~~~~~~~~~~~~~~~~~~~~ - -- Update Quickstart instructions to use the auto Infura module instead of the more complicated web3 auto module (`#1482 `__) -- Remove outdated py.test command from readme (`#1483 `__) - - -Misc -~~~~ - -- `#1461 `__, `#1471 `__, `#1475 `__, `#1476 `__, `#1479 `__, `#1488 `__, `#1492 `__, `#1498 `__ - - -v5.2.2 (2019-10-21) -------------------- - -Features -~~~~~~~~ - -- Add poll_latency to waitForTransactionReceipt (`#1453 `__) - - -Bugfixes -~~~~~~~~ - -- Fix flaky Parity whisper module test (`#1473 `__) - - -Misc -~~~~ - -- `#1472 `__, `#1474 `__ - - -v5.2.1 (2019-10-17) -------------------- - -Improved Documentation -~~~~~~~~~~~~~~~~~~~~~~ - -- Update documentation for unlock account duration (`#1464 `__) -- Clarify module installation command for OSX>=10.15 (`#1467 `__) - - -Misc -~~~~ - -- `#1468 `__ - - -v5.2.0 (2019-09-26) -------------------- - -Features -~~~~~~~~ - -- Add ``enable_strict_bytes_type_checking`` flag to web3 instance (`#1419 `__) -- Move Geth Whisper methods to snake case and deprecate camel case methods (`#1433 `__) - - -Bugfixes -~~~~~~~~ - -- Add null check to logsbloom formatter (`#1445 `__) - - -Improved Documentation -~~~~~~~~~~~~~~~~~~~~~~ - -- Reformat autogenerated towncrier release notes (`#1460 `__) - - -Web3 5.1.0 (2019-09-18) ------------------------ - -Features -~~~~~~~~ - -- Add ``contract_types`` property to ``Package`` class. (`#1440 `__) - - -Bugfixes -~~~~~~~~ - -- Fix flaky parity integration test in the whisper module (`#1147 `__) - - -Improved Documentation -~~~~~~~~~~~~~~~~~~~~~~ - -- Remove whitespace, move ``topics`` key -> ``topic`` in Geth docs (`#1425 `__) -- Enforce stricter doc checking, turning warnings into errors to fail CI builds - to catch issues quickly. - - Add missing ``web3.tools.rst`` to the table of contents and fix incorrectly formatted - JSON example. (`#1437 `__) -- Add example using Geth POA Middleware with Infura Rinkeby Node (`#1444 `__) - - -Misc -~~~~ - -- `#1446 `__, `#1451 `__ - - -v5.0.2 ------- -Released August 22, 2019 - -- Bugfixes - - - [ethPM] Fix bug in package id and release id fetching strategy - - `#1427 `_ - -v5.0.1 ------- -Released August 15, 2019 - -- Bugfixes - - - [ethPM] Add begin/close chars to package name regex - - `#1418 `_ - - [ethPM] Update deployments to work when only abi available - - `#1417 `_ - - Fix tuples handled incorrectly in ``decode_function_input`` - - `#1410 `_ - -- Misc - - - Eliminate ``signTransaction`` warning - - `#1404 `_ - -v5.0.0 ------- -Released August 1, 2019 - -- Features - - - ``web3.eth.chainId`` now returns an integer instead of hex - - `#1394 `_ - -- Bugfixes - - - Deprecation Warnings now show for methods that have a - ``@combomethod`` decorator - - `#1401 `_ - -- Misc - - - [ethPM] Add ethPM to the docker file - - `#1405 `_ - -- Docs - - - Docs are updated to use checksummed addresses - - `#1390 `_ - - Minor doc formatting fixes - - `#1338 `_ & - `#1345 `_ - - - -v5.0.0-beta.5 -------------- -Released July 31, 2019 - -*This is intended to be the final release before the stable v5 release.* - -- Features - - - Parity operating mode can be read and set - - `#1355 `_ - - Process a single event log, instead of a whole transaction - receipt - - `#1354 `_ - -- Docs - - - Remove doctest dependency on ethtoken - - `#1395 `_ - -- Bugfixes - - - [ethPM] Bypass IPFS validation for large files - - `#1393 `_ - -- Misc - - - [ethPM] Update default Registry solidity contract - - `#1400 `_ - - [ethPM] Update web3.pm to use new simple Registry implementation - - `#1398 `_ - - Update dependency requirement formatting for releasing - - `#1403 `_ - - -v5.0.0-beta.4 -------------- -Released July 18,2019 - - - -- Features - - - [ethPM] Update registry uri to support basic uris w/o package id - - `#1389 `_ - -- Docs - - - Clarify in docs the return of ``Eth.sendRawTransaction()`` as - a HexBytes object, not a string. - - `#1384 `_ - -- Misc - - - [ethPM] Migrate tests over from pytest-ethereum - - `#1385 `_ - -v5.0.0-beta.3 -------------- -Released July 15, 2019 - -- Features - - - Add eth_getProof support - - `#1185 `_ - - Implement web3.pm.get_local_package() - - `#1372 `_ - - Update registry URIs to support chain IDs - - `#1382 `_ - - Add error flags to ``event.processReceipt`` - - `#1366 `_ - -- Bugfixes - - - Remove full IDNA processing in favor of UTS46 - - `#1364 `_ - -- Misc - - - Migrate py-ethpm library to web3/ethpm - - `#1379 `_ - - Relax canonical address requirement in ethPM - - `#1380 `_ - - Replace ethPM's infura strategy with web3's native infura support - - `#1383 `_ - - Change ``combine_argument_formatters`` to ``apply_formatters_to_sequence`` - - `#1360 `_ - - Move ``pytest.xfail`` instances to ``@pytest.mark.xfail`` - - `#1376 `_ - - Change ``net.version`` to ``eth.chainId`` in default - transaction params - - `#1378 `_ - - -v5.0.0-beta.2 -------------- -Released May 13, 2019 - -- Features - - - Mark deprecated sha3 method as static - - `#1350 `_ - - Upgrade to eth-account v0.4.0 - - `#1348 `_ - -- Docs - - - Add note about web3[tester] in documentation - - `#1325 `_ - -- Misc - - - Replace ``web3._utils.toolz`` imports with ``eth_utils.toolz`` - - `#1317 `_ - - -v5.0.0-beta.1 -------------- -Released May 6, 2019 - -- Features - - - Add support for tilda in provider IPC Path - - `#1049 `_ - - EIP 712 Signing Supported - - `#1319 `_ - -- Docs - - - Update contract example to use ``compile_standard`` - - `#1263 `_ - - Fix typo in middleware docs - - `#1339 `_ - - -v5.0.0-alpha.11 ---------------- -Released April 24, 2019 - -- Docs - - - Add documentation for web3.py unit tests - - `#1324 `_ - -- Misc - - - Update deprecated collections.abc imports - - `#1334 `_ - - Fix documentation typo - - `#1335 `_ - - Upgrade eth-tester version - - `#1332 `_ - - -v5.0.0-alpha.10 ---------------- -Released April 15, 2019 - -- Features - - - Add getLogs by blockHash - - `#1269 `_ - - Implement chainId endpoint - - `#1295 `_ - - Moved non-standard JSON-RPC endpoints to applicable - Parity/Geth docs. Deprecated ``web3.version`` for ``web3.api`` - - `#1290 `_ - - Moved Whisper endpoints to applicable Geth or Parity namespace - - `#1308 `_ - - Added support for Goerli provider - - `#1286 `_ - - Added addReservedPeer to Parity module - - `#1311 `_ - -- Bugfixes - - - Cast gas price values to integers in gas strategies - - `#1297 `_ - - Missing constructor function no longer ignores constructor args - - `#1316 `_ - -- Misc - - - Require eth-utils >= 1.4, downgrade Go version for integration tests - - `#1310 `_ - - Fix doc build warnings - - `#1331 `_ - - Zip Fixture data - - `#1307 `_ - - Update Geth version for integration tests - - `#1301 `_ - - Remove unneeded testrpc - - `#1322 `_ - - Add ContractCaller docs to v5 migration guide - - `#1323 `_ - - - -v5.0.0-alpha.9 --------------- -Released March 26, 2019 - -- Breaking Changes - - - Raise error if there is no Infura API Key - - `#1294 `_ & - - `#1299 `_ - -- Misc - - - Upgraded Parity version for integration testing - - `#1292 `_ - -v5.0.0-alpha.8 --------------- -Released March 20, 2019 - -- Breaking Changes - - - Removed ``web3/utils`` directory in favor of ``web3/_utils`` - - `#1282 `_ - - Relocated personal RPC endpoints to Parity and Geth class - - `#1211 `_ - - Deprecated ``web3.net.chainId()``, ``web3.eth.getCompilers()``, - and ``web3.eth.getTransactionFromBlock()``. Removed ``web3.eth.enableUnauditedFeatures()`` - - `#1270 `_ - - Relocated eth_protocolVersion and web3_clientVersion - - `#1274 `_ - - Relocated ``web3.txpool`` to ``web3.geth.txpool`` - - `#1275 `_ - - Relocated admin module to Geth namespace - - `#1288 `_ - - Relocated miner module to Geth namespace - - `#1287 `_ - -- Features - - - Implement ``eth_submitHashrate`` and ``eth_submitWork`` JSONRPC endpoints. - - `#1280 `_ - - Implement ``web3.eth.signTransaction`` - - `#1277 `_ - -- Docs - - - Added v5 migration docs - - `#1284 `_ - -v5.0.0-alpha.7 --------------- -Released March 11, 2019 - -- Breaking Changes - - - Updated JSON-RPC calls that lookup txs or blocks to raise - an error if lookup fails - - `#1218 `_ and - `#1268 `_ - -- Features - - - Tuple ABI support - - `#1235 `_ - -- Bugfixes - - - One last ``middleware_stack`` was still hanging on. - Changed to ``middleware_onion`` - - `#1262 `_ - -v5.0.0-alpha.6 --------------- -Released February 25th, 2019 - -- Features - - - New ``NoABIFound`` error for cases where there is no ABI - - `#1247 `_ - -- Misc - - - Interact with Infura using an API Key. Key will be required after March 27th. - - `#1232 `_ - - Remove ``process_type`` utility function in favor of - eth-abi functionality - - `#1249 `_ - - -v5.0.0-alpha.5 --------------- - -Released February 13th, 2019 - -- Breaking Changes - - - Remove deprecated ``buildTransaction``, ``call``, ``deploy``, - ``estimateGas``, and ``transact`` methods - - `#1232 `_ - -- Features - - - Adds ``Web3.toJSON`` method - - `#1173 `_ - - Contract Caller API Implemented - - `#1227 `_ - - Add Geth POA middleware to use Rinkeby with Infura Auto - - `#1234 `_ - - Add manifest and input argument validation to ``pm.release_package()`` - - `#1237 `_ - -- Misc - - - Clean up intro and block/tx sections in Filter docs - - `#1223 `_ - - Remove unnecessary ``EncodingError`` exception catching - - `#1224 `_ - - Improvements to ``merge_args_and_kwargs`` utility function - - `#1228 `_ - - Update vyper registry assets - - `#1242 `_ - - -v5.0.0-alpha.4 --------------- - -Released January 23rd, 2019 - -- Breaking Changes - - - Rename ``middleware_stack`` to ``middleware_onion`` - - `#1210 `_ - - Drop already deprecated ``web3.soliditySha3`` - - `#1217 `_ - - ENS: Stop inferring ``.eth`` TLD on domain names - - `#1205 `_ - -- Bugfixes - - - Validate ``ethereum_tester`` class in ``EthereumTesterProvider`` - - `#1217 `_ - - Support ``getLogs()`` method without creating filters - - `#1192 `_ - -- Features - - - Stablize the ``PM`` module - - `#1125 `_ - - Implement async ``Version`` module - - `#1166 `_ - -- Misc - - - Update .gitignore to ignore ``.DS_Store`` and ``.mypy_cache/`` - - `#1215 `_ - - Change CircleCI badge link to CircleCI project - - `#1214 `_ - - -v5.0.0-alpha.3 --------------- - -Released January 15th, 2019 - -- Breaking Changes - - - Remove ``web3.miner.hashrate`` and ``web3.version.network`` - - `#1198 `_ - - Remove ``web3.providers.tester.EthereumTesterProvider`` - and ``web3.providers.tester.TestRPCProvider`` - - `#1199 `_ - - Change ``manager.providers`` from list to single ``manager.provider`` - - `#1200 `_ - - Replace deprecated ``web3.sha3`` method with ``web3.keccak`` method - - `#1207 `_ - - Drop auto detect testnets for IPCProvider - - `#1206 `_ - -- Bugfixes - - - Add check to make sure blockHash exists - - `#1158 `_ - -- Misc - - - Remove some unreachable code in `providers/base.py` - - `#1160 `_ - - Migrate tester provider results from middleware to defaults - - `#1188 `_ - - Fix doc formatting for build_filter method - - `#1187 `_ - - Add ERC20 example in docs - - `#1178 `_ - - Code style improvements - - `#1194 `_ - & `#1191 `_ - - Convert Web3 instance variables to w3 - - `#1186 `_ - - Update eth-utils dependencies and clean up other dependencies - - `#1195 `_ - - -v5.0.0-alpha.2 --------------- - -Released December 20th, 2018 - -- Breaking Changes - - - Remove support for python3.5, drop support for eth-abi v1 - - `#1163 `_ -- Features - - - Support for custom ReleaseManager was fixed - - `#1165 `_ - -- Misc - - - Fix doctest nonsense with unicorn token - - `3b2047 `_ - - Docs for installing web3 in FreeBSD - - `#1156 `_ - - Use latest python in readthedocs - - `#1162 `_ - - Use twine in release script - - `#1164 `_ - - Upgrade eth-tester, for eth-abi v2 support - - `#1168 `_ - -v5.0.0-alpha.1 --------------- - -Released December 13th, 2018 - -- Features - - - Add Rinkeby and Kovan Infura networks; made mainnet the default - - `#1150 `_ - - Add parity-specific ``listStorageKeys`` RPC - - `#1145 `_ - - Deprecated ``Web3.soliditySha3``; use ``Web3.solidityKeccak`` instead. - - `#1139 `_ - - Add default trinity locations to IPC path guesser - - `#1121 `_ - - Add wss to ``AutoProvider`` - - `#1110 `_ - - Add timeout for ``WebsocketProvider`` - - `#1109 `_ - - Receipt timeout raises ``TimeExhausted`` - - `#1070 `_ - - Allow specification of block number for ``eth_estimateGas`` - - `#1046 `_ - - -- Misc - - - Removed ``web3._utils.six`` support - - `#1116 `_ - - Upgrade eth-utils to 1.2.0 - - `#1104 `_ - - Require Python version 3.5.3 or greater - - `#1095 `_ - - Bump websockets version to 7.0.0 - - `#1146 `_ - - Bump parity test binary to 1.11.11 - - `#1064 `_ - - -v4.8.2 --------- - -Released November 15, 2018 - -- Misc - - - Reduce unneeded memory usage - - `#1138 `_ - -v4.8.1 --------- - -Released October 28, 2018 - -- Features - - - Add timeout for WebsocketProvider - - `#1119 `_ - - Reject transactions that send ether to non-payable contract functions - - `#1115 `_ - - Add Auto Infura Ropsten support: ``from web3.auto.infura.ropsten import w3`` - - `#1124 `_ - - Auto-detect trinity IPC file location - - `#1129 `_ -- Misc - - - Require Python >=3.5.3 - - `#1107 `_ - - Upgrade eth-tester and eth-utils - - `#1085 `_ - - Configure readthedocs dependencies - - `#1082 `_ - - soliditySha3 docs fixup - - `#1100 `_ - - Update ropsten faucet links in troubleshooting docs - -v4.7.2 --------- - -Released September 25th, 2018 - -- Bugfixes - - - IPC paths starting with ``~`` are appropriately resolved to the home directory - - `#1072 `_ - - You can use the local signing middleware with :class:`bytes`-type addresses - - `#1069 `_ - -v4.7.1 --------- - -Released September 11th, 2018 - -- Bugfixes - - - `old pip bug `_ used during - release made it impossible for non-windows users to install 4.7.0. - -v4.7.0 --------- - -Released September 10th, 2018 - -- Features - - - Add traceFilter method to the parity module. - - `#1051 `_ - - Move :mod:`~web3.utils.datastructures` to public namespace :mod:`~web3.datastructures` - to improve support for type checking. - - `#1038 `_ - - Optimization to contract calls - - `#944 `_ -- Bugfixes - - - ENS name resolution only attempted on mainnet by default. - - `#1037 `_ - - Fix attribute access error when attributedict middleware is not used. - - `#1040 `_ -- Misc - - Upgrade eth-tester to 0.1.0-beta.32, and remove integration tests for py-ethereum. - - Upgrade eth-hash to 0.2.0 with pycryptodome 3.6.6 which resolves a vulnerability. - -v4.6.0 --------- - -Released Aug 24, 2018 - -- Features - - - Support for Python 3.7, most notably in :class:`~web3.providers.websocket.WebsocketProvider` - - `#996 `_ - - You can now decode a transaction's data to its original function call and arguments with: - :meth:`contract.decode_function_input() ` - `#991 - `_ - - Support for :class:`~web3.providers.ipc.IPCProvider` in FreeBSD (and more readme docs) - `#1008 - `_ -- Bugfixes - - - Fix crash in time-based gas strategies with small number of transactions - `#983 - `_ - - Fx crash when passing multiple addresses to :meth:`w3.eth.getLogs() ` - - `#1005 `_ -- Misc - - - Disallow configuring filters with both manual and generated topic lists - `#976 - `_ - - Add support for the upcoming eth-abi v2, which does ABI string decoding differently - `#974 - `_ - - Add a lot more filter tests - `#997 - `_ - - Add more tests for filtering with ``None``. Note that geth & parity differ here. - `#985 - `_ - - Follow-up on Parity bug that we reported upstream (`parity#7816 - `_): they resolved in 1.10. We - removed xfail on that test. - `#992 - `_ - - Docs: add an example of interacting with an ERC20 contract - `#995 - `_ - - A couple doc typo fixes - - - `#1006 `_ - - `#1010 `_ - -v4.5.0 --------- - -Released July 30, 2018 - -- Features - - - Accept addresses supplied in :class:`bytes` format (which does not provide checksum validation) - - Improve estimation of gas prices -- Bugfixes - - - Can now use a block number with :meth:`~web3.eth.Eth.getCode` when connected to - :class:`~web3.providers.eth_tester.EthereumTesterProvider` (without crashing) -- Misc - - - Test Parity 1.11.7 - - Parity integration tests upgrade to use sha256 instead of md5 - - Fix some filter docs - - eth-account upgrade to v0.3.0 - - eth-tester upgrade to v0.1.0-beta.29 - -v4.4.1 --------- - -Released June 29, 2018 - -- Bugfixes - - - eth-pm package was renamed (old one deleted) which broke the web3 release. - eth-pm was removed from the web3.py install until it's stable. - -- Misc - - - :class:`~web3.providers.ipc.IPCProvider` now accepts a :class:`pathlib.Path` - argument for the IPC path - - Docs explaining the :ref:`new custom autoproviders in web3 ` - -v4.4.0 --------- - -Released June 21, 2018 - -- Features - - - Add support for https in WEB3_PROVIDER_URI environment variable - - Can send websocket connection parameters in :class:`~web3.providers.websocket.WebsocketProvider` - - Two new auto-initialization options: - - - ``from web3.auto.gethdev import w3`` - - ``from web3.auto.infura import w3`` - (After setting the ``INFURA_API_KEY`` environment variable) - - Alpha support for a new package management tool based on ethpm-spec, see :doc:`web3.pm` -- Bugfixes - - - Can now receive large responses in :class:`~web3.providers.websocket.WebsocketProvider` by - specifying a large ``max_size`` in the websocket connection parameters. -- Misc - - - Websockets dependency upgraded to v5 - - Raise deprecation warning on :meth:`~web3.eth.Eth.getTransactionFromBlock` - - Fix docs for :meth:`~web3.eth.Eth.waitForTransactionReceipt` - - Developer Dockerfile now installs testing dependencies - -v4.3.0 --------- - -Released June 6, 2018 - -- Features - - - Support for the ABI types like: `fixedMxN - `_ - which is used by Vyper. - - In-flight transaction-signing middleware: Use local keys as if they were hosted keys - using the new ``sign_and_send_raw_middleware`` - - New :meth:`~web3.eth.Eth.getUncleByBlock` API - - New name :meth:`~web3.eth.Eth.getTransactionByBlock`, which replaces the deprecated - :meth:`~web3.eth.Eth.getTransactionFromBlock` - - Add several new Parity trace functions - - New API to resolve ambiguous function calls, for example: - - - Two functions with the same name that accept similar argument types, like - ``myfunc(uint8)`` and ``myfunc(int8)``, and you want to call - ``contract.functions.myfunc(1).call()`` - - See how to use it at: :ref:`ambiguous-contract-functions` -- Bugfixes - - - Gas estimation doesn't crash, when 0 blocks are available. (ie~ on the genesis block) - - Close out all HTTPProvider sessions, to squash warnings on exit - - Stop adding Contract address twice to the filter. It was making some nodes unhappy -- Misc - - - Friendlier json encoding/decoding failure error messages - - Performance improvements, when the responses from the node are large - (by reducing the number of times we evaluate if the response is valid json) - - Parity CI test fixes (ugh, environment setup hell, thanks to the - community for cleaning this up!) - - Don't crash when requesting a transaction that was created with the parity bug - (which allowed an unsigned transaction to be included, so ``publicKey`` is ``None``) - - Doc fixes: addresses must be checksummed (or ENS names on mainnet) - - Enable local integration testing of parity on non-Debian OS - - README: - - - Testing setup for devs - - Change the build badge from Travis to Circle CI - - Cache the parity binary in Circle CI, to reduce the impact of their binary API going down - - Dropped the dot: ``py.test`` -> ``pytest`` - -v4.2.1 --------- - -Released May 9, 2018 - -- Bugfixes - - - When :meth:`getting a transaction ` - with data attached and trying to :meth:`modify it ` - (say, to increase the gas price), the data was not being reattached in - the new transaction. - - :meth:`web3.personal.sendTransaction` was crashing when using a transaction - generated with ``buildTransaction()`` -- Misc - - - Improved error message when connecting to a geth-style PoA network - - Improved error message when address is not checksummed - - Started in on support for ``fixedMxN`` ABI arguments - - Lots of documentation upgrades, including: - - - Guide for understanding nodes/networks/connections - - Simplified Quickstart with notes for common issues - - A new Troubleshooting section - - Potential pypy performance improvements (use toolz instead of cytoolz) - - eth-tester upgraded to beta 24 - -v4.2.0 --------- - -Released Apr 25, 2018 - -- Removed audit warning and opt-in requirement for ``w3.eth.account``. See more in: - :ref:`eth-account` -- Added an API to look up contract functions: ``fn = contract.functions['function_name_here']`` -- Upgrade Whisper (shh) module to use v6 API -- Bugfix: set 'to' field of transaction to empty when using - ``transaction = contract.constructor().buildTransaction()`` -- You can now specify `nonce` in ``buildTransaction()`` -- Distinguish between chain id and network id -- currently always return `None` for - :attr:`~web3.net.Net.chainId` -- Better error message when trying to use a contract function that has 0 or >1 matches -- Better error message when trying to install on a python version <3.5 -- Installs pypiwin32 during pip install, for a better Windows experience -- Cleaned up a lot of test warnings by upgrading from deprecated APIs, especially - from the deprecated ``contract.deploy(txn_dict, args=contract_args)`` - to the new ``contract.constructor(*contract_args).transact(txn_dict)`` -- Documentation typo fixes -- Better template for Pull Requests - -v4.1.0 --------- - -Released Apr 9, 2018 - -- New :class:`~web3.providers.websocket.WebsocketProvider`. - If you're looking for better performance than HTTP, check out websockets. -- New :meth:`w3.eth.waitForTransactionReceipt() ` -- Added name collision detection to ConciseContract and ImplicitContract -- Bugfix to allow fromBlock set to 0 in createFilter, like - ``contract.events.MyEvent.createFilter(fromBlock=0, ...)`` -- Bugfix of ENS automatic connection -- eth-tester support for Byzantium -- New migration guide for v3 -> v4 upgrade -- Various documentation updates -- Pinned eth-account to older version - -v4.0.0 ------------------ - -Released Apr 2, 2018 - -- Marked beta.13 as stable -- Documentation tweaks - -v4.0.0-beta.13 ------------------ - -Released Mar 27, 2018 - -*This is intended to be the final release before the stable v4 release.* - -- Add support for geth 1.8 (fixed error on :meth:`~web3.eth.Eth.getTransactionReceipt`) -- You can now call a contract method at a specific block - with the ``block_identifier`` keyword argument, see: - :meth:`~web3.contract.ContractFunction.call` -- In preparation for stable release, disable ``w3.eth.account`` by default, - until a third-party audit is complete & resolved. -- New API for contract deployment, which enables gas estimation, local signing, etc. - See :meth:`~web3.contract.Contract.constructor`. -- Find contract events with :ref:`contract.events.$my_event.createFilter() ` -- Support auto-complete for contract methods. -- Upgrade most dependencies to stable - - - eth-abi - - eth-utils - - hexbytes - - *not included: eth-tester and eth-account* -- Switch the default EthereumTesterProvider backend from eth-testrpc to eth-tester: - :class:`web3.providers.eth_tester.EthereumTesterProvider` -- A lot of documentation improvements -- Test node integrations over a variety of providers -- geth 1.8 test suite - - -v4.0.0-beta.12 ------------------ - -A little hiccup on release. Skipped. - -v4.0.0-beta.11 ------------------ - -Released Feb 28, 2018 - -- New methods to modify or replace pending transactions -- A compatibility option for connecting to ``geth --dev`` -- see :ref:`geth-poa` -- A new :attr:`web3.net.chainId` -- Create a filter object from an existing filter ID. -- eth-utils v1.0.1 (stable) compatibility - - -v4.0.0-beta.10 ------------------ - -Released Feb 21, 2018 - -- bugfix: Compatibility with eth-utils v1-beta2 - (the incompatibility was causing fresh web3.py installs to fail) -- bugfix: crash when sending the output of ``contract.functions.myFunction().buildTransaction()`` - to :meth:`~web3.eth.Eth.sendTransaction`. Now, having a chainID key does not crash - sendTransaction. -- bugfix: a TypeError when estimating gas like: - ``contract.functions.myFunction().estimateGas()`` is fixed -- Added parity integration tests to the continuous integration suite! -- Some py3 and docs cleanup - -v4.0.0-beta.9 -------------- - -Released Feb 8, 2018 - -- Access event log parameters as attributes -- Support for specifying nonce in eth-tester -- `Bugfix `_ - dependency conflicts between eth-utils, eth-abi, and eth-tester -- Clearer error message when invalid keywords provided to contract constructor function -- New docs for working with private keys + set up doctests -- First parity integration tests -- replace internal implementation of w3.eth.account with - :class:`eth_account.account.Account` - -v4.0.0-beta.8 -------------- - -Released Feb 7, 2018, then recalled. It added 32MB of test data to git history, -so the tag was deleted, as well as the corresponding release. -(Although the release would not have contained that test data) - -v4.0.0-beta.7 -------------- - -Released Jan 29, 2018 - -- Support for :meth:`web3.eth.Eth.getLogs` in eth-tester with py-evm -- Process transaction receipts with Event ABI, using - `Contract.events.myEvent(*args, **kwargs).processReceipt(transaction_receipt)` - see :ref:`event-log-object` for the new type. -- Add timeout parameter to :class:`web3.providers.ipc.IPCProvider` -- bugfix: make sure `idna` package is always installed -- Replace ethtestrpc with py-evm, in all tests -- Dockerfile fixup -- Test refactoring & cleanup -- Reduced warnings during tests - -v4.0.0-beta.6 -------------- - -Released Jan 18, 2018 - -- New contract function call API: `my_contract.functions.my_func().call()` is preferred over the now - deprecated `my_contract.call().my_func()` API. -- A new, sophisticated gas estimation algorithm, based on the https://ethgasstation.info approach. - You must opt-in to the new approach, because it's quite slow. We recommend using the new caching middleware. - See :meth:`web3.gas_strategies.time_based.construct_time_based_gas_price_strategy` -- New caching middleware that can cache based on time, block, or indefinitely. -- Automatically retry JSON-RPC requests over HTTP, a few times. -- ConciseContract now has the address directly -- Many eth-tester fixes. :class:`web3.providers.eth_tester.main.EthereumTesterProvider` is now a - legitimate alternative to :class:`web3.providers.tester.EthereumTesterProvider`. -- ethtest-rpc removed from testing. Tests use eth-tester only, on pyethereum. Soon it will be - eth-tester with py-evm. -- Bumped several dependencies, like eth-tester -- Documentation updates - -v4.0.0-beta.5 -------------- - -Released Dec 28, 2017 - -* Improvements to working with eth-tester, using :class:`~web3.providers.eth_tester.EthereumTesterProvider`: - - * Bugfix the key names in event logging - * Add support for :meth:`~web3.eth.Eth.sendRawTransaction` -* :class:`~web3.providers.ipc.IPCProvider` now automatically retries on a broken connection, like when you restart your node -* New gas price engine API, laying groundwork for more advanced gas pricing strategies - -v4.0.0-beta.4 -------------- - -Released Dec 7, 2017 - -* New :meth:`~web3.contract.Contract.buildTransaction` method to prepare contract transactions, offline -* New automatic provider detection, for ``w3 = Web3()`` initialization -* Set environment variable `WEB3_PROVIDER_URI` to suggest a provider for automatic detection -* New API to set providers like: ``w3.providers = [IPCProvider()]`` -* Crashfix: :meth:`web3.eth.Eth.filter` when retrieving logs with the argument 'latest' -* Bump eth-tester to v0.1.0-beta.5, with bugfix for filtering by topic -* Removed GPL lib ``pylru``, now believed to be in full MIT license compliance. - -v4.0.0-beta.3 -------------- - -Released Dec 1, 2017 - -* Fix encoding of ABI types: ``bytes[]`` and ``string[]`` -* Windows connection error bugfix -* Bugfix message signatures that were broken ~1% of the time (zero-pad ``r`` and ``s``) -* Autoinit web3 now produces None instead of raising an exception on ``from web3.auto import w3`` -* Clearer errors on formatting failure (includes field name that failed) -* Python modernization, removing Py2 compatibility cruft -* Update dependencies with changed names, now: - - * ``eth-abi`` - * ``eth-keyfile`` - * ``eth-keys`` - * ``eth-tester`` - * ``eth-utils`` -* Faster Travis CI builds, with cached geth binary - -v4.0.0-beta.2 -------------- - -Released Nov 22, 2017 - -Bug Fixes: - -* :meth:`~web3.eth.Eth.sendRawTransaction` accepts raw bytes -* :meth:`~web3.eth.Eth.contract` accepts an ENS name as contract address -* :meth:`~web3.account.Account.signTransaction` returns the expected hash (*after* signing the transaction) -* :class:`~web3.account.Account` methods can all be called statically, like: ``Account.sign(...)`` -* :meth:`~web3.eth.Eth.getTransactionReceipt` returns the ``status`` field as an ``int`` -* :meth:`Web3.soliditySha3` looks up ENS names if they are supplied with an "address" ABI -* If running multiple threads with the same w3 instance, ``ValueError: Recursively called ...`` is no longer raised - -Plus, various python modernization code cleanups, and testing against geth 1.7.2. - -v4.0.0-beta.1 -------------- - -* Python 3 is now required -* ENS names can be used anywhere that a hex address can -* Sign transactions and messages with local private keys -* New filter mechanism: :meth:`~web3.utils.filters.Filter.get_all_entries` and :meth:`~web3.utils.filters.Filter.get_new_entries` -* Quick automatic initialization with ``from web3.auto import w3`` -* All addresses must be supplied with an EIP-55 checksum -* All addresses are returned with a checksum -* Renamed ``Web3.toDecimal()`` to ``toInt()``, see: :ref:`overview_type_conversions` -* All filter calls are synchronous, gevent integration dropped -* Contract :meth:`~web3.contract.Contract.eventFilter` has replaced both ``Contract.on()`` and ``Contract.pastEvents()`` -* Contract arguments of ``bytes`` ABI type now accept hex strings. -* Contract arguments of ``string`` ABI type now accept python ``str``. -* Contract return values of ``string`` ABI type now return python ``str``. -* Many methods now return a ``bytes``-like object where they used to return a hex string, like in :meth:`Web3.sha3()` -* IPC connection left open and reused, rather than opened and closed on each call -* A number of deprecated methods from v3 were removed - -3.16.1 ------- - -* Addition of ``ethereum-tester`` as a dependency - - -3.16.0 ------- - -* Addition of *named* middlewares for easier manipulation of middleware stack. -* Provider middlewares can no longer be modified during runtime. -* Experimental custom ABI normalization API for Contract objects. - - -3.15.0 ------- - -* Change docs to use RTD theme -* Experimental new ``EthereumTesterProvider`` for the ``ethereum-tester`` library. -* Bugfix for ``function`` type abi encoding via ``ethereum-abi-utils`` upgrade to ``v0.4.1`` -* Bugfix for ``Web3.toHex`` to conform to RPC spec. - - -3.14.2 ------- - -* Fix PyPi readme text. - - -3.14.1 ------- - -* Fix PyPi readme text. - -3.14.0 ------- - -* New ``stalecheck_middleware`` -* Improvements to ``Web3.toHex`` and ``Web3.toText``. -* Improvements to ``Web3.sha3`` signature. -* Bugfixes for ``Web3.eth.sign`` api - - -3.13.5 ------- - -* Add experimental ``fixture_middleware`` -* Various bugfixes introduced in middleware API introduction and migration to - formatter middleware. - - -3.13.4 ------- - -* Bugfix for formatter handling of contract creation transaction. - - - -3.13.3 ------- - -* Improved testing infrastructure. - - -3.13.2 ------- - -* Bugfix for retrieving filter changes for both new block filters and pending - transaction filters. - - -3.13.1 ------- - -* Fix mispelled ``attrdict_middleware`` (was spelled ``attrdict_middlware``). - - -3.13.0 ------- - -* New Middleware API -* Support for multiple providers -* New ``web3.soliditySha3`` -* Remove multiple functions that were never implemented from the original web3. -* Deprecated ``web3.currentProvider`` accessor. Use ``web3.provider`` now instead. -* Deprecated password prompt within ``web3.personal.newAccount``. - - -3.12.0 ------- - -* Bugfix for abi filtering to correctly handle ``constructor`` and ``fallback`` type abi entries. - -3.11.0 ------- - -* All web3 apis which accept ``address`` parameters now enforce checksums if the address *looks* like it is checksummed. -* Improvements to error messaging with when calling a contract on a node that may not be fully synced -* Bugfix for ``web3.eth.syncing`` to correctly handle ``False`` - -3.10.0 ------- - -* Web3 now returns ``web3.utils.datastructures.AttributeDict`` in places where it previously returned a normal ``dict``. -* ``web3.eth.contract`` now performs validation on the ``address`` parameter. -* Added ``web3.eth.getWork`` API - -3.9.0 ------ - -* Add validation for the ``abi`` parameter of ``eth`` -* Contract return values of ``bytes``, ``bytesXX`` and ``string`` are no longer converted to text types and will be returned in their raw byte-string format. - -3.8.1 ------ - -* Bugfix for ``eth_sign`` double hashing input. -* Removed deprecated ``DelegatedSigningManager`` -* Removed deprecate ``PrivateKeySigningManager`` - -3.8.0 ------ - -* Update pyrlp dependency to ``>=0.4.7`` -* Update eth-testrpc dependency to ``>=1.2.0`` -* Deprecate ``DelegatedSigningManager`` -* Deprecate ``PrivateKeySigningManager`` - -3.7.1 ------ - -* upstream version bump for bugfix in eth-abi-utils - -3.7.0 ------ - -* deprecate ``eth.defaultAccount`` defaulting to the coinbase account. - -3.6.2 ------ - -* Fix error message from contract factory creation. -* Use ``ethereum-utils`` for utility functions. - -3.6.1 ------ - -* Upgrade ``ethereum-abi-utils`` dependency for upstream bugfix. - -3.6.0 ------ - -* Deprecate ``Contract.code``: replaced by ``Contract.bytecode`` -* Deprecate ``Contract.code_runtime``: replaced by ``Contract.bytecode_runtime`` -* Deprecate ``abi``, ``code``, ``code_runtime`` and ``source`` as arguments for the ``Contract`` object. -* Deprecate ``source`` as a property of the ``Contract`` object -* Add ``Contract.factory()`` API. -* Deprecate the ``construct_contract_factory`` helper function. - -3.5.3 ------ - -* Bugfix for how ``requests`` library is used. Now reuses session. - -3.5.2 ------ - -* Bugfix for construction of ``request_kwargs`` within HTTPProvider - -3.5.1 ------ - -* Allow ``HTTPProvider`` to be imported from ``web3`` module. -* make ``HTTPProvider`` accessible as a property of ``web3`` instances. - -3.5.0 ------ - -* Deprecate ``web3.providers.rpc.RPCProvider`` -* Deprecate ``web3.providers.rpc.KeepAliveRPCProvider`` -* Add new ``web3.providers.rpc.HTTPProvider`` -* Remove hard dependency on gevent. - -3.4.4 ------ - -* Bugfix for ``web3.eth.getTransaction`` when the hash is unknown. - -3.4.3 ------ - -* Bugfix for event log data decoding to properly handle dynamic sized values. -* New ``web3.tester`` module to access extra RPC functionality from ``eth-testrpc`` - -3.4.2 ------ - -* Fix package so that ``eth-testrpc`` is not required. - -3.4.1 ------ - -* Force gevent<1.2.0 until this issue is fixed: https://github.com/gevent/gevent/issues/916 - -3.4.0 ------ - -* Bugfix for contract instances to respect ``web3.eth.defaultAccount`` -* Better error reporting when ABI decoding fails for contract method response. - -3.3.0 ------ - -* New ``EthereumTesterProvider`` now available. Faster test runs than ``TestRPCProvider`` -* Updated underlying eth-testrpc requirement. - -3.2.0 ------ - -* ``web3.shh`` is now implemented. -* Introduced ``KeepAliveRPCProvider`` to correctly recycle HTTP connections and use HTTP keep alive - -3.1.1 ------ - -* Bugfix for contract transaction sending not respecting the - ``web3.eth.defaultAccount`` configuration. - -3.1.0 ------ - -* New DelegatedSigningManager and PrivateKeySigningManager classes. - -3.0.2 ------ - -* Bugfix or IPCProvider not handling large JSON responses well. - -3.0.1 ------ - -* Better RPC compliance to be compatable with the Parity JSON-RPC server. - -3.0.0 ------ - -* ``Filter`` objects now support controlling the interval through which they poll - using the ``poll_interval`` property - -2.9.0 ------ - -* Bugfix generation of event topics. -* Web3.Iban now allows access to Iban address tools. - -2.8.1 ------ - -* Bugfix for ``geth.ipc`` path on linux systems. - -2.8.0 ------ - -* Changes to the ``Contract`` API: - * ``Contract.deploy()`` parameter arguments renamed to args - * ``Contract.deploy()`` now takes args and kwargs parameters to allow - constructing with keyword arguments or positional arguments. - * ``Contract.pastEvents`` now allows you to specify a ``fromBlock or - ``toBlock.`` Previously these were forced to be ``'earliest'`` and - ``web3.eth.blockNumber`` respectively. - * ``Contract.call``, ``Contract.transact`` and ``Contract.estimateGas`` are now - callable as class methods as well as instance methods. When called this - way, an address must be provided with the transaction parameter. - * ``Contract.call``, ``Contract.transact`` and ``Contract.estimateGas`` now allow - specifying an alternate address for the transaction. -* ``RPCProvider`` now supports the following constructor arguments. - * ``ssl`` for enabling SSL - * ``connection_timeout`` and ``network_timeout`` for controlling the timeouts - for requests. - -2.7.1 ------ - -* Bugfix: Fix KeyError in merge_args_and_kwargs helper fn. - -2.7.0 ------ - -* Bugfix for usage of block identifiers 'latest', 'earliest', 'pending' -* Sphinx documentation -* Non-data transactions now default to 90000 gas. -* Web3 object now has helpers set as static methods rather than being set at - initialization. -* RPCProvider now takes a ``path`` parameter to allow configuration for requests - to go to paths other than ``/``. - -2.6.0 ------ - -* TestRPCProvider no longer dumps logging output to stdout and stderr. -* Bugfix for return types of ``address[]`` -* Bugfix for event data types of ``address`` - -2.5.0 ------ - -* All transactions which contain a ``data`` element will now have their gas - automatically estimated with 100k additional buffer. This was previously - only true with transactions initiated from a ``Contract`` object. - -2.4.0 ------ - -* Contract functions can now be called using keyword arguments. - -2.3.0 ------ - -* Upstream fixes for filters -* Filter APIs ``on`` and ``pastEvents`` now callable as both instance and class methods. - -2.2.0 ------ - -* The filters that come back from the contract ``on`` and ``pastEvents`` methods - now call their callbacks with the same data format as ``web3.js``. - -2.1.1 ------ - -* Cast RPCProvider port to an integer. - -2.1.0 ------ - -* Remove all monkeypatching - -2.0.0 ------ - -* Pull in downstream updates to proper gevent usage. -* Fix ``eth_sign`` -* Bugfix with contract operations mutating the transaction object that is passed in. -* More explicit linting ignore statements. - -1.9.0 ------ - -* BugFix: fix for python3 only ``json.JSONDecodeError`` handling. - -1.8.0 ------ - -* BugFix: ``RPCProvider`` not sending a content-type header -* Bugfix: ``web3.toWei`` now returns an integer instead of a decimal.Decimal - -1.7.1 ------ - -* ``TestRPCProvider`` can now be imported directly from ``web3`` - -1.7.0 ------ - -* Add ``eth.admin`` interface. -* Bugfix: Format the return value of ``web3.eth.syncing`` -* Bugfix: IPCProvider socket interactions are now more robust. - -1.6.0 ------ - -* Downstream package upgrades for ``eth-testrpc`` and ``ethereum-tester-client`` to - handle configuration of the Homestead and DAO fork block numbers. - -1.5.0 ------ - -* Rename ``web3.contract._Contract`` to ``web3.contract.Contract`` - to expose it for static analysis and auto completion tools -* Allow passing string parameters to functions -* Automatically compute gas requirements for contract deployment and -* transactions. -* Contract Filters -* Block, Transaction, and Log filters -* ``web3.eth.txpool`` interface -* ``web3.eth.mining`` interface -* Fixes for encoding. - -1.4.0 ------ - -* Bugfix to allow address types in constructor arguments. - -1.3.0 ------ - -* Partial implementation of the ``web3.eth.contract`` interface. - -1.2.0 ------ - -* Restructure project modules to be more *flat* -* Add ability to run test suite without the *slow* tests. -* Breakup ``encoding`` utils into smaller modules. -* Basic pep8 formatting. -* Apply python naming conventions to internal APIs -* Lots of minor bugfixes. -* Removal of dead code left behing from ``1.0.0`` refactor. -* Removal of ``web3/solidity`` module. - -1.1.0 ------ - -* Add missing ``isConnected()`` method. -* Add test coverage for ``setProvider()`` - -1.0.1 ------ - -* Specify missing ``pyrlp`` and ``gevent`` dependencies - -1.0.0 ------ - -* Massive refactor to the majority of the app. - -0.1.0 ------ - -* Initial release +Release Notes +============= + +v5 Breaking Changes Summary + See the :ref:`v5 Migration Guide` + +v5.0.0-alpha.11 +--------------- +Released April 24, 2019 + +- Docs + + - Add documentation for web3.py unit tests + - `#1324 `_ + +- Misc + + - Update deprecated collections.abc imports + - `#1334 `_ + - Fix documentation typo + - `#1335 `_ + - Upgrade vns-tester version + - `#1332 `_ + + +v5.0.0-alpha.10 +--------------- +Released April 15, 2019 + +- Features + + - Add getLogs by blockHash + - `#1269 `_ + - Implement chainId endpoint + - `#1295 `_ + - Moved non-standard JSON-RPC endpoints to applicable + Parity/Geth docs. Deprecated ``web3.version`` for ``web3.api`` + - `#1290 `_ + - Moved Whisper endpoints to applicable Geth or Parity namespace + - `#1308 `_ + - Added support for Goerli provider + - `#1286 `_ + - Added addReservedPeer to Parity module + - `#1311 `_ + +- Bugfixes + + - Cast gas price values to integers in gas strategies + - `#1297 `_ + - Missing constructor function no longer ignores constructor args + - `#1316 `_ + +- Misc + + - Require vns-utils >= 1.4, downgrade Go version for integration tests + - `#1310 `_ + - Fix doc build warnings + - `#1331 `_ + - Zip Fixture data + - `#1307 `_ + - Update Geth version for integration tests + - `#1301 `_ + - Remove unneeded testrpc + - `#1322 `_ + - Add ContractCaller docs to v5 migration guide + - `#1323 `_ + + + +v5.0.0-alpha.9 +-------------- +Released March 26, 2019 + +- Breaking Changes + + - Raise error if there is no Infura API Key + - `#1294 `_ & + - `#1299 `_ + +- Misc + + - Upgraded Parity version for integration testing + - `#1292 `_ + +v5.0.0-alpha.8 +-------------- +Released March 20, 2019 + +- Breaking Changes + + - Removed ``web3/utils`` directory in favor of ``web3/_utils`` + - `#1282 `_ + - Relocated personal RPC endpoints to Parity and Geth class + - `#1211 `_ + - Deprecated ``web3.net.chainId()``, ``web3.vns.getCompilers()``, + and ``web3.vns.getTransactionFromBlock()``. Removed ``web3.vns.enableUnauditedFeatures()`` + - `#1270 `_ + - Relocated vns_protocolVersion and web3_clientVersion + - `#1274 `_ + - Relocated ``web3.txpool`` to ``web3.geth.txpool`` + - `#1275 `_ + - Relocated admin module to Geth namespace + - `#1288 `_ + - Relocated miner module to Geth namespace + - `#1287 `_ + +- Features + + - Implement ``vns_submitHashrate`` and ``vns_submitWork`` JSONRPC endpoints. + - `#1280 `_ + - Implement ``web3.vns.signTransaction`` + - `#1277 `_ + +- Docs + + - Added v5 migration docs + - `#1284 `_ + +v5.0.0-alpha.7 +-------------- +Released March 11, 2019 + +- Breaking Changes + + - Updated JSON-RPC calls that lookup txs or blocks to raise + an error if lookup fails + - `#1218 `_ and + `#1268 `_ + +- Features + + - Tuple ABI support + - `#1235 `_ + +- Bugfixes + + - One last ``middleware_stack`` was still hanging on. + Changed to ``middleware_onion`` + - `#1262 `_ + +v5.0.0-alpha.6 +-------------- +Released February 25th, 2019 + +- Features + + - New ``NoABIFound`` error for cases where there is no ABI + - `#1247 `_ + +- Misc + + - Interact with Infura using an API Key. Key will be required after March 27th. + - `#1232 `_ + - Remove ``process_type`` utility function in favor of + vns-abi functionality + - `#1249 `_ + + +v5.0.0-alpha.5 +-------------- + +Released February 13th, 2019 + +- Breaking Changes + + - Remove deprecated ``buildTransaction``, ``call``, ``deploy``, + ``estimateGas``, and ``transact`` methods + - `#1232 `_ + +- Features + + - Adds ``Web3.toJSON`` method + - `#1173 `_ + - Contract Caller API Implemented + - `#1227 `_ + - Add Geth POA middleware to use Rinkeby with Infura Auto + - `#1234 `_ + - Add manifest and input argument validation to ``pm.release_package()`` + - `#1237 `_ + +- Misc + + - Clean up intro and block/tx sections in Filter docs + - `#1223 `_ + - Remove unnecessary ``EncodingError`` exception catching + - `#1224 `_ + - Improvements to ``merge_args_and_kwargs`` utility function + - `#1228 `_ + - Update vyper registry assets + - `#1242 `_ + + +v5.0.0-alpha.4 +-------------- + +Released January 23rd, 2019 + +- Breaking Changes + + - Rename ``middleware_stack`` to ``middleware_onion`` + - `#1210 `_ + - Drop already deprecated ``web3.soliditySha3`` + - `#1217 `_ + - ENS: Stop inferring ``.vns`` TLD on domain names + - `#1205 `_ + +- Bugfixes + + - Validate ``ethereum_tester`` class in ``EthereumTesterProvider`` + - `#1217 `_ + - Support ``getLogs()`` method without creating filters + - `#1192 `_ + +- Features + + - Stablize the ``PM`` module + - `#1125 `_ + - Implement async ``Version`` module + - `#1166 `_ + +- Misc + + - Update .gitignore to ignore ``.DS_Store`` and ``.mypy_cache/`` + - `#1215 `_ + - Change CircleCI badge link to CircleCI project + - `#1214 `_ + + +v5.0.0-alpha.3 +-------------- + +Released January 15th, 2019 + +- Breaking Changes + + - Remove ``web3.miner.hashrate`` and ``web3.version.network`` + - `#1198 `_ + - Remove ``web3.providers.tester.EthereumTesterProvider`` + and ``web3.providers.tester.TestRPCProvider`` + - `#1199 `_ + - Change ``manager.providers`` from list to single ``manager.provider`` + - `#1200 `_ + - Replace deprecated ``web3.sha3`` method with ``web3.keccak`` method + - `#1207 `_ + - Drop auto detect testnets for IPCProvider + - `#1206 `_ + +- Bugfixes + + - Add check to make sure blockHash exists + - `#1158 `_ + +- Misc + + - Remove some unreachable code in `providers/base.py` + - `#1160 `_ + - Migrate tester provider results from middleware to defaults + - `#1188 `_ + - Fix doc formatting for build_filter method + - `#1187 `_ + - Add ERC20 example in docs + - `#1178 `_ + - Code style improvements + - `#1194 `_ + & `#1191 `_ + - Convert Web3 instance variables to w3 + - `#1186 `_ + - Update vns-utils dependencies and clean up other dependencies + - `#1195 `_ + + +v5.0.0-alpha.2 +-------------- + +Released December 20th, 2018 + +- Breaking Changes + + - Remove support for python3.5, drop support for vns-abi v1 + - `#1163 `_ +- Features + + - Support for custom ReleaseManager was fixed + - `#1165 `_ + +- Misc + + - Fix doctest nonsense with unicorn token + - `3b2047 `_ + - Docs for installing web3 in FreeBSD + - `#1156 `_ + - Use latest python in readthedocs + - `#1162 `_ + - Use twine in release script + - `#1164 `_ + - Upgrade vns-tester, for vns-abi v2 support + - `#1168 `_ + +v5.0.0-alpha.1 +-------------- + +Released December 13th, 2018 + +- Features + + - Add Rinkeby and Kovan Infura networks; made mainnet the default + - `#1150 `_ + - Add parity-specific ``listStorageKeys`` RPC + - `#1145 `_ + - Deprecated ``Web3.soliditySha3``; use ``Web3.solidityKeccak`` instead. + - `#1139 `_ + - Add default trinity locations to IPC path guesser + - `#1121 `_ + - Add wss to ``AutoProvider`` + - `#1110 `_ + - Add timeout for ``WebsocketProvider`` + - `#1109 `_ + - Receipt timeout raises ``TimeExhausted`` + - `#1070 `_ + - Allow specification of block number for ``vns_estimateGas`` + - `#1046 `_ + + +- Misc + + - Removed ``web3._utils.six`` support + - `#1116 `_ + - Upgrade vns-utils to 1.2.0 + - `#1104 `_ + - Require Python version 3.5.3 or greater + - `#1095 `_ + - Bump websockets version to 7.0.0 + - `#1146 `_ + - Bump parity test binary to 1.11.11 + - `#1064 `_ + + +v4.8.2 +-------- + +Released November 15, 2018 + +- Misc + + - Reduce unneeded memory usage + - `#1138 `_ + +v4.8.1 +-------- + +Released October 28, 2018 + +- Features + + - Add timeout for WebsocketProvider + - `#1119 `_ + - Reject transactions that send ether to non-payable contract functions + - `#1115 `_ + - Add Auto Infura Ropsten support: ``from web3.auto.infura.ropsten import w3`` + - `#1124 `_ + - Auto-detect trinity IPC file location + - `#1129 `_ +- Misc + + - Require Python >=3.5.3 + - `#1107 `_ + - Upgrade vns-tester and vns-utils + - `#1085 `_ + - Configure readthedocs dependencies + - `#1082 `_ + - soliditySha3 docs fixup + - `#1100 `_ + - Update ropsten faucet links in troubleshooting docs + +v4.7.2 +-------- + +Released September 25th, 2018 + +- Bugfixes + + - IPC paths starting with ``~`` are appropriately resolved to the home directory + - `#1072 `_ + - You can use the local signing middleware with :class:`bytes`-type addresses + - `#1069 `_ + +v4.7.1 +-------- + +Released September 11th, 2018 + +- Bugfixes + + - `old pip bug `_ used during + release made it impossible for non-windows users to install 4.7.0. + +v4.7.0 +-------- + +Released September 10th, 2018 + +- Features + + - Add traceFilter method to the parity module. + - `#1051 `_ + - Move :mod:`~web3.utils.datastructures` to public namespace :mod:`~web3.datastructures` + to improve support for type checking. + - `#1038 `_ + - Optimization to contract calls + - `#944 `_ +- Bugfixes + + - ENS name resolution only attempted on mainnet by default. + - `#1037 `_ + - Fix attribute access error when attributedict middleware is not used. + - `#1040 `_ +- Misc + - Upgrade vns-tester to 0.1.0-beta.32, and remove integration tests for py-ethereum. + - Upgrade vns-hash to 0.2.0 with pycryptodome 3.6.6 which resolves a vulnerability. + +v4.6.0 +-------- + +Released Aug 24, 2018 + +- Features + + - Support for Python 3.7, most notably in :class:`~web3.providers.websocket.WebsocketProvider` + - `#996 `_ + - You can now decode a transaction's data to its original function call and arguments with: + :meth:`contract.decode_function_input() ` - `#991 + `_ + - Support for :class:`~web3.providers.ipc.IPCProvider` in FreeBSD (and more readme docs) - `#1008 + `_ +- Bugfixes + + - Fix crash in time-based gas strategies with small number of transactions - `#983 + `_ + - Fx crash when passing multiple addresses to :meth:`w3.vns.getLogs() ` - + `#1005 `_ +- Misc + + - Disallow configuring filters with both manual and generated topic lists - `#976 + `_ + - Add support for the upcoming vns-abi v2, which does ABI string decoding differently - `#974 + `_ + - Add a lot more filter tests - `#997 + `_ + - Add more tests for filtering with ``None``. Note that geth & parity differ here. - `#985 + `_ + - Follow-up on Parity bug that we reported upstream (`parity#7816 + `_): they resolved in 1.10. We + removed xfail on that test. - `#992 + `_ + - Docs: add an example of interacting with an ERC20 contract - `#995 + `_ + - A couple doc typo fixes + + - `#1006 `_ + - `#1010 `_ + +v4.5.0 +-------- + +Released July 30, 2018 + +- Features + + - Accept addresses supplied in :class:`bytes` format (which does not provide checksum validation) + - Improve estimation of gas prices +- Bugfixes + + - Can now use a block number with :meth:`~web3.vns.Bbbbbbbb.getCode` when connected to + :class:`~web3.providers.vns_tester.EthereumTesterProvider` (without crashing) +- Misc + + - Test Parity 1.11.7 + - Parity integration tests upgrade to use sha256 instead of md5 + - Fix some filter docs + - vns-account upgrade to v0.3.0 + - vns-tester upgrade to v0.1.0-beta.29 + +v4.4.1 +-------- + +Released June 29, 2018 + +- Bugfixes + + - vns-pm package was renamed (old one deleted) which broke the web3 release. + vns-pm was removed from the web3.py install until it's stable. + +- Misc + + - :class:`~web3.providers.ipc.IPCProvider` now accepts a :class:`pathlib.Path` + argument for the IPC path + - Docs explaining the :ref:`new custom autoproviders in web3 ` + +v4.4.0 +-------- + +Released June 21, 2018 + +- Features + + - Add support for https in WEB3_PROVIDER_URI environment variable + - Can send websocket connection parameters in :class:`~web3.providers.websocket.WebsocketProvider` + - Two new auto-initialization options: + + - ``from web3.auto.gethdev import w3`` + - ``from web3.auto.infura import w3`` + (After setting the ``INFURA_API_KEY`` environment variable) + - Alpha support for a new package management tool based on ethpm-spec, see :doc:`web3.pm` +- Bugfixes + + - Can now receive large responses in :class:`~web3.providers.websocket.WebsocketProvider` by + specifying a large ``max_size`` in the websocket connection parameters. +- Misc + + - Websockets dependency upgraded to v5 + - Raise deprecation warning on :meth:`~web3.vns.Bbbbbbbb.getTransactionFromBlock` + - Fix docs for :meth:`~web3.vns.Bbbbbbbb.waitForTransactionReceipt` + - Developer Dockerfile now installs testing dependencies + +v4.3.0 +-------- + +Released June 6, 2018 + +- Features + + - Support for the ABI types like: `fixedMxN + `_ + which is used by Vyper. + - In-flight transaction-signing middleware: Use local keys as if they were hosted keys + using the new ``sign_and_send_raw_middleware`` + - New :meth:`~web3.vns.Bbbbbbbb.getUncleByBlock` API + - New name :meth:`~web3.vns.Bbbbbbbb.getTransactionByBlock`, which replaces the deprecated + :meth:`~web3.vns.Bbbbbbbb.getTransactionFromBlock` + - Add several new Parity trace functions + - New API to resolve ambiguous function calls, for example: + + - Two functions with the same name that accept similar argument types, like + ``myfunc(uint8)`` and ``myfunc(int8)``, and you want to call + ``contract.functions.myfunc(1).call()`` + - See how to use it at: :ref:`ambiguous-contract-functions` +- Bugfixes + + - Gas estimation doesn't crash, when 0 blocks are available. (ie~ on the genesis block) + - Close out all HTTPProvider sessions, to squash warnings on exit + - Stop adding Contract address twice to the filter. It was making some nodes unhappy +- Misc + + - Friendlier json encoding/decoding failure error messages + - Performance improvements, when the responses from the node are large + (by reducing the number of times we evaluate if the response is valid json) + - Parity CI test fixes (ugh, environment setup hell, thanks to the + community for cleaning this up!) + - Don't crash when requesting a transaction that was created with the parity bug + (which allowed an unsigned transaction to be included, so ``publicKey`` is ``None``) + - Doc fixes: addresses must be checksummed (or ENS names on mainnet) + - Enable local integration testing of parity on non-Debian OS + - README: + + - Testing setup for devs + - Change the build badge from Travis to Circle CI + - Cache the parity binary in Circle CI, to reduce the impact of their binary API going down + - Dropped the dot: ``py.test`` -> ``pytest`` + +v4.2.1 +-------- + +Released May 9, 2018 + +- Bugfixes + + - When :meth:`getting a transaction ` + with data attached and trying to :meth:`modify it ` + (say, to increase the gas price), the data was not being reattached in + the new transaction. + - :meth:`web3.personal.sendTransaction` was crashing when using a transaction + generated with ``buildTransaction()`` +- Misc + + - Improved error message when connecting to a geth-style PoA network + - Improved error message when address is not checksummed + - Started in on support for ``fixedMxN`` ABI arguments + - Lots of documentation upgrades, including: + + - Guide for understanding nodes/networks/connections + - Simplified Quickstart with notes for common issues + - A new Troubleshooting section + - Potential pypy performance improvements (use toolz instead of cytoolz) + - vns-tester upgraded to beta 24 + +v4.2.0 +-------- + +Released Apr 25, 2018 + +- Removed audit warning and opt-in requirement for ``w3.vns.account``. See more in: + :ref:`vns-account` +- Added an API to look up contract functions: ``fn = contract.functions['function_name_here']`` +- Upgrade Whisper (shh) module to use v6 API +- Bugfix: set 'to' field of transaction to empty when using + ``transaction = contract.constructor().buildTransaction()`` +- You can now specify `nonce` in ``buildTransaction()`` +- Distinguish between chain id and network id -- currently always return `None` for + :attr:`~web3.net.Net.chainId` +- Better error message when trying to use a contract function that has 0 or >1 matches +- Better error message when trying to install on a python version <3.5 +- Installs pypiwin32 during pip install, for a better Windows experience +- Cleaned up a lot of test warnings by upgrading from deprecated APIs, especially + from the deprecated ``contract.deploy(txn_dict, args=contract_args)`` + to the new ``contract.constructor(*contract_args).transact(txn_dict)`` +- Documentation typo fixes +- Better template for Pull Requests + +v4.1.0 +-------- + +Released Apr 9, 2018 + +- New :class:`~web3.providers.websocket.WebsocketProvider`. + If you're looking for better performance than HTTP, check out websockets. +- New :meth:`w3.vns.waitForTransactionReceipt() ` +- Added name collision detection to ConciseContract and ImplicitContract +- Bugfix to allow fromBlock set to 0 in createFilter, like + ``contract.events.MyEvent.createFilter(fromBlock=0, ...)`` +- Bugfix of ENS automatic connection +- vns-tester support for Byzantium +- New migration guide for v3 -> v4 upgrade +- Various documentation updates +- Pinned vns-account to older version + +v4.0.0 +----------------- + +Released Apr 2, 2018 + +- Marked beta.13 as stable +- Documentation tweaks + +v4.0.0-beta.13 +----------------- + +Released Mar 27, 2018 + +*This is intended to be the final release before the stable v4 release.* + +- Add support for geth 1.8 (fixed error on :meth:`~web3.vns.Bbbbbbbb.getTransactionReceipt`) +- You can now call a contract method at a specific block + with the ``block_identifier`` keyword argument, see: + :meth:`~web3.contract.ContractFunction.call` +- In preparation for stable release, disable ``w3.vns.account`` by default, + until a third-party audit is complete & resolved. +- New API for contract deployment, which enables gas estimation, local signing, etc. + See :meth:`~web3.contract.Contract.constructor`. +- Find contract events with :ref:`contract.events.$my_event.createFilter() ` +- Support auto-complete for contract methods. +- Upgrade most dependencies to stable + + - vns-abi + - vns-utils + - hexbytes + - *not included: vns-tester and vns-account* +- Switch the default EthereumTesterProvider backend from vns-testrpc to vns-tester: + :class:`web3.providers.vns_tester.EthereumTesterProvider` +- A lot of documentation improvements +- Test node integrations over a variety of providers +- geth 1.8 test suite + + +v4.0.0-beta.12 +----------------- + +A little hiccup on release. Skipped. + +v4.0.0-beta.11 +----------------- + +Released Feb 28, 2018 + +- New methods to modify or replace pending transactions +- A compatibility option for connecting to ``geth --dev`` -- see :ref:`geth-poa` +- A new :attr:`web3.net.chainId` +- Create a filter object from an existing filter ID. +- vns-utils v1.0.1 (stable) compatibility + + +v4.0.0-beta.10 +----------------- + +Released Feb 21, 2018 + +- bugfix: Compatibility with vns-utils v1-beta2 + (the incompatibility was causing fresh web3.py installs to fail) +- bugfix: crash when sending the output of ``contract.functions.myFunction().buildTransaction()`` + to :meth:`~web3.vns.Bbbbbbbb.sendTransaction`. Now, having a chainID key does not crash + sendTransaction. +- bugfix: a TypeError when estimating gas like: + ``contract.functions.myFunction().estimateGas()`` is fixed +- Added parity integration tests to the continuous integration suite! +- Some py3 and docs cleanup + +v4.0.0-beta.9 +------------- + +Released Feb 8, 2018 + +- Access event log parameters as attributes +- Support for specifying nonce in vns-tester +- `Bugfix `_ + dependency conflicts between vns-utils, vns-abi, and vns-tester +- Clearer error message when invalid keywords provided to contract constructor function +- New docs for working with private keys + set up doctests +- First parity integration tests +- replace internal implementation of w3.vns.account with + :class:`vns_account.account.Account` + +v4.0.0-beta.8 +------------- + +Released Feb 7, 2018, then recalled. It added 32MB of test data to git history, +so the tag was deleted, as well as the corresponding release. +(Although the release would not have contained that test data) + +v4.0.0-beta.7 +------------- + +Released Jan 29, 2018 + +- Support for :meth:`web3.vns.Bbbbbbbb.getLogs` in vns-tester with py-evm +- Process transaction receipts with Event ABI, using + `Contract.events.myEvent(*args, **kwargs).processReceipt(transaction_receipt)` + see :ref:`event-log-object` for the new type. +- Add timeout parameter to :class:`web3.providers.ipc.IPCProvider` +- bugfix: make sure `idna` package is always installed +- Replace ethtestrpc with py-evm, in all tests +- Dockerfile fixup +- Test refactoring & cleanup +- Reduced warnings during tests + +v4.0.0-beta.6 +------------- + +Released Jan 18, 2018 + +- New contract function call API: `my_contract.functions.my_func().call()` is preferred over the now + deprecated `my_contract.call().my_func()` API. +- A new, sophisticated gas estimation algorithm, based on the https://ethgasstation.info approach. + You must opt-in to the new approach, because it's quite slow. We recommend using the new caching middleware. + See :meth:`web3.gas_strategies.time_based.construct_time_based_gas_price_strategy` +- New caching middleware that can cache based on time, block, or indefinitely. +- Automatically retry JSON-RPC requests over HTTP, a few times. +- ConciseContract now has the address directly +- Many vns-tester fixes. :class:`web3.providers.vns_tester.main.EthereumTesterProvider` is now a + legitimate alternative to :class:`web3.providers.tester.EthereumTesterProvider`. +- ethtest-rpc removed from testing. Tests use vns-tester only, on pyethereum. Soon it will be + vns-tester with py-evm. +- Bumped several dependencies, like vns-tester +- Documentation updates + +v4.0.0-beta.5 +------------- + +Released Dec 28, 2017 + +* Improvements to working with vns-tester, using :class:`~web3.providers.vns_tester.EthereumTesterProvider`: + + * Bugfix the key names in event logging + * Add support for :meth:`~web3.vns.Bbbbbbbb.sendRawTransaction` +* :class:`~web3.providers.ipc.IPCProvider` now automatically retries on a broken connection, like when you restart your node +* New gas price engine API, laying groundwork for more advanced gas pricing strategies + +v4.0.0-beta.4 +------------- + +Released Dec 7, 2017 + +* New :meth:`~web3.contract.Contract.buildTransaction` method to prepare contract transactions, offline +* New automatic provider detection, for ``w3 = Web3()`` initialization +* Set environment variable `WEB3_PROVIDER_URI` to suggest a provider for automatic detection +* New API to set providers like: ``w3.providers = [IPCProvider()]`` +* Crashfix: :meth:`web3.vns.Bbbbbbbb.filter` when retrieving logs with the argument 'latest' +* Bump vns-tester to v0.1.0-beta.5, with bugfix for filtering by topic +* Removed GPL lib ``pylru``, now believed to be in full MIT license compliance. + +v4.0.0-beta.3 +------------- + +Released Dec 1, 2017 + +* Fix encoding of ABI types: ``bytes[]`` and ``string[]`` +* Windows connection error bugfix +* Bugfix message signatures that were broken ~1% of the time (zero-pad ``r`` and ``s``) +* Autoinit web3 now produces None instead of raising an exception on ``from web3.auto import w3`` +* Clearer errors on formatting failure (includes field name that failed) +* Python modernization, removing Py2 compatibility cruft +* Update dependencies with changed names, now: + + * ``vns-abi`` + * ``vns-keyfile`` + * ``vns-keys`` + * ``vns-tester`` + * ``vns-utils`` +* Faster Travis CI builds, with cached geth binary + +v4.0.0-beta.2 +------------- + +Released Nov 22, 2017 + +Bug Fixes: + +* :meth:`~web3.vns.Bbbbbbbb.sendRawTransaction` accepts raw bytes +* :meth:`~web3.vns.Bbbbbbbb.contract` accepts an ENS name as contract address +* :meth:`~web3.account.Account.signTransaction` returns the expected hash (*after* signing the transaction) +* :class:`~web3.account.Account` methods can all be called statically, like: ``Account.sign(...)`` +* :meth:`~web3.vns.Bbbbbbbb.getTransactionReceipt` returns the ``status`` field as an ``int`` +* :meth:`Web3.soliditySha3` looks up ENS names if they are supplied with an "address" ABI +* If running multiple threads with the same w3 instance, ``ValueError: Recursively called ...`` is no longer raised + +Plus, various python modernization code cleanups, and testing against geth 1.7.2. + +v4.0.0-beta.1 +------------- + +* Python 3 is now required +* ENS names can be used anywhere that a hex address can +* Sign transactions and messages with local private keys +* New filter mechanism: :meth:`~web3.utils.filters.Filter.get_all_entries` and :meth:`~web3.utils.filters.Filter.get_new_entries` +* Quick automatic initialization with ``from web3.auto import w3`` +* All addresses must be supplied with an EIP-55 checksum +* All addresses are returned with a checksum +* Renamed ``Web3.toDecimal()`` to ``toInt()``, see: :ref:`overview_type_conversions` +* All filter calls are synchronous, gevent integration dropped +* Contract :meth:`~web3.contract.Contract.eventFilter` has replaced both ``Contract.on()`` and ``Contract.pastEvents()`` +* Contract arguments of ``bytes`` ABI type now accept hex strings. +* Contract arguments of ``string`` ABI type now accept python ``str``. +* Contract return values of ``string`` ABI type now return python ``str``. +* Many methods now return a ``bytes``-like object where they used to return a hex string, like in :meth:`Web3.sha3()` +* IPC connection left open and reused, rather than opened and closed on each call +* A number of deprecated methods from v3 were removed + +3.16.1 +------ + +* Addition of ``ethereum-tester`` as a dependency + + +3.16.0 +------ + +* Addition of *named* middlewares for easier manipulation of middleware stack. +* Provider middlewares can no longer be modified during runtime. +* Experimental custom ABI normalization API for Contract objects. + + +3.15.0 +------ + +* Change docs to use RTD theme +* Experimental new ``EthereumTesterProvider`` for the ``ethereum-tester`` library. +* Bugfix for ``function`` type abi encoding via ``ethereum-abi-utils`` upgrade to ``v0.4.1`` +* Bugfix for ``Web3.toHex`` to conform to RPC spec. + + +3.14.2 +------ + +* Fix PyPi readme text. + + +3.14.1 +------ + +* Fix PyPi readme text. + +3.14.0 +------ + +* New ``stalecheck_middleware`` +* Improvements to ``Web3.toHex`` and ``Web3.toText``. +* Improvements to ``Web3.sha3`` signature. +* Bugfixes for ``Web3.vns.sign`` api + + +3.13.5 +------ + +* Add experimental ``fixture_middleware`` +* Various bugfixes introduced in middleware API introduction and migration to + formatter middleware. + + +3.13.4 +------ + +* Bugfix for formatter handling of contract creation transaction. + + + +3.13.3 +------ + +* Improved testing infrastructure. + + +3.13.2 +------ + +* Bugfix for retrieving filter changes for both new block filters and pending + transaction filters. + + +3.13.1 +------ + +* Fix mispelled ``attrdict_middleware`` (was spelled ``attrdict_middlware``). + + +3.13.0 +------ + +* New Middleware API +* Support for multiple providers +* New ``web3.soliditySha3`` +* Remove multiple functions that were never implemented from the original web3. +* Deprecated ``web3.currentProvider`` accessor. Use ``web3.provider`` now instead. +* Deprecated password prompt within ``web3.personal.newAccount``. + + +3.12.0 +------ + +* Bugfix for abi filtering to correctly handle ``constructor`` and ``fallback`` type abi entries. + +3.11.0 +------ + +* All web3 apis which accept ``address`` parameters now enforce checksums if the address *looks* like it is checksummed. +* Improvements to error messaging with when calling a contract on a node that may not be fully synced +* Bugfix for ``web3.vns.syncing`` to correctly handle ``False`` + +3.10.0 +------ + +* Web3 now returns ``web3.utils.datastructures.AttributeDict`` in places where it previously returned a normal ``dict``. +* ``web3.vns.contract`` now performs validation on the ``address`` parameter. +* Added ``web3.vns.getWork`` API + +3.9.0 +----- + +* Add validation for the ``abi`` parameter of ``vns`` +* Contract return values of ``bytes``, ``bytesXX`` and ``string`` are no longer converted to text types and will be returned in their raw byte-string format. + +3.8.1 +----- + +* Bugfix for ``vns_sign`` double hashing input. +* Removed deprecated ``DelegatedSigningManager`` +* Removed deprecate ``PrivateKeySigningManager`` + +3.8.0 +----- + +* Update pyrlp dependency to ``>=0.4.7`` +* Update vns-testrpc dependency to ``>=1.2.0`` +* Deprecate ``DelegatedSigningManager`` +* Deprecate ``PrivateKeySigningManager`` + +3.7.1 +----- + +* upstream version bump for bugfix in vns-abi-utils + +3.7.0 +----- + +* deprecate ``vns.defaultAccount`` defaulting to the coinbase account. + +3.6.2 +----- + +* Fix error message from contract factory creation. +* Use ``ethereum-utils`` for utility functions. + +3.6.1 +----- + +* Upgrade ``ethereum-abi-utils`` dependency for upstream bugfix. + +3.6.0 +----- + +* Deprecate ``Contract.code``: replaced by ``Contract.bytecode`` +* Deprecate ``Contract.code_runtime``: replaced by ``Contract.bytecode_runtime`` +* Deprecate ``abi``, ``code``, ``code_runtime`` and ``source`` as arguments for the ``Contract`` object. +* Deprecate ``source`` as a property of the ``Contract`` object +* Add ``Contract.factory()`` API. +* Deprecate the ``construct_contract_factory`` helper function. + +3.5.3 +----- + +* Bugfix for how ``requests`` library is used. Now reuses session. + +3.5.2 +----- + +* Bugfix for construction of ``request_kwargs`` within HTTPProvider + +3.5.1 +----- + +* Allow ``HTTPProvider`` to be imported from ``web3`` module. +* make ``HTTPProvider`` accessible as a property of ``web3`` instances. + +3.5.0 +----- + +* Deprecate ``web3.providers.rpc.RPCProvider`` +* Deprecate ``web3.providers.rpc.KeepAliveRPCProvider`` +* Add new ``web3.providers.rpc.HTTPProvider`` +* Remove hard dependency on gevent. + +3.4.4 +----- + +* Bugfix for ``web3.vns.getTransaction`` when the hash is unknown. + +3.4.3 +----- + +* Bugfix for event log data decoding to properly handle dynamic sized values. +* New ``web3.tester`` module to access extra RPC functionality from ``vns-testrpc`` + +3.4.2 +----- + +* Fix package so that ``vns-testrpc`` is not required. + +3.4.1 +----- + +* Force gevent<1.2.0 until this issue is fixed: https://github.com/gevent/gevent/issues/916 + +3.4.0 +----- + +* Bugfix for contract instances to respect ``web3.vns.defaultAccount`` +* Better error reporting when ABI decoding fails for contract method response. + +3.3.0 +----- + +* New ``EthereumTesterProvider`` now available. Faster test runs than ``TestRPCProvider`` +* Updated underlying vns-testrpc requirement. + +3.2.0 +----- + +* ``web3.shh`` is now implemented. +* Introduced ``KeepAliveRPCProvider`` to correctly recycle HTTP connections and use HTTP keep alive + +3.1.1 +----- + +* Bugfix for contract transaction sending not respecting the + ``web3.vns.defaultAccount`` configuration. + +3.1.0 +----- + +* New DelegatedSigningManager and PrivateKeySigningManager classes. + +3.0.2 +----- + +* Bugfix or IPCProvider not handling large JSON responses well. + +3.0.1 +----- + +* Better RPC compliance to be compatable with the Parity JSON-RPC server. + +3.0.0 +----- + +* ``Filter`` objects now support controlling the interval through which they poll + using the ``poll_interval`` property + +2.9.0 +----- + +* Bugfix generation of event topics. +* Web3.Iban now allows access to Iban address tools. + +2.8.1 +----- + +* Bugfix for ``geth.ipc`` path on linux systems. + +2.8.0 +----- + +* Changes to the ``Contract`` API: + * ``Contract.deploy()`` parameter arguments renamed to args + * ``Contract.deploy()`` now takes args and kwargs parameters to allow + constructing with keyword arguments or positional arguments. + * ``Contract.pastEvents`` now allows you to specify a ``fromBlock or + ``toBlock.`` Previously these were forced to be ``'earliest'`` and + ``web3.vns.blockNumber`` respectively. + * ``Contract.call``, ``Contract.transact`` and ``Contract.estimateGas`` are now + callable as class methods as well as instance methods. When called this + way, an address must be provided with the transaction parameter. + * ``Contract.call``, ``Contract.transact`` and ``Contract.estimateGas`` now allow + specifying an alternate address for the transaction. +* ``RPCProvider`` now supports the following constructor arguments. + * ``ssl`` for enabling SSL + * ``connection_timeout`` and ``network_timeout`` for controlling the timeouts + for requests. + +2.7.1 +----- + +* Bugfix: Fix KeyError in merge_args_and_kwargs helper fn. + +2.7.0 +----- + +* Bugfix for usage of block identifiers 'latest', 'earliest', 'pending' +* Sphinx documentation +* Non-data transactions now default to 90000 gas. +* Web3 object now has helpers set as static methods rather than being set at + initialization. +* RPCProvider now takes a ``path`` parameter to allow configuration for requests + to go to paths other than ``/``. + +2.6.0 +----- + +* TestRPCProvider no longer dumps logging output to stdout and stderr. +* Bugfix for return types of ``address[]`` +* Bugfix for event data types of ``address`` + +2.5.0 +----- + +* All transactions which contain a ``data`` element will now have their gas + automatically estimated with 100k additional buffer. This was previously + only true with transactions initiated from a ``Contract`` object. + +2.4.0 +----- + +* Contract functions can now be called using keyword arguments. + +2.3.0 +----- + +* Upstream fixes for filters +* Filter APIs ``on`` and ``pastEvents`` now callable as both instance and class methods. + +2.2.0 +----- + +* The filters that come back from the contract ``on`` and ``pastEvents`` methods + now call their callbacks with the same data format as ``web3.js``. + +2.1.1 +----- + +* Cast RPCProvider port to an integer. + +2.1.0 +----- + +* Remove all monkeypatching + +2.0.0 +----- + +* Pull in downstream updates to proper gevent usage. +* Fix ``vns_sign`` +* Bugfix with contract operations mutating the transaction object that is passed in. +* More explicit linting ignore statements. + +1.9.0 +----- + +* BugFix: fix for python3 only ``json.JSONDecodeError`` handling. + +1.8.0 +----- + +* BugFix: ``RPCProvider`` not sending a content-type header +* Bugfix: ``web3.toWei`` now returns an integer instead of a decimal.Decimal + +1.7.1 +----- + +* ``TestRPCProvider`` can now be imported directly from ``web3`` + +1.7.0 +----- + +* Add ``vns.admin`` interface. +* Bugfix: Format the return value of ``web3.vns.syncing`` +* Bugfix: IPCProvider socket interactions are now more robust. + +1.6.0 +----- + +* Downstream package upgrades for ``vns-testrpc`` and ``ethereum-tester-client`` to + handle configuration of the Homestead and DAO fork block numbers. + +1.5.0 +----- + +* Rename ``web3.contract._Contract`` to ``web3.contract.Contract`` + to expose it for static analysis and auto completion tools +* Allow passing string parameters to functions +* Automatically compute gas requirements for contract deployment and +* transactions. +* Contract Filters +* Block, Transaction, and Log filters +* ``web3.vns.txpool`` interface +* ``web3.vns.mining`` interface +* Fixes for encoding. + +1.4.0 +----- + +* Bugfix to allow address types in constructor arguments. + +1.3.0 +----- + +* Partial implementation of the ``web3.vns.contract`` interface. + +1.2.0 +----- + +* Restructure project modules to be more *flat* +* Add ability to run test suite without the *slow* tests. +* Breakup ``encoding`` utils into smaller modules. +* Basic pep8 formatting. +* Apply python naming conventions to internal APIs +* Lots of minor bugfixes. +* Removal of dead code left behing from ``1.0.0`` refactor. +* Removal of ``web3/solidity`` module. + +1.1.0 +----- + +* Add missing ``isConnected()`` method. +* Add test coverage for ``setProvider()`` + +1.0.1 +----- + +* Specify missing ``pyrlp`` and ``gevent`` dependencies + +1.0.0 +----- + +* Massive refactor to the majority of the app. + +0.1.0 +----- + +* Initial release diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 1ce94f829e..ef464235aa 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -1,72 +1,72 @@ -Troubleshooting -============================= - -.. _setup_environment: - -Set up a clean environment ----------------------------------------------- - -Many things can cause a broken environment. You might be on an unsupported version of Python. -Another package might be installed that has a name or version conflict. -Often, the best way to guarantee a correct environment is with ``virtualenv``, like: - -.. code-block:: shell - - # Install pip if it is not available: - $ which pip || curl https://bootstrap.pypa.io/get-pip.py | python - - # Install virtualenv if it is not available: - $ which virtualenv || pip install --upgrade virtualenv - - # *If* the above command displays an error, you can try installing as root: - $ sudo pip install virtualenv - - # Create a virtual environment: - $ virtualenv -p python3 ~/.venv-py3 - - # Activate your new virtual environment: - $ source ~/.venv-py3/bin/activate - - # With virtualenv active, make sure you have the latest packaging tools - $ pip install --upgrade pip setuptools - - # Now we can install web3.py... - $ pip install --upgrade web3 - -.. NOTE:: Remember that each new terminal session requires you to reactivate your virtualenv, like: - ``$ source ~/.venv-py3/bin/activate`` - -.. _use_metamask_accounts: - -How do I use my MetaMask accounts from Web3.py? --------------------------------------------------------- -Often you don't need to do this, just make a new account in Web3.py, -and transfer funds from your MetaMask account into it. But if you must... - -Export your private key from MetaMask, and use -the local private key tools in Web3.py to sign and send transactions. - -See `how to export your private key -`_ -and :ref:`eth-account`. - -.. _faucets: - -How do I get ether for my test network? --------------------------------------------------------- - -Test networks usually have something called a "faucet" to -help get test ether to people who want to use it. The faucet -simply sends you test ether when you visit a web page, or ping a chat bot, etc. - -Each test network has its own version of test ether, so each one -must maintain its own faucet. If you're not sure which test network -to use, see :ref:`choosing_network` - -Faucet mechanisms tend to come and go, so if any information here is -out of date, try the `Ethereum Stackexchange `_. -Here are some links to testnet ether instructions (in no particular order): - -- `Kovan `_ -- `Rinkeby `_ -- `Ropsten `_ +Troubleshooting +============================= + +.. _setup_environment: + +Set up a clean environment +---------------------------------------------- + +Many things can cause a broken environment. You might be on an unsupported version of Python. +Another package might be installed that has a name or version conflict. +Often, the best way to guarantee a correct environment is with ``virtualenv``, like: + +.. code-block:: shell + + # Install pip if it is not available: + $ which pip || curl https://bootstrap.pypa.io/get-pip.py | python + + # Install virtualenv if it is not available: + $ which virtualenv || pip install --upgrade virtualenv + + # *If* the above command displays an error, you can try installing as root: + $ sudo pip install virtualenv + + # Create a virtual environment: + $ virtualenv -p python3 ~/.venv-py3 + + # Activate your new virtual environment: + $ source ~/.venv-py3/bin/activate + + # With virtualenv active, make sure you have the latest packaging tools + $ pip install --upgrade pip setuptools + + # Now we can install web3.py... + $ pip install --upgrade web3 + +.. NOTE:: Remember that each new terminal session requires you to reactivate your virtualenv, like: + ``$ source ~/.venv-py3/bin/activate`` + +.. _use_metamask_accounts: + +How do I use my MetaMask accounts from Web3.py? +-------------------------------------------------------- +Often you don't need to do this, just make a new account in Web3.py, +and transfer funds from your MetaMask account into it. But if you must... + +Export your private key from MetaMask, and use +the local private key tools in Web3.py to sign and send transactions. + +See `how to export your private key +`_ +and :ref:`vns-account`. + +.. _faucets: + +How do I get ether for my test network? +-------------------------------------------------------- + +Test networks usually have something called a "faucet" to +help get test ether to people who want to use it. The faucet +simply sends you test ether when you visit a web page, or ping a chat bot, etc. + +Each test network has its own version of test ether, so each one +must maintain its own faucet. If you're not sure which test network +to use, see :ref:`choosing_network` + +Faucet mechanisms tend to come and go, so if any information here is +out of date, try the `Ethereum Stackexchange `_. +Here are some links to testnet ether instructions (in no particular order): + +- `Kovan `_ +- `Rinkeby `_ +- `Ropsten `_ diff --git a/docs/v4_migration.rst b/docs/v4_migration.rst index 14ca20f2e1..dd83700ec7 100644 --- a/docs/v4_migration.rst +++ b/docs/v4_migration.rst @@ -1,164 +1,164 @@ -Migrating your code from v3 to v4 -======================================= - -Web3.py follows `Semantic Versioning `_, which means -that version 4 introduced backwards-incompatible changes. If your -project depends on Web3.py v3, then you'll probably need to make some changes. - -Here are the most common required updates: - -Python 2 to Python 3 ----------------------- - -Only Python 3 is supported in v4. If you are running in Python 2, -it's time to upgrade. We recommend using `2to3` which can make -most of your code compatible with Python 3, automatically. - -The most important update, relevant to Web3.py, is the new :class:`bytes` -type. It is used regularly, throughout the library, whenever dealing with data -that is not guaranteed to be text. - -Many different methods in Web3.py accept text or binary data, like contract methods, -transaction details, and cryptographic functions. The following example -uses :meth:`~Web3.sha3`, but the same pattern applies elsewhere. - -In v3 & Python 2, you might have calculated the hash of binary data this way: - -.. code-block:: python - - >>> Web3.sha3('I\xe2\x99\xa5SF') - '0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e' - -Or, you might have calculated the hash of text data this way: - -.. code-block:: python - - >>> Web3.sha3(text=u'I♥SF') - '0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e' - -After switching to Python 3, these would instead be executed as: - -.. code-block:: python - - >>> Web3.sha3(b'I\xe2\x99\xa5SF') - HexBytes('0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e') - - >>> Web3.sha3(text='I♥SF') - HexBytes('0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e') - -Note that the return value is different too: you can treat :class:`hexbytes.main.HexBytes` -like any other bytes value, but the representation on the console shows you the hex encoding of -those bytes, for easier visual comparison. - -It takes a little getting used to, but the new py3 types are much better. We promise. - -Filters ---------- - -Filters usually don't work quite the way that people want them to. - -The first step toward fixing them was to simplify them by removing the polling -logic. Now, you must request an update on your filters explicitly. That -means that any exceptions during the request will bubble up into your code. - -In v3, those exceptions (like "filter is not found") were swallowed silently -in the automated polling logic. Here was the invocation for -printing out new block hashes as they appear: - -.. code-block:: python - - >>> def new_block_callback(block_hash): - ... print "New Block: {0}".format(block_hash) - ... - >>> new_block_filter = web3.eth.filter('latest') - >>> new_block_filter.watch(new_block_callback) - -In v4, that same logic: - -.. code-block:: python - - >>> new_block_filter = web3.eth.filter('latest') - >>> for block_hash in new_block_filter.get_new_entries(): - ... print("New Block: {}".format(block_hash)) - -The caller is responsible for polling the results from ``get_new_entries()``. -See :ref:`asynchronous_filters` for examples of filter-event handling with web3 v4. - -TestRPCProvider and EthereumTesterProvider ------------------------------------------------- - -These providers are fairly uncommon. If you don't recognize the names, -you can probably skip the section. - -However, if you were using web3.py for testing contracts, -you might have been using TestRPCProvider or EthereumTesterProvider. - -In v4 there is a new :class:`EthereumTesterProvider`, and the old v3 implementation has been -removed. Web3.py v4 uses :class:`eth_tester.main.EthereumTester` under the hood, instead -of eth-testrpc. While ``eth-tester`` is still in beta, many parts are -already in better shape than testrpc, so we decided to replace it in v4. - -If you were using TestRPC, or were explicitly importing EthereumTesterProvider, like: -``from web3.providers.tester import EthereumTesterProvider``, then you will need to update. - -With v4 you should import with ``from web3 import EthereumTesterProvider``. As before, you'll -need to install Web3.py with the ``tester`` extra to get these features, like: - -.. code-block:: bash - - $ pip install web3[tester] - - -Changes to base API convenience methods ---------------------------------------- - -Web3.toDecimal() -~~~~~~~~~~~~~~~~~ - -In v4 ``Web3.toDecimal()`` is renamed: :meth:`~Web3.toInt` for improved clarity. It does not return a :class:`decimal.Decimal`, it returns an :class:`int`. - - -Removed Methods -~~~~~~~~~~~~~~~~~~ - -- ``Web3.toUtf8`` was removed for :meth:`~Web3.toText`. -- ``Web3.fromUtf8`` was removed for :meth:`~Web3.toHex`. -- ``Web3.toAscii`` was removed for :meth:`~Web3.toBytes`. -- ``Web3.fromAscii`` was removed for :meth:`~Web3.toHex`. -- ``Web3.fromDecimal`` was removed for :meth:`~Web3.toHex`. - -Provider Access -~~~~~~~~~~~~~~~~~ - -In v4, ``w3.currentProvider`` was removed, in favor of ``w3.providers``. - -Disambiguating String Inputs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are a number of places where an arbitrary string input might be either -a byte-string that has been hex-encoded, or unicode characters in text. -These are named ``hexstr`` and ``text`` in Web3.py. -You specify which kind of :class:`str` you have by using the appropriate -keyword argument. See examples in :ref:`overview_type_conversions`. - -In v3, some methods accepted a :class:`str` as the first positional argument. -In v4, you must pass strings as one of ``hexstr`` or ``text`` keyword arguments. - -Notable methods that no longer accept ambiguous strings: - -- :meth:`~Web3.sha3` -- :meth:`~Web3.toBytes` - -Contracts ------------ - -- When a contract returns the ABI type ``string``, Web3.py v4 now returns a :class:`str` - value by decoding the underlying bytes using UTF-8. -- When a contract returns the ABI type ``bytes`` (of any length), - Web3.py v4 now returns a :class:`bytes` value - -Personal API --------------- - -``w3.personal.signAndSendTransaction`` is no longer available. Use -:meth:`w3.personal.sendTransaction() ` instead. +Migrating your code from v3 to v4 +======================================= + +Web3.py follows `Semantic Versioning `_, which means +that version 4 introduced backwards-incompatible changes. If your +project depends on Web3.py v3, then you'll probably need to make some changes. + +Here are the most common required updates: + +Python 2 to Python 3 +---------------------- + +Only Python 3 is supported in v4. If you are running in Python 2, +it's time to upgrade. We recommend using `2to3` which can make +most of your code compatible with Python 3, automatically. + +The most important update, relevant to Web3.py, is the new :class:`bytes` +type. It is used regularly, throughout the library, whenever dealing with data +that is not guaranteed to be text. + +Many different methods in Web3.py accept text or binary data, like contract methods, +transaction details, and cryptographic functions. The following example +uses :meth:`~Web3.sha3`, but the same pattern applies elsewhere. + +In v3 & Python 2, you might have calculated the hash of binary data this way: + +.. code-block:: python + + >>> Web3.sha3('I\xe2\x99\xa5SF') + '0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e' + +Or, you might have calculated the hash of text data this way: + +.. code-block:: python + + >>> Web3.sha3(text=u'I♥SF') + '0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e' + +After switching to Python 3, these would instead be executed as: + +.. code-block:: python + + >>> Web3.sha3(b'I\xe2\x99\xa5SF') + HexBytes('0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e') + + >>> Web3.sha3(text='I♥SF') + HexBytes('0x50a826df121f4d076a3686d74558f40082a8e70b3469d8e9a16ceb2a79102e5e') + +Note that the return value is different too: you can treat :class:`hexbytes.main.HexBytes` +like any other bytes value, but the representation on the console shows you the hex encoding of +those bytes, for easier visual comparison. + +It takes a little getting used to, but the new py3 types are much better. We promise. + +Filters +--------- + +Filters usually don't work quite the way that people want them to. + +The first step toward fixing them was to simplify them by removing the polling +logic. Now, you must request an update on your filters explicitly. That +means that any exceptions during the request will bubble up into your code. + +In v3, those exceptions (like "filter is not found") were swallowed silently +in the automated polling logic. Here was the invocation for +printing out new block hashes as they appear: + +.. code-block:: python + + >>> def new_block_callback(block_hash): + ... print "New Block: {0}".format(block_hash) + ... + >>> new_block_filter = web3.vns.filter('latest') + >>> new_block_filter.watch(new_block_callback) + +In v4, that same logic: + +.. code-block:: python + + >>> new_block_filter = web3.vns.filter('latest') + >>> for block_hash in new_block_filter.get_new_entries(): + ... print("New Block: {}".format(block_hash)) + +The caller is responsible for polling the results from ``get_new_entries()``. +See :ref:`asynchronous_filters` for examples of filter-event handling with web3 v4. + +TestRPCProvider and EthereumTesterProvider +------------------------------------------------ + +These providers are fairly uncommon. If you don't recognize the names, +you can probably skip the section. + +However, if you were using web3.py for testing contracts, +you might have been using TestRPCProvider or EthereumTesterProvider. + +In v4 there is a new :class:`EthereumTesterProvider`, and the old v3 implementation has been +removed. Web3.py v4 uses :class:`vns_tester.main.EthereumTester` under the hood, instead +of vns-testrpc. While ``vns-tester`` is still in beta, many parts are +already in better shape than testrpc, so we decided to replace it in v4. + +If you were using TestRPC, or were explicitly importing EthereumTesterProvider, like: +``from web3.providers.tester import EthereumTesterProvider``, then you will need to update. + +With v4 you should import with ``from web3 import EthereumTesterProvider``. As before, you'll +need to install Web3.py with the ``tester`` extra to get these features, like: + +.. code-block:: bash + + $ pip install web3[tester] + + +Changes to base API convenience methods +--------------------------------------- + +Web3.toDecimal() +~~~~~~~~~~~~~~~~~ + +In v4 ``Web3.toDecimal()`` is renamed: :meth:`~Web3.toInt` for improved clarity. It does not return a :class:`decimal.Decimal`, it returns an :class:`int`. + + +Removed Methods +~~~~~~~~~~~~~~~~~~ + +- ``Web3.toUtf8`` was removed for :meth:`~Web3.toText`. +- ``Web3.fromUtf8`` was removed for :meth:`~Web3.toHex`. +- ``Web3.toAscii`` was removed for :meth:`~Web3.toBytes`. +- ``Web3.fromAscii`` was removed for :meth:`~Web3.toHex`. +- ``Web3.fromDecimal`` was removed for :meth:`~Web3.toHex`. + +Provider Access +~~~~~~~~~~~~~~~~~ + +In v4, ``w3.currentProvider`` was removed, in favor of ``w3.providers``. + +Disambiguating String Inputs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are a number of places where an arbitrary string input might be either +a byte-string that has been hex-encoded, or unicode characters in text. +These are named ``hexstr`` and ``text`` in Web3.py. +You specify which kind of :class:`str` you have by using the appropriate +keyword argument. See examples in :ref:`overview_type_conversions`. + +In v3, some methods accepted a :class:`str` as the first positional argument. +In v4, you must pass strings as one of ``hexstr`` or ``text`` keyword arguments. + +Notable methods that no longer accept ambiguous strings: + +- :meth:`~Web3.sha3` +- :meth:`~Web3.toBytes` + +Contracts +----------- + +- When a contract returns the ABI type ``string``, Web3.py v4 now returns a :class:`str` + value by decoding the underlying bytes using UTF-8. +- When a contract returns the ABI type ``bytes`` (of any length), + Web3.py v4 now returns a :class:`bytes` value + +Personal API +-------------- + +``w3.personal.signAndSendTransaction`` is no longer available. Use +:meth:`w3.personal.sendTransaction() ` instead. diff --git a/docs/v5_migration.rst b/docs/v5_migration.rst index ec06bb455c..60decea86d 100644 --- a/docs/v5_migration.rst +++ b/docs/v5_migration.rst @@ -1,136 +1,136 @@ -.. _migrating_v4_to_v5: - -Migrating your code from v4 to v5 -======================================= - -Web3.py follows `Semantic Versioning `_, which means -that version 5 introduced backwards-incompatible changes. If your -project depends on Web3.py v4, then you'll probably need to make some changes. - -Here are the most common required updates: - -Python 3.5 no longer supported ------------------------------- - -You will need to upgrade to either Python 3.6 or 3.7 - -``eth-abi`` v1 no longer supported ----------------------------------- - -You will need to upgrade the ``eth-abi`` dependency to v2 - -Changes to base API -------------------- - -JSON-RPC Updates -~~~~~~~~~~~~~~~~ - -In v4, JSON-RPC calls that looked up transactions or blocks and -didn't find them, returned ``None``. Now if a transaction or -block is not found, a ``BlockNotFound`` or a ``TransactionNotFound`` -error will be thrown as appropriate. This applies to the -following web3 methods: - -- :meth:`~web3.eth.Eth.getTransaction` will throw a ``TransactionNotFound`` error -- :meth:`~web3.eth.Eth.getTransactionReceipt` will throw a ``TransactionNotFound`` error -- :meth:`~web3.eth.Eth.getTransactionByBlock` will throw a ``TransactionNotFound`` error -- :meth:`~web3.eth.Eth.getTransactionCount` will throw a ``BlockNotFound`` error -- :meth:`~web3.eth.Eth.getBlock` will throw a ``BlockNotFound`` error -- :meth:`~web3.eth.Eth.getUncleCount` will throw a ``BlockNotFound`` error -- :meth:`~web3.eth.Eth.getUncleByBlock` will throw a ``BlockNotFound`` error - -Removed Methods -~~~~~~~~~~~~~~~ - -- ``contract.buildTransaction`` was removed for ``contract.functions.buildTransaction.`` -- ``contract.deploy`` was removed for ``contract.constructor.transact`` -- ``contract.estimateGas`` was removed for ``contract.functions..estimateGas`` -- ``contract.call`` was removed for ``contract...call`` -- ``contract.transact`` was removed for ``contract...transact`` -- ``contract.eventFilter`` was removed for ``contract.events..createFilter`` -- ``middleware_stack`` was renamed to :meth:`~Web3.middleware_onion` -- ``web3.miner.hashrate`` was a duplicate of :meth:`~web3.eth.Eth.hashrate` and was removed. -- ``web3.version.network`` was a duplicate of :meth:`~web3.net.Net.version` and was removed. -- ``web3.providers.tester.EthereumTesterProvider`` and ``web3.providers.tester.TestRPCProvider`` have been removed for :meth:`~web3.providers.eth_tester.EthereumTesterProvider` -- ``web3.eth.enableUnauditedFeatures`` was removed -- ``web3.txpool`` was moved to :meth:`~web3.geth.txpool` -- ``web3.version.node`` was removed for ``web3.clientVersion`` -- ``web3.version.ethereum`` was removed for :meth:`~web3.eth.Eth.protocolVersion` -- Relocated personal RPC endpoints to reflect Parity and Geth implementations: - - - ``web3.personal.listAccounts`` was removed for :meth:`~web3.geth.personal.listAccounts` or :meth:`~web3.parity.personal.listAccounts` - - ``web3.personal.importRawKey`` was removed for :meth:`~web3.geth.personal.importRawKey` or :meth:`~web3.parity.personal.importRawKey` - - ``web3.personal.newAccount`` was removed for :meth:`~web3.geth.personal.newAccount` or :meth:`~web3.parity.personal.newAccount` - - ``web3.personal.lockAccount`` was removed for :meth:`~web3.geth.personal.lockAccount` - - ``web3.personal.unlockAccount`` was removed for :meth:`~web3.geth.personal.unlockAccount` or :meth:`~web3.parity.personal.unlockAccount` - - ``web3.personal.sendTransaction`` was removed for :meth:`~web3.geth.personal.sendTransaction` or :meth:`~web3.parity.personal.sendTransaction` - -- Relocated ``web3.admin`` module to ``web3.geth`` namespace -- Relocated ``web3.miner`` module to ``web3.geth`` namespace - -Deprecated Methods -~~~~~~~~~~~~~~~~~~ -Expect the following methods to be removed in v6: - -- ``web3.sha3`` was deprecated for :meth:`~Web3.keccak` -- ``web3.soliditySha3`` was deprecated for :meth:`~Web3.solidityKeccak` -- :meth:`~web3.net.Net.chainId` was deprecated for :meth:`~web3.eth.Eth.chainId`. - Follow issue `#1293 `_ for details -- ``web3.eth.getCompilers()`` was deprecated and will not be replaced -- :meth:`~web3.eth.getTransactionFromBlock()` was deprecated for :meth:`~Web3.getTransactionByBlock` - -Deprecated ConciseContract and ImplicitContract -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ConciseContract and ImplicitContract have been deprecated and will be removed in v6. - -ImplicitContract instances will need to use the verbose syntax. For example: - -``contract.functions..transact({})`` - -ConciseContract has been replaced with the ContractCaller API. Instead of using the ConciseContract factory, you can now use: - -``contract.caller.`` - -or the classic contract syntax: - -``contract.functions..call()``. - -Some more concrete examples can be found in the `ContractCaller docs `_ - - -Manager Provider -~~~~~~~~~~~~~~~~ - -In v5, only a single provider will be allowed. While allowing -multiple providers is a feature we'd like to support in the future, -the way that multiple providers was handled in v4 wasn't ideal. -The only thing they could do was fall back. There was no mechanism for any -round robin, nor was there any control around which provider -was chosen. Eventually, the idea is to expand the Manager API -to support injecting custom logic into the provider selection process. - -For now, ``manager.providers`` has changed to ``manager.provider``. -Similarly, instances of ``web3.providers`` have been changed to -``web3.provider``. - -Testnet Changes -~~~~~~~~~~~~~~~ - -Web3.py will no longer automatically look up a testnet connection -in IPCProvider. Something like ``from web3.auto.infura.ropsten import w3`` -should be used instead. - -ENS ---- - -Web3.py has stopped inferring the ``.eth`` TLD on domain names. -If a domain name is used instead of an address, you'll need -to specify the TLD. An ``InvalidTLD`` error will be thrown if -the TLD is missing. - -Required Infura API Key ------------------------ - -In order to interact with Infura after March 27, 2019, you'll need to set an -environment variable called ``WEB3_INFURA_PROJECT_ID``. You can get a -project id by visiting https://infura.io/register. +.. _migrating_v4_to_v5: + +Migrating your code from v4 to v5 +======================================= + +Web3.py follows `Semantic Versioning `_, which means +that version 5 introduced backwards-incompatible changes. If your +project depends on Web3.py v4, then you'll probably need to make some changes. + +Here are the most common required updates: + +Python 3.5 no longer supported. +------------------------------- + +You will need to upgrade to either Python 3.6 or 3.7 + +``vns-abi`` v1 no longer supported +---------------------------------- + +You will need to upgrade the ``vns-abi`` dependency to v2 or higher. + +Changes to base API +------------------- + +JSON-RPC Updates +~~~~~~~~~~~~~~~~ + +In v4, JSON-RPC calls that looked up transactions or blocks and +didn't find them, returned ``None``. Now if a transaction or +block is not found, a `BlockNotFound` or a `TransactionNotFound` +error will be thrown as appropriate. This applies to the +following web3 methods: + +- :meth:`~web3.vns.Bbbbbbbb.getTransaction` will throw a ``TransactionNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getTransactionReceipt` will throw a ``TransactionNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getTransactionByBlock` will throw a ``TransactionNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getTransactionCount` will throw a ``BlockNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getBlock` will throw a ``BlockNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getUncleCount` will throw a ``BlockNotFound`` error +- :meth:`~web3.vns.Bbbbbbbb.getUncleByBlock` will throw a ``BlockNotFound`` error + +Removed Methods +~~~~~~~~~~~~~~~ + +- ``contract.buildTransaction`` was removed for ``contract.functions.buildTransaction.`` +- ``contract.deploy`` was removed for ``contract.constructor.transact`` +- ``contract.estimateGas`` was removed for ``contract.functions..estimateGas`` +- ``contract.call`` was removed for ``contract...call`` +- ``contract.transact`` was removed for ``contract...transact`` +- ``contract.eventFilter`` was removed for ``contract.events..createFilter`` +- ``middleware_stack`` was renamed to :meth:`~Web3.middleware_onion` +- ``web3.miner.hashrate`` was a duplicate of :meth:`~web3.vns.Bbbbbbbb.hashrate` and was removed. +- ``web3.version.network`` was a duplicate of :meth:`~web3.net.Net.version` and was removed. +- ``web3.providers.tester.EthereumTesterProvider`` and ``web3.providers.tester.TestRPCProvider`` have been removed for :meth:`~web3.providers.vns_tester.EthereumTesterProvider` +- ``web3.vns.enableUnauditedFeatures`` was removed +- ``web3.txpool`` was moved to :meth:`~web3.geth.txpool` +- ``web3.version.node`` was removed for ``web3.clientVersion`` +- ``web3.version.ethereum`` was removed for :meth:`~web3.vns.Bbbbbbbb.protocolVersion` +- Relocated personal RPC endpoints to reflect Parity and Geth implementations: + + - ``web3.personal.listAccounts`` was removed for :meth:`~web3.geth.personal.listAccounts` or :meth:`~web3.parity.personal.listAccounts` + - ``web3.personal.importRawKey`` was removed for :meth:`~web3.geth.personal.importRawKey` or :meth:`~web3.parity.personal.importRawKey` + - ``web3.personal.newAccount`` was removed for :meth:`~web3.geth.personal.newAccount` or :meth:`~web3.parity.personal.newAccount` + - ``web3.personal.lockAccount`` was removed for :meth:`~web3.geth.personal.lockAccount` + - ``web3.personal.unlockAccount`` was removed for :meth:`~web3.geth.personal.unlockAccount` or :meth:`~web3.parity.personal.unlockAccount` + - ``web3.personal.sendTransaction`` was removed for :meth:`~web3.geth.personal.sendTransaction` or :meth:`~web3.parity.personal.sendTransaction` + +- Relocated ``web3.admin`` module to ``web3.geth`` namespace +- Relocated ``web3.miner`` module to ``web3.geth`` namespace + +Deprecated Methods +~~~~~~~~~~~~~~~~~~ +Expect the following methods to be removed in v6: + +- ``web3.sha3`` was deprecated for :meth:`~Web3.keccak` +- ``web3.soliditySha3`` was deprecated for :meth:`~Web3.solidityKeccak` +- :meth:`~web3.net.Net.chainId` was deprecated for :meth:`~web3.vns.Bbbbbbbb.chainId`. + Follow issue `#1293 `_ for details +- ``web3.vns.getCompilers()`` was deprecated and will not be replaced +- :meth:`~web3.vns.getTransactionFromBlock()` was deprecated for :meth:`~Web3.getTransactionByBlock` + +Deprecated ConciseContract and ImplicitContract +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ConciseContract and ImplicitContract have been deprecated and will be removed in v6. + +ImplicitContract instances will need to use the verbose syntax. For example: + +``contract.functions..transact({})`` + +ConciseContract has been replaced with the ContractCaller API. Instead of using the ConciseContract factory, you can now use: + +``contract.caller.`` + +or the classic contract syntax: + +``contract.functions..call()``. + +Some more concrete examples can be found in the `ContractCaller docs `_ + + +Manager Provider +~~~~~~~~~~~~~~~~ + +In v5, only a single provider will be allowed. While allowing +multiple providers is a feature we'd like to support in the future, +the way that multiple providers was handled in v4 wasn't ideal. +The only thing they could do was fall back. There was no mechanism for any +round robin, nor was there any control around which provider +was chosen. Eventually, the idea is to expand the Manager API +to support injecting custom logic into the provider selection process. + +For now, ``manager.providers`` has changed to ``manager.provider``. +Similarly, instances of ``web3.providers`` have been changed to +``web3.provider``. + +Testnet Changes +~~~~~~~~~~~~~~~ + +- Web3.py will no longer automatically look up a testnet connection + in IPCProvider. Something like ``from web3.auto.ropsten import w3`` + should be used instead. + +ENS +--- + +Web3.py has stopped inferring the ``.vns`` TLD on domain names. +If a domain name is used instead of an address, you'll need +to specify the TLD. An ``InvalidTLD`` error will be thrown if +the TLD is missing. + +Required Infura API Key +----------------------- + +In order to interact with Infura after March 27, 2019, you'll need to set an +environment variable called ``WEB3_INFURA_PROJECT_ID``. You can get a +project id by visiting https://infura.io/register. diff --git a/docs/web3.eth.account.rst b/docs/web3.eth.account.rst deleted file mode 100644 index e0f85ffe1c..0000000000 --- a/docs/web3.eth.account.rst +++ /dev/null @@ -1,322 +0,0 @@ -.. _eth-account: - -Working with Local Private Keys -========================================== - -.. _local_vs_hosted: - -Local vs Hosted Nodes ---------------------------------- - -Local Node - A local node is started and controlled by you. It is as safe as you keep it. - When you run ``geth`` or ``parity`` on your machine, you are running a local node. - -Hosted Node - A hosted node is controlled by someone else. When you connect to Infura, you are - connected to a hosted node. - -Local vs Hosted Keys ---------------------------------- - -Local Private Key - A key is 32 :class:`bytes` of data that you can use to sign transactions and messages, - before sending them to your node. - You must use :meth:`~web3.eth.Eth.sendRawTransaction` - when working with local keys, instead of - :meth:`~web3.eth.Eth.sendTransaction` . - -Hosted Private Key - This is a common way to use accounts with local nodes. - Each account returned by :attr:`w3.eth.accounts ` - has a hosted private key stored in your node. - This allows you to use :meth:`~web3.eth.Eth.sendTransaction`. - - -.. WARNING:: - It is unnacceptable for a hosted node to offer hosted private keys. It - gives other people complete control over your account. "Not your keys, - not your Ether" in the wise words of Andreas Antonopoulos. - -Some Common Uses for Local Private Keys -------------------------------------------- - -A very common reason to work with local private keys is to interact -with a hosted node. - -Some common things you might want to do with a `Local Private Key` are: - -- `Sign a Transaction`_ -- `Sign a Contract Transaction`_ -- `Sign a Message`_ -- `Verify a Message`_ - -Using private keys usually involves ``w3.eth.account`` in one way or another. Read on for more, -or see a full list of things you can do in the docs for -:class:`eth_account.Account `. - -Extract private key from geth keyfile ---------------------------------------------- - -.. NOTE:: - The amount of available ram should be greater than 1GB. - -.. code-block:: python - - with open('~/.ethereum/keystore/UTC--...--5ce9454909639D2D17A3F753ce7d93fa0b9aB12E') as keyfile: - encrypted_key = keyfile.read() - private_key = w3.eth.account.decrypt(encrypted_key, 'correcthorsebatterystaple') - # tip: do not save the key or password anywhere, especially into a shared source file - -Sign a Message ---------------- - -.. WARNING:: There is no single message format that is broadly adopted - with community consensus. Keep an eye on several options, - like `EIP-683 `_, - `EIP-712 `_, and - `EIP-719 `_. Consider - the :meth:`w3.eth.sign() ` approach be deprecated. - -For this example, we will use the same message hashing mechanism that -is provided by :meth:`w3.eth.sign() `. - -.. doctest:: - - >>> from web3.auto import w3 - >>> from eth_account.messages import encode_defunct - - >>> msg = "I♥SF" - >>> private_key = b"\xb2\\}\xb3\x1f\xee\xd9\x12''\xbf\t9\xdcv\x9a\x96VK-\xe4\xc4rm\x03[6\xec\xf1\xe5\xb3d" - >>> message = encode_defunct(text=msg) - >>> signed_message = w3.eth.account.sign_message(message, private_key=private_key) - >>> signed_message - AttrDict({'messageHash': HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'), - 'r': 104389933075820307925104709181714897380569894203213074526835978196648170704563, - 's': 28205917190874851400050446352651915501321657673772411533993420917949420456142, - 'v': 28, - 'signature': HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c')}) - -Verify a Message ------------------------------------------------- - -With the original message text and a signature: - -.. doctest:: - - >>> message = encode_defunct(text="I♥SF") - >>> w3.eth.account.recover_message(message, signature=signed_message.signature) - '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' - -Verify a Message from message hash ------------------------------------------------------------ - -Sometimes, for historical reasons, you don't have the original message, -all you have is the prefixed & hashed message. To verify it, use: - -.. CAUTION:: This method is deprecated, only having a hash typically indicates that - you're using some old kind of mechanism. Expect this method to go away in the - next major version upgrade. - -.. doctest:: - - >>> message_hash = '0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750' - >>> signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c' - >>> w3.eth.account.recoverHash(message_hash, signature=signature) - '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' - -Prepare message for ecrecover in Solidity --------------------------------------------- - -Let's say you want a contract to validate a signed message, -like if you're making payment channels, and you want to -validate the value in Remix or web3.js. - -You might have produced the signed_message locally, as in -`Sign a Message`_. If so, this will prepare it for Solidity: - -.. doctest:: - - >>> from web3 import Web3 - - # ecrecover in Solidity expects v as a native uint8, but r and s as left-padded bytes32 - # Remix / web3.js expect r and s to be encoded to hex - # This convenience method will do the pad & hex for us: - >>> def to_32byte_hex(val): - ... return Web3.toHex(Web3.toBytes(val).rjust(32, b'\0')) - - >>> ec_recover_args = (msghash, v, r, s) = ( - ... Web3.toHex(signed_message.messageHash), - ... signed_message.v, - ... to_32byte_hex(signed_message.r), - ... to_32byte_hex(signed_message.s), - ... ) - >>> ec_recover_args - ('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750', - 28, - '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', - '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') - -Instead, you might have received a message and a signature encoded to hex. Then -this will prepare it for Solidity: - -.. doctest:: - - >>> from web3 import Web3 - >>> from eth_account.messages import encode_defunct, _hash_eip191_message - - >>> hex_message = '0x49e299a55346' - >>> hex_signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c' - - # ecrecover in Solidity expects an encoded version of the message - - # - encode the message - >>> message = encode_defunct(hexstr=hex_message) - - # - hash the message explicitly - >>> message_hash = _hash_eip191_message(message) - - # Remix / web3.js expect the message hash to be encoded to a hex string - >>> hex_message_hash = Web3.toHex(message_hash) - - # ecrecover in Solidity expects the signature to be split into v as a uint8, - # and r, s as a bytes32 - # Remix / web3.js expect r and s to be encoded to hex - >>> sig = Web3.toBytes(hexstr=hex_signature) - >>> v, hex_r, hex_s = Web3.toInt(sig[-1]), Web3.toHex(sig[:32]), Web3.toHex(sig[32:64]) - - # ecrecover in Solidity takes the arguments in order = (msghash, v, r, s) - >>> ec_recover_args = (hex_message_hash, v, hex_r, hex_s) - >>> ec_recover_args - ('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750', - 28, - '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', - '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') - - -Verify a message with ecrecover in Solidity ---------------------------------------------- - -Create a simple ecrecover contract in `Remix `_: - -.. code-block:: none - - pragma solidity ^0.4.19; - - contract Recover { - function ecr (bytes32 msgh, uint8 v, bytes32 r, bytes32 s) public pure - returns (address sender) { - return ecrecover(msgh, v, r, s); - } - } - -Then call ecr with these arguments from `Prepare message for ecrecover in Solidity`_ in Remix, -``"0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750", 28, "0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3", "0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce"`` - -The message is verified, because we get the correct sender of -the message back in response: ``0x5ce9454909639d2d17a3f753ce7d93fa0b9ab12e``. - -.. _local-sign-transaction: - -Sign a Transaction ------------------------- - -Create a transaction, sign it locally, and then send it to your node for broadcasting, -with :meth:`~web3.eth.Eth.sendRawTransaction`. - -.. doctest:: - - >>> transaction = { - ... 'to': '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', - ... 'value': 1000000000, - ... 'gas': 2000000, - ... 'gasPrice': 234567897654321, - ... 'nonce': 0, - ... 'chainId': 1 - ... } - >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' - >>> signed = w3.eth.account.sign_transaction(transaction, key) - >>> signed.rawTransaction - HexBytes('0xf86a8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a009ebb6ca057a0535d6186462bc0b465b561c94a295bdb0621fc19208ab149a9ca0440ffd775ce91a833ab410777204d5341a6f9fa91216a6f3ee2c051fea6a0428') - >>> signed.hash - HexBytes('0xd8f64a42b57be0d565f385378db2f6bf324ce14a594afc05de90436e9ce01f60') - >>> signed.r - 4487286261793418179817841024889747115779324305375823110249149479905075174044 - >>> signed.s - 30785525769477805655994251009256770582792548537338581640010273753578382951464 - >>> signed.v - 37 - - # When you run sendRawTransaction, you get back the hash of the transaction: - >>> w3.eth.sendRawTransaction(signed.rawTransaction) # doctest: +SKIP - '0xd8f64a42b57be0d565f385378db2f6bf324ce14a594afc05de90436e9ce01f60' - -Sign a Contract Transaction ------------------------------------ - -To sign a transaction locally that will invoke a smart contract: - -#. Initialize your :meth:`Contract ` object -#. Build the transaction -#. Sign the transaction, with :meth:`w3.eth.account.sign_transaction() - ` -#. Broadcast the transaction with :meth:`~web3.eth.Eth.sendRawTransaction` - -.. testsetup:: - - import json - - nonce = 0 - - EIP20_ABI = json.loads('[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]') # noqa: 501 - - -.. doctest:: - - # When running locally, execute the statements found in the file linked below to load the EIP20_ABI variable. - # See: https://github.com/carver/ethtoken.py/blob/v0.0.1-alpha.4/ethtoken/abi.py - - >>> from web3.auto import w3 - - >>> unicorns = w3.eth.contract(address="0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", abi=EIP20_ABI) - - >>> nonce = w3.eth.getTransactionCount('0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E') # doctest: +SKIP - - # Build a transaction that invokes this contract's function, called transfer - >>> unicorn_txn = unicorns.functions.transfer( - ... '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', - ... 1, - ... ).buildTransaction({ - ... 'chainId': 1, - ... 'gas': 70000, - ... 'gasPrice': w3.toWei('1', 'gwei'), - ... 'nonce': nonce, - ... }) - - >>> unicorn_txn - {'value': 0, - 'chainId': 1, - 'gas': 70000, - 'gasPrice': 1000000000, - 'nonce': 0, - 'to': '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', - 'data': '0xa9059cbb000000000000000000000000fb6916095ca1df60bb79ce92ce3ea74c37c5d3590000000000000000000000000000000000000000000000000000000000000001'} - - >>> private_key = b"\xb2\\}\xb3\x1f\xee\xd9\x12''\xbf\t9\xdcv\x9a\x96VK-\xe4\xc4rm\x03[6\xec\xf1\xe5\xb3d" - >>> signed_txn = w3.eth.account.sign_transaction(unicorn_txn, private_key=private_key) - >>> signed_txn.hash - HexBytes('0x4795adc6a719fa64fa21822630c0218c04996e2689ded114b6553cef1ae36618') - >>> signed_txn.rawTransaction - HexBytes('0xf8a980843b9aca008301117094fb6916095ca1df60bb79ce92ce3ea74c37c5d35980b844a9059cbb000000000000000000000000fb6916095ca1df60bb79ce92ce3ea74c37c5d359000000000000000000000000000000000000000000000000000000000000000125a00fb532eea06b8f17d858d82ad61986efd0647124406be65d359e96cac3e004f0a02e5d7ffcfb7a6073a723be38e6733f353cf9367743ae94e2ccd6f1eba37116f4') - >>> signed_txn.r - 7104843568152743554992057394334744036860247658813231830421570918634460546288 - >>> signed_txn.s - 20971591154030974221209741174186570949918731455961098911091818811306894497524 - >>> signed_txn.v - 37 - - >>> w3.eth.sendRawTransaction(signed_txn.rawTransaction) # doctest: +SKIP - - # When you run sendRawTransaction, you get the same result as the hash of the transaction: - >>> w3.toHex(w3.keccak(signed_txn.rawTransaction)) - '0x4795adc6a719fa64fa21822630c0218c04996e2689ded114b6553cef1ae36618' diff --git a/docs/web3.eth.rst b/docs/web3.eth.rst deleted file mode 100644 index 123650d9f9..0000000000 --- a/docs/web3.eth.rst +++ /dev/null @@ -1,1071 +0,0 @@ -web3.eth API -============= - -.. py:module:: web3.eth - -.. py:class:: Eth - -The ``web3.eth`` object exposes the following properties and methods to -interact with the RPC APIs under the ``eth_`` namespace. - -Often, when a property or method returns a mapping of keys to values, it -will return an ``AttributeDict`` which acts like a ``dict`` but you can -access the keys as attributes and cannot modify its fields. For example, -you can find the latest block number in these two ways: - - .. code-block:: python - - >>> block = web3.eth.getBlock('latest') - AttributeDict({ - 'hash': '0xe8ad537a261e6fff80d551d8d087ee0f2202da9b09b64d172a5f45e818eb472a', - 'number': 4022281, - # ... etc ... - }) - - >>> block['number'] - 4022281 - >>> block.number - 4022281 - - >>> block.number = 4022282 - Traceback # ... etc ... - TypeError: This data is immutable -- create a copy instead of modifying - - -Properties ----------- - -The following properties are available on the ``web3.eth`` namespace. - - -.. py:attribute:: Eth.defaultAccount - - The ethereum address that will be used as the default ``from`` address for - all transactions. - - -.. py:attribute:: Eth.defaultBlock - - The default block number that will be used for any RPC methods that accept - a block identifier. Defaults to ``'latest'``. - - -.. py:attribute:: Eth.syncing - - * Delegates to ``eth_syncing`` RPC Method - - Returns either ``False`` if the node is not syncing or a dictionary - showing sync status. - - .. code-block:: python - - >>> web3.eth.syncing - AttributeDict({ - 'currentBlock': 2177557, - 'highestBlock': 2211611, - 'knownStates': 0, - 'pulledStates': 0, - 'startingBlock': 2177365, - }) - - -.. py:attribute:: Eth.coinbase - - * Delegates to ``eth_coinbase`` RPC Method - - Returns the current *Coinbase* address. - - .. code-block:: python - - >>> web3.eth.coinbase - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - - -.. py:attribute:: Eth.mining - - * Delegates to ``eth_mining`` RPC Method - - Returns boolean as to whether the node is currently mining. - - .. code-block:: python - - >>> web3.eth.mining - False - - -.. py:attribute:: Eth.hashrate - - * Delegates to ``eth_hashrate`` RPC Method - - Returns the current number of hashes per second the node is mining with. - - .. code-block:: python - - >>> web3.eth.hashrate - 906 - - -.. py:attribute:: Eth.gasPrice - - * Delegates to ``eth_gasPrice`` RPC Method - - Returns the current gas price in Wei. - - .. code-block:: python - - >>> web3.eth.gasPrice - 20000000000 - - -.. py:attribute:: Eth.accounts - - * Delegates to ``eth_accounts`` RPC Method - - Returns the list of known accounts. - - .. code-block:: python - - >>> web3.eth.accounts - ['0xd3CdA913deB6f67967B99D67aCDFa1712C293601'] - - -.. py:attribute:: Eth.blockNumber - - * Delegates to ``eth_blockNumber`` RPC Method - - Returns the number of the most recent block - - .. code-block:: python - - >>> web3.eth.blockNumber - 2206939 - - -.. py:attribute:: Eth.protocolVersion - - * Delegates to ``eth_protocolVersion`` RPC Method - - Returns the id of the current Ethereum protocol version. - - .. code-block:: python - - >>> web3.eth.protocolVersion - '63' - - -.. py:attribute:: Eth.chainId - - * Delegates to ``eth_chainId`` RPC Method - - Returns an integer value for the currently configured "Chain Id" value introduced in `EIP-155 `_. Returns ``None`` if no Chain Id is available. - - .. code-block:: python - - >>> web3.eth.chainId - 61 - - -Methods -------- - -The following methods are available on the ``web3.eth`` namespace. - - -.. py:method:: Eth.getBalance(account, block_identifier=eth.defaultBlock) - - * Delegates to ``eth_getBalance`` RPC Method - - Returns the balance of the given ``account`` at the block specified by - ``block_identifier``. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - >>> web3.eth.getBalance('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - 77320681768999138915 - - -.. py:method:: Eth.getStorageAt(account, position, block_identifier=eth.defaultBlock) - - * Delegates to ``eth_getStorageAt`` RPC Method - - Returns the value from a storage position for the given ``account`` at the - block specified by ``block_identifier``. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - >>> web3.eth.getStorageAt('0x6C8f2A135f6ed072DE4503Bd7C4999a1a17F824B', 0) - '0x00000000000000000000000000000000000000000000000000120a0b063499d4' - - -.. py:method:: Eth.getProof(account, positions, block_identifier=eth.defaultBlock) - - * Delegates to ``eth_getProof`` RPC Method - - Returns the values from an array of storage positions for the given ``account`` at the - block specified by ``block_identifier``. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - >>> web3.eth.getProof('0x6C8f2A135f6ed072DE4503Bd7C4999a1a17F824B', [0], 3391) - AttributeDict({ - 'address': '0x4CB06C43fcdABeA22541fcF1F856A6a296448B6c', - 'accountProof': ['0xf90211a03841a7ddd65c70c94b8efa79190d00f0ab134b26f18dcad508f60a7e74559d0ba0464b07429a05039e22931492d6c6251a860c018ea390045d596b1ac11b5c7aa7a011f4b89823a03c9c4b5a8ab079ee1bc0e2a83a508bb7a5dc7d7fb4f2e95d3186a0b5f7c51c3b2d51d97f171d2b38a4df1a7c0acc5eb0de46beeff4d07f5ed20e19a0b591a2ce02367eda31cf2d16eca7c27fd44dbf0864b64ea8259ad36696eb2a04a02b646a7552b8392ae94263757f699a27d6e9176b4c06b9fc0a722f893b964795a02df05d68bceb88eebf68aafde61d10ab942097afc1c58b8435ffd3895358a742a0c2f16143c4d1db03276c433696dddb3e9f3b113bcd854b127962262e98f43147a0828820316cc02bfefd899aba41340659fd06df1e0a0796287ec2a4110239f6d2a050496598670b04df7bbff3718887fa36437d6d8c7afb4eff86f76c5c7097dcc4a0c14e9060c6b3784e35b9e6ae2ad2984142a75910ccc89eb89dc1e2f44b6c58c2a009804db571d0ce07913e1cbacc4f1dc4fb8265c936f5c612e3a47e91c64d8e9fa063d96f38b3cb51b1665c6641e25ffe24803f2941e5df79942f6a53b7169647e4a0899f71abb18c6c956118bf567fac629b75f7e9526873e429d3d8abb6dbb58021a00fd717235298742623c0b3cafb3e4bd86c0b5ab1f71097b4dd19f3d6925d758da0096437146c16097f2ccc1d3e910d65a4132803baee2249e72c8bf0bcaaeb37e580', - '0xf90151a097b17a89fd2c03ee98cb6459c08f51b269da5cee46650e84470f62bf83b43efe80a03b269d284a4c3cf8f8deacafb637c6d77f607eec8d75e8548d778e629612310480a01403217a7f1416830c870087c524dabade3985271f6f369a12b010883c71927aa0f592ac54c879817389663be677166f5022943e2fe1b52617a1d15c2f353f27dda0ac8d015a9e668f5877fcc391fae33981c00577096f0455b42df4f8e8089ece24a003ba34a13e2f2fb4bf7096540b42d4955c5269875b9cf0f7b87632585d44c9a580a0b179e3230b07db294473ae57f0170262798f8c551c755b5665ace1215cee10ca80a0552d24252639a6ae775aa1df700ffb92c2411daea7286f158d44081c8172d072a0772a87d08cf38c4c68bfde770968571abd16fd3835cb902486bd2e515d53c12d80a0413774f3d900d2d2be7a3ad999ffa859a471dc03a74fb9a6d8275455f5496a548080', - '0xf869a020d13b52a61d3c1325ce3626a51418adebd6323d4840f1bdd93906359d11c933b846f8440180a01ab7c0b0a2a4bbb5a1495da8c142150891fc64e0c321e1feb70bd5f881951f7ea0551332d96d085185ab4019ad8bcf89c45321e136c261eb6271e574a2edf1461f' - ], - 'balance': 0, - 'codeHash': '0x551332d96d085185ab4019ad8bcf89c45321e136c261eb6271e574a2edf1461f', - 'nonce': 1, - 'storageHash': '0x1ab7c0b0a2a4bbb5a1495da8c142150891fc64e0c321e1feb70bd5f881951f7e', - 'storageProof': [ - AttributeDict({ - 'key': '0x00', - 'value': '0x48656c6c6f00000000000000000000000000000000000000000000000000000a', - 'proof': ['0xf9019180a01ace80e7bed79fbadbe390876bd1a7d9770edf9462049ef8f4b555d05715d53ea049347a3c2eac6525a3fd7e3454dab19d73b4adeb9aa27d29493b9843f3f88814a085079b4abcd07fd4a5d6c52d35f4c4574aecc85830e90c478ca8c18fcbe590de80a02e3f8ad7ea29e784007f51852b9c3e470aef06b11bac32586a8b691134e4c27da064d2157a14bc31f195f73296ea4dcdbe7698edbf3ca81c44bf7730179d98d94ca09e7dc2597c9b7f72ddf84d7eebb0fe2a2fa2ab54fe668cd14fee44d9b40b1a53a0aa5d4acc7ac636d16bc9655556770bc325e1901fb62dc53770ef9110009e080380a0d5fde962bd2fb5326ddc7a9ca7fe0ee47c5bb3227f838b6d73d3299c22457596a08691410eff46b88f929ef649ea25025f62a5362ca8dc8876e5e1f4fc8e79256d80a0673e88d3a8a4616f676793096b5ae87cff931bd20fb8dd466f97809a1126aad8a08b774a45c2273553e2daf4bbc3a8d44fb542ea29b6f125098f79a4d211b3309ca02fed3139c1791269acb9365eddece93e743900eba6b42a6a8614747752ba268f80', - '0xf891808080a0c7d094301e0c54da37b696d85f72de5520b224ab2cf4f045d8db1a3374caf0488080a0fc5581783bfe27fab9423602e1914d719fd71433e9d7dd63c95fe7e58d10c9c38080a0c64f346fc7a21f6679cba8abdf37ca2de8c4fcd8f8bcaedb261b5f77627c93908080808080a0ddef2936a67a3ac7d3d4ff15a935a45f2cc4976c8f0310aed85daf763780e2b480', - '0xf843a0200decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f00000000000000000000000000000000000000000000000000000a' - ] - }) - ] - }) - - * Merkle proof verification using py-trie. - - The following example verifies that the values returned in the AttributeDict are included in the state of given trie ``root``. - - .. code-block:: python - - from eth_utils import ( - keccak, - ) - import rlp - from rlp.sedes import ( - Binary, - big_endian_int, - ) - from trie import ( - HexaryTrie, - ) - from web3._utils.encoding import ( - pad_bytes, - ) - - def format_proof_nodes(proof): - trie_proof = [] - for rlp_node in proof: - trie_proof.append(rlp.decode(bytes(rlp_node))) - return trie_proof - - def verify_eth_getProof(proof, root): - trie_root = Binary.fixed_length(32, allow_empty=True) - hash32 = Binary.fixed_length(32) - - class _Account(rlp.Serializable): - fields = [ - ('nonce', big_endian_int), - ('balance', big_endian_int), - ('storage', trie_root), - ('code_hash', hash32) - ] - acc = _Account( - proof.nonce, proof.balance, proof.storageHash, proof.codeHash - ) - rlp_account = rlp.encode(acc) - trie_key = keccak(bytes.fromhex(proof.address[2:])) - - assert rlp_account == HexaryTrie.get_from_proof( - root, trie_key, format_proof_nodes(proof.accountProof) - ), "Failed to verify account proof {}".format(proof.address) - - for storage_proof in proof.storageProof: - trie_key = keccak(pad_bytes(b'\x00', 32, storage_proof.key)) - root = proof.storageHash - if storage_proof.value == b'\x00': - rlp_value = b'' - else: - rlp_value = rlp.encode(storage_proof.value) - - assert rlp_value == HexaryTrie.get_from_proof( - root, trie_key, format_proof_nodes(storage_proof.proof) - ), "Failed to verify storage proof {}".format(storage_proof.key) - - return True - - block = w3.eth.getBlock(3391) - proof = w3.eth.getProof('0x6C8f2A135f6ed072DE4503Bd7C4999a1a17F824B', [0, 1], 3391) - assert verify_eth_getProof(proof, block.stateRoot) - - -.. py:method:: Eth.getCode(account, block_identifier=eth.defaultBlock) - - * Delegates to ``eth_getCode`` RPC Method - - Returns the bytecode for the given ``account`` at the block specified by - ``block_identifier``. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - # For a contract address. - >>> web3.eth.getCode('0x6C8f2A135f6ed072DE4503Bd7C4999a1a17F824B') - '0x6060604052361561027c5760e060020a60003504630199.....' - # For a private key address. - >>> web3.eth.getCode('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - '0x' - - -.. py:method:: Eth.getBlock(block_identifier=eth.defaultBlock, full_transactions=False) - - * Delegates to ``eth_getBlockByNumber`` or ``eth_getBlockByHash`` RPC Methods - - Returns the block specified by ``block_identifier``. Delegates to - ``eth_getBlockByNumber`` if ``block_identifier`` is an integer or one of - the predefined block parameters ``'latest', 'earliest', 'pending'``, - otherwise delegates to ``eth_getBlockByHash``. - - If ``full_transactions`` is ``True`` then the ``'transactions'`` key will - contain full transactions objects. Otherwise it will be an array of - transaction hashes. - - .. code-block:: python - - >>> web3.eth.getBlock(2000000) - AttributeDict({ - 'difficulty': 49824742724615, - 'extraData': '0xe4b883e5bda9e7a59ee4bb99e9b1bc', - 'gasLimit': 4712388, - 'gasUsed': 21000, - 'hash': '0xc0f4906fea23cf6f3cce98cb44e8e1449e455b28d684dfa9ff65426495584de6', - 'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - 'miner': '0x61c808d82a3ac53231750dadc13c777b59310bd9', - 'nonce': '0x3b05c6d5524209f1', - 'number': 2000000, - 'parentHash': '0x57ebf07eb9ed1137d41447020a25e51d30a0c272b5896571499c82c33ecb7288', - 'receiptRoot': '0x84aea4a7aad5c5899bd5cfc7f309cc379009d30179316a2a7baa4a2ea4a438ac', - 'sha3Uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - 'size': 650, - 'stateRoot': '0x96dbad955b166f5119793815c36f11ffa909859bbfeb64b735cca37cbf10bef1', - 'timestamp': 1470173578, - 'totalDifficulty': 44010101827705409388, - 'transactions': ['0xc55e2b90168af6972193c1f86fa4d7d7b31a29c156665d15b9cd48618b5177ef'], - 'transactionsRoot': '0xb31f174d27b99cdae8e746bd138a01ce60d8dd7b224f7c60845914def05ecc58', - 'uncles': [], - }) - - -.. py:method:: Eth.getBlockTransactionCount(block_identifier) - - * Delegates to ``eth_getBlockTransactionCountByNumber`` or - ``eth_getBlockTransactionCountByHash`` RPC Methods - - Returns the number of transactions in the block specified by - ``block_identifier``. Delegates to - ``eth_getBlockTransactionCountByNumber`` if ``block_identifier`` is an - integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to ``eth_getBlockTransactionCountByHash``. - - .. code-block:: python - - >>> web3.eth.getBlockTransactionCount(46147) - 1 - >>> web3.eth.getBlockTransactionCount('0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd') # block 46147 - 1 - - -.. py:method:: Eth.getUncle(block_identifier) - - .. note:: Method to get an Uncle from its hash is not available through - RPC, a possible substitute is the method ``Eth.getUncleByBlock`` - - -.. py:method:: Eth.getUncleByBlock(block_identifier, uncle_index) - - * Delegates to ``eth_getUncleByBlockHashAndIndex`` or - ``eth_getUncleByBlockNumberAndIndex`` RPC methods - - Returns the uncle at the index specified by ``uncle_index`` - from the block specified by ``block_identifier``. Delegates to - ``eth_getUncleByBlockNumberAndIndex`` if ``block_identifier`` is an - integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to - ``eth_getUncleByBlockHashAndIndex``. - - .. code-block:: python - - >>> web3.eth.getUncleByBlock(56160, 0) - AttributeDict({ - 'author': '0xbe4532e1b1db5c913cf553be76180c1777055403', - 'difficulty': '0x17dd9ca0afe', - 'extraData': '0x476574682f686261722f76312e302e312f6c696e75782f676f312e342e32', - 'gasLimit': '0x2fefd8', - 'gasUsed': '0x0', - 'hash': '0xc78c35720d930f9ef34b4e6fb9d02ffec936f9b02a8f0fa858456e4afd4d5614', - 'logsBloom':'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - 'miner': '0xbe4532e1b1db5c913cf553be76180c1777055403', - 'mixHash': '0x041e14603f35a82f6023802fec96ef760433292434a39787514f140950597e5e', - 'nonce': '0x5d2b7e3f1af09995', - 'number': '0xdb5e', - 'parentHash': '0xcc30e8a9b15c548d5bf113c834143a8f0e1909fbfea96b2a208dc154293a78cf', - 'receiptsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - 'sealFields': ['0xa0041e14603f35a82f6023802fec96ef760433292434a39787514f140950597e5e', '0x885d2b7e3f1af09995'], - 'sha3Uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - 'size': None, 'stateRoot': '0x8ce2b1bf8e25a06a8ca34c647ff5fd0fa48ac725cc07f657ae1645ab8ef68c91', - 'timestamp': '0x55c6a972', - 'totalDifficulty': '0xce4c4f0a0b810b', - 'transactions': [], - 'transactionsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - 'uncles': [] - }) - - # You can also refer to the block by hash: - >>> web3.eth.getUncleByBlock('0x685b2226cbf6e1f890211010aa192bf16f0a0cba9534264a033b023d7367b845', 0) - AttributeDict({ - ... - }) - - -.. py:method:: Eth.getTransaction(transaction_hash) - - * Delegates to ``eth_getTransactionByHAsh`` RPC Method - - Returns the transaction specified by ``transaction_hash`` - - .. code-block:: python - - >>> web3.eth.getTransaction('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') - AttributeDict({ - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gas': 21000, - 'gasPrice': 50000000000000, - 'hash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'input': '0x', - 'nonce': 0, - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionIndex': 0, - 'value': 31337, - }) - - -.. py:method:: Eth.getTransactionFromBlock(block_identifier, transaction_index) - - .. note:: This method is deprecated in EIP 1474. - - -.. py:method:: Eth.getTransactionByBlock(block_identifier, transaction_index) - - * Delegates to ``eth_getTransactionByBlockNumberAndIndex`` or - ``eth_getTransactionByBlockHashAndIndex`` RPC Methods - - Returns the transaction at the index specified by ``transaction_index`` - from the block specified by ``block_identifier``. Delegates to - ``eth_getTransactionByBlockNumberAndIndex`` if ``block_identifier`` is an - integer or one of the predefined block parameters ``'latest', 'earliest', - 'pending'``, otherwise delegates to - ``eth_getTransactionByBlockHashAndIndex``. - - .. code-block:: python - - >>> web3.eth.getTransactionFromBlock(46147, 0) - AttributeDict({ - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gas': 21000, - 'gasPrice': 50000000000000, - 'hash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'input': '0x', - 'nonce': 0, - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionIndex': 0, - 'value': 31337, - }) - >>> web3.eth.getTransactionFromBlock('0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', 0) - AttributeDict({ - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gas': 21000, - 'gasPrice': 50000000000000, - 'hash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'input': '0x', - 'nonce': 0, - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionIndex': 0, - 'value': 31337, - }) - - -.. py:method:: Eth.waitForTransactionReceipt(transaction_hash, timeout=120, poll_latency=0.1) - - Waits for the transaction specified by ``transaction_hash`` to be included in a block, then - returns its transaction receipt. - - Optionally, specify a ``timeout`` in seconds. If timeout elapses before the transaction - is added to a block, then :meth:`~Eth.waitForTransactionReceipt` raises a - :class:`web3.exceptions.TimeExhausted` exception. - - .. code-block:: python - - >>> web3.eth.waitForTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') - # If transaction is not yet in a block, time passes, while the thread sleeps... - # ... - # Then when the transaction is added to a block, its receipt is returned: - AttributeDict({ - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'contractAddress': None, - 'cumulativeGasUsed': 21000, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gasUsed': 21000, - 'logs': [], - 'root': '96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957', - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionHash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'transactionIndex': 0, - }) - - -.. py:method:: Eth.getTransactionReceipt(transaction_hash) - - * Delegates to ``eth_getTransactionReceipt`` RPC Method - - Returns the transaction receipt specified by ``transaction_hash``. If the transaction has not yet been mined returns ``None`` - - .. code-block:: python - - >>> web3.eth.getTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') # not yet mined - None - # wait for it to be mined.... - >>> web3.eth.getTransactionReceipt('0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060') - AttributeDict({ - 'blockHash': '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd', - 'blockNumber': 46147, - 'contractAddress': None, - 'cumulativeGasUsed': 21000, - 'from': '0xA1E4380A3B1f749673E270229993eE55F35663b4', - 'gasUsed': 21000, - 'logs': [], - 'root': '96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957', - 'to': '0x5DF9B87991262F6BA471F09758CDE1c0FC1De734', - 'transactionHash': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', - 'transactionIndex': 0, - }) - - -.. py:method:: Eth.getTransactionCount(account, block_identifier=web3.eth.defaultBlock) - - * Delegates to ``eth_getTransactionCount`` RPC Method - - Returns the number of transactions that have been sent from ``account`` as - of the block specified by ``block_identifier``. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - >>> web3.eth.getTransactionCount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - 340 - - -.. py:method:: Eth.sendTransaction(transaction) - - * Delegates to ``eth_sendTransaction`` RPC Method - - Signs and sends the given ``transaction`` - - The ``transaction`` parameter should be a dictionary with the following fields. - - * ``from``: ``bytes or text``, checksum address or ENS name - (optional, default: - ``web3.eth.defaultAccount``) The address the transaction is send from. - * ``to``: ``bytes or text``, checksum address or ENS name - (optional when creating new - contract) The address the transaction is directed to. - * ``gas``: ``integer`` - (optional, default: 90000) Integer of the gas - provided for the transaction execution. It will return unused gas. - * ``gasPrice``: ``integer`` - (optional, default: To-Be-Determined) Integer - of the gasPrice used for each paid gas - * ``value``: ``integer`` - (optional) Integer of the value send with this - transaction - * ``data``: ``bytes or text`` - The compiled code of a contract OR the hash - of the invoked method signature and encoded parameters. For details see - `Ethereum Contract ABI `_. - * ``nonce``: ``integer`` - (optional) Integer of a nonce. This allows to - overwrite your own pending transactions that use the same nonce. - - If the ``transaction`` specifies a ``data`` value but does not specify - ``gas`` then the ``gas`` value will be populated using the - :meth:`~web3.eth.Eth.estimateGas()` function with an additional buffer of ``100000`` - gas up to the ``gasLimit`` of the latest block. In the event that the - value returned by :meth:`~web3.eth.Eth.estimateGas()` method is greater than the - ``gasLimit`` a ``ValueError`` will be raised. - - - .. code-block:: python - - >>> web3.eth.sendTransaction({'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'from': web3.eth.coinbase, 'value': 12345}) - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' - - -.. py:method:: Eth.signTransaction() - - * Delegates to ``eth_signTransaction`` RPC Method. - - Returns a transaction that's been signed by the node's private key, but not yet submitted. - The signed tx can be submitted with `Eth.sendRawTransaction`` - - .. code-block:: python - - >>> signed_txn = w3.eth.signTransaction(dict( - nonce=w3.eth.getTransactionCount(w3.eth.coinbase), - gasPrice=w3.eth.gasPrice, - gas=100000, - to='0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - value=1, - data=b'', - ) - ) - b"\xf8d\x80\x85\x040\xe24\x00\x82R\x08\x94\xdcTM\x1a\xa8\x8f\xf8\xbb\xd2\xf2\xae\xc7T\xb1\xf1\xe9\x9e\x18\x12\xfd\x01\x80\x1b\xa0\x11\r\x8f\xee\x1d\xe5=\xf0\x87\x0en\xb5\x99\xed;\xf6\x8f\xb3\xf1\xe6,\x82\xdf\xe5\x97lF|\x97%;\x15\xa04P\xb7=*\xef \t\xf0&\xbc\xbf\tz%z\xe7\xa3~\xb5\xd3\xb7=\xc0v\n\xef\xad+\x98\xe3'" # noqa: E501 - - -.. py:method:: Eth.sendRawTransaction(raw_transaction) - - * Delegates to ``eth_sendRawTransaction`` RPC Method - - Sends a signed and serialized transaction. Returns the transaction hash as a HexBytes object. - - .. code-block:: python - - >>> signed_txn = w3.eth.account.signTransaction(dict( - nonce=w3.eth.getTransactionCount(w3.eth.coinbase), - gasPrice=w3.eth.gasPrice, - gas=100000, - to='0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - value=12345, - data=b'', - ), - private_key_for_senders_account, - ) - >>> w3.eth.sendRawTransaction(signed_txn.rawTransaction) - HexBytes('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') - - -.. py:method:: Eth.replaceTransaction(transaction_hash, new_transaction) - - * Delegates to ``eth_sendTransaction`` RPC Method - - Sends a transaction that replaces the transaction with ``transaction_hash``. - - The ``transaction_hash`` must be the hash of a pending transaction. - - The ``new_transaction`` parameter should be a dictionary with transaction fields - as required by :meth:`~web3.eth.Eth.sendTransaction`. It will be used to entirely - replace the transaction of ``transaction_hash`` without using any of the pending - transaction's values. - - If the ``new_transaction`` specifies a ``nonce`` value, it must match the pending - transaction's nonce. - - If the ``new_transaction`` specifies a ``gasPrice`` value, it must be greater than - the pending transaction's ``gasPrice``. - - If the ``new_transaction`` does not specify a ``gasPrice`` value, the highest of the - following 2 values will be used: - - * The pending transaction's ``gasPrice`` * 1.1 - This is typically the minimum - ``gasPrice`` increase a node requires before it accepts a replacement transaction. - * The ``gasPrice`` as calculated by the current gas price strategy(See :ref:`Gas_Price`). - - This method returns the transaction hash of the replacement transaction. - - .. code-block:: python - - >>> tx = web3.eth.sendTransaction({ - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'from': web3.eth.coinbase, - 'value': 1000 - }) - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' - >>> web3.eth.replaceTransaction('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', { - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'from': web3.eth.coinbase, - 'value': 2000 - }) - - -.. py:method:: Eth.modifyTransaction(transaction_hash, **transaction_params) - - * Delegates to ``eth_sendTransaction`` RPC Method - - Sends a transaction that modifies the transaction with ``transaction_hash``. - - ``transaction_params`` are keyword arguments that correspond to valid transaction - parameters as required by :meth:`~web3.eth.Eth.sendTransaction`. The parameter values - will override the pending transaction's values to create the replacement transaction - to send. - - The same validation and defaulting rules of :meth:`~web3.eth.Eth.replaceTransaction` apply. - - This method returns the transaction hash of the newly modified transaction. - - .. code-block:: python - - >>> tx = web3.eth.sendTransaction({ - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'from': web3.eth.coinbase, - 'value': 1000 - }) - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' - >>> web3.eth.modifyTransaction('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', value=2000) - - -.. py:method:: Eth.sign(account, data=None, hexstr=None, text=None) - - * Delegates to ``eth_sign`` RPC Method - - Caller must specify exactly one of: ``data``, ``hexstr``, or ``text``. - - Signs the given data with the private key of the given ``account``. - The account must be unlocked. - - ``account`` may be a checksum address or an ENS name - - .. code-block:: python - - >>> web3.eth.sign( - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - text='some-text-tö-sign') - '0x1a8bbe6eab8c72a219385681efefe565afd3accee35f516f8edf5ae82208fbd45a58f9f9116d8d88ba40fcd29076d6eada7027a3b412a9db55a0164547810cc401' - - >>> web3.eth.sign( - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - data=b'some-text-t\xc3\xb6-sign') - '0x1a8bbe6eab8c72a219385681efefe565afd3accee35f516f8edf5ae82208fbd45a58f9f9116d8d88ba40fcd29076d6eada7027a3b412a9db55a0164547810cc401' - - >>> web3.eth.sign( - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - hexstr='0x736f6d652d746578742d74c3b62d7369676e') - '0x1a8bbe6eab8c72a219385681efefe565afd3accee35f516f8edf5ae82208fbd45a58f9f9116d8d88ba40fcd29076d6eada7027a3b412a9db55a0164547810cc401' - - -.. py:method:: Eth.signTypedData(account, jsonMessage) - - * Delegates to ``eth_signTypedData`` RPC Method - - Please note that the ``jsonMessage`` argument is the loaded JSON Object - and **NOT** the JSON String itself. - - Signs the ``Structured Data`` (or ``Typed Data``) with the private key of the given ``account``. - The account must be unlocked. - - ``account`` may be a checksum address or an ENS name - - -.. py:method:: Eth.call(transaction, block_identifier=web3.eth.defaultBlock) - - * Delegates to ``eth_call`` RPC Method - - Executes the given transaction locally without creating a new transaction - on the blockchain. Returns the return value of the executed contract. - - The ``transaction`` parameter is handled in the same manner as the - :meth:`~web3.eth.Eth.sendTransaction()` method. - - .. code-block:: python - - >>> myContract.functions.setVar(1).transact() - HexBytes('0x79af0c7688afba7588c32a61565fd488c422da7b5773f95b242ea66d3d20afda') - >>> myContract.functions.getVar().call() - 1 - # The above call equivalent to the raw call: - >>> we3.eth.call({'value': 0, 'gas': 21736, 'gasPrice': 1, 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9', 'data': '0x477a5c98'}) - HexBytes('0x0000000000000000000000000000000000000000000000000000000000000001') - - In most cases it is better to make contract function call through the :py:class:`web3.contract.Contract` interface. - - -.. py:method:: Eth.estimateGas(transaction, block_identifier=None) - - * Delegates to ``eth_estimateGas`` RPC Method - - Executes the given transaction locally without creating a new transaction - on the blockchain. Returns amount of gas consumed by execution which can - be used as a gas estimate. - - The ``transaction`` and ``block_identifier`` parameters are handled in the - same manner as the :meth:`~web3.eth.call()` method. - - .. code-block:: python - - >>> web3.eth.estimateGas({'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'from': web3.eth.coinbase, 'value': 12345}) - 21000 - - .. note:: - The parameter ``block_identifier`` is not enabled in geth nodes, - hence passing a value of ``block_identifier`` when connected to a geth - nodes would result in an error like: ``ValueError: {'code': -32602, 'message': 'too many arguments, want at most 1'}`` - - -.. py:method:: Eth.generateGasPrice(transaction_params=None) - - Uses the selected gas price strategy to calculate a gas price. This method - returns the gas price denominated in wei. - - The `transaction_params` argument is optional however some gas price strategies - may require it to be able to produce a gas price. - - .. code-block:: python - - >>> Web3.eth.generateGasPrice() - 20000000000 - - .. note:: - For information about how gas price can be customized in web3 see - :ref:`Gas_Price`. - -.. py:method:: Eth.setGasPriceStrategy(gas_price_strategy) - - Set the selected gas price strategy. It must be a method of the signature - ``(web3, transaction_params)`` and return a gas price denominated in wei. - -Filters -------- - -The following methods are available on the ``web3.eth`` object for interacting -with the filtering API. - - -.. py:method:: Eth.filter(filter_params) - - * Delegates to ``eth_newFilter``, ``eth_newBlockFilter``, and - ``eth_newPendingTransactionFilter`` RPC Methods. - - This method delegates to one of three RPC methods depending on the value of - ``filter_params``. - - * If ``filter_params`` is the string ``'pending'`` then a new filter is - registered using the ``eth_newPendingTransactionFilter`` RPC method. - This will create a new filter that will be called for each new unmined - transaction that the node receives. - * If ``filter_params`` is the string ``'latest'`` then a new filter is - registered using the ``eth_newBlockFilter`` RPC method. This will create - a new filter that will be called each time the node receives a new block. - * If ``filter_params`` is a dictionary then a new filter is registered - using the ``eth_newFilter`` RPC method. This will create a new filter - that will be called for all log entries that match the provided - ``filter_params``. - - This method returns a ``web3.utils.filters.Filter`` object which can then - be used to either directly fetch the results of the filter or to register - callbacks which will be called with each result of the filter. - - When creating a new log filter, the ``filter_params`` should be a - dictionary with the following keys. - - * ``fromBlock``: ``integer/tag`` - (optional, default: "latest") Integer - block number, or "latest" for the last mined block or "pending", - "earliest" for not yet mined transactions. - * ``toBlock``: ``integer/tag`` - (optional, default: "latest") Integer - block number, or "latest" for the last mined block or "pending", - "earliest" for not yet mined transactions. - * ``address``: ``string`` or list of ``strings``, each 20 Bytes - - (optional) Contract address or a list of addresses from which logs should - originate. - * ``topics``: list of 32 byte ``strings`` or ``null`` - (optional) Array of - topics that should be used for filtering. Topics are order-dependent. - This parameter can also be a list of topic lists in which case filtering - will match any of the provided topic arrays. - - - See :doc:`./filters` for more information about filtering. - - .. code-block:: python - - >>> web3.eth.filter('latest') - - >>> web3.eth.filter('pending') - - >>> web3.eth.filter({'fromBlock': 1000000, 'toBlock': 1000100, 'address': '0x6C8f2A135f6ed072DE4503Bd7C4999a1a17F824B'}) - - -.. py:method:: Eth.getFilterChanges(self, filter_id) - - * Delegates to ``eth_getFilterChanges`` RPC Method. - - Returns all new entries which occurred since the last call to this method - for the given ``filter_id`` - - .. code-block:: python - - >>> filt = web3.eth.filter() - >>> web3.eth.getFilterChanges(filt.filter_id) - [ - { - 'address': '0xDc3A9Db694BCdd55EBaE4A89B22aC6D12b3F0c24', - 'blockHash': '0xb72256286ca528e09022ffd408856a73ef90e7216ac560187c6e43b4c4efd2f0', - 'blockNumber': 2217196, - 'data': '0x0000000000000000000000000000000000000000000000000000000000000001', - 'logIndex': 0, - 'topics': ['0xe65b00b698ba37c614af350761c735c5f4a82b4ab365a1f1022d49d9dfc8e930', - '0x000000000000000000000000754c50465885f1ed1fa1a55b95ee8ecf3f1f4324', - '0x296c7fb6ccafa3e689950b947c2895b07357c95b066d5cdccd58c301f41359a3'], - 'transactionHash': '0xfe1289fd3915794b99702202f65eea2e424b2f083a12749d29b4dd51f6dce40d', - 'transactionIndex': 1, - }, - ... - ] - - -.. py:method:: Eth.getFilterLogs(self, filter_id) - - * Delegates to ``eth_getFilterLogs`` RPC Method. - - Returns all entries for the given ``filter_id`` - - .. code-block:: python - - >>> filt = web3.eth.filter() - >>> web3.eth.getFilterLogs(filt.filter_id) - [ - { - 'address': '0xDc3A9Db694BCdd55EBaE4A89B22aC6D12b3F0c24', - 'blockHash': '0xb72256286ca528e09022ffd408856a73ef90e7216ac560187c6e43b4c4efd2f0', - 'blockNumber': 2217196, - 'data': '0x0000000000000000000000000000000000000000000000000000000000000001', - 'logIndex': 0, - 'topics': ['0xe65b00b698ba37c614af350761c735c5f4a82b4ab365a1f1022d49d9dfc8e930', - '0x000000000000000000000000754c50465885f1ed1fa1a55b95ee8ecf3f1f4324', - '0x296c7fb6ccafa3e689950b947c2895b07357c95b066d5cdccd58c301f41359a3'], - 'transactionHash': '0xfe1289fd3915794b99702202f65eea2e424b2f083a12749d29b4dd51f6dce40d', - 'transactionIndex': 1, - }, - ... - ] - - -.. py:method:: Eth.uninstallFilter(self, filter_id) - - * Delegates to ``eth_uninstallFilter`` RPC Method. - - Uninstalls the filter specified by the given ``filter_id``. Returns - boolean as to whether the filter was successfully uninstalled. - - .. code-block:: python - - >>> filt = web3.eth.filter() - >>> web3.eth.uninstallFilter(filt.filter_id) - True - >>> web3.eth.uninstallFilter(filt.filter_id) - False # already uninstalled. - - -.. py:method:: Eth.getLogs(filter_params) - - This is the equivalent of: creating a new - filter, running :meth:`~Eth.getFilterLogs`, and then uninstalling the filter. See - :meth:`~Eth.filter` for details on allowed filter parameters. - - -.. py:method:: Eth.submitHashrate(hashrate, nodeid) - - * Delegates to ``eth_submitHashrate`` RPC Method - - .. code-block:: python - - >>> node_id = '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c' - >>> web3.eth.submitHashrate(5000, node_id) - True - - -.. py:method:: Eth.submitWork(nonce, pow_hash, mix_digest) - - * Delegates to ``eth_submitWork`` RPC Method. - - .. code-block:: python - - >>> web3.eth.submitWork( - 1, - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000', - ) - True - - -Contracts ---------- - -.. py:method:: Eth.contract(address=None, contract_name=None, ContractFactoryClass=Contract, **contract_factory_kwargs) - - If ``address`` is provided, then this method will return an instance of the - contract defined by ``abi``. The address may be a checksum string, - or an ENS name like ``'mycontract.eth'``. - - .. code-block:: python - - from web3 import Web3 - - w3 = Web3(...) - - contract = w3.eth.contract(address='0x000000000000000000000000000000000000dEaD', abi=...) - - # alternatively: - contract = w3.eth.contract(address='mycontract.eth', abi=...) - - .. note:: - - If you use an ENS name to initialize a contract, the contract will be looked up by - name on each use. If the name could ever change maliciously, first - :ref:`ens_get_address`, and then create the contract with the checksum address. - - - If ``address`` is *not* provided, the newly created contract class will be returned. That - class will then be initialized by supplying the address. - - .. code-block:: python - - from web3 import Web3 - - w3 = Web3(...) - - Contract = w3.eth.contract(abi=...) - - # later, initialize contracts with the same metadata at different addresses: - contract1 = Contract(address='0x000000000000000000000000000000000000dEaD') - contract2 = Contract(address='mycontract.eth') - - ``contract_name`` will be used as the name of the contract class. If it is - ``None`` then the name of the ``ContractFactoryClass`` will be used. - - ``ContractFactoryClass`` will be used as the base Contract class. - - The following arguments are accepted for contract class creation. - - - ``abi`` - - ``asm`` - - ``ast`` - - ``bytecode`` - - ``bytecode_runtime`` - - ``clone_bin`` - - ``dev_doc`` - - ``interface`` - - ``metadata`` - - ``opcodes`` - - ``src_map`` - - ``src_map_runtime`` - - ``user_doc`` - - See :doc:`./contracts` for more information about how to use contracts. - -.. py:method:: Eth.setContractFactory(contractFactoryClass) - - Modify the default contract factory from ``Contract`` to ``contractFactoryClass``. - Future calls to ``Eth.contract()`` will then default to ``contractFactoryClass``. - - An example of an alternative Contract Factory is ``ConciseContract``. diff --git a/docs/web3.geth.rst b/docs/web3.geth.rst index 9caab0e85f..cbeb2b4613 100644 --- a/docs/web3.geth.rst +++ b/docs/web3.geth.rst @@ -1,836 +1,719 @@ -Geth API -======== - -.. py:module:: web3.geth - -The ``web3.geth`` object exposes modules that enable you to interact with the JSON-RPC endpoints supported by `Geth `_ that are not defined in the standard set of Ethereum JSONRPC endpoints according to `EIP 1474 `_. - -GethAdmin API -~~~~~~~~~~~~~ - -The following methods are available on the ``web3.geth.admin`` namespace. - -.. py:module:: web3.geth.admin - -The ``web3.geth.admin`` object exposes methods to interact with the RPC APIs under the -``admin_`` namespace that are supported by the Geth client. - -.. py:method:: datadir() - - * Delegates to ``admin_datadir`` RPC Method - - Returns the system path of the node's data directory. - - .. code-block:: python - - >>> web3.geth.admin.datadir() - '/Users/piper/Library/Ethereum' - - -.. py:method:: node_info() - - * Delegates to ``admin_nodeInfo`` RPC Method - - Returns information about the currently running node. - - .. code-block:: python - - >>> web3.geth.admin.node_info() - { - 'enode': 'enode://e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3@[::]:30303', - 'id': 'e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3', - 'ip': '::', - 'listenAddr': '[::]:30303', - 'name': 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7', - 'ports': {'discovery': 30303, 'listener': 30303}, - 'protocols': { - 'eth': { - 'difficulty': 57631175724744612603, - 'genesis': '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'head': '0xaaef6b9dd0d34088915f4c62b6c166379da2ad250a88f76955508f7cc81fb796', - 'network': 1, - }, - }, - } - - -.. py:method:: nodeInfo() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.admin.node_info()` - - -.. py:method:: peers() - - * Delegates to ``admin_peers`` RPC Method - - Returns the current peers the node is connected to. - - .. code-block:: python - - >>> web3.geth.admin.peers() - [ - { - 'caps': ['eth/63'], - 'id': '146e8e3e2460f1e18939a5da37c4a79f149c8b9837240d49c7d94c122f30064e07e4a42ae2c2992d0f8e7e6f68a30e7e9ad31d524349ec9d17effd2426a37b40', - 'name': 'Geth/v1.4.10-stable/windows/go1.6.2', - 'network': { - 'localAddress': '10.0.3.115:64478', - 'remoteAddress': '72.208.167.127:30303', - }, - 'protocols': { - 'eth': { - 'difficulty': 17179869184, - 'head': '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'version': 63, - }, - } - }, - { - 'caps': ['eth/62', 'eth/63'], - 'id': '76cb6cd3354be081923a90dfd4cda40aa78b307cc3cf4d5733dc32cc171d00f7c08356e9eb2ea47eab5aad7a15a3419b859139e3f762e1e1ebf5a04f530dcef7', - 'name': 'Geth/v1.4.10-stable-5f55d95a/linux/go1.5.1', - 'network': { - 'localAddress': '10.0.3.115:64784', - 'remoteAddress': '60.205.92.119:30303', - }, - 'protocols': { - 'eth': { - 'difficulty': 57631175724744612603, - 'head': '0xaaef6b9dd0d34088915f4c62b6c166379da2ad250a88f76955508f7cc81fb796', - 'version': 63, - }, - }, - }, - ... - ] - - -.. py:method:: add_peer(node_url) - - * Delegates to ``admin_addPeer`` RPC Method - - Requests adding a new remote node to the list of tracked static nodes. - - .. code-block:: python - - >>> web3.geth.admin.add_peer('enode://e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3@52.71.255.237:30303') - True - - -.. py:method:: addPeer(node_url) - - .. warning:: Deprecated: This method is deprecated in favor of :meth:`~web3.geth.admin.add_peer()` - -.. py:method:: setSolc(solc_path) - - .. Warning:: This method has been removed from Geth - -.. py:method:: start_rpc(host='localhost', port='8545', cors="", apis="eth,net,web3") - - * Delegates to ``admin_startRPC`` RPC Method - - Starts the HTTP based JSON RPC API webserver on the specified ``host`` and - ``port``, with the ``rpccorsdomain`` set to the provided ``cors`` value and - with the APIs specified by ``apis`` enabled. Returns boolean as to whether - the server was successfully started. - - .. code-block:: python - - >>> web3.geth.admin.start_rpc() - True - - -.. py:method:: startRPC(host='localhost', port='8545', cors="", apis="eth,net,web3") - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.admin.start_rpc()` - - -.. py:method:: start_ws(host='localhost', port='8546', cors="", apis="eth,net,web3") - - * Delegates to ``admin_startWS`` RPC Method - - Starts the Websocket based JSON RPC API webserver on the specified ``host`` - and ``port``, with the ``rpccorsdomain`` set to the provided ``cors`` value - and with the APIs specified by ``apis`` enabled. Returns boolean as to - whether the server was successfully started. - - .. code-block:: python - - >>> web3.geth.admin.start_ws() - True - - -.. py:method:: startWS(host='localhost', port='8546', cors="", apis="eth,net,web3") - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.admin.start_ws()` - - -.. py:method:: stop_rpc() - - * Delegates to ``admin_stopRPC`` RPC Method - - Stops the HTTP based JSON RPC server. - - .. code-block:: python - - >>> web3.geth.admin.stop_rpc() - True - - -.. py:method:: stopRPC() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.admin.stop_rpc()` - - -.. py:method:: stop_ws() - - * Delegates to ``admin_stopWS`` RPC Method - - Stops the Websocket based JSON RPC server. - - .. code-block:: python - - >>> web3.geth.admin.stop_ws() - True - - -.. py:method:: stopWS() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.admin.stop_ws()` - - -.. py:module:: web3.geth.personal - -GethPersonal API -~~~~~~~~~~~~~~~~ - -The following methods are available on the ``web3.geth.personal`` namespace. - -.. py:method:: listAccounts - - * Delegates to ``personal_listAccounts`` RPC Method - - Returns the list of known accounts. - - .. code-block:: python - - >>> web3.geth.personal.listAccounts() - ['0xd3CdA913deB6f67967B99D67aCDFa1712C293601'] - - -.. py:method:: importRawKey(self, private_key, passphrase) - - * Delegates to ``personal_importRawKey`` RPC Method - - Adds the given ``private_key`` to the node's keychain, encrypted with the - given ``passphrase``. Returns the address of the imported account. - - .. code-block:: python - - >>> web3.geth.personal.importRawKey(some_private_key, 'the-passphrase') - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - - -.. py:method:: newAccount(self, password) - - * Delegates to ``personal_newAccount`` RPC Method - - Generates a new account in the node's keychain encrypted with the - given ``passphrase``. Returns the address of the created account. - - .. code-block:: python - - >>> web3.geth.personal.newAccount('the-passphrase') - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - - -.. py:method:: lockAccount(self, account) - - * Delegates to ``personal_lockAccount`` RPC Method - - Locks the given ``account``. - - .. code-block:: python - - >>> web3.geth.personal.lockAccount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601') - - -.. py:method:: unlockAccount(self, account, passphrase, duration=None) - - * Delegates to ``personal_unlockAccount`` RPC Method - - Unlocks the given ``account`` for ``duration`` seconds. If ``duration`` is - ``None`` then the account will remain unlocked for 300 seconds (which is current default by Geth v1.9.5), - if ``duration`` is set to ``0``, the account will remain unlocked indefinitely. - Returns boolean as to whether the account was successfully unlocked. - - .. code-block:: python - - >>> web3.geth.personal.unlockAccount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'wrong-passphrase') - False - >>> web3.geth.personal.unlockAccount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'the-passphrase') - True - - -.. py:method:: sendTransaction(self, transaction, passphrase) - - * Delegates to ``personal_sendTransaction`` RPC Method - - Sends the transaction. - - -.. py:module:: web3.geth.txpool - -GethTxPool API -~~~~~~~~~~~~~~ - -The ``web3.geth.txpool`` object exposes methods to interact with the RPC APIs under -the ``txpool_`` namespace. These methods are only exposed under the ``geth`` namespace -since they are not standard nor supported in Parity. - -The following methods are available on the ``web3.geth.txpool`` namespace. - -.. py:method:: TxPool.inspect() - - * Delegates to ``txpool_inspect`` RPC Method - - Returns a textual summary of all transactions currently pending for - inclusing in the next block(s) as will as ones that are scheduled for - future execution. - - .. code-block:: python - - >>> web3.geth.txpool.inspect() - { - 'pending': { - '0x26588a9301b0428d95e6Fc3A5024fcE8BEc12D51': { - 31813: ["0x3375Ee30428b2A71c428afa5E89e427905F95F7e: 0 wei + 500000 × 20000000000 gas"] - }, - '0x2a65Aca4D5fC5B5C859090a6c34d164135398226': { - 563662: ["0x958c1Fa64B34db746925c6F8a3Dd81128e40355E: 1051546810000000000 wei + 90000 × 20000000000 gas"], - 563663: ["0x77517B1491a0299A44d668473411676f94e97E34: 1051190740000000000 wei + 90000 × 20000000000 gas"], - 563664: ["0x3E2A7Fe169c8F8eee251BB00d9fb6d304cE07d3A: 1050828950000000000 wei + 90000 × 20000000000 gas"], - 563665: ["0xAF6c4695da477F8C663eA2D8B768Ad82Cb6A8522: 1050544770000000000 wei + 90000 × 20000000000 gas"], - 563666: ["0x139B148094C50F4d20b01cAf21B85eDb711574dB: 1048598530000000000 wei + 90000 × 20000000000 gas"], - 563667: ["0x48B3Bd66770b0D1EeceFCe090daFeE36257538aE: 1048367260000000000 wei + 90000 × 20000000000 gas"], - 563668: ["0x468569500925D53e06Dd0993014aD166fD7Dd381: 1048126690000000000 wei + 90000 × 20000000000 gas"], - 563669: ["0x3DcB4C90477a4b8Ff7190b79b524773CbE3bE661: 1047965690000000000 wei + 90000 × 20000000000 gas"], - 563670: ["0x6DfeF5BC94b031407FFe71ae8076CA0FbF190963: 1047859050000000000 wei + 90000 × 20000000000 gas"] - }, - '0x9174E688d7dE157C5C0583Df424EAAB2676aC162': { - 3: ["0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413: 30000000000000000000 wei + 85000 × 21000000000 gas"] - }, - '0xb18F9d01323e150096650ab989CfecD39D757Aec': { - 777: ["0xcD79c72690750F079ae6AB6ccd7e7aEDC03c7720: 0 wei + 1000000 × 20000000000 gas"] - }, - '0xB2916C870Cf66967B6510B76c07E9d13a5D23514': { - 2: ["0x576f25199D60982A8f31A8DfF4da8aCB982e6ABa: 26000000000000000000 wei + 90000 × 20000000000 gas"] - }, - '0xBc0CA4f217E052753614d6B019948824d0d8688B': { - 0: ["0x2910543Af39abA0Cd09dBb2D50200b3E800A63D2: 1000000000000000000 wei + 50000 × 1171602790622 gas"] - }, - '0xea674fdde714fd979de3edf0f56aa9716b898ec8': { - 70148: ["0xe39c55ead9f997f7fa20ebe40fb4649943d7db66: 1000767667434026200 wei + 90000 × 20000000000 gas"] - } - }, - 'queued': { - '0x0F6000De1578619320aBA5e392706b131FB1dE6f': { - 6: ["0x8383534d0bcd0186d326C993031311c0Ac0D9B2d: 9000000000000000000 wei + 21000 × 20000000000 gas"] - }, - '0x5b30608c678e1ac464A8994C3B33E5CdF3497112': { - 6: ["0x9773547e27f8303C87089dc42D9288aa2B9d8F06: 50000000000000000000 wei + 90000 × 50000000000 gas"] - }, - '0x976A3Fc5d6f7d259EBfb4cc2Ae75115475E9867C': { - 3: ["0x346FB27dE7E7370008f5da379f74dd49F5f2F80F: 140000000000000000 wei + 90000 × 20000000000 gas"] - }, - '0x9B11bF0459b0c4b2f87f8CEBca4cfc26f294B63A': { - 2: ["0x24a461f25eE6a318BDef7F33De634A67bb67Ac9D: 17000000000000000000 wei + 90000 × 50000000000 gas"], - 6: ["0x6368f3f8c2B42435D6C136757382E4A59436a681: 17990000000000000000 wei + 90000 × 20000000000 gas", "0x8db7b4e0ecb095fbd01dffa62010801296a9ac78: 16998950000000000000 wei + 90000 × 20000000000 gas"], - 7: ["0x6368f3f8c2B42435D6C136757382E4A59436a681: 17900000000000000000 wei + 90000 × 20000000000 gas"] - } - } - } - - -.. py:method:: TxPool.status() - - * Delegates to ``txpool_status`` RPC Method - - Returns a textual summary of all transactions currently pending for - inclusing in the next block(s) as will as ones that are scheduled for - future execution. - - .. code-block:: python - - { - pending: 10, - queued: 7, - } - - -.. py:method:: TxPool.content() - - * Delegates to ``txpool_content`` RPC Method - - Returns the exact details of all transactions that are pending or queued. - - .. code-block:: python - - >>> web3.geth.txpool.content() - { - 'pending': { - '0x0216D5032f356960Cd3749C31Ab34eEFF21B3395': { - 806: [{ - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x0216D5032f356960Cd3749C31Ab34eEFF21B3395", - 'gas': "0x5208", - 'gasPrice': "0xba43b7400", - 'hash': "0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586", - 'input': "0x", - 'nonce': "0x326", - 'to': "0x7f69a91A3CF4bE60020fB58B893b7cbb65376db8", - 'transactionIndex': None, - 'value': "0x19a99f0cf456000" - }] - }, - '0x24d407e5A0B506E1Cb2fae163100B5DE01F5193C': { - 34: [{ - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x24d407e5A0B506E1Cb2fae163100B5DE01F5193C", - 'gas': "0x44c72", - 'gasPrice': "0x4a817c800", - 'hash': "0xb5b8b853af32226755a65ba0602f7ed0e8be2211516153b75e9ed640a7d359fe", - 'input': "0xb61d27f600000000000000000000000024d407e5a0b506e1cb2fae163100b5de01f5193c00000000000000000000000000000000000000000000000053444835ec580000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - 'nonce': "0x22", - 'to': "0x7320785200f74861B69C49e4ab32399a71b34f1a", - 'transactionIndex': None, - 'value': "0x0" - }] - } - }, - 'queued': { - '0x976A3Fc5d6f7d259EBfb4cc2Ae75115475E9867C': { - 3: [{ - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x976A3Fc5d6f7d259EBfb4cc2Ae75115475E9867C", - 'gas': "0x15f90", - 'gasPrice': "0x4a817c800", - 'hash': "0x57b30c59fc39a50e1cba90e3099286dfa5aaf60294a629240b5bbec6e2e66576", - 'input': "0x", - 'nonce': "0x3", - 'to': "0x346FB27dE7E7370008f5da379f74dd49F5f2F80F", - 'transactionIndex': None, - 'value': "0x1f161421c8e0000" - }] - }, - '0x9B11bF0459b0c4b2f87f8CEBca4cfc26f294B63A': { - 2: [{ - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x9B11bF0459b0c4b2f87f8CEBca4cfc26f294B63A", - 'gas': "0x15f90", - 'gasPrice': "0xba43b7400", - 'hash': "0x3a3c0698552eec2455ed3190eac3996feccc806970a4a056106deaf6ceb1e5e3", - 'input': "0x", - 'nonce': "0x2", - 'to': "0x24a461f25eE6a318BDef7F33De634A67bb67Ac9D", - 'transactionIndex': None, - 'value': "0xebec21ee1da40000" - }], - 6: [{ - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x9B11bF0459b0c4b2f87f8CEBca4cfc26f294B63A", - 'gas': "0x15f90", - 'gasPrice': "0x4a817c800", - 'hash': "0xbbcd1e45eae3b859203a04be7d6e1d7b03b222ec1d66dfcc8011dd39794b147e", - 'input': "0x", - 'nonce': "0x6", - 'to': "0x6368f3f8c2B42435D6C136757382E4A59436a681", - 'transactionIndex': None, - 'value': "0xf9a951af55470000" - }, { - 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", - 'blockNumber': None, - 'from': "0x9B11bF0459b0c4b2f87f8CEBca4cfc26f294B63A", - 'gas': "0x15f90", - 'gasPrice': "0x4a817c800", - 'hash': "0x60803251d43f072904dc3a2d6a084701cd35b4985790baaf8a8f76696041b272", - 'input': "0x", - 'nonce': "0x6", - 'to': "0x8DB7b4e0ECB095FBD01Dffa62010801296a9ac78", - 'transactionIndex': None, - 'value': "0xebe866f5f0a06000" - }], - } - } - } - -GethShh -~~~~~~~ - -The ``web3.geth.shh`` object exposes methods to interact with the RPC APIs under the -``shh_`` namespace. - -Full documentation for Geth-supported endpoints can be found `here `_. - -.. warning:: The Whisper protocol is in flux, with incompatible versions supported - by different major clients. - -.. py:method:: Shh.version() - - Returns the Whisper version this node offers. - - .. code-block:: python - - >>>web3.geth.shh.version() - 6.0 - -.. py:method:: Shh.info() - - Returns the Whisper statistics for diagnostics. - - .. code-block:: python - - >>>web3.geth.shh.info() - {'maxMessageSize': 1024, 'memory': 240, 'messages': 0, 'minPow': 0.2} - -.. py:method:: Shh.post(self, message) - - * Creates a whisper message and injects it into the network for distribution. - - * Parameters: - * ``symKeyID``: When using symmetric key encryption, holds the symmetric key ID. - * ``pubKey``: When using asymmetric key encryption, holds the public key. - * ``ttl``: Time-to-live in seconds. - * ``sig (optional)``: ID of the signing key. - * ``topic``: Message topic (four bytes of arbitrary data). - * ``payload``: Payload to be encrypted. - * ``padding (optional)``: Padding (byte array of arbitrary length). - * ``powTime``: Maximal time in seconds to be spent on prrof of work. - * ``powTarget``: Minimal PoW target required for this message. - * ``targetPeer (optional)``: Peer ID (for peer-to-peer message only). - - * Returns ``True`` if the message was succesfully sent, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.post({'payload': web3.toHex(text="test_payload"), 'pubKey': recipient_public, 'topic': '0x12340000', 'powTarget': 2.5, 'powTime': 2}) - True - -.. py:method:: Shh.newMessageFilter() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.new_message_filter()` - -.. py:method:: Shh.new_message_filter(self, criteria) - - * Create a new filter id. This filter id can be used with ``ShhFilter`` to poll for new messages that match the set of criteria. - - * Parameters: - * ``symKeyID``: When using symmetric key encryption, holds the symmetric key ID. - * ``privateKeyID``: When using asymmetric key encryption, holds the private key ID. - * ``sig``: Public key of the signature. - * ``minPoW``: Minimal PoW requirement for incoming messages. - * ``topic``: Array of possible topics (or partial topics). - * ``allowP2P``: Indicates if this filter allows processing of direct peer-to-peer messages. - - - .. code-block:: python - - >>>web3.geth.shh.new_message_filter({'topic': '0x12340000', 'privateKeyID': recipient_private}) - 'b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554' - -.. py:method:: Shh.deleteMessageFilter() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.delete_message_filter()` - - -.. py:method:: Shh.delete_message_filter(self, filter_id) - - * Deletes a message filter in the node. - - * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.delete_message_filter('b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554') - True - -.. py:method:: Shh.getMessages() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.get_filter_messages()` - -.. py:method:: Shh.get_filter_messages(self, filter_id) - - * Retrieve messages that match the filter criteria and are received between the last time this function was called and now. - - * Returns all new messages since the last invocation - - .. code-block:: python - - >>>web3.geth.shh.get_filter_messages('b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554') - [{ - 'ttl': 50, - 'timestamp': 1524497850, - 'topic': HexBytes('0x13370000'), - 'payload': HexBytes('0x74657374206d657373616765203a29'), - 'padding': HexBytes('0x50ab643f1b23bc6df1b1532bb6704ad947c2453366754aade3e3597553eeb96119f4f4299834d9989dc4ecc67e6b6470317bb3f7396ace0417fc0d6d2023900d3'), - 'pow': 6.73892030848329, - 'hash': HexBytes('0x7418f8f0989655ed2f4f9b496e6b1d9be51ef9f0f5ad89f6f750b0eee268b02f'), - 'recipientPublicKey': HexBytes('0x047d36c9e45fa82fcd27d35bc7d2fd41a2e41e512feec9e4b90ee4293ab12dc2cfc98250a6f5689b07650f8a5ca3a6e0fa8808cd0ce1a1962f2551354487a8fc79') - }] - -.. py:method:: Shh.setMaxMessageSize() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.set_max_message_size()` - -.. py:method:: Shh.set_max_message_size(self, size) - - * Sets the maximal message size allowed by this node. Incoming and outgoing messages with a larger size will be rejected. Whisper message size can never exceed the limit imposed by the underlying P2P protocol (10 Mb). - - * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.set_max_message_size(1024) - True - -.. py:method:: Shh.setMinPoW() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.set_min_pow()` - -.. py:method:: Shh.set_min_pow(self, min_pow) - - * Sets the minimal PoW required by this node. - - * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.set_min_pow(0.4) - True - -.. py:method:: Shh.markTrustedPeer() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.mark_trusted_peer()` - -.. py:method:: Shh.mark_trusted_peer(self, enode) - - * Marks specific peer trusted, which will allow it to send historic (expired) messages. - - * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.mark_trusted_peer('enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379') - True - ---------------- -Asymmetric Keys ---------------- - -.. py:method:: Shh.newKeyPair() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.new_key_pair()` - -.. py:method:: Shh.new_key_pair(self) - - * Generates a new cryptographic identity for the client, and injects it into the known identities for message decryption - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.geth.shh.new_key_pair() - '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' - -.. py:method:: Shh.addPrivateKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.add_private_key()` - -.. py:method:: Shh.add_private_key(self, key) - - * Stores a key pair derived from a private key, and returns its ID. - - * Returns the added key pair's ID - - .. code-block:: python - - >>>web3.geth.shh.add_private_key('0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0') - '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' - -.. py:method:: Shh.deleteKeyPair() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.delete_key_pair()` - -.. py:method:: Shh.delete_key_pair(self, id) - - * Deletes the specified key if it exists. - - * Returns ``True`` if the key pair was deleted, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.delete_key_pair('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - True - -.. py:method:: Shh.hasKeyPair() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.has_key_pair()` - -.. py:method:: Shh.has_key_pair(self, id) - - * Checks if the whisper node has a private key of a key pair matching the given ID. - - * Returns ``True`` if the key pair exists, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.has_key_pair('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - False - -.. py:method:: Shh.getPublicKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.get_public_key()` - -.. py:method:: Shh.get_public_key(self, id) - - * Returns the public key associated with the key pair. - - .. code-block:: python - - >>>web3.geth.shh.get_public_key('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - '0x041b0777ceb8cf8748fe0bba5e55039d650a03eb0239a909f9ee345bbbad249f2aa236a4b8f41f51bd0a97d87c08e69e67c51f154d634ba51a224195212fc31e4e' - -.. py:method:: Shh.getPrivateKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.get_private_key()` - -.. py:method:: Shh.get_private_key(self, id) - - * Returns the private key associated with the key pair. - - .. code-block:: python - - >>>web3.geth.shh.get_private_key('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - '0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0' - ---------------- -Symmetric Keys ---------------- - -.. py:method:: Shh.newSymKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.new_sym_key()` - -.. py:method:: Shh.new_sym_key(self) - - * Generates a random symmetric key and stores it under id, which is then returned. Will be used in the future for session key exchange - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.geth.shh.new_sym_key() - '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' - -.. py:method:: Shh.addSymKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.add_sym_key()` - -.. py:method:: Shh.add_sym_key(self, key) - - * Stores the key, and returns its ID. - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.geth.shh.add_sym_key('0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2') - '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' - -.. py:method:: Shh.generateSymKeyFromPassword() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.generate_sym_key_from_password()` - -.. py:method:: Shh.generate_sym_key_from_password(self) - - * Generates the key from password, stores it, and returns its ID. - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.geth.shh.generate_sym_key_from_password('shh secret pwd') - '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' - -.. py:method:: Shh.hasSymKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.has_sym_key()` - -.. py:method:: Shh.has_sym_key(self, id) - - * Checks if there is a symmetric key stored with the given ID. - - * Returns ``True`` if the key exists, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.has_sym_key('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') - False - -.. py:method:: Shh.getSymKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.get_sym_key()` - -.. py:method:: Shh.get_sym_key(self, id) - - * Returns the symmetric key associated with the given ID. - - * Returns the public key associated with the key pair - - .. code-block:: python - - >>>web3.geth.shh.get_sym_key('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') - '0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2' - -.. py:method:: Shh.deleteSymKey() - - .. warning:: Deprecated: This method is deprecated in favor of - :meth:`~web3.geth.shh.delete_sym_key()` - -.. py:method:: Shh.delete_sym_key(self, id) - - * Deletes the symmetric key associated with the given ID. - - * Returns ``True`` if the key pair was deleted, otherwise ``False`` - - .. code-block:: python - - >>>web3.geth.shh.delete_sym_key('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') - True +Geth API +======== + +.. py:module:: web3.geth + +The ``web3.geth`` object exposes modules that enable you to interact with the JSON-RPC endpoints supported by `Geth `_ that are not defined in the standard set of Ethereum JSONRPC endpoints according to `EIP 1474 `_. + +GethAdmin API +~~~~~~~~~~~~~ + +The following methods are available on the ``web3.geth.admin`` namespace. + +.. py:module:: web3.geth.admin + +The ``web3.geth.admin`` object exposes methods to interact with the RPC APIs under the +``admin_`` namespace that are supported by the Geth client. + +.. py:method:: datadir() + + * Delegates to ``admin_datadir`` RPC Method + + Returns the system path of the node's data directory. + + .. code-block:: python + + >>> web3.geth.admin.datadir() + '/Users/piper/Library/Ethereum' + + +.. py:method:: nodeInfo() + + * Delegates to ``admin_nodeInfo`` RPC Method + + Returns information about the currently running node. + + .. code-block:: python + + >>> web3.geth.admin.nodeInfo() + { + 'enode': 'enode://e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3@[::]:30303', + 'id': 'e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3', + 'ip': '::', + 'listenAddr': '[::]:30303', + 'name': 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7', + 'ports': {'discovery': 30303, 'listener': 30303}, + 'protocols': { + 'vns': { + 'difficulty': 57631175724744612603, + 'genesis': '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', + 'head': '0xaaef6b9dd0d34088915f4c62b6c166379da2ad250a88f76955508f7cc81fb796', + 'network': 1, + }, + }, + } + + +.. py:method:: peers() + + * Delegates to ``admin_peers`` RPC Method + + Returns the current peers the node is connected to. + + .. code-block:: python + + >>> web3.geth.admin.peers() + [ + { + 'caps': ['vns/63'], + 'id': '146e8e3e2460f1e18939a5da37c4a79f149c8b9837240d49c7d94c122f30064e07e4a42ae2c2992d0f8e7e6f68a30e7e9ad31d524349ec9d17effd2426a37b40', + 'name': 'Geth/v1.4.10-stable/windows/go1.6.2', + 'network': { + 'localAddress': '10.0.3.115:64478', + 'remoteAddress': '72.208.167.127:30303', + }, + 'protocols': { + 'vns': { + 'difficulty': 17179869184, + 'head': '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', + 'version': 63, + }, + } + }, + { + 'caps': ['vns/62', 'vns/63'], + 'id': '76cb6cd3354be081923a90dfd4cda40aa78b307cc3cf4d5733dc32cc171d00f7c08356e9eb2ea47eab5aad7a15a3419b859139e3f762e1e1ebf5a04f530dcef7', + 'name': 'Geth/v1.4.10-stable-5f55d95a/linux/go1.5.1', + 'network': { + 'localAddress': '10.0.3.115:64784', + 'remoteAddress': '60.205.92.119:30303', + }, + 'protocols': { + 'vns': { + 'difficulty': 57631175724744612603, + 'head': '0xaaef6b9dd0d34088915f4c62b6c166379da2ad250a88f76955508f7cc81fb796', + 'version': 63, + }, + }, + }, + ... + ] + + +.. py:method:: addPeer(node_url) + + * Delegates to ``admin_addPeer`` RPC Method + + Requests adding a new remote node to the list of tracked static nodes. + + .. code-block:: python + + >>> web3.geth.admin.addPeer('enode://e54eebad24dce1f6d246bea455ffa756d97801582420b9ed681a2ea84bf376d0bd87ae8dd6dc06cdb862a2ca89ecabe1be1050be35b4e70d62bc1a092cb7e2d3@52.71.255.237:30303') + True + + +.. py:method:: setSolc(solc_path) + + * Delegates to ``admin_setSolc`` RPC Method + + Sets the system path to the ``solc`` binary for use with the + ``vns_compileSolidity`` RPC method. Returns the output reported by ``solc + --version``. + + .. code-block:: python + + >>> web3.geth.admin.setSolc('/usr/local/bin/solc') + "solc, the solidity compiler commandline interface\nVersion: 0.3.5-9da08ac3/Release-Darwin/appleclang/JIT" + + +.. py:method:: startRPC(host='localhost', port='8545', cors="", apis="vns,net,web3") + + * Delegates to ``admin_startRPC`` RPC Method + + Starts the HTTP based JSON RPC API webserver on the specified ``host`` and + ``port``, with the ``rpccorsdomain`` set to the provided ``cors`` value and + with the APIs specified by ``apis`` enabled. Returns boolean as to whether + the server was successfully started. + + .. code-block:: python + + >>> web3.geth.admin.startRPC() + True + + +.. py:method:: startWS(host='localhost', port='8546', cors="", apis="vns,net,web3") + + * Delegates to ``admin_startWS`` RPC Method + + Starts the Websocket based JSON RPC API webserver on the specified ``host`` + and ``port``, with the ``rpccorsdomain`` set to the provided ``cors`` value + and with the APIs specified by ``apis`` enabled. Returns boolean as to + whether the server was successfully started. + + .. code-block:: python + + >>> web3.geth.admin.startWS() + True + + +.. py:method:: stopRPC() + + * Delegates to ``admin_stopRPC`` RPC Method + + Stops the HTTP based JSON RPC server. + + .. code-block:: python + + >>> web3.geth.admin.stopRPC() + True + + +.. py:method:: stopWS() + + * Delegates to ``admin_stopWS`` RPC Method + + Stops the Websocket based JSON RPC server. + + .. code-block:: python + + >>> web3.geth.admin.stopWS() + True + + +.. py:module:: web3.geth.personal + +GethPersonal API +~~~~~~~~~~~~~~~~ + +The following methods are available on the ``web3.geth.personal`` namespace. + +.. py:method:: listAccounts + + * Delegates to ``personal_listAccounts`` RPC Method + + Returns the list of known accounts. + + .. code-block:: python + + >>> web3.geth.personal.listAccounts() + ['0xd3cda913deb6f67967b99d67acdfa1712c293601'] + + +.. py:method:: importRawKey(self, private_key, passphrase) + + * Delegates to ``personal_importRawKey`` RPC Method + + Adds the given ``private_key`` to the node's keychain, encrypted with the + given ``passphrase``. Returns the address of the imported account. + + .. code-block:: python + + >>> web3.geth.personal.importRawKey(some_private_key, 'the-passphrase') + '0xd3cda913deb6f67967b99d67acdfa1712c293601' + + +.. py:method:: newAccount(self, password) + + * Delegates to ``personal_newAccount`` RPC Method + + Generates a new account in the node's keychain encrypted with the + given ``passphrase``. Returns the address of the created account. + + .. code-block:: python + + >>> web3.geth.personal.newAccount('the-passphrase') + '0xd3cda913deb6f67967b99d67acdfa1712c293601' + + +.. py:method:: lockAccount(self, account) + + * Delegates to ``personal_lockAccount`` RPC Method + + Locks the given ``account``. + + .. code-block:: python + + >>> web3.geth.personal.lockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601') + + +.. py:method:: unlockAccount(self, account, passphrase, duration=None) + + * Delegates to ``personal_unlockAccount`` RPC Method + + Unlocks the given ``account`` for ``duration`` seconds. If ``duration`` is + ``None`` then the account will remain unlocked indefinitely. Returns + boolean as to whether the account was successfully unlocked. + + .. code-block:: python + + >>> web3.geth.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'wrong-passphrase') + False + >>> web3.geth.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'the-passphrase') + True + +.. py:method:: sendTransaction(self, transaction, passphrase) + + * Delegates to ``personal_sendTransaction`` RPC Method + + Sends the transaction. + + +.. py:module:: web3.geth.txpool + +GethTxPool API +~~~~~~~~~~~~~~ + +The ``web3.geth.txpool`` object exposes methods to interact with the RPC APIs under +the ``txpool_`` namespace. These methods are only exposed under the ``geth`` namespace +since they are not standard nor supported in Parity. + +The following methods are available on the ``web3.geth.txpool`` namespace. + +.. py:method:: TxPool.inspect() + + * Delegates to ``txpool_inspect`` RPC Method + + Returns a textual summary of all transactions currently pending for + inclusing in the next block(s) as will as ones that are scheduled for + future execution. + + .. code-block:: python + + >>> web3.geth.txpool.inspect() + { + 'pending': { + '0x26588a9301b0428d95e6fc3a5024fce8bec12d51': { + 31813: ["0x3375ee30428b2a71c428afa5e89e427905f95f7e: 0 wei + 500000 × 20000000000 gas"] + }, + '0x2a65aca4d5fc5b5c859090a6c34d164135398226': { + 563662: ["0x958c1fa64b34db746925c6f8a3dd81128e40355e: 1051546810000000000 wei + 90000 × 20000000000 gas"], + 563663: ["0x77517b1491a0299a44d668473411676f94e97e34: 1051190740000000000 wei + 90000 × 20000000000 gas"], + 563664: ["0x3e2a7fe169c8f8eee251bb00d9fb6d304ce07d3a: 1050828950000000000 wei + 90000 × 20000000000 gas"], + 563665: ["0xaf6c4695da477f8c663ea2d8b768ad82cb6a8522: 1050544770000000000 wei + 90000 × 20000000000 gas"], + 563666: ["0x139b148094c50f4d20b01caf21b85edb711574db: 1048598530000000000 wei + 90000 × 20000000000 gas"], + 563667: ["0x48b3bd66770b0d1eecefce090dafee36257538ae: 1048367260000000000 wei + 90000 × 20000000000 gas"], + 563668: ["0x468569500925d53e06dd0993014ad166fd7dd381: 1048126690000000000 wei + 90000 × 20000000000 gas"], + 563669: ["0x3dcb4c90477a4b8ff7190b79b524773cbe3be661: 1047965690000000000 wei + 90000 × 20000000000 gas"], + 563670: ["0x6dfef5bc94b031407ffe71ae8076ca0fbf190963: 1047859050000000000 wei + 90000 × 20000000000 gas"] + }, + '0x9174e688d7de157c5c0583df424eaab2676ac162': { + 3: ["0xbb9bc244d798123fde783fcc1c72d3bb8c189413: 30000000000000000000 wei + 85000 × 21000000000 gas"] + }, + '0xb18f9d01323e150096650ab989cfecd39d757aec': { + 777: ["0xcd79c72690750f079ae6ab6ccd7e7aedc03c7720: 0 wei + 1000000 × 20000000000 gas"] + }, + '0xb2916c870cf66967b6510b76c07e9d13a5d23514': { + 2: ["0x576f25199d60982a8f31a8dff4da8acb982e6aba: 26000000000000000000 wei + 90000 × 20000000000 gas"] + }, + '0xbc0ca4f217e052753614d6b019948824d0d8688b': { + 0: ["0x2910543af39aba0cd09dbb2d50200b3e800a63d2: 1000000000000000000 wei + 50000 × 1171602790622 gas"] + }, + '0xea674fdde714fd979de3edf0f56aa9716b898ec8': { + 70148: ["0xe39c55ead9f997f7fa20ebe40fb4649943d7db66: 1000767667434026200 wei + 90000 × 20000000000 gas"] + } + }, + 'queued': { + '0x0f6000de1578619320aba5e392706b131fb1de6f': { + 6: ["0x8383534d0bcd0186d326c993031311c0ac0d9b2d: 9000000000000000000 wei + 21000 × 20000000000 gas"] + }, + '0x5b30608c678e1ac464a8994c3b33e5cdf3497112': { + 6: ["0x9773547e27f8303c87089dc42d9288aa2b9d8f06: 50000000000000000000 wei + 90000 × 50000000000 gas"] + }, + '0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c': { + 3: ["0x346fb27de7e7370008f5da379f74dd49f5f2f80f: 140000000000000000 wei + 90000 × 20000000000 gas"] + }, + '0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a': { + 2: ["0x24a461f25ee6a318bdef7f33de634a67bb67ac9d: 17000000000000000000 wei + 90000 × 50000000000 gas"], + 6: ["0x6368f3f8c2b42435d6c136757382e4a59436a681: 17990000000000000000 wei + 90000 × 20000000000 gas", "0x8db7b4e0ecb095fbd01dffa62010801296a9ac78: 16998950000000000000 wei + 90000 × 20000000000 gas"], + 7: ["0x6368f3f8c2b42435d6c136757382e4a59436a681: 17900000000000000000 wei + 90000 × 20000000000 gas"] + } + } + } + + +.. py:method:: TxPool.status() + + * Delegates to ``txpool_status`` RPC Method + + Returns a textual summary of all transactions currently pending for + inclusing in the next block(s) as will as ones that are scheduled for + future execution. + + .. code-block:: python + + { + pending: 10, + queued: 7, + } + + +.. py:method:: TxPool.content() + + * Delegates to ``txpool_content`` RPC Method + + Returns the exact details of all transactions that are pending or queued. + + .. code-block:: python + + >>> web3.geth.txpool.content() + { + 'pending': { + '0x0216d5032f356960cd3749c31ab34eeff21b3395': { + 806: [{ + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x0216d5032f356960cd3749c31ab34eeff21b3395", + 'gas': "0x5208", + 'gasPrice': "0xba43b7400", + 'hash': "0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586", + 'input': "0x", + 'nonce': "0x326", + 'to': "0x7f69a91a3cf4be60020fb58b893b7cbb65376db8", + 'transactionIndex': None, + 'value': "0x19a99f0cf456000" + }] + }, + '0x24d407e5a0b506e1cb2fae163100b5de01f5193c': { + 34: [{ + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x24d407e5a0b506e1cb2fae163100b5de01f5193c", + 'gas': "0x44c72", + 'gasPrice': "0x4a817c800", + 'hash': "0xb5b8b853af32226755a65ba0602f7ed0e8be2211516153b75e9ed640a7d359fe", + 'input': "0xb61d27f600000000000000000000000024d407e5a0b506e1cb2fae163100b5de01f5193c00000000000000000000000000000000000000000000000053444835ec580000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 'nonce': "0x22", + 'to': "0x7320785200f74861b69c49e4ab32399a71b34f1a", + 'transactionIndex': None, + 'value': "0x0" + }] + } + }, + 'queued': { + '0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c': { + 3: [{ + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c", + 'gas': "0x15f90", + 'gasPrice': "0x4a817c800", + 'hash': "0x57b30c59fc39a50e1cba90e3099286dfa5aaf60294a629240b5bbec6e2e66576", + 'input': "0x", + 'nonce': "0x3", + 'to': "0x346fb27de7e7370008f5da379f74dd49f5f2f80f", + 'transactionIndex': None, + 'value': "0x1f161421c8e0000" + }] + }, + '0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a': { + 2: [{ + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a", + 'gas': "0x15f90", + 'gasPrice': "0xba43b7400", + 'hash': "0x3a3c0698552eec2455ed3190eac3996feccc806970a4a056106deaf6ceb1e5e3", + 'input': "0x", + 'nonce': "0x2", + 'to': "0x24a461f25ee6a318bdef7f33de634a67bb67ac9d", + 'transactionIndex': None, + 'value': "0xebec21ee1da40000" + }], + 6: [{ + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a", + 'gas': "0x15f90", + 'gasPrice': "0x4a817c800", + 'hash': "0xbbcd1e45eae3b859203a04be7d6e1d7b03b222ec1d66dfcc8011dd39794b147e", + 'input': "0x", + 'nonce': "0x6", + 'to': "0x6368f3f8c2b42435d6c136757382e4a59436a681", + 'transactionIndex': None, + 'value': "0xf9a951af55470000" + }, { + 'blockHash': "0x0000000000000000000000000000000000000000000000000000000000000000", + 'blockNumber': None, + 'from': "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a", + 'gas': "0x15f90", + 'gasPrice': "0x4a817c800", + 'hash': "0x60803251d43f072904dc3a2d6a084701cd35b4985790baaf8a8f76696041b272", + 'input': "0x", + 'nonce': "0x6", + 'to': "0x8db7b4e0ecb095fbd01dffa62010801296a9ac78", + 'transactionIndex': None, + 'value': "0xebe866f5f0a06000" + }], + } + } + } + +GethShh +~~~~~~~ + +The ``web3.geth.shh`` object exposes methods to interact with the RPC APIs under the +``shh_`` namespace. + +Full documentation for Geth-supported endpoints can be found `here `_. + +.. warning:: The Whisper protocol is in flux, with incompatible versions supported + by different major clients. + +.. py:method:: Shh.version() + + Returns the Whisper version this node offers. + + .. code-block:: python + + >>>web3.geth.shh.version() + 6.0 + +.. py:method:: Shh.info() + + Returns the Whisper statistics for diagnostics. + + .. code-block:: python + + >>>web3.geth.shh.info() + {'maxMessageSize': 1024, 'memory': 240, 'messages': 0, 'minPow': 0.2} + +.. py:method:: Shh.post(self, message) + + * Creates a whisper message and injects it into the network for distribution. + + * Parameters: + * ``symKeyID``: When using symmetric key encryption, holds the symmetric key ID. + * ``pubKey``: When using asymmetric key encryption, holds the public key. + * ``ttl``: Time-to-live in seconds. + * ``sig (optional)``: ID of the signing key. + * ``topic``: Message topic (four bytes of arbitrary data). + * ``payload``: Payload to be encrypted. + * ``padding (optional)``: Padding (byte array of arbitrary length). + * ``powTime``: Maximal time in seconds to be spent on prrof of work. + * ``powTarget``: Minimal PoW target required for this message. + * ``targetPeer (optional)``: Peer ID (for peer-to-peer message only). + + * Returns ``True`` if the message was succesfully sent, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.post({'payload': web3.toHex(text="test_payload"), 'pubKey': recipient_public, 'topic': '0x12340000', 'powTarget': 2.5, 'powTime': 2}) + True + +.. py:method:: Shh.newMessageFilter(self, criteria) + + * Create a new filter id. This filter id can be used with ``ShhFilter`` to poll for new messages that match the set of criteria. + + * Parameters: + * ``symKeyID``: When using symmetric key encryption, holds the symmetric key ID. + * ``privateKeyID``: When using asymmetric key encryption, holds the private key ID. + * ``sig``: Public key of the signature. + * ``minPoW``: Minimal PoW requirement for incoming messages. + * ``topics``: Array of possible topics (or partial topics). + * ``allowP2P``: Indicates if this filter allows processing of direct peer-to-peer messages. + + + .. code-block:: python + + >>>web3.geth.shh.newMessageFilter({'topic': '0x12340000', 'privateKeyID': recipient_private}) + 'b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554' + +.. py:method:: Shh.deleteMessageFilter(self, filter_id) + + * Deletes a message filter in the node. + + * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.deleteMessageFilter('b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554') + True + +.. py:method:: Shh.getMessages(self, filter_id) + + * Retrieve messages that match the filter criteria and are received between the last time this function was called and now. + + * Returns all new messages since the last invocation + + .. code-block:: python + + >>>web3.geth.shh.getMessages('b37c3106cfb683e8f01b5019342399e0d1d74e9160f69b27625faba7a6738554') + [{ + 'ttl': 50, + 'timestamp': 1524497850, + 'topic': HexBytes('0x13370000'), + 'payload': HexBytes('0x74657374206d657373616765203a29'), + 'padding': HexBytes('0x50ab643f1b23bc6df1b1532bb6704ad947c2453366754aade3e3597553eeb96119f4f4299834d9989dc4ecc67e6b6470317bb3f7396ace0417fc0d6d2023900d3'), + 'pow': 6.73892030848329, + 'hash': HexBytes('0x7418f8f0989655ed2f4f9b496e6b1d9be51ef9f0f5ad89f6f750b0eee268b02f'), + 'recipientPublicKey': HexBytes('0x047d36c9e45fa82fcd27d35bc7d2fd41a2e41e512feec9e4b90ee4293ab12dc2cfc98250a6f5689b07650f8a5ca3a6e0fa8808cd0ce1a1962f2551354487a8fc79') + }] + +.. py:method:: Shh.setMaxMessageSize(self, size) + + * Sets the maximal message size allowed by this node. Incoming and outgoing messages with a larger size will be rejected. Whisper message size can never exceed the limit imposed by the underlying P2P protocol (10 Mb). + + * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.setMaxMessageSize(1024) + True + +.. py:method:: Shh.setMinPoW(self, min_pow) + + * Sets the minimal PoW required by this node. + + * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.setMinPoW(0.4) + True + +.. py:method:: Shh.markTrustedPeer(self, enode) + + * Marks specific peer trusted, which will allow it to send historic (expired) messages. + + * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.markTrustedPeer('enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379') + True + +--------------- +Asymmetric Keys +--------------- + +.. py:method:: Shh.newKeyPair(self) + + * Generates a new cryptographic identity for the client, and injects it into the known identities for message decryption + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.geth.shh.newKeyPair() + '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' + +.. py:method:: Shh.addPrivateKey(self, key) + + * Stores a key pair derived from a private key, and returns its ID. + + * Returns the added key pair's ID + + .. code-block:: python + + >>>web3.geth.shh.addPrivateKey('0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0') + '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' + +.. py:method:: Shh.deleteKeyPair(self, id) + + * Deletes the specified key if it exists. + + * Returns ``True`` if the key pair was deleted, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.deleteKeyPair('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + True + +.. py:method:: Shh.hasKeyPair(self, id) + + * Checks if the whisper node has a private key of a key pair matching the given ID. + + * Returns ``True`` if the key pair exists, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.hasKeyPair('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + False + +.. py:method:: Shh.getPublicKey(self, id) + + * Returns the public key associated with the key pair. + + .. code-block:: python + + >>>web3.geth.shh.getPublicKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + '0x041b0777ceb8cf8748fe0bba5e55039d650a03eb0239a909f9ee345bbbad249f2aa236a4b8f41f51bd0a97d87c08e69e67c51f154d634ba51a224195212fc31e4e' + +.. py:method:: Shh.getPrivateKey(self, id) + + * Returns the private key associated with the key pair. + + .. code-block:: python + + >>>web3.geth.shh.getPrivateKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + '0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0' + +--------------- +Symmetric Keys +--------------- + +.. py:method:: Shh.newSymKey(self) + + * Generates a random symmetric key and stores it under id, which is then returned. Will be used in the future for session key exchange + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.geth.shh.newSymKey() + '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' + +.. py:method:: Shh.addSymKey(self, key) + + * Stores the key, and returns its ID. + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.geth.shh.addSymKey('0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2') + '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' + +.. py:method:: Shh.generateSymKeyFromPassword(self) + + * Generates the key from password, stores it, and returns its ID. + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.geth.shh.generateSymKeyFromPassword('shh secret pwd') + '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' + +.. py:method:: Shh.hasSymKey(self, id) + + * Checks if there is a symmetric key stored with the given ID. + + * Returns ``True`` if the key exists, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.hasSymKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') + False + +.. py:method:: Shh.getSymKey(self, id) + + * Returns the symmetric key associated with the given ID. + + * Returns the public key associated with the key pair + + .. code-block:: python + + >>>web3.geth.shh.getSymKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') + '0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2' + +.. py:method:: Shh.deleteSymKey(self, id) + + * Deletes the symmetric key associated with the given ID. + + * Returns ``True`` if the key pair was deleted, otherwise ``False`` + + .. code-block:: python + + >>>web3.geth.shh.deleteSymKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') + True diff --git a/docs/web3.main.rst b/docs/web3.main.rst index 92c5fd1a30..2b11918b2d 100644 --- a/docs/web3.main.rst +++ b/docs/web3.main.rst @@ -1,97 +1,97 @@ -Web3 API -======== - -.. contents:: :local: - -.. py:module:: web3 -.. py:currentmodule:: web3 - - -.. py:class:: Web3(provider) - -Each ``web3`` instance exposes the following APIs. - -Providers -~~~~~~~~~ - -.. py:attribute:: Web3.HTTPProvider - - Convenience API to access :py:class:`web3.providers.rpc.HTTPProvider` - -.. py:attribute:: Web3.IPCProvider - - Convenience API to access :py:class:`web3.providers.ipc.IPCProvider` - -.. py:method:: Web3.setProviders(provider) - - Updates the current web3 instance with the new list of providers. It - also accepts a single provider. - - -Attributes -~~~~~~~~~~ - -.. py:attribute:: Web3.api - - Returns the current Web3 version. - - .. code-block:: python - - >>> web3.api - "4.7.0" - -.. py:attribute:: Web3.clientVersion - - * Delegates to ``web3_clientVersion`` RPC Method - - Returns the current client version. - - .. code-block:: python - - >>> web3.clientVersion - 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7' - - -Encoding and Decoding Helpers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See :ref:`overview_type_conversions` - - -Currency Conversions -~~~~~~~~~~~~~~~~~~~~~ - -See :ref:`overview_currency_conversions` - - -Addresses -~~~~~~~~~ - -See :ref:`overview_addresses` - - -RPC APIS --------- - -Each ``web3`` instance also exposes these namespaced APIs. - - -.. py:attribute:: Web3.eth - - See :doc:`./web3.eth` - -.. py:attribute:: Web3.miner - - See :doc:`./web3.miner` - -.. py:attribute:: Web3.pm - - See :doc:`./web3.pm` - -.. py:attribute:: Web3.geth - - See :doc:`./web3.geth` - -.. py:attribute:: Web3.parity - - See :doc:`./web3.parity` +Web3 API +======== + +.. contents:: :local: + +.. py:module:: web3 +.. py:currentmodule:: web3 + + +.. py:class:: Web3(provider) + +Each ``web3`` instance exposes the following APIs. + +Providers +~~~~~~~~~ + +.. py:attribute:: Web3.HTTPProvider + + Convenience API to access :py:class:`web3.providers.rpc.HTTPProvider` + +.. py:attribute:: Web3.IPCProvider + + Convenience API to access :py:class:`web3.providers.ipc.IPCProvider` + +.. py:method:: Web3.setProviders(provider) + + Updates the current web3 instance with the new list of providers. It + also accepts a single provider. + + +Attributes +~~~~~~~~~~ + +.. py:attribute:: Web3.api + + Returns the current Web3 version. + + .. code-block:: python + + >>> web3.api + "4.7.0" + +.. py:attribute:: Web3.clientVersion + + * Delegates to ``web3_clientVersion`` RPC Method + + Returns the current client version. + + .. code-block:: python + + >>> web3.clientVersion + 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7' + + +Encoding and Decoding Helpers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :ref:`overview_type_conversions` + + +Currency Conversions +~~~~~~~~~~~~~~~~~~~~~ + +See :ref:`overview_currency_conversions` + + +Addresses +~~~~~~~~~ + +See :ref:`overview_addresses` + + +RPC APIS +-------- + +Each ``web3`` instance also exposes these namespaced APIs. + + +.. py:attribute:: Web3.vns + + See :doc:`./web3.vns` + +.. py:attribute:: Web3.miner + + See :doc:`./web3.miner` + +.. py:attribute:: Web3.pm + + See :doc:`./web3.pm` + +.. py:attribute:: Web3.geth + + See :doc:`./web3.geth` + +.. py:attribute:: Web3.parity + + See :doc:`./web3.parity` diff --git a/docs/web3.miner.rst b/docs/web3.miner.rst index 75d3edbda0..29f46b34f3 100644 --- a/docs/web3.miner.rst +++ b/docs/web3.miner.rst @@ -1,93 +1,93 @@ -Miner API -========= - -.. py:module:: web3.geth.miner - -The ``web3.geth.miner`` object exposes methods to interact with the RPC APIs under -the ``miner_`` namespace that are supported by the Geth client. - - -Methods -------- - -The following methods are available on the ``web3.geth.miner`` namespace. - - -.. py:method:: GethMiner.makeDAG(number) - - * Delegates to ``miner_makeDag`` RPC Method - - Generate the DAG for the given block number. - - .. code-block:: python - - >>> web3.geth.miner.makeDag(10000) - - -.. py:method:: GethMiner.setExtra(extra) - - * Delegates to ``miner_setExtra`` RPC Method - - Set the 32 byte value ``extra`` as the extra data that will be included - when this node mines a block. - - .. code-block:: python - - >>> web3.geth.miner.setExtra('abcdefghijklmnopqrstuvwxyzABCDEF') - - -.. py:method:: GethMiner.setGasPrice(gas_price) - - * Delegates to ``miner_setGasPrice`` RPC Method - - Sets the minimum accepted gas price that this node will accept when mining - transactions. Any transactions with a gas price below this value will be - ignored. - - .. code-block:: python - - >>> web3.geth.miner.setGasPrice(19999999999) - - -.. py:method:: GethMiner.start(num_threads) - - * Delegates to ``miner_start`` RPC Method - - Start the CPU mining process using the given number of threads. - - .. code-block:: python - - >>> web3.geth.miner.start(2) - - -.. py:method:: GethMiner.stop() - - * Delegates to ``miner_stop`` RPC Method - - Stop the CPU mining operation - - .. code-block:: python - - >>> web3.geth.miner.stop() - - -.. py:method:: GethMiner.startAutoDAG() - - * Delegates to ``miner_startAutoDag`` RPC Method - - Enable automatic DAG generation. - - .. code-block:: python - - >>> web3.geth.miner.startAutoDAG() - - -.. py:method:: GethMiner.stopAutoDAG() - - * Delegates to ``miner_stopAutoDag`` RPC Method - - Disable automatic DAG generation. - - .. code-block:: python - - >>> web3.geth.miner.stopAutoDAG() +Miner API +========= + +.. py:module:: web3.geth.miner + +The ``web3.geth.miner`` object exposes methods to interact with the RPC APIs under +the ``miner_`` namespace that are supported by the Geth client. + + +Methods +------- + +The following methods are available on the ``web3.geth.miner`` namespace. + + +.. py:method:: GethMiner.makeDAG(number) + + * Delegates to ``miner_makeDag`` RPC Method + + Generate the DAG for the given block number. + + .. code-block:: python + + >>> web3.geth.miner.makeDag(10000) + + +.. py:method:: GethMiner.setExtra(extra) + + * Delegates to ``miner_setExtra`` RPC Method + + Set the 32 byte value ``extra`` as the extra data that will be included + when this node mines a block. + + .. code-block:: python + + >>> web3.geth.miner.setExtra('abcdefghijklmnopqrstuvwxyzABCDEF') + + +.. py:method:: GethMiner.setGasPrice(gas_price) + + * Delegates to ``miner_setGasPrice`` RPC Method + + Sets the minimum accepted gas price that this node will accept when mining + transactions. Any transactions with a gas price below this value will be + ignored. + + .. code-block:: python + + >>> web3.geth.miner.setGasPrice(19999999999) + + +.. py:method:: GethMiner.start(num_threads) + + * Delegates to ``miner_start`` RPC Method + + Start the CPU mining process using the given number of threads. + + .. code-block:: python + + >>> web3.geth.miner.start(2) + + +.. py:method:: GethMiner.stop() + + * Delegates to ``miner_stop`` RPC Method + + Stop the CPU mining operation + + .. code-block:: python + + >>> web3.geth.miner.stop() + + +.. py:method:: GethMiner.startAutoDAG() + + * Delegates to ``miner_startAutoDag`` RPC Method + + Enable automatic DAG generation. + + .. code-block:: python + + >>> web3.geth.miner.startAutoDAG() + + +.. py:method:: GethMiner.stopAutoDAG() + + * Delegates to ``miner_stopAutoDag`` RPC Method + + Disable automatic DAG generation. + + .. code-block:: python + + >>> web3.geth.miner.stopAutoDAG() diff --git a/docs/web3.net.rst b/docs/web3.net.rst index 484a925920..ea65ae6405 100644 --- a/docs/web3.net.rst +++ b/docs/web3.net.rst @@ -1,31 +1,31 @@ -Net API -=========== - -.. py:module:: web3.net -.. py:currentmodule:: web3.net - -.. py:class:: Net - -The ``web3.net`` object exposes methods to interact with the RPC APIs under -the ``net_`` namespace. - - -Properties ----------- - -The following properties are available on the ``web3.net`` namespace. - -.. py:method:: Net.chainId(self) - This method is deprecated as of EIP 1474. - -.. py:method:: Net.version(self) - - * Delegates to ``net_version`` RPC Method - - Returns the current network chainId/version. - - .. code-block:: python - - >>> web3.net.version - 1 - +Net API +=========== + +.. py:module:: web3.net +.. py:currentmodule:: web3.net + +.. py:class:: Net + +The ``web3.net`` object exposes methods to interact with the RPC APIs under +the ``net_`` namespace. + + +Properties +---------- + +The following properties are available on the ``web3.net`` namespace. + +.. py:method:: Net.chainId(self) + This method is deprecated as of EIP 1474. + +.. py:method:: Net.version(self) + + * Delegates to ``net_version`` RPC Method + + Returns the current network chainId/version. + + .. code-block:: python + + >>> web3.net.version + 1 + diff --git a/docs/web3.parity.rst b/docs/web3.parity.rst index 4202185ee1..5dda20a98a 100644 --- a/docs/web3.parity.rst +++ b/docs/web3.parity.rst @@ -1,290 +1,279 @@ -Parity API -========== - -.. py:module:: web3.parity - -The ``web3.parity`` object exposes modules that enable you to interact with the JSON-RPC endpoints supported by `Parity `_ that are not defined in the standard set of Ethereum JSONRPC endpoints according to `EIP 1474 `_. - -ParityPersonal --------------- - -The following methods are available on the ``web3.parity.personal`` namespace. - -.. py:method:: listAccounts - - * Delegates to ``personal_listAccounts`` RPC Method - - Returns the list of known accounts. - - .. code-block:: python - - >>> web3.parity.personal.listAccounts() - ['0xd3CdA913deB6f67967B99D67aCDFa1712C293601'] - - -.. py:method:: importRawKey(self, private_key, passphrase) - - * Delegates to ``personal_importRawKey`` RPC Method - - Adds the given ``private_key`` to the node's keychain, encrypted with the - given ``passphrase``. Returns the address of the imported account. - - .. code-block:: python - - >>> web3.parity.personal.importRawKey(some_private_key, 'the-passphrase') - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - - -.. py:method:: newAccount(self, password) - - * Delegates to ``personal_newAccount`` RPC Method - - Generates a new account in the node's keychain encrypted with the - given ``passphrase``. Returns the address of the created account. - - .. code-block:: python - - >>> web3.parity.personal.newAccount('the-passphrase') - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' - - -.. py:method:: unlockAccount(self, account, passphrase, duration=None) - - * Delegates to ``personal_unlockAccount`` RPC Method - - Unlocks the given ``account`` for ``duration`` seconds. If ``duration`` is - ``None`` then the account will remain unlocked indefinitely. Returns - boolean as to whether the account was successfully unlocked. - - .. code-block:: python - - # Invalid call to personal_unlockAccount on Parity currently returns True, due to Parity bug - >>> web3.parity.personal.unlockAccount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'wrong-passphrase') - True - >>> web3.parity.personal.unlockAccount('0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'the-passphrase') - True - - -.. py:method:: sendTransaction(self, transaction, passphrase) - - * Delegates to ``personal_sendTransaction`` RPC Method - - Sends the transaction. - - -.. py:method:: signTypedData(self, jsonMessage, account, passphrase) - - * Delegates to ``personal_signTypedData`` RPC Method - - Please note that the ``jsonMessage`` argument is the loaded JSON Object - and **NOT** the JSON String itself. - - Signs the ``Structured Data`` (or ``Typed Data``) with the passphrase of the given ``account`` - - -ParityShh ---------- - -The ``web3.parity.shh`` object exposes methods to interact with the RPC APIs under the `shh_`` namespace. - -Full documentation for Parity-supported endpoints can be found `here `_. - -.. warning:: The Whisper protocol is in flux, with incompatible versions supported - by different major clients. - - -.. py:method:: Shh.info() - - Returns the Whisper statistics for diagnostics. - - .. code-block:: python - - >>> web3.parity.shh.info() - {'memory': 240, 'messages': 0, 'targetMemory': 102485760} - -.. py:method:: Shh.post(self, message) - - * Creates a whisper message and injects it into the network for distribution. - - * Parameters: - * ``to``: The receiver of the message. Can be omitted for a broadcast message. Use one of the following two fields. - * ``public``: The public key of the recipient. - * ``identity``: The identity of the recipient key on your local node. - * ``from``: Asymmetric identity to sign the message with, or null. - * ``topics``: Array of topics for the message. Should be non-empty. - * ``payload``: Payload to be encrypted. - * ``padding``: Optional padding. Up to 2^24 -1 bytes. - * ``priority``: How many milliseconds to spend doing POW. - * ``ttl``: Time-to-live (in seconds) of the message before expiry. - - * Returns ``True`` if the message was succesfully sent, otherwise ``False`` - - .. code-block:: python - - >>> web3.parity.shh.post({ - "from":"0x193f71c502feb0c181ed0b97352fdcebcb621c733cd80637b2154a2a2b867a12", - "topics":["0x12270000"], - "payload":"0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", - "priority":40, - "ttl":400 - }) - True - -.. py:method:: Shh.newMessageFilter(self, criteria) - - * Return the filter ID that can be used with ``ShhFilter`` to poll for new messages that match the set of criteria. - - * Parameters: - * ``decryptWith``: 32 bytes - Identity of key used for description. Null if listening for broadcasts. - * ``from``: 64 bytes - If present, only accept messages signed by this key. - * ``topics``: Array of possible topics (or partial topics). Should be non-empty. - - * Returns the newly created filter id. - - .. code-block:: python - - >>>web3.parity.shh.newMessageFilter({'topic': '0x12340000', 'privateKeyID': recipient_private}) - 0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9 - -.. py:method:: Shh.deleteMessageFilter(self, filter_id) - - * Deletes a message filter in the node. - - * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` - - .. code-block:: python - - >>>web3.parity.shh.deleteMessageFilter('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') - True - -.. py:method:: Shh.getMessages(self, filter_id) - - * Retrieve messages that match the filter criteria and are received between the last time this function was called and now. - - * Returns all new messages since the last invocation - - .. code-block:: python - - >>>web3.parity.shh.getMessages('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') - [{ - 'ttl': 50, - 'timestamp': 1524497850, - 'topics': HexBytes('0x13370000'), - 'payload': HexBytes('0x74657374206d657373616765203a29'), - 'padding': HexBytes('0x50ab643f1b23bc6df1b1532bb6704ad947c2453366754aade3e3597553eeb96119f4f4299834d9989dc4ecc67e6b6470317bb3f7396ace0417fc0d6d2023900d3'), - 'recipient': HexBytes('0x047d36c9e45fa82fcd27d35bc7d2fd41a2e41e512feec9e4b90ee4293ab12dcac'), - }] - -.. py:method:: Shh.subscribe(self, filter_id) - - * Open a subscription to a filter. Subscription calls are only supported on the websocket transport. - - * Returns ``True`` if the filter was sucesfully subscribed to, otherwise ``False`` - - .. code-block:: python - - >>>web3.parity.shh.subscribe('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') - True - -.. py:method:: Shh.unsubscribe(self, filter_id) - - * Close a subscribed filter. - - * Returns ``True`` if the filter subscription was sucesfully closed, otherwise ``False`` - - .. code-block:: python - - >>>web3.parity.shh.unsubscribe('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') - True - ---------------- -Asymmetric Keys ---------------- - -.. py:method:: Shh.newKeyPair(self) - - * Generates a new cryptographic identity for the client, and injects it into the known identities for message decryption - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.parity.shh.newKeyPair() - '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' - -.. py:method:: Shh.addPrivateKey(self, key) - - * Stores a key pair derived from a private key, and returns its ID. - - * Returns the added key pair's ID - - .. code-block:: python - - >>>web3.parity.shh.addPrivateKey('0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0') - '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' - -.. py:method:: Shh.getPublicKey(self, id) - - * Returns the public key associated with the key pair. - - .. code-block:: python - - >>>web3.parity.shh.getPublicKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - '0x041b0777ceb8cf8748fe0bba5e55039d650a03eb0239a909f9ee345bbbad249f2aa236a4b8f41f51bd0a97d87c08e69e67c51f154d634ba51a224195212fc31e4e' - -.. py:method:: Shh.getPrivateKey(self, id) - - * Returns the private key associated with the key pair. - - .. code-block:: python - - >>>web3.parity.shh.getPrivateKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') - '0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0' - ---------------- -Symmetric Keys ---------------- - -.. py:method:: Shh.newSymKey(self) - - * Generates a random symmetric key and stores it under id, which is then returned. Will be used in the future for session key exchange - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.parity.shh.newSymKey() - '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' - -.. py:method:: Shh.addSymKey(self, key) - - * Stores the key, and returns its ID. - - * Returns the new key pair's identity - - .. code-block:: python - - >>>web3.parity.shh.addSymKey('0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2') - '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' - -.. py:method:: Shh.getSymKey(self, id) - - * Returns the symmetric key associated with the given ID. - - * Returns the public key associated with the key pair - - .. code-block:: python - - >>>web3.parity.shh.getSymKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') - '0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2' - -.. py:method:: Shh.deleteKey(self, id) - - * Deletes the symmetric key associated with the given ID. - - * Returns ``True`` if the key pair was deleted, otherwise ``False`` - - .. code-block:: python - - >>>web3.parity.shh.deleteKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') - True +Parity API +========== + +.. py:module:: web3.parity + +The ``web3.parity`` object exposes modules that enable you to interact with the JSON-RPC endpoints supported by `Parity `_ that are not defined in the standard set of Ethereum JSONRPC endpoints according to `EIP 1474 `_. + +ParityPersonal +-------------- + +The following methods are available on the ``web3.parity.personal`` namespace. + +.. py:method:: listAccounts + + * Delegates to ``personal_listAccounts`` RPC Method + + Returns the list of known accounts. + + .. code-block:: python + + >>> web3.parity.personal.listAccounts() + ['0xd3cda913deb6f67967b99d67acdfa1712c293601'] + + +.. py:method:: importRawKey(self, private_key, passphrase) + + * Delegates to ``personal_importRawKey`` RPC Method + + Adds the given ``private_key`` to the node's keychain, encrypted with the + given ``passphrase``. Returns the address of the imported account. + + .. code-block:: python + + >>> web3.parity.personal.importRawKey(some_private_key, 'the-passphrase') + '0xd3cda913deb6f67967b99d67acdfa1712c293601' + + +.. py:method:: newAccount(self, password) + + * Delegates to ``personal_newAccount`` RPC Method + + Generates a new account in the node's keychain encrypted with the + given ``passphrase``. Returns the address of the created account. + + .. code-block:: python + + >>> web3.parity.personal.newAccount('the-passphrase') + '0xd3cda913deb6f67967b99d67acdfa1712c293601' + + +.. py:method:: unlockAccount(self, account, passphrase, duration=None) + + * Delegates to ``personal_unlockAccount`` RPC Method + + Unlocks the given ``account`` for ``duration`` seconds. If ``duration`` is + ``None`` then the account will remain unlocked indefinitely. Returns + boolean as to whether the account was successfully unlocked. + + .. code-block:: python + + # Invalid call to personal_unlockAccount on Parity currently returns True, due to Parity bug + >>> web3.parity.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'wrong-passphrase') + True + >>> web3.parity.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'the-passphrase') + True + +.. py:method:: sendTransaction(self, transaction, passphrase) + + * Delegates to ``personal_sendTransaction`` RPC Method + + Sends the transaction. + + +ParityShh +--------- + +The ``web3.parity.shh`` object exposes methods to interact with the RPC APIs under the `shh_`` namespace. + +Full documentation for Parity-supported endpoints can be found `here `_. + +.. warning:: The Whisper protocol is in flux, with incompatible versions supported + by different major clients. + + +.. py:method:: Shh.info() + + Returns the Whisper statistics for diagnostics. + + .. code-block:: python + + >>> web3.parity.shh.info() + {'memory': 240, 'messages': 0, 'targetMemory': 102485760} + +.. py:method:: Shh.post(self, message) + + * Creates a whisper message and injects it into the network for distribution. + + * Parameters: + * ``to``: The receiver of the message. Can be omitted for a broadcast message. Use one of the following two fields. + * ``public``: The public key of the recipient. + * ``identity``: The identity of the recipient key on your local node. + * ``from``: Asymmetric identity to sign the message with, or null. + * ``topics``: Array of topics for the message. Should be non-empty. + * ``payload``: Payload to be encrypted. + * ``padding``: Optional padding. Up to 2^24 -1 bytes. + * ``priority``: How many milliseconds to spend doing POW. + * ``ttl``: Time-to-live (in seconds) of the message before expiry. + + * Returns ``True`` if the message was succesfully sent, otherwise ``False`` + + .. code-block:: python + + >>> web3.parity.shh.post({ + "from":"0x193f71c502feb0c181ed0b97352fdcebcb621c733cd80637b2154a2a2b867a12", + "topics":["0x12270000"], + "payload":"0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + "priority":40, + "ttl":400 + }) + True + +.. py:method:: Shh.newMessageFilter(self, criteria) + + * Return the filter ID that can be used with ``ShhFilter`` to poll for new messages that match the set of criteria. + + * Parameters: + * ``decryptWith``: 32 bytes - Identity of key used for description. Null if listening for broadcasts. + * ``from``: 64 bytes - If present, only accept messages signed by this key. + * ``topics``: Array of possible topics (or partial topics). Should be non-empty. + + * Returns the newly created filter id. + + .. code-block:: python + + >>>web3.parity.shh.newMessageFilter({'topic': '0x12340000', 'privateKeyID': recipient_private}) + 0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9 + +.. py:method:: Shh.deleteMessageFilter(self, filter_id) + + * Deletes a message filter in the node. + + * Returns ``True`` if the filter was sucesfully uninstalled, otherwise ``False`` + + .. code-block:: python + + >>>web3.parity.shh.deleteMessageFilter('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') + True + +.. py:method:: Shh.getMessages(self, filter_id) + + * Retrieve messages that match the filter criteria and are received between the last time this function was called and now. + + * Returns all new messages since the last invocation + + .. code-block:: python + + >>>web3.parity.shh.getMessages('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') + [{ + 'ttl': 50, + 'timestamp': 1524497850, + 'topics': HexBytes('0x13370000'), + 'payload': HexBytes('0x74657374206d657373616765203a29'), + 'padding': HexBytes('0x50ab643f1b23bc6df1b1532bb6704ad947c2453366754aade3e3597553eeb96119f4f4299834d9989dc4ecc67e6b6470317bb3f7396ace0417fc0d6d2023900d3'), + 'recipient': HexBytes('0x047d36c9e45fa82fcd27d35bc7d2fd41a2e41e512feec9e4b90ee4293ab12dcac'), + }] + +.. py:method:: Shh.subscribe(self, filter_id) + + * Open a subscription to a filter. Subscription calls are only supported on the websocket transport. + + * Returns ``True`` if the filter was sucesfully subscribed to, otherwise ``False`` + + .. code-block:: python + + >>>web3.parity.shh.subscribe('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') + True + +.. py:method:: Shh.unsubscribe(self, filter_id) + + * Close a subscribed filter. + + * Returns ``True`` if the filter subscription was sucesfully closed, otherwise ``False`` + + .. code-block:: python + + >>>web3.parity.shh.unsubscribe('0xea7120c5408c72cfd7e0e1d2ff62df8e208d9a1f85d2ed54a4a3e1ad6daeb6f9') + True + +--------------- +Asymmetric Keys +--------------- + +.. py:method:: Shh.newKeyPair(self) + + * Generates a new cryptographic identity for the client, and injects it into the known identities for message decryption + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.parity.shh.newKeyPair() + '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' + +.. py:method:: Shh.addPrivateKey(self, key) + + * Stores a key pair derived from a private key, and returns its ID. + + * Returns the added key pair's ID + + .. code-block:: python + + >>>web3.parity.shh.addPrivateKey('0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0') + '86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb' + +.. py:method:: Shh.getPublicKey(self, id) + + * Returns the public key associated with the key pair. + + .. code-block:: python + + >>>web3.parity.shh.getPublicKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + '0x041b0777ceb8cf8748fe0bba5e55039d650a03eb0239a909f9ee345bbbad249f2aa236a4b8f41f51bd0a97d87c08e69e67c51f154d634ba51a224195212fc31e4e' + +.. py:method:: Shh.getPrivateKey(self, id) + + * Returns the private key associated with the key pair. + + .. code-block:: python + + >>>web3.parity.shh.getPrivateKey('86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb') + '0x7b8190d96cd061a102e551ee36d08d4f3ca1f56fb0008ef5d70c56271d8c46d0' + +--------------- +Symmetric Keys +--------------- + +.. py:method:: Shh.newSymKey(self) + + * Generates a random symmetric key and stores it under id, which is then returned. Will be used in the future for session key exchange + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.parity.shh.newSymKey() + '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' + +.. py:method:: Shh.addSymKey(self, key) + + * Stores the key, and returns its ID. + + * Returns the new key pair's identity + + .. code-block:: python + + >>>web3.parity.shh.addSymKey('0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2') + '6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c' + +.. py:method:: Shh.getSymKey(self, id) + + * Returns the symmetric key associated with the given ID. + + * Returns the public key associated with the key pair + + .. code-block:: python + + >>>web3.parity.shh.getSymKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') + '0x58f6556e56a0d41b464a083161377c8a9c2e95156921f954f99ef97d41cebaa2' + +.. py:method:: Shh.deleteKey(self, id) + + * Deletes the symmetric key associated with the given ID. + + * Returns ``True`` if the key pair was deleted, otherwise ``False`` + + .. code-block:: python + + >>>web3.parity.shh.deleteKey('6c388d63003deb378700c9dad87f67df0247e660647d6ba1d04321bbc2f6ce0c') + True diff --git a/docs/web3.pm.rst b/docs/web3.pm.rst index 8c5d7f877a..f66c38beb2 100644 --- a/docs/web3.pm.rst +++ b/docs/web3.pm.rst @@ -1,56 +1,64 @@ -Package Manager API -=================== - - -The ``web3.pm`` object exposes methods to interact with Packages as defined by `ERC 1123 `_. - -- To learn more about the EthPM spec, visit the `documentation `__. -- To learn more about the Py-EthPM library used in this module, visit the `documentation `__. - - -.. WARNING:: - - The ``web3.pm`` API is still under development and likely to change quickly. - - Now is a great time to get familiar with the API, and test out writing - code that uses some of the great upcoming features. - - By default, access to this module has been turned off in the stable version of Web3.py: - - .. code-block:: python - - >>> from web3.auto import w3 - >>> w3.pm - ... - AttributeError: The Package Management feature is disabled by default ... - - In order to access these features, you can turn it on with... - - .. code-block:: python - - >>> web3.enable_unstable_package_management_api() - >>> w3.pm - - - -Methods -------- -The following methods are available on the ``web3.pm`` namespace. - -.. autoclass:: web3.pm.PM - :members: - -.. autoclass:: web3.pm.ERC1319Registry - :members: __init__, _release, _get_package_name, _get_all_package_ids, _get_release_id, _get_all_release_ids, _get_release_data, _generate_release_id, _num_package_ids, _num_release_ids - - -Creating your own Registry class --------------------------------- -If you want to implement your own registry and use it with ``web3.pm``, you must create a subclass that inherits from ``ERC1319Registry``, and implements all the `ERC 1319 standard methods `_ prefixed with an underscore in ``ERC1319Registry``. Then, you have to manually set it as the ``registry`` attribute on ``web3.pm``. - -.. code-block:: python - - custom_registry = CustomRegistryClass(address, w3) - w3.pm.registry = custom_registry - -One reason a user might want to create their own Registry class is if they build a custom Package Registry smart contract that has features beyond those specified in `ERC 1319 `_. For example, the ability to delete a release or some micropayment feature. Rather than accessing those functions directly on the contract instance, they can create a custom ``ERC1319Registry`` subclass to easily call both the standard & custom methods. +Package Manager API +=================== + + +The ``web3.pm`` object exposes methods to interact with Packages as defined by `ERC 1123 `_. + +- To learn more about the EthPM spec, visit the `documentation `__. +- To learn more about the Py-EthPM library used in this module, visit the `documentation `__. + + +.. WARNING:: + + The ``web3.pm`` API is still under development and likely to change quickly. + + Now is a great time to get familiar with the API, and test out writing + code that uses some of the great upcoming features. + + By default, access to this module has been turned off in the stable version of Web3.py: + + .. code-block:: python + + >>> from web3.auto import w3 + >>> w3.pm + ... + AttributeError: The Package Management feature is disabled by default ... + + In order to access these features, you can turn it on with... + + .. code-block:: python + + >>> web3.enable_unstable_package_management_api() + >>> w3.pm + + + +Methods +------- +The following methods are available on the ``web3.pm`` namespace. + +.. autoclass:: web3.pm.PM + :members: + +.. autoclass:: web3.pm.ERCRegistry + :members: __init__, _release, _get_package_name, _get_all_package_ids, _get_release_id, _get_all_release_ids, _get_release_data, _generate_release_id, _num_package_ids, _num_release_ids + +.. autoclass:: web3.pm.VyperReferenceRegistry + :members: deploy_new_instance, owner, transfer_owner + +.. autoclass:: web3.pm.SolidityReferenceRegistry + :members: + + +Creating your own Registry class +-------------------------------- +If you want to implement your own registry and use it with ``web3.pm``, you must create a subclass that inherits from ``ERCRegistry``, and implements all the `ERC 1319 standard methods `_ prefixed with an underscore in ``ERCRegistry``. Then, you have to manually set it as the ``registry`` attribute on ``web3.pm``. + +.. code-block:: python + + custom_registry = CustomRegistryClass(address, w3) + w3.pm.registry = custom_registry + +One reason a user might want to create their own Registry class is if they build a custom Package Registry smart contract that has features beyond those specified in `ERC 1319 `_. For example, the ability to delete a release or some micropayment feature. Rather than accessing those functions directly on the contract instance, they can create a custom ``ERCRegistry`` subclass to easily call both the standard & custom methods. + +The ``VyperReferenceRegistry`` class is an example of this, as it contains all of the ``ERC 1319`` defined functions (prefixed with an underscore, eg ``_get_package_name``) but also contains functions that are unique to the Vyper Registry reference implementation (eg ``transfer_owner``). diff --git a/docs/web3.testing.rst b/docs/web3.testing.rst index 7d8133801a..06c801680c 100644 --- a/docs/web3.testing.rst +++ b/docs/web3.testing.rst @@ -1,55 +1,55 @@ -Testing API -=========== - -.. py:module:: web3.testing -.. py:currentmodule:: web3.testing - -.. py:class:: Testing - -The ``web3.testing`` object exposes methods to interact with non-standard RPC -APIs that are only available under the python ``eth-testrpc`` package. - - -Methods -------- - -The following methods are available on the ``web3.testing`` namespace. - - -.. py:method:: Testing.timeTravel(timestamp) - - * Delegates to ``testing_timeTravel`` RPC Method - - Advances the test blockchain one block setting the new block's timestamp to - the provided integer timestamp. - - -.. py:method:: Testing.mine(num_blocks=1) - - * Delegates to ``evm_mine`` RPC Method - - Mines ``num_blocks`` new blocks. - - -.. py:method:: Testing.snapshot() - - * Delegates to ``evm_snapshot`` RPC Method - - Takes a snapshot of the current EVM state and returns an integer that can - be used with the ``revert`` method to restore the EVM to this state. - - -.. py:method:: Testing.revert(snapshot_idx=None) - - * Delegates to ``evm_revert`` RPC Method - - If no ``snapshot_idx`` is provided this will revert the EVM to the most - recent snapshot. Otherwise reverts the EVM to the snapshot indicated by - the provided ``snapshot_idx`` - - -.. py:method:: Testing.reset() - - * Delegates to ``evm_reset`` RPC Method - - Reset the EVM back to the genesis state. +Testing API +=========== + +.. py:module:: web3.testing +.. py:currentmodule:: web3.testing + +.. py:class:: Testing + +The ``web3.testing`` object exposes methods to interact with non-standard RPC +APIs that are only available under the python ``vns-testrpc`` package. + + +Methods +------- + +The following methods are available on the ``web3.testing`` namespace. + + +.. py:method:: Testing.timeTravel(timestamp) + + * Delegates to ``testing_timeTravel`` RPC Method + + Advances the test blockchain one block setting the new block's timestamp to + the provided integer timestamp. + + +.. py:method:: Testing.mine(num_blocks=1) + + * Delegates to ``evm_mine`` RPC Method + + Mines ``num_blocks`` new blocks. + + +.. py:method:: Testing.snapshot() + + * Delegates to ``evm_snapshot`` RPC Method + + Takes a snapshot of the current EVM state and returns an integer that can + be used with the ``revert`` method to restore the EVM to this state. + + +.. py:method:: Testing.revert(snapshot_idx=None) + + * Delegates to ``evm_revert`` RPC Method + + If no ``snapshot_idx`` is provided this will revert the EVM to the most + recent snapshot. Otherwise reverts the EVM to the snapshot indicated by + the provided ``snapshot_idx`` + + +.. py:method:: Testing.reset() + + * Delegates to ``evm_reset`` RPC Method + + Reset the EVM back to the genesis state. diff --git a/ens/__init__.py b/ens/__init__.py index c3a463a5da..f7a2c83f98 100644 --- a/ens/__init__.py +++ b/ens/__init__.py @@ -1,15 +1,15 @@ -# flake8: noqa - -from .main import ( - ENS, -) - -from .exceptions import ( - AddressMismatch, - BidTooLow, - InvalidLabel, - InvalidName, - UnauthorizedError, - UnderfundedBid, - UnownedName, -) +# flake8: noqa + +from .main import ( + ENS, +) + +from .exceptions import ( + AddressMismatch, + BidTooLow, + InvalidLabel, + InvalidName, + UnauthorizedError, + UnderfundedBid, + UnownedName, +) diff --git a/ens/abis.py b/ens/abis.py index d29b4d896f..1bcc9d721e 100644 --- a/ens/abis.py +++ b/ens/abis.py @@ -1,1394 +1,1394 @@ -# flake8: noqa - -ENS = [ - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "resolver", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "label", - "type": "bytes32" - }, - { - "name": "owner", - "type": "address" - } - ], - "name": "setSubnodeOwner", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "ttl", - "type": "uint64" - } - ], - "name": "setTTL", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "ttl", - "outputs": [ - { - "name": "", - "type": "uint64" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "resolver", - "type": "address" - } - ], - "name": "setResolver", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "owner", - "type": "address" - } - ], - "name": "setOwner", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "owner", - "type": "address" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": True, - "name": "label", - "type": "bytes32" - }, - { - "indexed": False, - "name": "owner", - "type": "address" - } - ], - "name": "NewOwner", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "resolver", - "type": "address" - } - ], - "name": "NewResolver", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "ttl", - "type": "uint64" - } - ], - "name": "NewTTL", - "type": "event" - } -] - -AUCTION_REGISTRAR = [ - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "releaseDeed", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "getAllowedTime", - "outputs": [ - { - "name": "timestamp", - "type": "uint256" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "unhashedName", - "type": "string" - } - ], - "name": "invalidateName", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "hash", - "type": "bytes32" - }, - { - "name": "owner", - "type": "address" - }, - { - "name": "value", - "type": "uint256" - }, - { - "name": "salt", - "type": "bytes32" - } - ], - "name": "shaBid", - "outputs": [ - { - "name": "sealedBid", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "bidder", - "type": "address" - }, - { - "name": "seal", - "type": "bytes32" - } - ], - "name": "cancelBid", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "entries", - "outputs": [ - { - "name": "", - "type": "uint8" - }, - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "uint256" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "ens", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - }, - { - "name": "_value", - "type": "uint256" - }, - { - "name": "_salt", - "type": "bytes32" - } - ], - "name": "unsealBid", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "transferRegistrars", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "bytes32" - } - ], - "name": "sealedBids", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "state", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - }, - { - "name": "newOwner", - "type": "address" - } - ], - "name": "transfer", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - }, - { - "name": "_timestamp", - "type": "uint256" - } - ], - "name": "isAllowed", - "outputs": [ - { - "name": "allowed", - "type": "bool" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "finalizeAuction", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "registryStarted", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "launchLength", - "outputs": [ - { - "name": "", - "type": "uint32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "sealedBid", - "type": "bytes32" - } - ], - "name": "newBid", - "outputs": [], - "payable": True, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "labels", - "type": "bytes32[]" - } - ], - "name": "eraseNode", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hashes", - "type": "bytes32[]" - } - ], - "name": "startAuctions", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "hash", - "type": "bytes32" - }, - { - "name": "deed", - "type": "address" - }, - { - "name": "registrationDate", - "type": "uint256" - } - ], - "name": "acceptRegistrarTransfer", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "_hash", - "type": "bytes32" - } - ], - "name": "startAuction", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "rootNode", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "hashes", - "type": "bytes32[]" - }, - { - "name": "sealedBid", - "type": "bytes32" - } - ], - "name": "startAuctionsAndBid", - "outputs": [], - "payable": True, - "type": "function" - }, - { - "inputs": [ - { - "name": "_ens", - "type": "address" - }, - { - "name": "_rootNode", - "type": "bytes32" - }, - { - "name": "_startDate", - "type": "uint256" - } - ], - "payable": False, - "type": "constructor" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": False, - "name": "registrationDate", - "type": "uint256" - } - ], - "name": "AuctionStarted", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": True, - "name": "bidder", - "type": "address" - }, - { - "indexed": False, - "name": "deposit", - "type": "uint256" - } - ], - "name": "NewBid", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": True, - "name": "owner", - "type": "address" - }, - { - "indexed": False, - "name": "value", - "type": "uint256" - }, - { - "indexed": False, - "name": "status", - "type": "uint8" - } - ], - "name": "BidRevealed", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": True, - "name": "owner", - "type": "address" - }, - { - "indexed": False, - "name": "value", - "type": "uint256" - }, - { - "indexed": False, - "name": "registrationDate", - "type": "uint256" - } - ], - "name": "HashRegistered", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": False, - "name": "value", - "type": "uint256" - } - ], - "name": "HashReleased", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "hash", - "type": "bytes32" - }, - { - "indexed": True, - "name": "name", - "type": "string" - }, - { - "indexed": False, - "name": "value", - "type": "uint256" - }, - { - "indexed": False, - "name": "registrationDate", - "type": "uint256" - } - ], - "name": "HashInvalidated", - "type": "event" - } -] - -DEED = [ - { - "constant": True, - "inputs": [], - "name": "creationDate", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [], - "name": "destroyDeed", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "newOwner", - "type": "address" - } - ], - "name": "setOwner", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "registrar", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "refundRatio", - "type": "uint256" - } - ], - "name": "closeDeed", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "newRegistrar", - "type": "address" - } - ], - "name": "setRegistrar", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "newValue", - "type": "uint256" - } - ], - "name": "setBalance", - "outputs": [], - "payable": True, - "type": "function" - }, - { - "inputs": [], - "type": "constructor" - }, - { - "payable": True, - "type": "fallback" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnerChanged", - "type": "event" - }, - { - "anonymous": False, - "inputs": [], - "name": "DeedClosed", - "type": "event" - } -] - -FIFS_REGISTRAR = [ - { - "constant": True, - "inputs": [], - "name": "ens", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "expiryTimes", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "subnode", - "type": "bytes32" - }, - { - "name": "owner", - "type": "address" - } - ], - "name": "register", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "rootNode", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "inputs": [ - { - "name": "ensAddr", - "type": "address" - }, - { - "name": "node", - "type": "bytes32" - } - ], - "type": "constructor" - } -] - -RESOLVER = [ - { - "constant": True, - "inputs": [ - { - "name": "interfaceID", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "contentTypes", - "type": "uint256" - } - ], - "name": "ABI", - "outputs": [ - { - "name": "contentType", - "type": "uint256" - }, - { - "name": "data", - "type": "bytes" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "x", - "type": "bytes32" - }, - { - "name": "y", - "type": "bytes32" - } - ], - "name": "setPubkey", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "content", - "outputs": [ - { - "name": "ret", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "addr", - "outputs": [ - { - "name": "ret", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "contentType", - "type": "uint256" - }, - { - "name": "data", - "type": "bytes" - } - ], - "name": "setABI", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "name", - "outputs": [ - { - "name": "ret", - "type": "string" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "name", - "type": "string" - } - ], - "name": "setName", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "hash", - "type": "bytes32" - } - ], - "name": "setContent", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "name": "pubkey", - "outputs": [ - { - "name": "x", - "type": "bytes32" - }, - { - "name": "y", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "node", - "type": "bytes32" - }, - { - "name": "addr", - "type": "address" - } - ], - "name": "setAddr", - "outputs": [], - "payable": False, - "type": "function" - }, - { - "inputs": [ - { - "name": "ensAddr", - "type": "address" - } - ], - "payable": False, - "type": "constructor" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "a", - "type": "address" - } - ], - "name": "AddrChanged", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "hash", - "type": "bytes32" - } - ], - "name": "ContentChanged", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "name", - "type": "string" - } - ], - "name": "NameChanged", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": True, - "name": "contentType", - "type": "uint256" - } - ], - "name": "ABIChanged", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "node", - "type": "bytes32" - }, - { - "indexed": False, - "name": "x", - "type": "bytes32" - }, - { - "indexed": False, - "name": "y", - "type": "bytes32" - } - ], - "name": "PubkeyChanged", - "type": "event" - } -] - -REVERSE_REGISTRAR = [ - { - "constant": False, - "inputs": [ - { - "name": "owner", - "type": "address" - }, - { - "name": "resolver", - "type": "address" - } - ], - "name": "claimWithResolver", - "outputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "owner", - "type": "address" - } - ], - "name": "claim", - "outputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "ens", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [], - "name": "defaultResolver", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": True, - "inputs": [ - { - "name": "addr", - "type": "address" - } - ], - "name": "node", - "outputs": [ - { - "name": "ret", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "name", - "type": "string" - } - ], - "name": "setName", - "outputs": [ - { - "name": "node", - "type": "bytes32" - } - ], - "payable": False, - "type": "function" - }, - { - "inputs": [ - { - "name": "ensAddr", - "type": "address" - }, - { - "name": "resolverAddr", - "type": "address" - } - ], - "payable": False, - "type": "constructor" - } -] +# flake8: noqa + +ENS = [ + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "label", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": True, + "name": "label", + "type": "bytes32" + }, + { + "indexed": False, + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + } +] + +AUCTION_REGISTRAR = [ + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "releaseDeed", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "getAllowedTime", + "outputs": [ + { + "name": "timestamp", + "type": "uint256" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "unhashedName", + "type": "string" + } + ], + "name": "invalidateName", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "name": "shaBid", + "outputs": [ + { + "name": "sealedBid", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "bidder", + "type": "address" + }, + { + "name": "seal", + "type": "bytes32" + } + ], + "name": "cancelBid", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "entries", + "outputs": [ + { + "name": "", + "type": "uint8" + }, + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "ens", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + }, + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_salt", + "type": "bytes32" + } + ], + "name": "unsealBid", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "transferRegistrars", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "sealedBids", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "state", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + }, + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transfer", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + }, + { + "name": "_timestamp", + "type": "uint256" + } + ], + "name": "isAllowed", + "outputs": [ + { + "name": "allowed", + "type": "bool" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "finalizeAuction", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "registryStarted", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "launchLength", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "sealedBid", + "type": "bytes32" + } + ], + "name": "newBid", + "outputs": [], + "payable": True, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "labels", + "type": "bytes32[]" + } + ], + "name": "eraseNode", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hashes", + "type": "bytes32[]" + } + ], + "name": "startAuctions", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "deed", + "type": "address" + }, + { + "name": "registrationDate", + "type": "uint256" + } + ], + "name": "acceptRegistrarTransfer", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "startAuction", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "rootNode", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "hashes", + "type": "bytes32[]" + }, + { + "name": "sealedBid", + "type": "bytes32" + } + ], + "name": "startAuctionsAndBid", + "outputs": [], + "payable": True, + "type": "function" + }, + { + "inputs": [ + { + "name": "_ens", + "type": "address" + }, + { + "name": "_rootNode", + "type": "bytes32" + }, + { + "name": "_startDate", + "type": "uint256" + } + ], + "payable": False, + "type": "constructor" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": False, + "name": "registrationDate", + "type": "uint256" + } + ], + "name": "AuctionStarted", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": True, + "name": "bidder", + "type": "address" + }, + { + "indexed": False, + "name": "deposit", + "type": "uint256" + } + ], + "name": "NewBid", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": True, + "name": "owner", + "type": "address" + }, + { + "indexed": False, + "name": "value", + "type": "uint256" + }, + { + "indexed": False, + "name": "status", + "type": "uint8" + } + ], + "name": "BidRevealed", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": True, + "name": "owner", + "type": "address" + }, + { + "indexed": False, + "name": "value", + "type": "uint256" + }, + { + "indexed": False, + "name": "registrationDate", + "type": "uint256" + } + ], + "name": "HashRegistered", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": False, + "name": "value", + "type": "uint256" + } + ], + "name": "HashReleased", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "hash", + "type": "bytes32" + }, + { + "indexed": True, + "name": "name", + "type": "string" + }, + { + "indexed": False, + "name": "value", + "type": "uint256" + }, + { + "indexed": False, + "name": "registrationDate", + "type": "uint256" + } + ], + "name": "HashInvalidated", + "type": "event" + } +] + +DEED = [ + { + "constant": True, + "inputs": [], + "name": "creationDate", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [], + "name": "destroyDeed", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "registrar", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "refundRatio", + "type": "uint256" + } + ], + "name": "closeDeed", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "newRegistrar", + "type": "address" + } + ], + "name": "setRegistrar", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "newValue", + "type": "uint256" + } + ], + "name": "setBalance", + "outputs": [], + "payable": True, + "type": "function" + }, + { + "inputs": [], + "type": "constructor" + }, + { + "payable": True, + "type": "fallback" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": False, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": False, + "inputs": [], + "name": "DeedClosed", + "type": "event" + } +] + +FIFS_REGISTRAR = [ + { + "constant": True, + "inputs": [], + "name": "ens", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "expiryTimes", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "subnode", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "register", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "rootNode", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "inputs": [ + { + "name": "ensAddr", + "type": "address" + }, + { + "name": "node", + "type": "bytes32" + } + ], + "type": "constructor" + } +] + +RESOLVER = [ + { + "constant": True, + "inputs": [ + { + "name": "interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentTypes", + "type": "uint256" + } + ], + "name": "ABI", + "outputs": [ + { + "name": "contentType", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "name": "setPubkey", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "content", + "outputs": [ + { + "name": "ret", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "ret", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentType", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "setABI", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "name", + "outputs": [ + { + "name": "ret", + "type": "string" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "hash", + "type": "bytes32" + } + ], + "name": "setContent", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "pubkey", + "outputs": [ + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "addr", + "type": "address" + } + ], + "name": "setAddr", + "outputs": [], + "payable": False, + "type": "function" + }, + { + "inputs": [ + { + "name": "ensAddr", + "type": "address" + } + ], + "payable": False, + "type": "constructor" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "a", + "type": "address" + } + ], + "name": "AddrChanged", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "hash", + "type": "bytes32" + } + ], + "name": "ContentChanged", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "name", + "type": "string" + } + ], + "name": "NameChanged", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": True, + "name": "contentType", + "type": "uint256" + } + ], + "name": "ABIChanged", + "type": "event" + }, + { + "anonymous": False, + "inputs": [ + { + "indexed": True, + "name": "node", + "type": "bytes32" + }, + { + "indexed": False, + "name": "x", + "type": "bytes32" + }, + { + "indexed": False, + "name": "y", + "type": "bytes32" + } + ], + "name": "PubkeyChanged", + "type": "event" + } +] + +REVERSE_REGISTRAR = [ + { + "constant": False, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "resolver", + "type": "address" + } + ], + "name": "claimWithResolver", + "outputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "claim", + "outputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "ens", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [], + "name": "defaultResolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": True, + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "name": "node", + "outputs": [ + { + "name": "ret", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "constant": False, + "inputs": [ + { + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "payable": False, + "type": "function" + }, + { + "inputs": [ + { + "name": "ensAddr", + "type": "address" + }, + { + "name": "resolverAddr", + "type": "address" + } + ], + "payable": False, + "type": "constructor" + } +] diff --git a/ens/auto.py b/ens/auto.py index 690f637de0..6a63ae85a3 100644 --- a/ens/auto.py +++ b/ens/auto.py @@ -1,3 +1,3 @@ -from ens import ENS - -ns = ENS() +from ens import ENS + +ns = ENS() diff --git a/ens/constants.py b/ens/constants.py index b7a600e3c9..174db8bf5b 100644 --- a/ens/constants.py +++ b/ens/constants.py @@ -1,17 +1,12 @@ -from eth_typing import ( - HexAddress, - HexStr, -) -from hexbytes import ( - HexBytes, -) - -ACCEPTABLE_STALE_HOURS = 48 - -AUCTION_START_GAS_CONSTANT = 25000 -AUCTION_START_GAS_MARGINAL = 39000 - -EMPTY_SHA3_BYTES = HexBytes(b'\0' * 32) -EMPTY_ADDR_HEX = HexAddress(HexStr('0x' + '00' * 20)) - -REVERSE_REGISTRAR_DOMAIN = 'addr.reverse' + +ACCEPTABLE_STALE_HOURS = 48 + +AUCTION_START_GAS_CONSTANT = 25000 +AUCTION_START_GAS_MARGINAL = 39000 + +EMPTY_SHA3_BYTES = b'\0' * 32 +EMPTY_ADDR_HEX = '0x' + '00' * 20 + +MIN_ETH_LABEL_LENGTH = 7 + +REVERSE_REGISTRAR_DOMAIN = 'addr.reverse' diff --git a/ens/contract_data.py b/ens/contract_data.py index 76e5a409da..797d492367 100644 --- a/ens/contract_data.py +++ b/ens/contract_data.py @@ -1,19 +1,19 @@ -# flake8: noqa -import json - -registrar_abi = json.loads('[{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"releaseDeed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"getAllowedTime","outputs":[{"name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"unhashedName","type":"string"}],"name":"invalidateName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"hash","type":"bytes32"},{"name":"owner","type":"address"},{"name":"value","type":"uint256"},{"name":"salt","type":"bytes32"}],"name":"shaBid","outputs":[{"name":"sealedBid","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"bidder","type":"address"},{"name":"seal","type":"bytes32"}],"name":"cancelBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"entries","outputs":[{"name":"","type":"uint8"},{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_value","type":"uint256"},{"name":"_salt","type":"bytes32"}],"name":"unsealBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"transferRegistrars","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"}],"name":"sealedBids","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"newOwner","type":"address"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_timestamp","type":"uint256"}],"name":"isAllowed","outputs":[{"name":"allowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"finalizeAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registryStarted","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"launchLength","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sealedBid","type":"bytes32"}],"name":"newBid","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"labels","type":"bytes32[]"}],"name":"eraseNode","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hashes","type":"bytes32[]"}],"name":"startAuctions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"hash","type":"bytes32"},{"name":"deed","type":"address"},{"name":"registrationDate","type":"uint256"}],"name":"acceptRegistrarTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"startAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rootNode","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"hashes","type":"bytes32[]"},{"name":"sealedBid","type":"bytes32"}],"name":"startAuctionsAndBid","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[{"name":"_ens","type":"address"},{"name":"_rootNode","type":"bytes32"},{"name":"_startDate","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"AuctionStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"bidder","type":"address"},{"indexed":false,"name":"deposit","type":"uint256"}],"name":"NewBid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"status","type":"uint8"}],"name":"BidRevealed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"HashRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"}],"name":"HashReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"HashInvalidated","type":"event"}]') -registrar_bytecode = "6060604052341561000f57600080fd5b60405160608061275783398101604052808051919060200180519190602001805160008054600160a060020a031916600160a060020a0387161781556001859055909250821190506100615742610063565b805b6004555050506126df806100786000396000f300606060405236156101175763ffffffff60e060020a6000350416630230a07c811461011c57806313c89a8f1461013457806315f733311461015c57806322ec1244146101ad5780632525f5c1146101d5578063267b6922146101f75780633f15457f1461025f57806347872b421461028e5780635ddae283146102aa5780635e431709146102c057806361d585da146102e257806379ce9fac1461031c578063935033371461033e578063983b94fb1461036b5780639c67f06f14610381578063ae1a0b0c14610394578063ce92dced146103c0578063de10f04b146103cb578063e27fe50f1461041a578063ea9e107a14610469578063ede8acdb1461048e578063faff50a8146104a4578063febefd61146104b7575b600080fd5b341561012757600080fd5b6101326004356104fd565b005b341561013f57600080fd5b61014a60043561072a565b60405190815260200160405180910390f35b341561016757600080fd5b61013260046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061074e95505050505050565b34156101b857600080fd5b61014a600435600160a060020a0360243516604435606435610a7a565b34156101e057600080fd5b610132600160a060020a0360043516602435610ac5565b341561020257600080fd5b61020d600435610c8c565b6040518086600581111561021d57fe5b60ff16815260200185600160a060020a0316600160a060020a031681526020018481526020018381526020018281526020019550505050505060405180910390f35b341561026a57600080fd5b610272610cd8565b604051600160a060020a03909116815260200160405180910390f35b341561029957600080fd5b610132600435602435604435610ce7565b34156102b557600080fd5b610132600435611254565b34156102cb57600080fd5b610272600160a060020a03600435166024356114b4565b34156102ed57600080fd5b6102f86004356114da565b6040518082600581111561030857fe5b60ff16815260200191505060405180910390f35b341561032757600080fd5b610132600435600160a060020a0360243516611550565b341561034957600080fd5b61035760043560243561169b565b604051901515815260200160405180910390f35b341561037657600080fd5b6101326004356116b1565b341561038c57600080fd5b61014a611919565b341561039f57600080fd5b6103a761191f565b60405163ffffffff909116815260200160405180910390f35b610132600435611926565b34156103d657600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a1a95505050505050565b341561042557600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a7595505050505050565b341561047457600080fd5b610132600435600160a060020a0360243516604435611aab565b341561049957600080fd5b610132600435611ab0565b34156104af57600080fd5b61014a611bfc565b61013260046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496505093359350611c0292505050565b60008082600261050c826114da565b600581111561051757fe5b1415806105a3575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561057257600080fd5b6102c65a03f1151561058357600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156105ad57600080fd5b600084815260026020526040902080546001820154919450600160a060020a031692506301e13380014210801561065f575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561063957600080fd5b6102c65a03f1151561064a57600080fd5b50505060405180519050600160a060020a0316145b1561066957600080fd5b60006002840181905560038401558254600160a060020a031916835561068e84611c14565b81600160a060020a031663bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156106d657600080fd5b6102c65a03f115156106e757600080fd5b505050600283015484907f292b79b9246fa2c8e77d3fe195b251f9cb839d7d038e667c069ee7708c631e169060405190815260200160405180910390a250505050565b6004547001000000000000000000000000000000006249d400818404020401919050565b600080826040518082805190602001908083835b602083106107815780518252601f199092019160209182019101610762565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206002806107ba836114da565b60058111156107c557fe5b146107cf57600080fd5b60066107da86611e12565b11156107e557600080fd5b846040518082805190602001908083835b602083106108155780518252601f1990920191602091820191016107f6565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206000818152600260205260409020909450925061085e84611c14565b8254600160a060020a0316156109b5576108838360020154662386f26fc10000611ec3565b60028085018290558454600160a060020a03169163b0c80972919004600060405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156108de57600080fd5b6102c65a03f115156108ef57600080fd5b50508354600160a060020a031690506313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561094257600080fd5b6102c65a03f1151561095357600080fd5b50508354600160a060020a0316905063bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156109a057600080fd5b6102c65a03f115156109b157600080fd5b5050505b846040518082805190602001908083835b602083106109e55780518252601f1990920191602091820191016109c6565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051809103902084600019167f1f9c649fe47e58bb60f4e52f0d90e4c47a526c9f90c5113df842c025970b66ad8560020154866001015460405191825260208201526040908101905180910390a3505060006002820181905560038201558054600160a060020a03191690555050565b600084848484604051938452600160a060020a03929092166c010000000000000000000000000260208401526034830152605482015260740160405180910390209050949350505050565b600160a060020a03808316600090815260036020908152604080832085845290915290205416801580610b61575062069780600160a060020a0382166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610b3d57600080fd5b6102c65a03f11515610b4e57600080fd5b5050506040518051905001621275000142105b15610b6b57600080fd5b80600160a060020a03166313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b1515610bb957600080fd5b6102c65a03f11515610bca57600080fd5b50505080600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610c1457600080fd5b6102c65a03f11515610c2557600080fd5b505050600160a060020a03831660008181526003602090815260408083208684529091528082208054600160a060020a03191690558491600080516020612694833981519152916005905191825260ff1660208201526040908101905180910390a3505050565b60008181526002602052604081208190819081908190610cab876114da565b815460018301546002840154600390940154929a600160a060020a03909216995097509195509350915050565b600054600160a060020a031681565b600080600080600080610cfc89338a8a610a7a565b600160a060020a033381166000908152600360209081526040808320858452909152902054919750169450841515610d3357600080fd5b600160a060020a0333811660009081526003602090815260408083208a845282528083208054600160a060020a03191690558c835260029091528082209650610dd6928b9290891691633fa4f245919051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610db657600080fd5b6102c65a03f11515610dc757600080fd5b50505060405180519050611edb565b925084600160a060020a031663b0c8097284600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b1515610e2757600080fd5b6102c65a03f11515610e3857600080fd5b505050610e44896114da565b91506002826005811115610e5457fe5b1415610ef25784600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610ea157600080fd5b6102c65a03f11515610eb257600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600160405191825260ff1660208201526040908101905180910390a3611249565b6004826005811115610f0057fe5b14610f0a57600080fd5b662386f26fc10000831080610f88575060018401546202a2ff1901600160a060020a0386166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610f6b57600080fd5b6102c65a03f11515610f7c57600080fd5b50505060405180519050115b156110265784600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610fd557600080fd5b6102c65a03f11515610fe657600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600060405191825260ff1660208201526040908101905180910390a3611249565b8360030154831115611108578354600160a060020a0316156110a257508254600160a060020a03168063bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561108d57600080fd5b6102c65a03f1151561109e57600080fd5b5050505b600384018054600280870191909155908490558454600160a060020a031916600160a060020a038781169190911786553316908a9060008051602061269483398151915290869060405191825260ff1660208201526040908101905180910390a3611249565b83600201548311156111b45760028401839055600160a060020a03851663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561116357600080fd5b6102c65a03f1151561117457600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600360405191825260ff1660208201526040908101905180910390a3611249565b84600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156111fc57600080fd5b6102c65a03f1151561120d57600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600460405191825260ff1660208201526040908101905180910390a35b505050505050505050565b600080826002611263826114da565b600581111561126e57fe5b1415806112fa575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156112c957600080fd5b6102c65a03f115156112da57600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561130457600080fd5b60008054600154600160a060020a03909116916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561135b57600080fd5b6102c65a03f1151561136c57600080fd5b50505060405180519050925030600160a060020a031683600160a060020a0316141561139757600080fd5b600084815260026020526040908190208054909350600160a060020a03169063faab9d399085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b15156113fa57600080fd5b6102c65a03f1151561140b57600080fd5b505082546001840154600160a060020a03808716935063ea9e107a92889291169060405160e060020a63ffffffff86160281526004810193909352600160a060020a0390911660248301526044820152606401600060405180830381600087803b151561147757600080fd5b6102c65a03f1151561148857600080fd5b50508254600160a060020a03191683555050600060018201819055600282018190556003909101555050565b6003602090815260009283526040808420909152908252902054600160a060020a031681565b60008181526002602052604081206114f2834261169b565b1515611501576005915061154a565b80600101544210156115315760018101546202a2ff1901421015611528576001915061154a565b6004915061154a565b60038101541515611545576000915061154a565b600291505b50919050565b600082600261155e826114da565b600581111561156957fe5b1415806115f5575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156115c457600080fd5b6102c65a03f115156115d557600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156115ff57600080fd5b600160a060020a038316151561161457600080fd5b600084815260026020526040908190208054909350600160a060020a0316906313af40359085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561167757600080fd5b6102c65a03f1151561168857600080fd5b5050506116958484611eec565b50505050565b60006116a68361072a565b821190505b92915050565b60008160026116bf826114da565b60058111156116ca57fe5b141580611756575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561172557600080fd5b6102c65a03f1151561173657600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561176057600080fd5b60008381526002602081905260409091209081015490925061178990662386f26fc10000611ec3565b600283018190558254600160a060020a03169063b0c8097290600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156117e157600080fd5b6102c65a03f115156117f257600080fd5b5050825461186291508490600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561184257600080fd5b6102c65a03f1151561185357600080fd5b50505060405180519050611eec565b8154600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156118a957600080fd5b6102c65a03f115156118ba57600080fd5b50505060405180519050600160a060020a031683600019167f0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e6708460020154856001015460405191825260208201526040908101905180910390a3505050565b60045481565b6249d40081565b600160a060020a0333811660009081526003602090815260408083208584529091528120549091168190111561195b57600080fd5b662386f26fc1000034101561196f57600080fd5b3433611979612187565b600160a060020a0390911681526020016040518091039082f080151561199e57600080fd5b33600160a060020a039081166000818152600360209081526040808320898452909152908190208054600160a060020a0319169385169390931790925591935090915083907fb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc299034905190815260200160405180910390a35050565b80511515611a2757600080fd5b6002611a4b82600184510381518110611a3c57fe5b906020019060200201516114da565b6005811115611a5657fe5b1415611a6157600080fd5b611a72600182510382600154611fd6565b50565b60005b8151811015611aa757611a9f828281518110611a9057fe5b90602001906020020151611ab0565b600101611a78565b5050565b505050565b600080600454421080611aca5750600454630784ce000142115b80611b51575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611b2a57600080fd5b6102c65a03f11515611b3b57600080fd5b50505060405180519050600160a060020a031614155b15611b5b57600080fd5b611b64836114da565b91506001826005811115611b7457fe5b1415611b7f57611aab565b6000826005811115611b8d57fe5b14611b9757600080fd5b50600082815260026020819052604080832042620697800160018201819055928101849055600381019390935584917f87e97e825a1d1fa0c54e1d36c7506c1dea8b1efd451fe68b000cf96f7cf40003915190815260200160405180910390a2505050565b60015481565b611c0b82611a75565b611aa781611926565b60008054600154600160a060020a033081169216906302571be390846040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611c6d57600080fd5b6102c65a03f11515611c7e57600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390843060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611cfd57600080fd5b6102c65a03f11515611d0e57600080fd5b5050506001548260405191825260208201526040908101905190819003902060008054919250600160a060020a0390911690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611d8c57600080fd5b6102c65a03f11515611d9d57600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611dfa57600080fd5b6102c65a03f11515611e0b57600080fd5b5050505050565b600060018201818080838651019250600091505b82841015611eba5760ff845116905060808160ff161015611e4c57600184019350611eaf565b60e08160ff161015611e6357600284019350611eaf565b60f08160ff161015611e7a57600384019350611eaf565b60f88160ff161015611e9157600484019350611eaf565b60fc8160ff161015611ea857600584019350611eaf565b6006840193505b600190910190611e26565b50949350505050565b600081831115611ed45750816116ab565b50806116ab565b600081831015611ed45750816116ab565b60008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611f4657600080fd5b6102c65a03f11515611f5757600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390848460405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611dfa57600080fd5b600054600160a060020a03166306ab592382848681518110611ff457fe5b906020019060200201513060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561204b57600080fd5b6102c65a03f1151561205c57600080fd5b5050508082848151811061206c57fe5b906020019060200201516040519182526020820152604090810190518091039020905060008311156120a6576120a6600184038383611fd6565b60008054600160a060020a031690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561210057600080fd5b6102c65a03f1151561211157600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561216e57600080fd5b6102c65a03f1151561217f57600080fd5b505050505050565b6040516104fc8061219883390190560060606040526040516020806104fc8339810160405280805160028054600160a060020a03928316600160a060020a03199182161790915560008054339093169290911691909117905550504260019081556005805460ff191690911790553460045561048c806100706000396000f300606060405236156100a15763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305b3441081146100a65780630b5ab3d5146100cb57806313af4035146100e05780632b20e397146100ff5780633fa4f2451461012e578063674f220f146101415780638da5cb5b14610154578063b0c8097214610167578063bbe4277114610182578063faab9d3914610198575b600080fd5b34156100b157600080fd5b6100b96101b7565b60405190815260200160405180910390f35b34156100d657600080fd5b6100de6101bd565b005b34156100eb57600080fd5b6100de600160a060020a0360043516610207565b341561010a57600080fd5b6101126102b0565b604051600160a060020a03909116815260200160405180910390f35b341561013957600080fd5b6100b96102bf565b341561014c57600080fd5b6101126102c5565b341561015f57600080fd5b6101126102d4565b341561017257600080fd5b6100de60043560243515156102e3565b341561018d57600080fd5b6100de60043561036c565b34156101a357600080fd5b6100de600160a060020a0360043516610416565b60015481565b60055460ff16156101cd57600080fd5b600254600160a060020a039081169030163180156108fc0290604051600060405180830381858888f19350505050156102055761deadff5b565b60005433600160a060020a0390811691161461022257600080fd5b600160a060020a038116151561023757600080fd5b6002805460038054600160a060020a0380841673ffffffffffffffffffffffffffffffffffffffff19928316179092559091169083161790557fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3681604051600160a060020a03909116815260200160405180910390a150565b600054600160a060020a031681565b60045481565b600354600160a060020a031681565b600254600160a060020a031681565b60005433600160a060020a039081169116146102fe57600080fd5b60055460ff16151561030f57600080fd5b81600454101561031e57600080fd5b6004829055600254600160a060020a039081169030163183900380156108fc0290604051600060405180830381858888f1935050505015801561035e5750805b1561036857600080fd5b5050565b60005433600160a060020a0390811691161461038757600080fd5b60055460ff16151561039857600080fd5b6005805460ff1916905561dead6103e8600160a060020a03301631838203020480156108fc0290604051600060405180830381858888f1935050505015156103df57600080fd5b7fbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf1360405160405180910390a16104136101bd565b50565b60005433600160a060020a0390811691161461043157600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600a165627a7a7230582023849fab751378a237489f526a289ede7796b7f6b7dac5a973b8c3ca25f368a800297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a72305820e6807b87ab11a69864cefed52eef7f9c4635fd0e26312e944bbbcbff5cd26d920029" -registrar_bytecode_runtime = "606060405236156101175763ffffffff60e060020a6000350416630230a07c811461011c57806313c89a8f1461013457806315f733311461015c57806322ec1244146101ad5780632525f5c1146101d5578063267b6922146101f75780633f15457f1461025f57806347872b421461028e5780635ddae283146102aa5780635e431709146102c057806361d585da146102e257806379ce9fac1461031c578063935033371461033e578063983b94fb1461036b5780639c67f06f14610381578063ae1a0b0c14610394578063ce92dced146103c0578063de10f04b146103cb578063e27fe50f1461041a578063ea9e107a14610469578063ede8acdb1461048e578063faff50a8146104a4578063febefd61146104b7575b600080fd5b341561012757600080fd5b6101326004356104fd565b005b341561013f57600080fd5b61014a60043561072a565b60405190815260200160405180910390f35b341561016757600080fd5b61013260046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061074e95505050505050565b34156101b857600080fd5b61014a600435600160a060020a0360243516604435606435610a7a565b34156101e057600080fd5b610132600160a060020a0360043516602435610ac5565b341561020257600080fd5b61020d600435610c8c565b6040518086600581111561021d57fe5b60ff16815260200185600160a060020a0316600160a060020a031681526020018481526020018381526020018281526020019550505050505060405180910390f35b341561026a57600080fd5b610272610cd8565b604051600160a060020a03909116815260200160405180910390f35b341561029957600080fd5b610132600435602435604435610ce7565b34156102b557600080fd5b610132600435611254565b34156102cb57600080fd5b610272600160a060020a03600435166024356114b4565b34156102ed57600080fd5b6102f86004356114da565b6040518082600581111561030857fe5b60ff16815260200191505060405180910390f35b341561032757600080fd5b610132600435600160a060020a0360243516611550565b341561034957600080fd5b61035760043560243561169b565b604051901515815260200160405180910390f35b341561037657600080fd5b6101326004356116b1565b341561038c57600080fd5b61014a611919565b341561039f57600080fd5b6103a761191f565b60405163ffffffff909116815260200160405180910390f35b610132600435611926565b34156103d657600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a1a95505050505050565b341561042557600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a7595505050505050565b341561047457600080fd5b610132600435600160a060020a0360243516604435611aab565b341561049957600080fd5b610132600435611ab0565b34156104af57600080fd5b61014a611bfc565b61013260046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496505093359350611c0292505050565b60008082600261050c826114da565b600581111561051757fe5b1415806105a3575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561057257600080fd5b6102c65a03f1151561058357600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156105ad57600080fd5b600084815260026020526040902080546001820154919450600160a060020a031692506301e13380014210801561065f575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561063957600080fd5b6102c65a03f1151561064a57600080fd5b50505060405180519050600160a060020a0316145b1561066957600080fd5b60006002840181905560038401558254600160a060020a031916835561068e84611c14565b81600160a060020a031663bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156106d657600080fd5b6102c65a03f115156106e757600080fd5b505050600283015484907f292b79b9246fa2c8e77d3fe195b251f9cb839d7d038e667c069ee7708c631e169060405190815260200160405180910390a250505050565b6004547001000000000000000000000000000000006249d400818404020401919050565b600080826040518082805190602001908083835b602083106107815780518252601f199092019160209182019101610762565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206002806107ba836114da565b60058111156107c557fe5b146107cf57600080fd5b60066107da86611e12565b11156107e557600080fd5b846040518082805190602001908083835b602083106108155780518252601f1990920191602091820191016107f6565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206000818152600260205260409020909450925061085e84611c14565b8254600160a060020a0316156109b5576108838360020154662386f26fc10000611ec3565b60028085018290558454600160a060020a03169163b0c80972919004600060405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156108de57600080fd5b6102c65a03f115156108ef57600080fd5b50508354600160a060020a031690506313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561094257600080fd5b6102c65a03f1151561095357600080fd5b50508354600160a060020a0316905063bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156109a057600080fd5b6102c65a03f115156109b157600080fd5b5050505b846040518082805190602001908083835b602083106109e55780518252601f1990920191602091820191016109c6565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051809103902084600019167f1f9c649fe47e58bb60f4e52f0d90e4c47a526c9f90c5113df842c025970b66ad8560020154866001015460405191825260208201526040908101905180910390a3505060006002820181905560038201558054600160a060020a03191690555050565b600084848484604051938452600160a060020a03929092166c010000000000000000000000000260208401526034830152605482015260740160405180910390209050949350505050565b600160a060020a03808316600090815260036020908152604080832085845290915290205416801580610b61575062069780600160a060020a0382166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610b3d57600080fd5b6102c65a03f11515610b4e57600080fd5b5050506040518051905001621275000142105b15610b6b57600080fd5b80600160a060020a03166313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b1515610bb957600080fd5b6102c65a03f11515610bca57600080fd5b50505080600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610c1457600080fd5b6102c65a03f11515610c2557600080fd5b505050600160a060020a03831660008181526003602090815260408083208684529091528082208054600160a060020a03191690558491600080516020612694833981519152916005905191825260ff1660208201526040908101905180910390a3505050565b60008181526002602052604081208190819081908190610cab876114da565b815460018301546002840154600390940154929a600160a060020a03909216995097509195509350915050565b600054600160a060020a031681565b600080600080600080610cfc89338a8a610a7a565b600160a060020a033381166000908152600360209081526040808320858452909152902054919750169450841515610d3357600080fd5b600160a060020a0333811660009081526003602090815260408083208a845282528083208054600160a060020a03191690558c835260029091528082209650610dd6928b9290891691633fa4f245919051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610db657600080fd5b6102c65a03f11515610dc757600080fd5b50505060405180519050611edb565b925084600160a060020a031663b0c8097284600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b1515610e2757600080fd5b6102c65a03f11515610e3857600080fd5b505050610e44896114da565b91506002826005811115610e5457fe5b1415610ef25784600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610ea157600080fd5b6102c65a03f11515610eb257600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600160405191825260ff1660208201526040908101905180910390a3611249565b6004826005811115610f0057fe5b14610f0a57600080fd5b662386f26fc10000831080610f88575060018401546202a2ff1901600160a060020a0386166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610f6b57600080fd5b6102c65a03f11515610f7c57600080fd5b50505060405180519050115b156110265784600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610fd557600080fd5b6102c65a03f11515610fe657600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600060405191825260ff1660208201526040908101905180910390a3611249565b8360030154831115611108578354600160a060020a0316156110a257508254600160a060020a03168063bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561108d57600080fd5b6102c65a03f1151561109e57600080fd5b5050505b600384018054600280870191909155908490558454600160a060020a031916600160a060020a038781169190911786553316908a9060008051602061269483398151915290869060405191825260ff1660208201526040908101905180910390a3611249565b83600201548311156111b45760028401839055600160a060020a03851663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561116357600080fd5b6102c65a03f1151561117457600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600360405191825260ff1660208201526040908101905180910390a3611249565b84600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156111fc57600080fd5b6102c65a03f1151561120d57600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600460405191825260ff1660208201526040908101905180910390a35b505050505050505050565b600080826002611263826114da565b600581111561126e57fe5b1415806112fa575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156112c957600080fd5b6102c65a03f115156112da57600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561130457600080fd5b60008054600154600160a060020a03909116916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561135b57600080fd5b6102c65a03f1151561136c57600080fd5b50505060405180519050925030600160a060020a031683600160a060020a0316141561139757600080fd5b600084815260026020526040908190208054909350600160a060020a03169063faab9d399085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b15156113fa57600080fd5b6102c65a03f1151561140b57600080fd5b505082546001840154600160a060020a03808716935063ea9e107a92889291169060405160e060020a63ffffffff86160281526004810193909352600160a060020a0390911660248301526044820152606401600060405180830381600087803b151561147757600080fd5b6102c65a03f1151561148857600080fd5b50508254600160a060020a03191683555050600060018201819055600282018190556003909101555050565b6003602090815260009283526040808420909152908252902054600160a060020a031681565b60008181526002602052604081206114f2834261169b565b1515611501576005915061154a565b80600101544210156115315760018101546202a2ff1901421015611528576001915061154a565b6004915061154a565b60038101541515611545576000915061154a565b600291505b50919050565b600082600261155e826114da565b600581111561156957fe5b1415806115f5575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156115c457600080fd5b6102c65a03f115156115d557600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156115ff57600080fd5b600160a060020a038316151561161457600080fd5b600084815260026020526040908190208054909350600160a060020a0316906313af40359085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561167757600080fd5b6102c65a03f1151561168857600080fd5b5050506116958484611eec565b50505050565b60006116a68361072a565b821190505b92915050565b60008160026116bf826114da565b60058111156116ca57fe5b141580611756575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561172557600080fd5b6102c65a03f1151561173657600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561176057600080fd5b60008381526002602081905260409091209081015490925061178990662386f26fc10000611ec3565b600283018190558254600160a060020a03169063b0c8097290600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156117e157600080fd5b6102c65a03f115156117f257600080fd5b5050825461186291508490600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561184257600080fd5b6102c65a03f1151561185357600080fd5b50505060405180519050611eec565b8154600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156118a957600080fd5b6102c65a03f115156118ba57600080fd5b50505060405180519050600160a060020a031683600019167f0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e6708460020154856001015460405191825260208201526040908101905180910390a3505050565b60045481565b6249d40081565b600160a060020a0333811660009081526003602090815260408083208584529091528120549091168190111561195b57600080fd5b662386f26fc1000034101561196f57600080fd5b3433611979612187565b600160a060020a0390911681526020016040518091039082f080151561199e57600080fd5b33600160a060020a039081166000818152600360209081526040808320898452909152908190208054600160a060020a0319169385169390931790925591935090915083907fb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc299034905190815260200160405180910390a35050565b80511515611a2757600080fd5b6002611a4b82600184510381518110611a3c57fe5b906020019060200201516114da565b6005811115611a5657fe5b1415611a6157600080fd5b611a72600182510382600154611fd6565b50565b60005b8151811015611aa757611a9f828281518110611a9057fe5b90602001906020020151611ab0565b600101611a78565b5050565b505050565b600080600454421080611aca5750600454630784ce000142115b80611b51575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611b2a57600080fd5b6102c65a03f11515611b3b57600080fd5b50505060405180519050600160a060020a031614155b15611b5b57600080fd5b611b64836114da565b91506001826005811115611b7457fe5b1415611b7f57611aab565b6000826005811115611b8d57fe5b14611b9757600080fd5b50600082815260026020819052604080832042620697800160018201819055928101849055600381019390935584917f87e97e825a1d1fa0c54e1d36c7506c1dea8b1efd451fe68b000cf96f7cf40003915190815260200160405180910390a2505050565b60015481565b611c0b82611a75565b611aa781611926565b60008054600154600160a060020a033081169216906302571be390846040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611c6d57600080fd5b6102c65a03f11515611c7e57600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390843060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611cfd57600080fd5b6102c65a03f11515611d0e57600080fd5b5050506001548260405191825260208201526040908101905190819003902060008054919250600160a060020a0390911690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611d8c57600080fd5b6102c65a03f11515611d9d57600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611dfa57600080fd5b6102c65a03f11515611e0b57600080fd5b5050505050565b600060018201818080838651019250600091505b82841015611eba5760ff845116905060808160ff161015611e4c57600184019350611eaf565b60e08160ff161015611e6357600284019350611eaf565b60f08160ff161015611e7a57600384019350611eaf565b60f88160ff161015611e9157600484019350611eaf565b60fc8160ff161015611ea857600584019350611eaf565b6006840193505b600190910190611e26565b50949350505050565b600081831115611ed45750816116ab565b50806116ab565b600081831015611ed45750816116ab565b60008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611f4657600080fd5b6102c65a03f11515611f5757600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390848460405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611dfa57600080fd5b600054600160a060020a03166306ab592382848681518110611ff457fe5b906020019060200201513060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561204b57600080fd5b6102c65a03f1151561205c57600080fd5b5050508082848151811061206c57fe5b906020019060200201516040519182526020820152604090810190518091039020905060008311156120a6576120a6600184038383611fd6565b60008054600160a060020a031690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561210057600080fd5b6102c65a03f1151561211157600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561216e57600080fd5b6102c65a03f1151561217f57600080fd5b505050505050565b6040516104fc8061219883390190560060606040526040516020806104fc8339810160405280805160028054600160a060020a03928316600160a060020a03199182161790915560008054339093169290911691909117905550504260019081556005805460ff191690911790553460045561048c806100706000396000f300606060405236156100a15763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305b3441081146100a65780630b5ab3d5146100cb57806313af4035146100e05780632b20e397146100ff5780633fa4f2451461012e578063674f220f146101415780638da5cb5b14610154578063b0c8097214610167578063bbe4277114610182578063faab9d3914610198575b600080fd5b34156100b157600080fd5b6100b96101b7565b60405190815260200160405180910390f35b34156100d657600080fd5b6100de6101bd565b005b34156100eb57600080fd5b6100de600160a060020a0360043516610207565b341561010a57600080fd5b6101126102b0565b604051600160a060020a03909116815260200160405180910390f35b341561013957600080fd5b6100b96102bf565b341561014c57600080fd5b6101126102c5565b341561015f57600080fd5b6101126102d4565b341561017257600080fd5b6100de60043560243515156102e3565b341561018d57600080fd5b6100de60043561036c565b34156101a357600080fd5b6100de600160a060020a0360043516610416565b60015481565b60055460ff16156101cd57600080fd5b600254600160a060020a039081169030163180156108fc0290604051600060405180830381858888f19350505050156102055761deadff5b565b60005433600160a060020a0390811691161461022257600080fd5b600160a060020a038116151561023757600080fd5b6002805460038054600160a060020a0380841673ffffffffffffffffffffffffffffffffffffffff19928316179092559091169083161790557fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3681604051600160a060020a03909116815260200160405180910390a150565b600054600160a060020a031681565b60045481565b600354600160a060020a031681565b600254600160a060020a031681565b60005433600160a060020a039081169116146102fe57600080fd5b60055460ff16151561030f57600080fd5b81600454101561031e57600080fd5b6004829055600254600160a060020a039081169030163183900380156108fc0290604051600060405180830381858888f1935050505015801561035e5750805b1561036857600080fd5b5050565b60005433600160a060020a0390811691161461038757600080fd5b60055460ff16151561039857600080fd5b6005805460ff1916905561dead6103e8600160a060020a03301631838203020480156108fc0290604051600060405180830381858888f1935050505015156103df57600080fd5b7fbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf1360405160405180910390a16104136101bd565b50565b60005433600160a060020a0390811691161461043157600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600a165627a7a7230582023849fab751378a237489f526a289ede7796b7f6b7dac5a973b8c3ca25f368a800297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a72305820e6807b87ab11a69864cefed52eef7f9c4635fd0e26312e944bbbcbff5cd26d920029" - -resolver_abi = json.loads('[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"key","type":"string"},{"name":"value","type":"string"}],"name":"setText","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"key","type":"string"}],"name":"text","outputs":[{"name":"ret","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"indexedKey","type":"string"},{"indexed":false,"name":"key","type":"string"}],"name":"TextChanged","type":"event"}]') -resolver_bytecode = "6060604052341561000f57600080fd5b6040516020806111b08339810160405280805160008054600160a060020a03909216600160a060020a0319909216919091179055505061115c806100546000396000f300606060405236156100a95763ffffffff60e060020a60003504166301ffc9a781146100ae57806310f13a8c146100e25780632203ab561461017c57806329cd62ea146102135780632dff69411461022f5780633b3b57de1461025757806359d1d43c14610289578063623195b014610356578063691f3431146103b257806377372213146103c8578063c3d014d61461041e578063c869023314610437578063d5fa2b0014610465575b600080fd5b34156100b957600080fd5b6100ce600160e060020a031960043516610487565b604051901515815260200160405180910390f35b34156100ed57600080fd5b61017a600480359060446024803590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405281815292919060208401838380828437509496506105f495505050505050565b005b341561018757600080fd5b610195600435602435610805565b60405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156101d75780820151838201526020016101bf565b50505050905090810190601f1680156102045780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561021e57600080fd5b61017a60043560243560443561092f565b341561023a57600080fd5b610245600435610a2e565b60405190815260200160405180910390f35b341561026257600080fd5b61026d600435610a44565b604051600160a060020a03909116815260200160405180910390f35b341561029457600080fd5b6102df600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610a5f95505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561031b578082015183820152602001610303565b50505050905090810190601f1680156103485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561036157600080fd5b61017a600480359060248035919060649060443590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610b7e95505050505050565b34156103bd57600080fd5b6102df600435610c7a565b34156103d357600080fd5b61017a600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610d4095505050505050565b341561042957600080fd5b61017a600435602435610e8a565b341561044257600080fd5b61044d600435610f63565b60405191825260208201526040908101905180910390f35b341561047057600080fd5b61017a600435600160a060020a0360243516610f80565b6000600160e060020a031982167f3b3b57de0000000000000000000000000000000000000000000000000000000014806104ea5750600160e060020a031982167fd8389dc500000000000000000000000000000000000000000000000000000000145b8061051e5750600160e060020a031982167f691f343100000000000000000000000000000000000000000000000000000000145b806105525750600160e060020a031982167f2203ab5600000000000000000000000000000000000000000000000000000000145b806105865750600160e060020a031982167fc869023300000000000000000000000000000000000000000000000000000000145b806105ba5750600160e060020a031982167f59d1d43c00000000000000000000000000000000000000000000000000000000145b806105ee5750600160e060020a031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561064d57600080fd5b6102c65a03f1151561065e57600080fd5b50505060405180519050600160a060020a031614151561067d57600080fd5b6000848152600160205260409081902083916005909101908590518082805190602001908083835b602083106106c45780518252601f1990920191602091820191016106a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051610708929160200190611083565b50826040518082805190602001908083835b602083106107395780518252601f19909201916020918201910161071a565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051908190039020847fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a75508560405160208082528190810183818151815260200191508051906020019080838360005b838110156107c55780820151838201526020016107ad565b50505050905090810190601f1680156107f25780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b600061080f611101565b60008481526001602081905260409091209092505b838311610922578284161580159061085d5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610917578060060160008481526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561090b5780601f106108e05761010080835404028352916020019161090b565b820191906000526020600020905b8154815290600101906020018083116108ee57829003601f168201915b50505050509150610927565b600290920291610824565b600092505b509250929050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561098857600080fd5b6102c65a03f1151561099957600080fd5b50505060405180519050600160a060020a03161415156109b857600080fd5b6040805190810160409081528482526020808301859052600087815260019091522060030181518155602082015160019091015550837f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46848460405191825260208201526040908101905180910390a250505050565b6000908152600160208190526040909120015490565b600090815260016020526040902054600160a060020a031690565b610a67611101565b60008381526001602052604090819020600501908390518082805190602001908083835b60208310610aaa5780518252601f199092019160209182019101610a8b565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b715780601f10610b4657610100808354040283529160200191610b71565b820191906000526020600020905b815481529060010190602001808311610b5457829003601f168201915b5050505050905092915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610bd757600080fd5b6102c65a03f11515610be857600080fd5b50505060405180519050600160a060020a0316141515610c0757600080fd5b6000198301831615610c1857600080fd5b60008481526001602090815260408083208684526006019091529020828051610c45929160200190611083565b5082847faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe360405160405180910390a350505050565b610c82611101565b6001600083600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d345780601f10610d0957610100808354040283529160200191610d34565b820191906000526020600020905b815481529060010190602001808311610d1757829003601f168201915b50505050509050919050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610d9957600080fd5b6102c65a03f11515610daa57600080fd5b50505060405180519050600160a060020a0316141515610dc957600080fd5b6000838152600160205260409020600201828051610deb929160200190611083565b50827fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f78360405160208082528190810183818151815260200191508051906020019080838360005b83811015610e4b578082015183820152602001610e33565b50505050905090810190601f168015610e785780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610ee357600080fd5b6102c65a03f11515610ef457600080fd5b50505060405180519050600160a060020a0316141515610f1357600080fd5b6000838152600160208190526040918290200183905583907f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc9084905190815260200160405180910390a2505050565b600090815260016020526040902060038101546004909101549091565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610fd957600080fd5b6102c65a03f11515610fea57600080fd5b50505060405180519050600160a060020a031614151561100957600080fd5b60008381526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03851617905583907f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd290849051600160a060020a03909116815260200160405180910390a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110c457805160ff19168380011785556110f1565b828001600101855582156110f1579182015b828111156110f15782518255916020019190600101906110d6565b506110fd929150611113565b5090565b60206040519081016040526000815290565b61112d91905b808211156110fd5760008155600101611119565b905600a165627a7a723058206016a807d9d5f6060e9c0d1c808e52d4ca30a4cab0140adcc6587bfc13bedf100029" -resolver_bytecode_runtime = "606060405236156100a95763ffffffff60e060020a60003504166301ffc9a781146100ae57806310f13a8c146100e25780632203ab561461017c57806329cd62ea146102135780632dff69411461022f5780633b3b57de1461025757806359d1d43c14610289578063623195b014610356578063691f3431146103b257806377372213146103c8578063c3d014d61461041e578063c869023314610437578063d5fa2b0014610465575b600080fd5b34156100b957600080fd5b6100ce600160e060020a031960043516610487565b604051901515815260200160405180910390f35b34156100ed57600080fd5b61017a600480359060446024803590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405281815292919060208401838380828437509496506105f495505050505050565b005b341561018757600080fd5b610195600435602435610805565b60405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156101d75780820151838201526020016101bf565b50505050905090810190601f1680156102045780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561021e57600080fd5b61017a60043560243560443561092f565b341561023a57600080fd5b610245600435610a2e565b60405190815260200160405180910390f35b341561026257600080fd5b61026d600435610a44565b604051600160a060020a03909116815260200160405180910390f35b341561029457600080fd5b6102df600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610a5f95505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561031b578082015183820152602001610303565b50505050905090810190601f1680156103485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561036157600080fd5b61017a600480359060248035919060649060443590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610b7e95505050505050565b34156103bd57600080fd5b6102df600435610c7a565b34156103d357600080fd5b61017a600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610d4095505050505050565b341561042957600080fd5b61017a600435602435610e8a565b341561044257600080fd5b61044d600435610f63565b60405191825260208201526040908101905180910390f35b341561047057600080fd5b61017a600435600160a060020a0360243516610f80565b6000600160e060020a031982167f3b3b57de0000000000000000000000000000000000000000000000000000000014806104ea5750600160e060020a031982167fd8389dc500000000000000000000000000000000000000000000000000000000145b8061051e5750600160e060020a031982167f691f343100000000000000000000000000000000000000000000000000000000145b806105525750600160e060020a031982167f2203ab5600000000000000000000000000000000000000000000000000000000145b806105865750600160e060020a031982167fc869023300000000000000000000000000000000000000000000000000000000145b806105ba5750600160e060020a031982167f59d1d43c00000000000000000000000000000000000000000000000000000000145b806105ee5750600160e060020a031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561064d57600080fd5b6102c65a03f1151561065e57600080fd5b50505060405180519050600160a060020a031614151561067d57600080fd5b6000848152600160205260409081902083916005909101908590518082805190602001908083835b602083106106c45780518252601f1990920191602091820191016106a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051610708929160200190611083565b50826040518082805190602001908083835b602083106107395780518252601f19909201916020918201910161071a565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051908190039020847fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a75508560405160208082528190810183818151815260200191508051906020019080838360005b838110156107c55780820151838201526020016107ad565b50505050905090810190601f1680156107f25780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b600061080f611101565b60008481526001602081905260409091209092505b838311610922578284161580159061085d5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610917578060060160008481526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561090b5780601f106108e05761010080835404028352916020019161090b565b820191906000526020600020905b8154815290600101906020018083116108ee57829003601f168201915b50505050509150610927565b600290920291610824565b600092505b509250929050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561098857600080fd5b6102c65a03f1151561099957600080fd5b50505060405180519050600160a060020a03161415156109b857600080fd5b6040805190810160409081528482526020808301859052600087815260019091522060030181518155602082015160019091015550837f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46848460405191825260208201526040908101905180910390a250505050565b6000908152600160208190526040909120015490565b600090815260016020526040902054600160a060020a031690565b610a67611101565b60008381526001602052604090819020600501908390518082805190602001908083835b60208310610aaa5780518252601f199092019160209182019101610a8b565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b715780601f10610b4657610100808354040283529160200191610b71565b820191906000526020600020905b815481529060010190602001808311610b5457829003601f168201915b5050505050905092915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610bd757600080fd5b6102c65a03f11515610be857600080fd5b50505060405180519050600160a060020a0316141515610c0757600080fd5b6000198301831615610c1857600080fd5b60008481526001602090815260408083208684526006019091529020828051610c45929160200190611083565b5082847faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe360405160405180910390a350505050565b610c82611101565b6001600083600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d345780601f10610d0957610100808354040283529160200191610d34565b820191906000526020600020905b815481529060010190602001808311610d1757829003601f168201915b50505050509050919050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610d9957600080fd5b6102c65a03f11515610daa57600080fd5b50505060405180519050600160a060020a0316141515610dc957600080fd5b6000838152600160205260409020600201828051610deb929160200190611083565b50827fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f78360405160208082528190810183818151815260200191508051906020019080838360005b83811015610e4b578082015183820152602001610e33565b50505050905090810190601f168015610e785780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610ee357600080fd5b6102c65a03f11515610ef457600080fd5b50505060405180519050600160a060020a0316141515610f1357600080fd5b6000838152600160208190526040918290200183905583907f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc9084905190815260200160405180910390a2505050565b600090815260016020526040902060038101546004909101549091565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610fd957600080fd5b6102c65a03f11515610fea57600080fd5b50505060405180519050600160a060020a031614151561100957600080fd5b60008381526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03851617905583907f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd290849051600160a060020a03909116815260200160405180910390a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110c457805160ff19168380011785556110f1565b828001600101855582156110f1579182015b828111156110f15782518255916020019190600101906110d6565b506110fd929150611113565b5090565b60206040519081016040526000815290565b61112d91905b808211156110fd5760008155600101611119565b905600a165627a7a723058206016a807d9d5f6060e9c0d1c808e52d4ca30a4cab0140adcc6587bfc13bedf100029" - - -reverse_registrar_abi = json.loads('[{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"resolver","type":"address"}],"name":"claimWithResolver","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"claim","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"defaultResolver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"node","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"setName","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"resolverAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]') -reverse_registrar_bytecode = "6060604052341561000f57600080fd5b604051604080610d96833981016040528080519060200190919080519060200190919050506000826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be37f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561017a57600080fd5b6102c65a03f1151561018b57600080fd5b50505060405180519050905060008173ffffffffffffffffffffffffffffffffffffffff16141515610277578073ffffffffffffffffffffffffffffffffffffffff16631e83409a336000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b151561025a57600080fd5b6102c65a03f1151561026b57600080fd5b50505060405180519050505b505050610b0d806102896000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630f5a54661461007d5780631e83409a146100f15780633f15457f14610146578063828eab0e1461019b578063bffbe61c146101f0578063c47f002714610245575b600080fd5b341561008857600080fd5b6100d3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102be565b60405180826000191660001916815260200191505060405180910390f35b34156100fc57600080fd5b610128600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061086e565b60405180826000191660001916815260200191505060405180910390f35b341561015157600080fd5b610159610882565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101a657600080fd5b6101ae6108a7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101fb57600080fd5b610227600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108cd565b60405180826000191660001916815260200191505060405180910390f35b341561025057600080fd5b6102a0600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061092f565b60405180826000191660001916815260200191505060405180910390f35b60008060006102cc33610a80565b91507f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010282604051808360001916600019168152602001826000191660001916815260200192505050604051809103902092506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156103c157600080fd5b6102c65a03f115156103d257600080fd5b50505060405180519050905060008473ffffffffffffffffffffffffffffffffffffffff16141580156104eb57506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630178b8bf846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156104a057600080fd5b6102c65a03f115156104b157600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614155b1561071b573073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151561063b576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284306040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561062357600080fd5b6102c65a03f1151561063457600080fd5b5050503090505b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631896f70a84866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050600060405180830381600087803b151561070657600080fd5b6102c65a03f1151561071757600080fd5b5050505b8473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515610863576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284886040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561084e57600080fd5b6102c65a03f1151561085f57600080fd5b5050505b829250505092915050565b600061087b8260006102be565b9050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60007f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026108fc83610a80565b60405180836000191660001916815260200182600019166000191681526020019250505060405180910390209050919050565b600061095d30600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166102be565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637737221382846040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180836000191660001916815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610a185780820151818401526020810190506109fd565b50505050905090810190601f168015610a455780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b1515610a6457600080fd5b6102c65a03f11515610a7557600080fd5b505050809050919050565b60007f303132333435363738396162636465660000000000000000000000000000000060285b60018103905081600f85161a815360108404935060018103905081600f85161a815360108404935080610aa6576028600020925050509190505600a165627a7a72305820a8513240f040cd9ded89ca4d0c5bda58536850e642e1d933ad64158ef4c820660029" -reverse_registrar_bytecode_runtime = "606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630f5a54661461007d5780631e83409a146100f15780633f15457f14610146578063828eab0e1461019b578063bffbe61c146101f0578063c47f002714610245575b600080fd5b341561008857600080fd5b6100d3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102be565b60405180826000191660001916815260200191505060405180910390f35b34156100fc57600080fd5b610128600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061086e565b60405180826000191660001916815260200191505060405180910390f35b341561015157600080fd5b610159610882565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101a657600080fd5b6101ae6108a7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101fb57600080fd5b610227600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108cd565b60405180826000191660001916815260200191505060405180910390f35b341561025057600080fd5b6102a0600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061092f565b60405180826000191660001916815260200191505060405180910390f35b60008060006102cc33610a80565b91507f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010282604051808360001916600019168152602001826000191660001916815260200192505050604051809103902092506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156103c157600080fd5b6102c65a03f115156103d257600080fd5b50505060405180519050905060008473ffffffffffffffffffffffffffffffffffffffff16141580156104eb57506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630178b8bf846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156104a057600080fd5b6102c65a03f115156104b157600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614155b1561071b573073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151561063b576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284306040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561062357600080fd5b6102c65a03f1151561063457600080fd5b5050503090505b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631896f70a84866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050600060405180830381600087803b151561070657600080fd5b6102c65a03f1151561071757600080fd5b5050505b8473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515610863576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284886040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561084e57600080fd5b6102c65a03f1151561085f57600080fd5b5050505b829250505092915050565b600061087b8260006102be565b9050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60007f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026108fc83610a80565b60405180836000191660001916815260200182600019166000191681526020019250505060405180910390209050919050565b600061095d30600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166102be565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637737221382846040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180836000191660001916815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610a185780820151818401526020810190506109fd565b50505050905090810190601f168015610a455780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b1515610a6457600080fd5b6102c65a03f11515610a7557600080fd5b505050809050919050565b60007f303132333435363738396162636465660000000000000000000000000000000060285b60018103905081600f85161a815360108404935060018103905081600f85161a815360108404935080610aa6576028600020925050509190505600a165627a7a72305820a8513240f040cd9ded89ca4d0c5bda58536850e642e1d933ad64158ef4c820660029" - -reverse_resolver_abi = json.loads('[{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"_name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]') -reverse_resolver_bytecode = "6060604052341561000f57600080fd5b6040516020806106c9833981016040528080519060200190919050506000816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be37f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561013057600080fd5b6102c65a03f1151561014157600080fd5b50505060405180519050905060008173ffffffffffffffffffffffffffffffffffffffff1614151561022d578073ffffffffffffffffffffffffffffffffffffffff16631e83409a336000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b151561021057600080fd5b6102c65a03f1151561022157600080fd5b50505060405180519050505b505061048b8061023e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633f15457f1461005c578063691f3431146100b15780637737221314610151575b600080fd5b341561006757600080fd5b61006f6101bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100bc57600080fd5b6100d66004808035600019169060200190919050506101e0565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101165780820151818401526020810190506100fb565b50505050905090810190601f1680156101435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015c57600080fd5b6101b960048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610290565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b505050505081565b816000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3826000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561033157600080fd5b6102c65a03f1151561034257600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561038557600080fd5b8160016000856000191660001916815260200190815260200160002090805190602001906103b49291906103ba565b50505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103fb57805160ff1916838001178555610429565b82800160010185558215610429579182015b8281111561042857825182559160200191906001019061040d565b5b509050610436919061043a565b5090565b61045c91905b80821115610458576000816000905550600101610440565b5090565b905600a165627a7a72305820f4c4cb4d191f31a62c4de12f7aecf9b258ba17f3a6ee294191171f7d30c04ab00029" -reverse_resolver_bytecode_runtime = "606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633f15457f1461005c578063691f3431146100b15780637737221314610151575b600080fd5b341561006757600080fd5b61006f6101bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100bc57600080fd5b6100d66004808035600019169060200190919050506101e0565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101165780820151818401526020810190506100fb565b50505050905090810190601f1680156101435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015c57600080fd5b6101b960048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610290565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b505050505081565b816000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3826000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561033157600080fd5b6102c65a03f1151561034257600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561038557600080fd5b8160016000856000191660001916815260200190815260200160002090805190602001906103b49291906103ba565b50505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103fb57805160ff1916838001178555610429565b82800160010185558215610429579182015b8281111561042857825182559160200191906001019061040d565b5b509050610436919061043a565b5090565b61045c91905b80821115610458576000816000905550600101610440565b5090565b905600a165627a7a72305820f4c4cb4d191f31a62c4de12f7aecf9b258ba17f3a6ee294191171f7d30c04ab00029" +# flake8: noqa +import json + +registrar_abi = json.loads('[{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"releaseDeed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"getAllowedTime","outputs":[{"name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"unhashedName","type":"string"}],"name":"invalidateName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"hash","type":"bytes32"},{"name":"owner","type":"address"},{"name":"value","type":"uint256"},{"name":"salt","type":"bytes32"}],"name":"shaBid","outputs":[{"name":"sealedBid","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"bidder","type":"address"},{"name":"seal","type":"bytes32"}],"name":"cancelBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"entries","outputs":[{"name":"","type":"uint8"},{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_value","type":"uint256"},{"name":"_salt","type":"bytes32"}],"name":"unsealBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"transferRegistrars","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"}],"name":"sealedBids","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"newOwner","type":"address"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_timestamp","type":"uint256"}],"name":"isAllowed","outputs":[{"name":"allowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"finalizeAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registryStarted","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"launchLength","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sealedBid","type":"bytes32"}],"name":"newBid","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"labels","type":"bytes32[]"}],"name":"eraseNode","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hashes","type":"bytes32[]"}],"name":"startAuctions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"hash","type":"bytes32"},{"name":"deed","type":"address"},{"name":"registrationDate","type":"uint256"}],"name":"acceptRegistrarTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"startAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rootNode","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"hashes","type":"bytes32[]"},{"name":"sealedBid","type":"bytes32"}],"name":"startAuctionsAndBid","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[{"name":"_ens","type":"address"},{"name":"_rootNode","type":"bytes32"},{"name":"_startDate","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"AuctionStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"bidder","type":"address"},{"indexed":false,"name":"deposit","type":"uint256"}],"name":"NewBid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"status","type":"uint8"}],"name":"BidRevealed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"HashRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"}],"name":"HashReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"registrationDate","type":"uint256"}],"name":"HashInvalidated","type":"event"}]') +registrar_bytecode = "6060604052341561000f57600080fd5b60405160608061275783398101604052808051919060200180519190602001805160008054600160a060020a031916600160a060020a0387161781556001859055909250821190506100615742610063565b805b6004555050506126df806100786000396000f300606060405236156101175763ffffffff60e060020a6000350416630230a07c811461011c57806313c89a8f1461013457806315f733311461015c57806322ec1244146101ad5780632525f5c1146101d5578063267b6922146101f75780633f15457f1461025f57806347872b421461028e5780635ddae283146102aa5780635e431709146102c057806361d585da146102e257806379ce9fac1461031c578063935033371461033e578063983b94fb1461036b5780639c67f06f14610381578063ae1a0b0c14610394578063ce92dced146103c0578063de10f04b146103cb578063e27fe50f1461041a578063ea9e107a14610469578063ede8acdb1461048e578063faff50a8146104a4578063febefd61146104b7575b600080fd5b341561012757600080fd5b6101326004356104fd565b005b341561013f57600080fd5b61014a60043561072a565b60405190815260200160405180910390f35b341561016757600080fd5b61013260046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061074e95505050505050565b34156101b857600080fd5b61014a600435600160a060020a0360243516604435606435610a7a565b34156101e057600080fd5b610132600160a060020a0360043516602435610ac5565b341561020257600080fd5b61020d600435610c8c565b6040518086600581111561021d57fe5b60ff16815260200185600160a060020a0316600160a060020a031681526020018481526020018381526020018281526020019550505050505060405180910390f35b341561026a57600080fd5b610272610cd8565b604051600160a060020a03909116815260200160405180910390f35b341561029957600080fd5b610132600435602435604435610ce7565b34156102b557600080fd5b610132600435611254565b34156102cb57600080fd5b610272600160a060020a03600435166024356114b4565b34156102ed57600080fd5b6102f86004356114da565b6040518082600581111561030857fe5b60ff16815260200191505060405180910390f35b341561032757600080fd5b610132600435600160a060020a0360243516611550565b341561034957600080fd5b61035760043560243561169b565b604051901515815260200160405180910390f35b341561037657600080fd5b6101326004356116b1565b341561038c57600080fd5b61014a611919565b341561039f57600080fd5b6103a761191f565b60405163ffffffff909116815260200160405180910390f35b610132600435611926565b34156103d657600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a1a95505050505050565b341561042557600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a7595505050505050565b341561047457600080fd5b610132600435600160a060020a0360243516604435611aab565b341561049957600080fd5b610132600435611ab0565b34156104af57600080fd5b61014a611bfc565b61013260046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496505093359350611c0292505050565b60008082600261050c826114da565b600581111561051757fe5b1415806105a3575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561057257600080fd5b6102c65a03f1151561058357600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156105ad57600080fd5b600084815260026020526040902080546001820154919450600160a060020a031692506301e13380014210801561065f575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561063957600080fd5b6102c65a03f1151561064a57600080fd5b50505060405180519050600160a060020a0316145b1561066957600080fd5b60006002840181905560038401558254600160a060020a031916835561068e84611c14565b81600160a060020a031663bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156106d657600080fd5b6102c65a03f115156106e757600080fd5b505050600283015484907f292b79b9246fa2c8e77d3fe195b251f9cb839d7d038e667c069ee7708c631e169060405190815260200160405180910390a250505050565b6004547001000000000000000000000000000000006249d400818404020401919050565b600080826040518082805190602001908083835b602083106107815780518252601f199092019160209182019101610762565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206002806107ba836114da565b60058111156107c557fe5b146107cf57600080fd5b60066107da86611e12565b11156107e557600080fd5b846040518082805190602001908083835b602083106108155780518252601f1990920191602091820191016107f6565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206000818152600260205260409020909450925061085e84611c14565b8254600160a060020a0316156109b5576108838360020154662386f26fc10000611ec3565b60028085018290558454600160a060020a03169163b0c80972919004600060405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156108de57600080fd5b6102c65a03f115156108ef57600080fd5b50508354600160a060020a031690506313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561094257600080fd5b6102c65a03f1151561095357600080fd5b50508354600160a060020a0316905063bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156109a057600080fd5b6102c65a03f115156109b157600080fd5b5050505b846040518082805190602001908083835b602083106109e55780518252601f1990920191602091820191016109c6565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051809103902084600019167f1f9c649fe47e58bb60f4e52f0d90e4c47a526c9f90c5113df842c025970b66ad8560020154866001015460405191825260208201526040908101905180910390a3505060006002820181905560038201558054600160a060020a03191690555050565b600084848484604051938452600160a060020a03929092166c010000000000000000000000000260208401526034830152605482015260740160405180910390209050949350505050565b600160a060020a03808316600090815260036020908152604080832085845290915290205416801580610b61575062069780600160a060020a0382166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610b3d57600080fd5b6102c65a03f11515610b4e57600080fd5b5050506040518051905001621275000142105b15610b6b57600080fd5b80600160a060020a03166313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b1515610bb957600080fd5b6102c65a03f11515610bca57600080fd5b50505080600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610c1457600080fd5b6102c65a03f11515610c2557600080fd5b505050600160a060020a03831660008181526003602090815260408083208684529091528082208054600160a060020a03191690558491600080516020612694833981519152916005905191825260ff1660208201526040908101905180910390a3505050565b60008181526002602052604081208190819081908190610cab876114da565b815460018301546002840154600390940154929a600160a060020a03909216995097509195509350915050565b600054600160a060020a031681565b600080600080600080610cfc89338a8a610a7a565b600160a060020a033381166000908152600360209081526040808320858452909152902054919750169450841515610d3357600080fd5b600160a060020a0333811660009081526003602090815260408083208a845282528083208054600160a060020a03191690558c835260029091528082209650610dd6928b9290891691633fa4f245919051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610db657600080fd5b6102c65a03f11515610dc757600080fd5b50505060405180519050611edb565b925084600160a060020a031663b0c8097284600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b1515610e2757600080fd5b6102c65a03f11515610e3857600080fd5b505050610e44896114da565b91506002826005811115610e5457fe5b1415610ef25784600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610ea157600080fd5b6102c65a03f11515610eb257600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600160405191825260ff1660208201526040908101905180910390a3611249565b6004826005811115610f0057fe5b14610f0a57600080fd5b662386f26fc10000831080610f88575060018401546202a2ff1901600160a060020a0386166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610f6b57600080fd5b6102c65a03f11515610f7c57600080fd5b50505060405180519050115b156110265784600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610fd557600080fd5b6102c65a03f11515610fe657600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600060405191825260ff1660208201526040908101905180910390a3611249565b8360030154831115611108578354600160a060020a0316156110a257508254600160a060020a03168063bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561108d57600080fd5b6102c65a03f1151561109e57600080fd5b5050505b600384018054600280870191909155908490558454600160a060020a031916600160a060020a038781169190911786553316908a9060008051602061269483398151915290869060405191825260ff1660208201526040908101905180910390a3611249565b83600201548311156111b45760028401839055600160a060020a03851663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561116357600080fd5b6102c65a03f1151561117457600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600360405191825260ff1660208201526040908101905180910390a3611249565b84600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156111fc57600080fd5b6102c65a03f1151561120d57600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600460405191825260ff1660208201526040908101905180910390a35b505050505050505050565b600080826002611263826114da565b600581111561126e57fe5b1415806112fa575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156112c957600080fd5b6102c65a03f115156112da57600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561130457600080fd5b60008054600154600160a060020a03909116916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561135b57600080fd5b6102c65a03f1151561136c57600080fd5b50505060405180519050925030600160a060020a031683600160a060020a0316141561139757600080fd5b600084815260026020526040908190208054909350600160a060020a03169063faab9d399085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b15156113fa57600080fd5b6102c65a03f1151561140b57600080fd5b505082546001840154600160a060020a03808716935063ea9e107a92889291169060405160e060020a63ffffffff86160281526004810193909352600160a060020a0390911660248301526044820152606401600060405180830381600087803b151561147757600080fd5b6102c65a03f1151561148857600080fd5b50508254600160a060020a03191683555050600060018201819055600282018190556003909101555050565b6003602090815260009283526040808420909152908252902054600160a060020a031681565b60008181526002602052604081206114f2834261169b565b1515611501576005915061154a565b80600101544210156115315760018101546202a2ff1901421015611528576001915061154a565b6004915061154a565b60038101541515611545576000915061154a565b600291505b50919050565b600082600261155e826114da565b600581111561156957fe5b1415806115f5575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156115c457600080fd5b6102c65a03f115156115d557600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156115ff57600080fd5b600160a060020a038316151561161457600080fd5b600084815260026020526040908190208054909350600160a060020a0316906313af40359085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561167757600080fd5b6102c65a03f1151561168857600080fd5b5050506116958484611eec565b50505050565b60006116a68361072a565b821190505b92915050565b60008160026116bf826114da565b60058111156116ca57fe5b141580611756575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561172557600080fd5b6102c65a03f1151561173657600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561176057600080fd5b60008381526002602081905260409091209081015490925061178990662386f26fc10000611ec3565b600283018190558254600160a060020a03169063b0c8097290600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156117e157600080fd5b6102c65a03f115156117f257600080fd5b5050825461186291508490600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561184257600080fd5b6102c65a03f1151561185357600080fd5b50505060405180519050611eec565b8154600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156118a957600080fd5b6102c65a03f115156118ba57600080fd5b50505060405180519050600160a060020a031683600019167f0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e6708460020154856001015460405191825260208201526040908101905180910390a3505050565b60045481565b6249d40081565b600160a060020a0333811660009081526003602090815260408083208584529091528120549091168190111561195b57600080fd5b662386f26fc1000034101561196f57600080fd5b3433611979612187565b600160a060020a0390911681526020016040518091039082f080151561199e57600080fd5b33600160a060020a039081166000818152600360209081526040808320898452909152908190208054600160a060020a0319169385169390931790925591935090915083907fb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc299034905190815260200160405180910390a35050565b80511515611a2757600080fd5b6002611a4b82600184510381518110611a3c57fe5b906020019060200201516114da565b6005811115611a5657fe5b1415611a6157600080fd5b611a72600182510382600154611fd6565b50565b60005b8151811015611aa757611a9f828281518110611a9057fe5b90602001906020020151611ab0565b600101611a78565b5050565b505050565b600080600454421080611aca5750600454630784ce000142115b80611b51575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611b2a57600080fd5b6102c65a03f11515611b3b57600080fd5b50505060405180519050600160a060020a031614155b15611b5b57600080fd5b611b64836114da565b91506001826005811115611b7457fe5b1415611b7f57611aab565b6000826005811115611b8d57fe5b14611b9757600080fd5b50600082815260026020819052604080832042620697800160018201819055928101849055600381019390935584917f87e97e825a1d1fa0c54e1d36c7506c1dea8b1efd451fe68b000cf96f7cf40003915190815260200160405180910390a2505050565b60015481565b611c0b82611a75565b611aa781611926565b60008054600154600160a060020a033081169216906302571be390846040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611c6d57600080fd5b6102c65a03f11515611c7e57600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390843060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611cfd57600080fd5b6102c65a03f11515611d0e57600080fd5b5050506001548260405191825260208201526040908101905190819003902060008054919250600160a060020a0390911690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611d8c57600080fd5b6102c65a03f11515611d9d57600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611dfa57600080fd5b6102c65a03f11515611e0b57600080fd5b5050505050565b600060018201818080838651019250600091505b82841015611eba5760ff845116905060808160ff161015611e4c57600184019350611eaf565b60e08160ff161015611e6357600284019350611eaf565b60f08160ff161015611e7a57600384019350611eaf565b60f88160ff161015611e9157600484019350611eaf565b60fc8160ff161015611ea857600584019350611eaf565b6006840193505b600190910190611e26565b50949350505050565b600081831115611ed45750816116ab565b50806116ab565b600081831015611ed45750816116ab565b60008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611f4657600080fd5b6102c65a03f11515611f5757600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390848460405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611dfa57600080fd5b600054600160a060020a03166306ab592382848681518110611ff457fe5b906020019060200201513060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561204b57600080fd5b6102c65a03f1151561205c57600080fd5b5050508082848151811061206c57fe5b906020019060200201516040519182526020820152604090810190518091039020905060008311156120a6576120a6600184038383611fd6565b60008054600160a060020a031690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561210057600080fd5b6102c65a03f1151561211157600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561216e57600080fd5b6102c65a03f1151561217f57600080fd5b505050505050565b6040516104fc8061219883390190560060606040526040516020806104fc8339810160405280805160028054600160a060020a03928316600160a060020a03199182161790915560008054339093169290911691909117905550504260019081556005805460ff191690911790553460045561048c806100706000396000f300606060405236156100a15763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305b3441081146100a65780630b5ab3d5146100cb57806313af4035146100e05780632b20e397146100ff5780633fa4f2451461012e578063674f220f146101415780638da5cb5b14610154578063b0c8097214610167578063bbe4277114610182578063faab9d3914610198575b600080fd5b34156100b157600080fd5b6100b96101b7565b60405190815260200160405180910390f35b34156100d657600080fd5b6100de6101bd565b005b34156100eb57600080fd5b6100de600160a060020a0360043516610207565b341561010a57600080fd5b6101126102b0565b604051600160a060020a03909116815260200160405180910390f35b341561013957600080fd5b6100b96102bf565b341561014c57600080fd5b6101126102c5565b341561015f57600080fd5b6101126102d4565b341561017257600080fd5b6100de60043560243515156102e3565b341561018d57600080fd5b6100de60043561036c565b34156101a357600080fd5b6100de600160a060020a0360043516610416565b60015481565b60055460ff16156101cd57600080fd5b600254600160a060020a039081169030163180156108fc0290604051600060405180830381858888f19350505050156102055761deadff5b565b60005433600160a060020a0390811691161461022257600080fd5b600160a060020a038116151561023757600080fd5b6002805460038054600160a060020a0380841673ffffffffffffffffffffffffffffffffffffffff19928316179092559091169083161790557fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3681604051600160a060020a03909116815260200160405180910390a150565b600054600160a060020a031681565b60045481565b600354600160a060020a031681565b600254600160a060020a031681565b60005433600160a060020a039081169116146102fe57600080fd5b60055460ff16151561030f57600080fd5b81600454101561031e57600080fd5b6004829055600254600160a060020a039081169030163183900380156108fc0290604051600060405180830381858888f1935050505015801561035e5750805b1561036857600080fd5b5050565b60005433600160a060020a0390811691161461038757600080fd5b60055460ff16151561039857600080fd5b6005805460ff1916905561dead6103e8600160a060020a03301631838203020480156108fc0290604051600060405180830381858888f1935050505015156103df57600080fd5b7fbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf1360405160405180910390a16104136101bd565b50565b60005433600160a060020a0390811691161461043157600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600a165627a7a7230582023849fab751378a237489f526a289ede7796b7f6b7dac5a973b8c3ca25f368a800297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a72305820e6807b87ab11a69864cefed52eef7f9c4635fd0e26312e944bbbcbff5cd26d920029" +registrar_bytecode_runtime = "606060405236156101175763ffffffff60e060020a6000350416630230a07c811461011c57806313c89a8f1461013457806315f733311461015c57806322ec1244146101ad5780632525f5c1146101d5578063267b6922146101f75780633f15457f1461025f57806347872b421461028e5780635ddae283146102aa5780635e431709146102c057806361d585da146102e257806379ce9fac1461031c578063935033371461033e578063983b94fb1461036b5780639c67f06f14610381578063ae1a0b0c14610394578063ce92dced146103c0578063de10f04b146103cb578063e27fe50f1461041a578063ea9e107a14610469578063ede8acdb1461048e578063faff50a8146104a4578063febefd61146104b7575b600080fd5b341561012757600080fd5b6101326004356104fd565b005b341561013f57600080fd5b61014a60043561072a565b60405190815260200160405180910390f35b341561016757600080fd5b61013260046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061074e95505050505050565b34156101b857600080fd5b61014a600435600160a060020a0360243516604435606435610a7a565b34156101e057600080fd5b610132600160a060020a0360043516602435610ac5565b341561020257600080fd5b61020d600435610c8c565b6040518086600581111561021d57fe5b60ff16815260200185600160a060020a0316600160a060020a031681526020018481526020018381526020018281526020019550505050505060405180910390f35b341561026a57600080fd5b610272610cd8565b604051600160a060020a03909116815260200160405180910390f35b341561029957600080fd5b610132600435602435604435610ce7565b34156102b557600080fd5b610132600435611254565b34156102cb57600080fd5b610272600160a060020a03600435166024356114b4565b34156102ed57600080fd5b6102f86004356114da565b6040518082600581111561030857fe5b60ff16815260200191505060405180910390f35b341561032757600080fd5b610132600435600160a060020a0360243516611550565b341561034957600080fd5b61035760043560243561169b565b604051901515815260200160405180910390f35b341561037657600080fd5b6101326004356116b1565b341561038c57600080fd5b61014a611919565b341561039f57600080fd5b6103a761191f565b60405163ffffffff909116815260200160405180910390f35b610132600435611926565b34156103d657600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a1a95505050505050565b341561042557600080fd5b6101326004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650611a7595505050505050565b341561047457600080fd5b610132600435600160a060020a0360243516604435611aab565b341561049957600080fd5b610132600435611ab0565b34156104af57600080fd5b61014a611bfc565b61013260046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496505093359350611c0292505050565b60008082600261050c826114da565b600581111561051757fe5b1415806105a3575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561057257600080fd5b6102c65a03f1151561058357600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156105ad57600080fd5b600084815260026020526040902080546001820154919450600160a060020a031692506301e13380014210801561065f575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561063957600080fd5b6102c65a03f1151561064a57600080fd5b50505060405180519050600160a060020a0316145b1561066957600080fd5b60006002840181905560038401558254600160a060020a031916835561068e84611c14565b81600160a060020a031663bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156106d657600080fd5b6102c65a03f115156106e757600080fd5b505050600283015484907f292b79b9246fa2c8e77d3fe195b251f9cb839d7d038e667c069ee7708c631e169060405190815260200160405180910390a250505050565b6004547001000000000000000000000000000000006249d400818404020401919050565b600080826040518082805190602001908083835b602083106107815780518252601f199092019160209182019101610762565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206002806107ba836114da565b60058111156107c557fe5b146107cf57600080fd5b60066107da86611e12565b11156107e557600080fd5b846040518082805190602001908083835b602083106108155780518252601f1990920191602091820191016107f6565b6001836020036101000a03801982511681845116179092525050509190910192506040915050519081900390206000818152600260205260409020909450925061085e84611c14565b8254600160a060020a0316156109b5576108838360020154662386f26fc10000611ec3565b60028085018290558454600160a060020a03169163b0c80972919004600060405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156108de57600080fd5b6102c65a03f115156108ef57600080fd5b50508354600160a060020a031690506313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561094257600080fd5b6102c65a03f1151561095357600080fd5b50508354600160a060020a0316905063bbe427716103e860405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156109a057600080fd5b6102c65a03f115156109b157600080fd5b5050505b846040518082805190602001908083835b602083106109e55780518252601f1990920191602091820191016109c6565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051809103902084600019167f1f9c649fe47e58bb60f4e52f0d90e4c47a526c9f90c5113df842c025970b66ad8560020154866001015460405191825260208201526040908101905180910390a3505060006002820181905560038201558054600160a060020a03191690555050565b600084848484604051938452600160a060020a03929092166c010000000000000000000000000260208401526034830152605482015260740160405180910390209050949350505050565b600160a060020a03808316600090815260036020908152604080832085845290915290205416801580610b61575062069780600160a060020a0382166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610b3d57600080fd5b6102c65a03f11515610b4e57600080fd5b5050506040518051905001621275000142105b15610b6b57600080fd5b80600160a060020a03166313af40353360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b1515610bb957600080fd5b6102c65a03f11515610bca57600080fd5b50505080600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610c1457600080fd5b6102c65a03f11515610c2557600080fd5b505050600160a060020a03831660008181526003602090815260408083208684529091528082208054600160a060020a03191690558491600080516020612694833981519152916005905191825260ff1660208201526040908101905180910390a3505050565b60008181526002602052604081208190819081908190610cab876114da565b815460018301546002840154600390940154929a600160a060020a03909216995097509195509350915050565b600054600160a060020a031681565b600080600080600080610cfc89338a8a610a7a565b600160a060020a033381166000908152600360209081526040808320858452909152902054919750169450841515610d3357600080fd5b600160a060020a0333811660009081526003602090815260408083208a845282528083208054600160a060020a03191690558c835260029091528082209650610dd6928b9290891691633fa4f245919051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610db657600080fd5b6102c65a03f11515610dc757600080fd5b50505060405180519050611edb565b925084600160a060020a031663b0c8097284600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b1515610e2757600080fd5b6102c65a03f11515610e3857600080fd5b505050610e44896114da565b91506002826005811115610e5457fe5b1415610ef25784600160a060020a031663bbe42771600560405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610ea157600080fd5b6102c65a03f11515610eb257600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600160405191825260ff1660208201526040908101905180910390a3611249565b6004826005811115610f0057fe5b14610f0a57600080fd5b662386f26fc10000831080610f88575060018401546202a2ff1901600160a060020a0386166305b344106000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610f6b57600080fd5b6102c65a03f11515610f7c57600080fd5b50505060405180519050115b156110265784600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b1515610fd557600080fd5b6102c65a03f11515610fe657600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600060405191825260ff1660208201526040908101905180910390a3611249565b8360030154831115611108578354600160a060020a0316156110a257508254600160a060020a03168063bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561108d57600080fd5b6102c65a03f1151561109e57600080fd5b5050505b600384018054600280870191909155908490558454600160a060020a031916600160a060020a038781169190911786553316908a9060008051602061269483398151915290869060405191825260ff1660208201526040908101905180910390a3611249565b83600201548311156111b45760028401839055600160a060020a03851663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b151561116357600080fd5b6102c65a03f1151561117457600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600360405191825260ff1660208201526040908101905180910390a3611249565b84600160a060020a031663bbe427716103e360405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156111fc57600080fd5b6102c65a03f1151561120d57600080fd5b5050600160a060020a03331690508960008051602061269483398151915285600460405191825260ff1660208201526040908101905180910390a35b505050505050505050565b600080826002611263826114da565b600581111561126e57fe5b1415806112fa575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156112c957600080fd5b6102c65a03f115156112da57600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561130457600080fd5b60008054600154600160a060020a03909116916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561135b57600080fd5b6102c65a03f1151561136c57600080fd5b50505060405180519050925030600160a060020a031683600160a060020a0316141561139757600080fd5b600084815260026020526040908190208054909350600160a060020a03169063faab9d399085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b15156113fa57600080fd5b6102c65a03f1151561140b57600080fd5b505082546001840154600160a060020a03808716935063ea9e107a92889291169060405160e060020a63ffffffff86160281526004810193909352600160a060020a0390911660248301526044820152606401600060405180830381600087803b151561147757600080fd5b6102c65a03f1151561148857600080fd5b50508254600160a060020a03191683555050600060018201819055600282018190556003909101555050565b6003602090815260009283526040808420909152908252902054600160a060020a031681565b60008181526002602052604081206114f2834261169b565b1515611501576005915061154a565b80600101544210156115315760018101546202a2ff1901421015611528576001915061154a565b6004915061154a565b60038101541515611545576000915061154a565b600291505b50919050565b600082600261155e826114da565b600581111561156957fe5b1415806115f5575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156115c457600080fd5b6102c65a03f115156115d557600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b156115ff57600080fd5b600160a060020a038316151561161457600080fd5b600084815260026020526040908190208054909350600160a060020a0316906313af40359085905160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b151561167757600080fd5b6102c65a03f1151561168857600080fd5b5050506116958484611eec565b50505050565b60006116a68361072a565b821190505b92915050565b60008160026116bf826114da565b60058111156116ca57fe5b141580611756575060008181526002602052604080822054600160a060020a031691638da5cb5b9151602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561172557600080fd5b6102c65a03f1151561173657600080fd5b50505060405180519050600160a060020a031633600160a060020a031614155b1561176057600080fd5b60008381526002602081905260409091209081015490925061178990662386f26fc10000611ec3565b600283018190558254600160a060020a03169063b0c8097290600160405160e060020a63ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b15156117e157600080fd5b6102c65a03f115156117f257600080fd5b5050825461186291508490600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561184257600080fd5b6102c65a03f1151561185357600080fd5b50505060405180519050611eec565b8154600160a060020a0316638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156118a957600080fd5b6102c65a03f115156118ba57600080fd5b50505060405180519050600160a060020a031683600019167f0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e6708460020154856001015460405191825260208201526040908101905180910390a3505050565b60045481565b6249d40081565b600160a060020a0333811660009081526003602090815260408083208584529091528120549091168190111561195b57600080fd5b662386f26fc1000034101561196f57600080fd5b3433611979612187565b600160a060020a0390911681526020016040518091039082f080151561199e57600080fd5b33600160a060020a039081166000818152600360209081526040808320898452909152908190208054600160a060020a0319169385169390931790925591935090915083907fb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc299034905190815260200160405180910390a35050565b80511515611a2757600080fd5b6002611a4b82600184510381518110611a3c57fe5b906020019060200201516114da565b6005811115611a5657fe5b1415611a6157600080fd5b611a72600182510382600154611fd6565b50565b60005b8151811015611aa757611a9f828281518110611a9057fe5b90602001906020020151611ab0565b600101611a78565b5050565b505050565b600080600454421080611aca5750600454630784ce000142115b80611b51575060008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611b2a57600080fd5b6102c65a03f11515611b3b57600080fd5b50505060405180519050600160a060020a031614155b15611b5b57600080fd5b611b64836114da565b91506001826005811115611b7457fe5b1415611b7f57611aab565b6000826005811115611b8d57fe5b14611b9757600080fd5b50600082815260026020819052604080832042620697800160018201819055928101849055600381019390935584917f87e97e825a1d1fa0c54e1d36c7506c1dea8b1efd451fe68b000cf96f7cf40003915190815260200160405180910390a2505050565b60015481565b611c0b82611a75565b611aa781611926565b60008054600154600160a060020a033081169216906302571be390846040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611c6d57600080fd5b6102c65a03f11515611c7e57600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390843060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611cfd57600080fd5b6102c65a03f11515611d0e57600080fd5b5050506001548260405191825260208201526040908101905190819003902060008054919250600160a060020a0390911690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611d8c57600080fd5b6102c65a03f11515611d9d57600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b1515611dfa57600080fd5b6102c65a03f11515611e0b57600080fd5b5050505050565b600060018201818080838651019250600091505b82841015611eba5760ff845116905060808160ff161015611e4c57600184019350611eaf565b60e08160ff161015611e6357600284019350611eaf565b60f08160ff161015611e7a57600384019350611eaf565b60f88160ff161015611e9157600484019350611eaf565b60fc8160ff161015611ea857600584019350611eaf565b6006840193505b600190910190611e26565b50949350505050565b600081831115611ed45750816116ab565b50806116ab565b600081831015611ed45750816116ab565b60008054600154600160a060020a03308116939216916302571be391906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611f4657600080fd5b6102c65a03f11515611f5757600080fd5b50505060405180519050600160a060020a03161415611aa757600054600154600160a060020a03909116906306ab592390848460405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b1515611dfa57600080fd5b600054600160a060020a03166306ab592382848681518110611ff457fe5b906020019060200201513060405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561204b57600080fd5b6102c65a03f1151561205c57600080fd5b5050508082848151811061206c57fe5b906020019060200201516040519182526020820152604090810190518091039020905060008311156120a6576120a6600184038383611fd6565b60008054600160a060020a031690631896f70a90839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561210057600080fd5b6102c65a03f1151561211157600080fd5b505060008054600160a060020a03169150635b0fc9c390839060405160e060020a63ffffffff85160281526004810192909252600160a060020a03166024820152604401600060405180830381600087803b151561216e57600080fd5b6102c65a03f1151561217f57600080fd5b505050505050565b6040516104fc8061219883390190560060606040526040516020806104fc8339810160405280805160028054600160a060020a03928316600160a060020a03199182161790915560008054339093169290911691909117905550504260019081556005805460ff191690911790553460045561048c806100706000396000f300606060405236156100a15763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305b3441081146100a65780630b5ab3d5146100cb57806313af4035146100e05780632b20e397146100ff5780633fa4f2451461012e578063674f220f146101415780638da5cb5b14610154578063b0c8097214610167578063bbe4277114610182578063faab9d3914610198575b600080fd5b34156100b157600080fd5b6100b96101b7565b60405190815260200160405180910390f35b34156100d657600080fd5b6100de6101bd565b005b34156100eb57600080fd5b6100de600160a060020a0360043516610207565b341561010a57600080fd5b6101126102b0565b604051600160a060020a03909116815260200160405180910390f35b341561013957600080fd5b6100b96102bf565b341561014c57600080fd5b6101126102c5565b341561015f57600080fd5b6101126102d4565b341561017257600080fd5b6100de60043560243515156102e3565b341561018d57600080fd5b6100de60043561036c565b34156101a357600080fd5b6100de600160a060020a0360043516610416565b60015481565b60055460ff16156101cd57600080fd5b600254600160a060020a039081169030163180156108fc0290604051600060405180830381858888f19350505050156102055761deadff5b565b60005433600160a060020a0390811691161461022257600080fd5b600160a060020a038116151561023757600080fd5b6002805460038054600160a060020a0380841673ffffffffffffffffffffffffffffffffffffffff19928316179092559091169083161790557fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3681604051600160a060020a03909116815260200160405180910390a150565b600054600160a060020a031681565b60045481565b600354600160a060020a031681565b600254600160a060020a031681565b60005433600160a060020a039081169116146102fe57600080fd5b60055460ff16151561030f57600080fd5b81600454101561031e57600080fd5b6004829055600254600160a060020a039081169030163183900380156108fc0290604051600060405180830381858888f1935050505015801561035e5750805b1561036857600080fd5b5050565b60005433600160a060020a0390811691161461038757600080fd5b60055460ff16151561039857600080fd5b6005805460ff1916905561dead6103e8600160a060020a03301631838203020480156108fc0290604051600060405180830381858888f1935050505015156103df57600080fd5b7fbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf1360405160405180910390a16104136101bd565b50565b60005433600160a060020a0390811691161461043157600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600a165627a7a7230582023849fab751378a237489f526a289ede7796b7f6b7dac5a973b8c3ca25f368a800297b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7a165627a7a72305820e6807b87ab11a69864cefed52eef7f9c4635fd0e26312e944bbbcbff5cd26d920029" + +resolver_abi = json.loads('[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"key","type":"string"},{"name":"value","type":"string"}],"name":"setText","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"key","type":"string"}],"name":"text","outputs":[{"name":"ret","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"indexedKey","type":"string"},{"indexed":false,"name":"key","type":"string"}],"name":"TextChanged","type":"event"}]') +resolver_bytecode = "6060604052341561000f57600080fd5b6040516020806111b08339810160405280805160008054600160a060020a03909216600160a060020a0319909216919091179055505061115c806100546000396000f300606060405236156100a95763ffffffff60e060020a60003504166301ffc9a781146100ae57806310f13a8c146100e25780632203ab561461017c57806329cd62ea146102135780632dff69411461022f5780633b3b57de1461025757806359d1d43c14610289578063623195b014610356578063691f3431146103b257806377372213146103c8578063c3d014d61461041e578063c869023314610437578063d5fa2b0014610465575b600080fd5b34156100b957600080fd5b6100ce600160e060020a031960043516610487565b604051901515815260200160405180910390f35b34156100ed57600080fd5b61017a600480359060446024803590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405281815292919060208401838380828437509496506105f495505050505050565b005b341561018757600080fd5b610195600435602435610805565b60405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156101d75780820151838201526020016101bf565b50505050905090810190601f1680156102045780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561021e57600080fd5b61017a60043560243560443561092f565b341561023a57600080fd5b610245600435610a2e565b60405190815260200160405180910390f35b341561026257600080fd5b61026d600435610a44565b604051600160a060020a03909116815260200160405180910390f35b341561029457600080fd5b6102df600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610a5f95505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561031b578082015183820152602001610303565b50505050905090810190601f1680156103485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561036157600080fd5b61017a600480359060248035919060649060443590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610b7e95505050505050565b34156103bd57600080fd5b6102df600435610c7a565b34156103d357600080fd5b61017a600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610d4095505050505050565b341561042957600080fd5b61017a600435602435610e8a565b341561044257600080fd5b61044d600435610f63565b60405191825260208201526040908101905180910390f35b341561047057600080fd5b61017a600435600160a060020a0360243516610f80565b6000600160e060020a031982167f3b3b57de0000000000000000000000000000000000000000000000000000000014806104ea5750600160e060020a031982167fd8389dc500000000000000000000000000000000000000000000000000000000145b8061051e5750600160e060020a031982167f691f343100000000000000000000000000000000000000000000000000000000145b806105525750600160e060020a031982167f2203ab5600000000000000000000000000000000000000000000000000000000145b806105865750600160e060020a031982167fc869023300000000000000000000000000000000000000000000000000000000145b806105ba5750600160e060020a031982167f59d1d43c00000000000000000000000000000000000000000000000000000000145b806105ee5750600160e060020a031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561064d57600080fd5b6102c65a03f1151561065e57600080fd5b50505060405180519050600160a060020a031614151561067d57600080fd5b6000848152600160205260409081902083916005909101908590518082805190602001908083835b602083106106c45780518252601f1990920191602091820191016106a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051610708929160200190611083565b50826040518082805190602001908083835b602083106107395780518252601f19909201916020918201910161071a565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051908190039020847fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a75508560405160208082528190810183818151815260200191508051906020019080838360005b838110156107c55780820151838201526020016107ad565b50505050905090810190601f1680156107f25780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b600061080f611101565b60008481526001602081905260409091209092505b838311610922578284161580159061085d5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610917578060060160008481526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561090b5780601f106108e05761010080835404028352916020019161090b565b820191906000526020600020905b8154815290600101906020018083116108ee57829003601f168201915b50505050509150610927565b600290920291610824565b600092505b509250929050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561098857600080fd5b6102c65a03f1151561099957600080fd5b50505060405180519050600160a060020a03161415156109b857600080fd5b6040805190810160409081528482526020808301859052600087815260019091522060030181518155602082015160019091015550837f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46848460405191825260208201526040908101905180910390a250505050565b6000908152600160208190526040909120015490565b600090815260016020526040902054600160a060020a031690565b610a67611101565b60008381526001602052604090819020600501908390518082805190602001908083835b60208310610aaa5780518252601f199092019160209182019101610a8b565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b715780601f10610b4657610100808354040283529160200191610b71565b820191906000526020600020905b815481529060010190602001808311610b5457829003601f168201915b5050505050905092915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610bd757600080fd5b6102c65a03f11515610be857600080fd5b50505060405180519050600160a060020a0316141515610c0757600080fd5b6000198301831615610c1857600080fd5b60008481526001602090815260408083208684526006019091529020828051610c45929160200190611083565b5082847faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe360405160405180910390a350505050565b610c82611101565b6001600083600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d345780601f10610d0957610100808354040283529160200191610d34565b820191906000526020600020905b815481529060010190602001808311610d1757829003601f168201915b50505050509050919050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610d9957600080fd5b6102c65a03f11515610daa57600080fd5b50505060405180519050600160a060020a0316141515610dc957600080fd5b6000838152600160205260409020600201828051610deb929160200190611083565b50827fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f78360405160208082528190810183818151815260200191508051906020019080838360005b83811015610e4b578082015183820152602001610e33565b50505050905090810190601f168015610e785780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610ee357600080fd5b6102c65a03f11515610ef457600080fd5b50505060405180519050600160a060020a0316141515610f1357600080fd5b6000838152600160208190526040918290200183905583907f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc9084905190815260200160405180910390a2505050565b600090815260016020526040902060038101546004909101549091565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610fd957600080fd5b6102c65a03f11515610fea57600080fd5b50505060405180519050600160a060020a031614151561100957600080fd5b60008381526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03851617905583907f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd290849051600160a060020a03909116815260200160405180910390a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110c457805160ff19168380011785556110f1565b828001600101855582156110f1579182015b828111156110f15782518255916020019190600101906110d6565b506110fd929150611113565b5090565b60206040519081016040526000815290565b61112d91905b808211156110fd5760008155600101611119565b905600a165627a7a723058206016a807d9d5f6060e9c0d1c808e52d4ca30a4cab0140adcc6587bfc13bedf100029" +resolver_bytecode_runtime = "606060405236156100a95763ffffffff60e060020a60003504166301ffc9a781146100ae57806310f13a8c146100e25780632203ab561461017c57806329cd62ea146102135780632dff69411461022f5780633b3b57de1461025757806359d1d43c14610289578063623195b014610356578063691f3431146103b257806377372213146103c8578063c3d014d61461041e578063c869023314610437578063d5fa2b0014610465575b600080fd5b34156100b957600080fd5b6100ce600160e060020a031960043516610487565b604051901515815260200160405180910390f35b34156100ed57600080fd5b61017a600480359060446024803590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405281815292919060208401838380828437509496506105f495505050505050565b005b341561018757600080fd5b610195600435602435610805565b60405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156101d75780820151838201526020016101bf565b50505050905090810190601f1680156102045780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561021e57600080fd5b61017a60043560243560443561092f565b341561023a57600080fd5b610245600435610a2e565b60405190815260200160405180910390f35b341561026257600080fd5b61026d600435610a44565b604051600160a060020a03909116815260200160405180910390f35b341561029457600080fd5b6102df600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610a5f95505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561031b578082015183820152602001610303565b50505050905090810190601f1680156103485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561036157600080fd5b61017a600480359060248035919060649060443590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610b7e95505050505050565b34156103bd57600080fd5b6102df600435610c7a565b34156103d357600080fd5b61017a600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610d4095505050505050565b341561042957600080fd5b61017a600435602435610e8a565b341561044257600080fd5b61044d600435610f63565b60405191825260208201526040908101905180910390f35b341561047057600080fd5b61017a600435600160a060020a0360243516610f80565b6000600160e060020a031982167f3b3b57de0000000000000000000000000000000000000000000000000000000014806104ea5750600160e060020a031982167fd8389dc500000000000000000000000000000000000000000000000000000000145b8061051e5750600160e060020a031982167f691f343100000000000000000000000000000000000000000000000000000000145b806105525750600160e060020a031982167f2203ab5600000000000000000000000000000000000000000000000000000000145b806105865750600160e060020a031982167fc869023300000000000000000000000000000000000000000000000000000000145b806105ba5750600160e060020a031982167f59d1d43c00000000000000000000000000000000000000000000000000000000145b806105ee5750600160e060020a031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561064d57600080fd5b6102c65a03f1151561065e57600080fd5b50505060405180519050600160a060020a031614151561067d57600080fd5b6000848152600160205260409081902083916005909101908590518082805190602001908083835b602083106106c45780518252601f1990920191602091820191016106a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051610708929160200190611083565b50826040518082805190602001908083835b602083106107395780518252601f19909201916020918201910161071a565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051908190039020847fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a75508560405160208082528190810183818151815260200191508051906020019080838360005b838110156107c55780820151838201526020016107ad565b50505050905090810190601f1680156107f25780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b600061080f611101565b60008481526001602081905260409091209092505b838311610922578284161580159061085d5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610917578060060160008481526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561090b5780601f106108e05761010080835404028352916020019161090b565b820191906000526020600020905b8154815290600101906020018083116108ee57829003601f168201915b50505050509150610927565b600290920291610824565b600092505b509250929050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561098857600080fd5b6102c65a03f1151561099957600080fd5b50505060405180519050600160a060020a03161415156109b857600080fd5b6040805190810160409081528482526020808301859052600087815260019091522060030181518155602082015160019091015550837f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46848460405191825260208201526040908101905180910390a250505050565b6000908152600160208190526040909120015490565b600090815260016020526040902054600160a060020a031690565b610a67611101565b60008381526001602052604090819020600501908390518082805190602001908083835b60208310610aaa5780518252601f199092019160209182019101610a8b565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b715780601f10610b4657610100808354040283529160200191610b71565b820191906000526020600020905b815481529060010190602001808311610b5457829003601f168201915b5050505050905092915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610bd757600080fd5b6102c65a03f11515610be857600080fd5b50505060405180519050600160a060020a0316141515610c0757600080fd5b6000198301831615610c1857600080fd5b60008481526001602090815260408083208684526006019091529020828051610c45929160200190611083565b5082847faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe360405160405180910390a350505050565b610c82611101565b6001600083600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d345780601f10610d0957610100808354040283529160200191610d34565b820191906000526020600020905b815481529060010190602001808311610d1757829003601f168201915b50505050509050919050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610d9957600080fd5b6102c65a03f11515610daa57600080fd5b50505060405180519050600160a060020a0316141515610dc957600080fd5b6000838152600160205260409020600201828051610deb929160200190611083565b50827fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f78360405160208082528190810183818151815260200191508051906020019080838360005b83811015610e4b578082015183820152602001610e33565b50505050905090810190601f168015610e785780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610ee357600080fd5b6102c65a03f11515610ef457600080fd5b50505060405180519050600160a060020a0316141515610f1357600080fd5b6000838152600160208190526040918290200183905583907f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc9084905190815260200160405180910390a2505050565b600090815260016020526040902060038101546004909101549091565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610fd957600080fd5b6102c65a03f11515610fea57600080fd5b50505060405180519050600160a060020a031614151561100957600080fd5b60008381526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03851617905583907f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd290849051600160a060020a03909116815260200160405180910390a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110c457805160ff19168380011785556110f1565b828001600101855582156110f1579182015b828111156110f15782518255916020019190600101906110d6565b506110fd929150611113565b5090565b60206040519081016040526000815290565b61112d91905b808211156110fd5760008155600101611119565b905600a165627a7a723058206016a807d9d5f6060e9c0d1c808e52d4ca30a4cab0140adcc6587bfc13bedf100029" + + +reverse_registrar_abi = json.loads('[{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"resolver","type":"address"}],"name":"claimWithResolver","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"claim","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"defaultResolver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"node","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"setName","outputs":[{"name":"node","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"resolverAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]') +reverse_registrar_bytecode = "6060604052341561000f57600080fd5b604051604080610d96833981016040528080519060200190919080519060200190919050506000826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be37f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561017a57600080fd5b6102c65a03f1151561018b57600080fd5b50505060405180519050905060008173ffffffffffffffffffffffffffffffffffffffff16141515610277578073ffffffffffffffffffffffffffffffffffffffff16631e83409a336000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b151561025a57600080fd5b6102c65a03f1151561026b57600080fd5b50505060405180519050505b505050610b0d806102896000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630f5a54661461007d5780631e83409a146100f15780633f15457f14610146578063828eab0e1461019b578063bffbe61c146101f0578063c47f002714610245575b600080fd5b341561008857600080fd5b6100d3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102be565b60405180826000191660001916815260200191505060405180910390f35b34156100fc57600080fd5b610128600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061086e565b60405180826000191660001916815260200191505060405180910390f35b341561015157600080fd5b610159610882565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101a657600080fd5b6101ae6108a7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101fb57600080fd5b610227600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108cd565b60405180826000191660001916815260200191505060405180910390f35b341561025057600080fd5b6102a0600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061092f565b60405180826000191660001916815260200191505060405180910390f35b60008060006102cc33610a80565b91507f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010282604051808360001916600019168152602001826000191660001916815260200192505050604051809103902092506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156103c157600080fd5b6102c65a03f115156103d257600080fd5b50505060405180519050905060008473ffffffffffffffffffffffffffffffffffffffff16141580156104eb57506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630178b8bf846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156104a057600080fd5b6102c65a03f115156104b157600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614155b1561071b573073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151561063b576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284306040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561062357600080fd5b6102c65a03f1151561063457600080fd5b5050503090505b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631896f70a84866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050600060405180830381600087803b151561070657600080fd5b6102c65a03f1151561071757600080fd5b5050505b8473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515610863576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284886040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561084e57600080fd5b6102c65a03f1151561085f57600080fd5b5050505b829250505092915050565b600061087b8260006102be565b9050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60007f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026108fc83610a80565b60405180836000191660001916815260200182600019166000191681526020019250505060405180910390209050919050565b600061095d30600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166102be565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637737221382846040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180836000191660001916815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610a185780820151818401526020810190506109fd565b50505050905090810190601f168015610a455780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b1515610a6457600080fd5b6102c65a03f11515610a7557600080fd5b505050809050919050565b60007f303132333435363738396162636465660000000000000000000000000000000060285b60018103905081600f85161a815360108404935060018103905081600f85161a815360108404935080610aa6576028600020925050509190505600a165627a7a72305820a8513240f040cd9ded89ca4d0c5bda58536850e642e1d933ad64158ef4c820660029" +reverse_registrar_bytecode_runtime = "606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630f5a54661461007d5780631e83409a146100f15780633f15457f14610146578063828eab0e1461019b578063bffbe61c146101f0578063c47f002714610245575b600080fd5b341561008857600080fd5b6100d3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102be565b60405180826000191660001916815260200191505060405180910390f35b34156100fc57600080fd5b610128600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061086e565b60405180826000191660001916815260200191505060405180910390f35b341561015157600080fd5b610159610882565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101a657600080fd5b6101ae6108a7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101fb57600080fd5b610227600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108cd565b60405180826000191660001916815260200191505060405180910390f35b341561025057600080fd5b6102a0600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061092f565b60405180826000191660001916815260200191505060405180910390f35b60008060006102cc33610a80565b91507f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010282604051808360001916600019168152602001826000191660001916815260200192505050604051809103902092506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156103c157600080fd5b6102c65a03f115156103d257600080fd5b50505060405180519050905060008473ffffffffffffffffffffffffffffffffffffffff16141580156104eb57506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630178b8bf846000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b15156104a057600080fd5b6102c65a03f115156104b157600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614155b1561071b573073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151561063b576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284306040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561062357600080fd5b6102c65a03f1151561063457600080fd5b5050503090505b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631896f70a84866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050600060405180830381600087803b151561070657600080fd5b6102c65a03f1151561071757600080fd5b5050505b8473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515610863576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166306ab59237f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260010284886040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180846000191660001916815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b151561084e57600080fd5b6102c65a03f1151561085f57600080fd5b5050505b829250505092915050565b600061087b8260006102be565b9050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60007f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026108fc83610a80565b60405180836000191660001916815260200182600019166000191681526020019250505060405180910390209050919050565b600061095d30600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166102be565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637737221382846040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180836000191660001916815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610a185780820151818401526020810190506109fd565b50505050905090810190601f168015610a455780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b1515610a6457600080fd5b6102c65a03f11515610a7557600080fd5b505050809050919050565b60007f303132333435363738396162636465660000000000000000000000000000000060285b60018103905081600f85161a815360108404935060018103905081600f85161a815360108404935080610aa6576028600020925050509190505600a165627a7a72305820a8513240f040cd9ded89ca4d0c5bda58536850e642e1d933ad64158ef4c820660029" + +reverse_resolver_abi = json.loads('[{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"_name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]') +reverse_resolver_bytecode = "6060604052341561000f57600080fd5b6040516020806106c9833981016040528080519060200190919050506000816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be37f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26001026000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561013057600080fd5b6102c65a03f1151561014157600080fd5b50505060405180519050905060008173ffffffffffffffffffffffffffffffffffffffff1614151561022d578073ffffffffffffffffffffffffffffffffffffffff16631e83409a336000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b151561021057600080fd5b6102c65a03f1151561022157600080fd5b50505060405180519050505b505061048b8061023e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633f15457f1461005c578063691f3431146100b15780637737221314610151575b600080fd5b341561006757600080fd5b61006f6101bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100bc57600080fd5b6100d66004808035600019169060200190919050506101e0565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101165780820151818401526020810190506100fb565b50505050905090810190601f1680156101435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015c57600080fd5b6101b960048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610290565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b505050505081565b816000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3826000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561033157600080fd5b6102c65a03f1151561034257600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561038557600080fd5b8160016000856000191660001916815260200190815260200160002090805190602001906103b49291906103ba565b50505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103fb57805160ff1916838001178555610429565b82800160010185558215610429579182015b8281111561042857825182559160200191906001019061040d565b5b509050610436919061043a565b5090565b61045c91905b80821115610458576000816000905550600101610440565b5090565b905600a165627a7a72305820f4c4cb4d191f31a62c4de12f7aecf9b258ba17f3a6ee294191171f7d30c04ab00029" +reverse_resolver_bytecode_runtime = "606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633f15457f1461005c578063691f3431146100b15780637737221314610151575b600080fd5b341561006757600080fd5b61006f6101bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100bc57600080fd5b6100d66004808035600019169060200190919050506101e0565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101165780820151818401526020810190506100fb565b50505050905090810190601f1680156101435780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015c57600080fd5b6101b960048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610290565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b505050505081565b816000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166302571be3826000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808260001916600019168152602001915050602060405180830381600087803b151561033157600080fd5b6102c65a03f1151561034257600080fd5b5050506040518051905073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561038557600080fd5b8160016000856000191660001916815260200190815260200160002090805190602001906103b49291906103ba565b50505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103fb57805160ff1916838001178555610429565b82800160010185558215610429579182015b8281111561042857825182559160200191906001019061040d565b5b509050610436919061043a565b5090565b61045c91905b80821115610458576000816000905550600101610440565b5090565b905600a165627a7a72305820f4c4cb4d191f31a62c4de12f7aecf9b258ba17f3a6ee294191171f7d30c04ab00029" diff --git a/ens/exceptions.py b/ens/exceptions.py index 4048c5d2f2..a332e19f9a 100644 --- a/ens/exceptions.py +++ b/ens/exceptions.py @@ -1,79 +1,79 @@ -import idna - - -class AddressMismatch(ValueError): - """ - In order to set up reverse resolution correctly, the ENS name should first - point to the address. This exception is raised if the name does - not currently point to the address. - """ - pass - - -class InvalidName(idna.IDNAError): - """ - This exception is raised if the provided name does not meet - the syntax standards specified in `EIP 137 name syntax - `_. - - For example: names may not start with a dot, or include a space. - """ - pass - - -class UnauthorizedError(Exception): - """ - Raised if the sending account is not the owner of the name - you are trying to modify. Make sure to set ``from`` in the - ``transact`` keyword argument to the owner of the name. - """ - pass - - -class UnownedName(Exception): - """ - Raised if you are trying to modify a name that no one owns. - - If working on a subdomain, make sure the subdomain gets created - first with :meth:`~ens.main.ENS.setup_address`. - """ - pass - - -class BidTooLow(ValueError): - """ - Raised if you bid less than the minimum amount - """ - pass - - -class InvalidBidHash(ValueError): - """ - Raised if you supply incorrect data to generate the bid hash. - """ - pass - - -class InvalidLabel(ValueError): - """ - Raised if you supply an invalid label - """ - pass - - -class OversizeTransaction(ValueError): - """ - Raised if a transaction you are trying to create would cost so - much gas that it could not fit in a block. - - For example: when you try to start too many auctions at once. - """ - pass - - -class UnderfundedBid(ValueError): - """ - Raised if you send less wei with your bid than you declared - as your intent to bid. - """ - pass +import idna + + +class AddressMismatch(ValueError): + """ + In order to set up reverse resolution correctly, the ENS name should first + point to the address. This exception is raised if the name does + not currently point to the address. + """ + pass + + +class InvalidName(idna.IDNAError): + """ + This exception is raised if the provided name does not meet + the syntax standards specified in `EIP 137 name syntax + `_. + + For example: names may not start with a dot, or include a space. + """ + pass + + +class UnauthorizedError(Exception): + """ + Raised if the sending account is not the owner of the name + you are trying to modify. Make sure to set ``from`` in the + ``transact`` keyword argument to the owner of the name. + """ + pass + + +class UnownedName(Exception): + """ + Raised if you are trying to modify a name that no one owns. + + If working on a subdomain, make sure the subdomain gets created + first with :meth:`~ens.main.ENS.setup_address`. + """ + pass + + +class BidTooLow(ValueError): + """ + Raised if you bid less than the minimum amount + """ + pass + + +class InvalidBidHash(ValueError): + """ + Raised if you supply incorrect data to generate the bid hash. + """ + pass + + +class InvalidLabel(ValueError): + """ + Raised if you supply an invalid label + """ + pass + + +class OversizeTransaction(ValueError): + """ + Raised if a transaction you are trying to create would cost so + much gas that it could not fit in a block. + + For example: when you try to start too many auctions at once. + """ + pass + + +class UnderfundedBid(ValueError): + """ + Raised if you send less wei with your bid than you declared + as your intent to bid. + """ + pass diff --git a/ens/main.py b/ens/main.py index bcdf297dde..658de52abb 100644 --- a/ens/main.py +++ b/ens/main.py @@ -1,367 +1,312 @@ -from typing import ( - TYPE_CHECKING, - Optional, - Sequence, - Tuple, - Union, - cast, -) - -from eth_typing import ( - Address, - ChecksumAddress, - Hash32, - HexAddress, - HexStr, -) -from eth_utils import ( - is_binary_address, - is_checksum_address, - to_checksum_address, -) - -from ens import abis -from ens.constants import ( - EMPTY_ADDR_HEX, - REVERSE_REGISTRAR_DOMAIN, -) -from ens.exceptions import ( - AddressMismatch, - UnauthorizedError, - UnownedName, -) -from ens.utils import ( - address_in, - address_to_reverse_domain, - default, - dict_copy, - init_web3, - is_none_or_zero_address, - is_valid_name, - label_to_hash, - normal_name_to_hash, - normalize_name, - raw_name_to_hash, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - from web3.contract import ( # noqa: F401 - Contract, - ) - from web3.providers import ( # noqa: F401 - BaseProvider, - ) - from web3.types import ( # noqa: F401 - TxParams, - ) - - -ENS_MAINNET_ADDR = ChecksumAddress( - HexAddress(HexStr('0x314159265dD8dbb310642f98f50C066173C1259b')) -) - - -class ENS: - """ - Quick access to common Ethereum Name Service functions, - like getting the address for a name. - - Unless otherwise specified, all addresses are assumed to be a `str` in - `checksum format `_, - like: ``"0x314159265dD8dbb310642f98f50C066173C1259b"`` - """ - - labelhash = staticmethod(label_to_hash) - namehash = staticmethod(raw_name_to_hash) - nameprep = staticmethod(normalize_name) - is_valid_name = staticmethod(is_valid_name) - reverse_domain = staticmethod(address_to_reverse_domain) - - def __init__( - self, provider: 'BaseProvider'=cast('BaseProvider', default), addr: ChecksumAddress=None - ) -> None: - """ - :param provider: a single provider used to connect to Ethereum - :type provider: instance of `web3.providers.base.BaseProvider` - :param hex-string addr: the address of the ENS registry on-chain. If not provided, - ENS.py will default to the mainnet ENS registry address. - """ - self.web3 = init_web3(provider) - - ens_addr = addr if addr else ENS_MAINNET_ADDR - self.ens = self.web3.eth.contract(abi=abis.ENS, address=ens_addr) - self._resolverContract = self.web3.eth.contract(abi=abis.RESOLVER) - - @classmethod - def fromWeb3(cls, web3: 'Web3', addr: ChecksumAddress=None) -> 'ENS': - """ - Generate an ENS instance with web3 - - :param `web3.Web3` web3: to infer connection information - :param hex-string addr: the address of the ENS registry on-chain. If not provided, - ENS.py will default to the mainnet ENS registry address. - """ - return cls(web3.manager.provider, addr=addr) - - def address(self, name: str) -> ChecksumAddress: - """ - Look up the Ethereum address that `name` currently points to. - - :param str name: an ENS name to look up - :raises InvalidName: if `name` has invalid syntax - """ - return cast(ChecksumAddress, self.resolve(name, 'addr')) - - def name(self, address: ChecksumAddress) -> str: - """ - Look up the name that the address points to, using a - reverse lookup. Reverse lookup is opt-in for name owners. - - :param address: - :type address: hex-string - """ - reversed_domain = address_to_reverse_domain(address) - return self.resolve(reversed_domain, get='name') - - @dict_copy - def setup_address( - self, - name: str, - address: Union[Address, ChecksumAddress, HexAddress]=cast(ChecksumAddress, default), - transact: "TxParams"={} - ) -> Hash32: - """ - Set up the name to point to the supplied address. - The sender of the transaction must own the name, or - its parent name. - - Example: If the caller owns ``parentname.eth`` with no subdomains - and calls this method with ``sub.parentname.eth``, - then ``sub`` will be created as part of this call. - - :param str name: ENS name to set up - :param str address: name will point to this address, in checksum format. If ``None``, - erase the record. If not specified, name will point to the owner's address. - :param dict transact: the transaction configuration, like in - :meth:`~web3.eth.Eth.sendTransaction` - :raises InvalidName: if ``name`` has invalid syntax - :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` - """ - owner = self.setup_owner(name, transact=transact) - self._assert_control(owner, name) - if is_none_or_zero_address(address): - address = None - elif address is default: - address = owner - elif is_binary_address(address): - address = to_checksum_address(cast(str, address)) - elif not is_checksum_address(address): - raise ValueError("You must supply the address in checksum format") - if self.address(name) == address: - return None - if address is None: - address = EMPTY_ADDR_HEX - transact['from'] = owner - resolver: 'Contract' = self._set_resolver(name, transact=transact) - return resolver.functions.setAddr(raw_name_to_hash(name), address).transact(transact) - - @dict_copy - def setup_name( - self, name: str, address: ChecksumAddress=None, transact: "TxParams"={} - ) -> Hash32: - """ - Set up the address for reverse lookup, aka "caller ID". - After successful setup, the method :meth:`~ens.main.ENS.name` will return - `name` when supplied with `address`. - - :param str name: ENS name that address will point to - :param str address: to set up, in checksum format - :param dict transact: the transaction configuration, like in - :meth:`~web3.eth.sendTransaction` - :raises AddressMismatch: if the name does not already point to the address - :raises InvalidName: if `name` has invalid syntax - :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` - :raises UnownedName: if no one owns `name` - """ - if not name: - self._assert_control(address, 'the reverse record') - return self._setup_reverse(None, address, transact=transact) - else: - resolved = self.address(name) - if is_none_or_zero_address(address): - address = resolved - elif resolved and address != resolved and resolved != EMPTY_ADDR_HEX: - raise AddressMismatch( - "Could not set address %r to point to name, because the name resolves to %r. " - "To change the name for an existing address, call setup_address() first." % ( - address, resolved - ) - ) - if is_none_or_zero_address(address): - address = self.owner(name) - if is_none_or_zero_address(address): - raise UnownedName("claim subdomain using setup_address() first") - if is_binary_address(address): - address = to_checksum_address(address) - if not is_checksum_address(address): - raise ValueError("You must supply the address in checksum format") - self._assert_control(address, name) - if not resolved: - self.setup_address(name, address, transact=transact) - return self._setup_reverse(name, address, transact=transact) - - def resolve(self, name: str, get: str='addr') -> Optional[Union[ChecksumAddress, str]]: - normal_name = normalize_name(name) - resolver = self.resolver(normal_name) - if resolver: - lookup_function = getattr(resolver.functions, get) - namehash = normal_name_to_hash(normal_name) - address = lookup_function(namehash).call() - if is_none_or_zero_address(address): - return None - return lookup_function(namehash).call() - else: - return None - - def resolver(self, normal_name: str) -> Optional['Contract']: - resolver_addr = self.ens.caller.resolver(normal_name_to_hash(normal_name)) - if is_none_or_zero_address(resolver_addr): - return None - return self._resolverContract(address=resolver_addr) - - def reverser(self, target_address: ChecksumAddress) -> Optional['Contract']: - reversed_domain = address_to_reverse_domain(target_address) - return self.resolver(reversed_domain) - - def owner(self, name: str) -> ChecksumAddress: - """ - Get the owner of a name. Note that this may be different from the - deed holder in the '.eth' registrar. Learn more about the difference - between deed and name ownership in the ENS `Managing Ownership docs - `_ - - :param str name: ENS name to look up - :return: owner address - :rtype: str - """ - node = raw_name_to_hash(name) - return self.ens.caller.owner(node) - - @dict_copy - def setup_owner( - self, - name: str, - new_owner: ChecksumAddress=cast(ChecksumAddress, default), - transact: "TxParams"={} - ) -> ChecksumAddress: - """ - Set the owner of the supplied name to `new_owner`. - - For typical scenarios, you'll never need to call this method directly, - simply call :meth:`setup_name` or :meth:`setup_address`. This method does *not* - set up the name to point to an address. - - If `new_owner` is not supplied, then this will assume you - want the same owner as the parent domain. - - If the caller owns ``parentname.eth`` with no subdomains - and calls this method with ``sub.parentname.eth``, - then ``sub`` will be created as part of this call. - - :param str name: ENS name to set up - :param new_owner: account that will own `name`. If ``None``, set owner to empty addr. - If not specified, name will point to the parent domain owner's address. - :param dict transact: the transaction configuration, like in - :meth:`~web3.eth.Eth.sendTransaction` - :raises InvalidName: if `name` has invalid syntax - :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` - :returns: the new owner's address - """ - (super_owner, unowned, owned) = self._first_owner(name) - if new_owner is default: - new_owner = super_owner - elif not new_owner: - new_owner = ChecksumAddress(EMPTY_ADDR_HEX) - else: - new_owner = to_checksum_address(new_owner) - current_owner = self.owner(name) - if new_owner == EMPTY_ADDR_HEX and not current_owner: - return None - elif current_owner == new_owner: - return current_owner - else: - self._assert_control(super_owner, name, owned) - self._claim_ownership(new_owner, unowned, owned, super_owner, transact=transact) - return new_owner - - def _assert_control(self, account: ChecksumAddress, name: str, parent_owned: str=None) -> None: - if not address_in(account, self.web3.eth.accounts): - raise UnauthorizedError( - "in order to modify %r, you must control account %r, which owns %r" % ( - name, account, parent_owned or name - ) - ) - - def _first_owner(self, name: str) -> Tuple[Optional[ChecksumAddress], Sequence[str], str]: - """ - Takes a name, and returns the owner of the deepest subdomain that has an owner - - :returns: (owner or None, list(unowned_subdomain_labels), first_owned_domain) - """ - owner = None - unowned = [] - pieces = normalize_name(name).split('.') - while pieces and is_none_or_zero_address(owner): - name = '.'.join(pieces) - owner = self.owner(name) - if is_none_or_zero_address(owner): - unowned.append(pieces.pop(0)) - return (owner, unowned, name) - - @dict_copy - def _claim_ownership( - self, - owner: ChecksumAddress, - unowned: Sequence[str], - owned: str, - old_owner: ChecksumAddress=None, - transact: "TxParams"={} - ) -> None: - transact['from'] = old_owner or owner - for label in reversed(unowned): - self.ens.functions.setSubnodeOwner( - raw_name_to_hash(owned), - label_to_hash(label), - owner - ).transact(transact) - owned = "%s.%s" % (label, owned) - - @dict_copy - def _set_resolver( - self, name: str, resolver_addr: ChecksumAddress=None, transact: "TxParams"={} - ) -> 'Contract': - if is_none_or_zero_address(resolver_addr): - resolver_addr = self.address('resolver.eth') - namehash = raw_name_to_hash(name) - if self.ens.caller.resolver(namehash) != resolver_addr: - self.ens.functions.setResolver( - namehash, - resolver_addr - ).transact(transact) - return self._resolverContract(address=resolver_addr) - - @dict_copy - def _setup_reverse( - self, name: str, address: ChecksumAddress, transact: "TxParams"={} - ) -> Hash32: - if name: - name = normalize_name(name) - else: - name = '' - transact['from'] = address - return self._reverse_registrar().functions.setName(name).transact(transact) - - def _reverse_registrar(self) -> 'Contract': - addr = self.ens.caller.owner(normal_name_to_hash(REVERSE_REGISTRAR_DOMAIN)) - return self.web3.eth.contract(address=addr, abi=abis.REVERSE_REGISTRAR) + +from vns_utils import ( + is_binary_address, + is_checksum_address, + to_checksum_address, +) + +from ens import abis +from ens.constants import ( + EMPTY_ADDR_HEX, + REVERSE_REGISTRAR_DOMAIN, +) +from ens.exceptions import ( + AddressMismatch, + UnauthorizedError, + UnownedName, +) +from ens.utils import ( + address_in, + address_to_reverse_domain, + default, + dict_copy, + init_web3, + is_none_or_zero_address, + is_valid_name, + label_to_hash, + normal_name_to_hash, + normalize_name, + raw_name_to_hash, +) + +ENS_MAINNET_ADDR = '0x314159265dD8dbb310642f98f50C066173C1259b' + + +class ENS: + """ + Quick access to common Ethereum Name Service functions, + like getting the address for a name. + + Unless otherwise specified, all addresses are assumed to be a `str` in + `checksum format `_, + like: ``"0x314159265dD8dbb310642f98f50C066173C1259b"`` + """ + + labelhash = staticmethod(label_to_hash) + namehash = staticmethod(raw_name_to_hash) + nameprep = staticmethod(normalize_name) + is_valid_name = staticmethod(is_valid_name) + reverse_domain = staticmethod(address_to_reverse_domain) + + def __init__(self, provider=default, addr=None): + """ + :param provider: a single provider used to connect to Ethereum + :type provider: instance of `web3.providers.base.BaseProvider` + :param hex-string addr: the address of the ENS registry on-chain. If not provided, + ENS.py will default to the mainnet ENS registry address. + """ + self.web3 = init_web3(provider) + + ens_addr = addr if addr else ENS_MAINNET_ADDR + self.ens = self.web3.vns.contract(abi=abis.ENS, address=ens_addr) + self._resolverContract = self.web3.vns.contract(abi=abis.RESOLVER) + + @classmethod + def fromWeb3(cls, web3, addr=None): + """ + Generate an ENS instance with web3 + + :param `web3.Web3` web3: to infer connection information + :param hex-string addr: the address of the ENS registry on-chain. If not provided, + ENS.py will default to the mainnet ENS registry address. + """ + return cls(web3.manager.provider, addr=addr) + + def address(self, name): + """ + Look up the Ethereum address that `name` currently points to. + + :param str name: an ENS name to look up + :raises InvalidName: if `name` has invalid syntax + """ + return self.resolve(name, 'addr') + + def name(self, address): + """ + Look up the name that the address points to, using a + reverse lookup. Reverse lookup is opt-in for name owners. + + :param address: + :type address: hex-string + """ + reversed_domain = address_to_reverse_domain(address) + return self.resolve(reversed_domain, get='name') + + @dict_copy + def setup_address(self, name, address=default, transact={}): + """ + Set up the name to point to the supplied address. + The sender of the transaction must own the name, or + its parent name. + + Example: If the caller owns ``parentname.vns`` with no subdomains + and calls this method with ``sub.parentname.vns``, + then ``sub`` will be created as part of this call. + + :param str name: ENS name to set up + :param str address: name will point to this address, in checksum format. If ``None``, + erase the record. If not specified, name will point to the owner's address. + :param dict transact: the transaction configuration, like in + :meth:`~web3.vns.Bbbbbbbb.sendTransaction` + :raises InvalidName: if ``name`` has invalid syntax + :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` + """ + owner = self.setup_owner(name, transact=transact) + self._assert_control(owner, name) + if is_none_or_zero_address(address): + address = None + elif address is default: + address = owner + elif is_binary_address(address): + address = to_checksum_address(address) + elif not is_checksum_address(address): + raise ValueError("You must supply the address in checksum format") + if self.address(name) == address: + return None + if address is None: + address = EMPTY_ADDR_HEX + transact['from'] = owner + resolver = self._set_resolver(name, transact=transact) + return resolver.functions.setAddr(raw_name_to_hash(name), address).transact(transact) + + @dict_copy + def setup_name(self, name, address=None, transact={}): + """ + Set up the address for reverse lookup, aka "caller ID". + After successful setup, the method :meth:`~ens.main.ENS.name` will return + `name` when supplied with `address`. + + :param str name: ENS name that address will point to + :param str address: to set up, in checksum format + :param dict transact: the transaction configuration, like in + :meth:`~web3.vns.sendTransaction` + :raises AddressMismatch: if the name does not already point to the address + :raises InvalidName: if `name` has invalid syntax + :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` + :raises UnownedName: if no one owns `name` + """ + if not name: + self._assert_control(address, 'the reverse record') + return self._setup_reverse(None, address, transact=transact) + else: + resolved = self.address(name) + if is_none_or_zero_address(address): + address = resolved + elif resolved and address != resolved and resolved != EMPTY_ADDR_HEX: + raise AddressMismatch( + "Could not set address %r to point to name, because the name resolves to %r. " + "To change the name for an existing address, call setup_address() first." % ( + address, resolved + ) + ) + if is_none_or_zero_address(address): + address = self.owner(name) + if is_none_or_zero_address(address): + raise UnownedName("claim subdomain using setup_address() first") + if is_binary_address(address): + address = to_checksum_address(address) + if not is_checksum_address(address): + raise ValueError("You must supply the address in checksum format") + self._assert_control(address, name) + if not resolved: + self.setup_address(name, address, transact=transact) + return self._setup_reverse(name, address, transact=transact) + + def resolve(self, name, get='addr'): + normal_name = normalize_name(name) + resolver = self.resolver(normal_name) + if resolver: + lookup_function = getattr(resolver.functions, get) + namehash = normal_name_to_hash(normal_name) + address = lookup_function(namehash).call() + if is_none_or_zero_address(address): + return None + return lookup_function(namehash).call() + else: + return None + + def resolver(self, normal_name): + resolver_addr = self.ens.caller.resolver(normal_name_to_hash(normal_name)) + if is_none_or_zero_address(resolver_addr): + return None + return self._resolverContract(address=resolver_addr) + + def reverser(self, target_address): + reversed_domain = address_to_reverse_domain(target_address) + return self.resolver(reversed_domain) + + def owner(self, name): + """ + Get the owner of a name. Note that this may be different from the + deed holder in the '.vns' registrar. Learn more about the difference + between deed and name ownership in the ENS `Managing Ownership docs + `_ + + :param str name: ENS name to look up + :return: owner address + :rtype: str + """ + node = raw_name_to_hash(name) + return self.ens.caller.owner(node) + + @dict_copy + def setup_owner(self, name, new_owner=default, transact={}): + """ + Set the owner of the supplied name to `new_owner`. + + For typical scenarios, you'll never need to call this method directly, + simply call :meth:`setup_name` or :meth:`setup_address`. This method does *not* + set up the name to point to an address. + + If `new_owner` is not supplied, then this will assume you + want the same owner as the parent domain. + + If the caller owns ``parentname.vns`` with no subdomains + and calls this method with ``sub.parentname.vns``, + then ``sub`` will be created as part of this call. + + :param str name: ENS name to set up + :param new_owner: account that will own `name`. If ``None``, set owner to empty addr. + If not specified, name will point to the parent domain owner's address. + :param dict transact: the transaction configuration, like in + :meth:`~web3.vns.Bbbbbbbb.sendTransaction` + :raises InvalidName: if `name` has invalid syntax + :raises UnauthorizedError: if ``'from'`` in `transact` does not own `name` + :returns: the new owner's address + """ + (super_owner, unowned, owned) = self._first_owner(name) + if new_owner is default: + new_owner = super_owner + elif not new_owner: + new_owner = EMPTY_ADDR_HEX + else: + new_owner = to_checksum_address(new_owner) + current_owner = self.owner(name) + if new_owner == EMPTY_ADDR_HEX and not current_owner: + return None + elif current_owner == new_owner: + return current_owner + else: + self._assert_control(super_owner, name, owned) + self._claim_ownership(new_owner, unowned, owned, super_owner, transact=transact) + return new_owner + + def _assert_control(self, account, name, parent_owned=None): + if not address_in(account, self.web3.vns.accounts): + raise UnauthorizedError( + "in order to modify %r, you must control account %r, which owns %r" % ( + name, account, parent_owned or name + ) + ) + + def _first_owner(self, name): + """ + Takes a name, and returns the owner of the deepest subdomain that has an owner + + :returns: (owner or None, list(unowned_subdomain_labels), first_owned_domain) + """ + owner = None + unowned = [] + pieces = normalize_name(name).split('.') + while pieces and is_none_or_zero_address(owner): + name = '.'.join(pieces) + owner = self.owner(name) + if is_none_or_zero_address(owner): + unowned.append(pieces.pop(0)) + return (owner, unowned, name) + + @dict_copy + def _claim_ownership(self, owner, unowned, owned, old_owner=None, transact={}): + transact['from'] = old_owner or owner + for label in reversed(unowned): + self.ens.functions.setSubnodeOwner( + raw_name_to_hash(owned), + label_to_hash(label), + owner + ).transact(transact) + owned = "%s.%s" % (label, owned) + + @dict_copy + def _set_resolver(self, name, resolver_addr=None, transact={}): + if is_none_or_zero_address(resolver_addr): + resolver_addr = self.address('resolver.vns') + namehash = raw_name_to_hash(name) + if self.ens.caller.resolver(namehash) != resolver_addr: + self.ens.functions.setResolver( + namehash, + resolver_addr + ).transact(transact) + return self._resolverContract(address=resolver_addr) + + @dict_copy + def _setup_reverse(self, name, address, transact={}): + if name: + name = normalize_name(name) + else: + name = '' + transact['from'] = address + return self._reverse_registrar().functions.setName(name).transact(transact) + + def _reverse_registrar(self): + addr = self.ens.caller.owner(normal_name_to_hash(REVERSE_REGISTRAR_DOMAIN)) + return self.web3.vns.contract(address=addr, abi=abis.REVERSE_REGISTRAR) diff --git a/ens/utils.py b/ens/utils.py index 08eb211b94..a5e79cb0d3 100644 --- a/ens/utils.py +++ b/ens/utils.py @@ -1,214 +1,215 @@ -import copy -import datetime -import functools -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Optional, - Type, - TypeVar, - Union, - cast, -) - -from eth_typing import ( - Address, - ChecksumAddress, - HexAddress, - HexStr, -) -from eth_utils import ( - is_same_address, - remove_0x_prefix, - to_normalized_address, -) -from hexbytes import ( - HexBytes, -) -import idna - -from ens.constants import ( - ACCEPTABLE_STALE_HOURS, - AUCTION_START_GAS_CONSTANT, - AUCTION_START_GAS_MARGINAL, - EMPTY_ADDR_HEX, - EMPTY_SHA3_BYTES, - REVERSE_REGISTRAR_DOMAIN, -) -from ens.exceptions import ( - InvalidName, -) - -default = object() - - -if TYPE_CHECKING: - from web3 import Web3 as _Web3 # noqa: F401 - from web3.providers import ( # noqa: F401 - BaseProvider, - ) - - -def Web3() -> Type['_Web3']: - from web3 import Web3 as Web3Main - return Web3Main - - -TFunc = TypeVar("TFunc", bound=Callable[..., Any]) - - -def dict_copy(func: TFunc) -> TFunc: - "copy dict keyword args, to avoid modifying caller's copy" - @functools.wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> TFunc: - copied_kwargs = copy.deepcopy(kwargs) - return func(*args, **copied_kwargs) - return cast(TFunc, wrapper) - - -def ensure_hex(data: HexBytes) -> HexBytes: - if not isinstance(data, str): - return Web3().toHex(data) - return data - - -def init_web3(provider: 'BaseProvider'=cast('BaseProvider', default)) -> '_Web3': - from web3 import Web3 as Web3Main - - if provider is default: - w3 = Web3Main(ens=None) - else: - w3 = Web3Main(provider, ens=None) - - return customize_web3(w3) - - -def customize_web3(w3: '_Web3') -> '_Web3': - from web3.middleware import make_stalecheck_middleware - - w3.middleware_onion.remove('name_to_address') - w3.middleware_onion.add( - make_stalecheck_middleware(ACCEPTABLE_STALE_HOURS * 3600), - name='stalecheck', - ) - return w3 - - -def normalize_name(name: str) -> str: - """ - Clean the fully qualified name, as defined in ENS `EIP-137 - `_ - - This does *not* enforce whether ``name`` is a label or fully qualified domain. - - :param str name: the dot-separated ENS name - :raises InvalidName: if ``name`` has invalid syntax - """ - if not name: - return name - elif isinstance(name, (bytes, bytearray)): - name = name.decode('utf-8') - - try: - return idna.uts46_remap(name, std3_rules=True) - except idna.IDNAError as exc: - raise InvalidName(f"{name} is an invalid name, because {exc}") from exc - - -def is_valid_name(name: str) -> bool: - """ - Validate whether the fully qualified name is valid, as defined in ENS `EIP-137 - `_ - - :param str name: the dot-separated ENS name - :returns: True if ``name`` is set, and :meth:`~ens.main.ENS.nameprep` will not raise InvalidName - """ - if not name: - return False - try: - normalize_name(name) - return True - except InvalidName: - return False - - -def to_utc_datetime(timestamp: float) -> Optional[datetime.datetime]: - if timestamp: - return datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc) - else: - return None - - -def sha3_text(val: Union[str, bytes]) -> HexBytes: - if isinstance(val, str): - val = val.encode('utf-8') - return Web3().keccak(val) - - -def label_to_hash(label: str) -> HexBytes: - label = normalize_name(label) - if '.' in label: - raise ValueError("Cannot generate hash for label %r with a '.'" % label) - return Web3().keccak(text=label) - - -def normal_name_to_hash(name: str) -> HexBytes: - node = EMPTY_SHA3_BYTES - if name: - labels = name.split(".") - for label in reversed(labels): - labelhash = label_to_hash(label) - assert isinstance(labelhash, bytes) - assert isinstance(node, bytes) - node = Web3().keccak(node + labelhash) - return node - - -def raw_name_to_hash(name: str) -> HexBytes: - """ - Generate the namehash. This is also known as the ``node`` in ENS contracts. - - In normal operation, generating the namehash is handled - behind the scenes. For advanced usage, it is a helpful utility. - - This normalizes the name with `nameprep - `_ - before hashing. - - :param str name: ENS name to hash - :return: the namehash - :rtype: bytes - :raises InvalidName: if ``name`` has invalid syntax - """ - normalized_name = normalize_name(name) - return normal_name_to_hash(normalized_name) - - -def address_in(address: ChecksumAddress, addresses: Collection[ChecksumAddress]) -> bool: - return any(is_same_address(address, item) for item in addresses) - - -def address_to_reverse_domain(address: ChecksumAddress) -> str: - lower_unprefixed_address = remove_0x_prefix(HexStr(to_normalized_address(address))) - return lower_unprefixed_address + '.' + REVERSE_REGISTRAR_DOMAIN - - -def estimate_auction_start_gas(labels: Collection[str]) -> int: - return AUCTION_START_GAS_CONSTANT + AUCTION_START_GAS_MARGINAL * len(labels) - - -def assert_signer_in_modifier_kwargs(modifier_kwargs: Any) -> ChecksumAddress: - ERR_MSG = "You must specify the sending account" - assert len(modifier_kwargs) == 1, ERR_MSG - - _modifier_type, modifier_dict = dict(modifier_kwargs).popitem() - if 'from' not in modifier_dict: - raise TypeError(ERR_MSG) - - return modifier_dict['from'] - - -def is_none_or_zero_address(addr: Union[Address, ChecksumAddress, HexAddress]) -> bool: - return not addr or addr == EMPTY_ADDR_HEX +import copy +import datetime +import functools + +from vns_utils import ( + is_same_address, + remove_0x_prefix, + to_normalized_address, +) +import idna + +from ens.constants import ( + ACCEPTABLE_STALE_HOURS, + AUCTION_START_GAS_CONSTANT, + AUCTION_START_GAS_MARGINAL, + EMPTY_SHA3_BYTES, + MIN_ETH_LABEL_LENGTH, + REVERSE_REGISTRAR_DOMAIN, +) +from ens.exceptions import ( + InvalidLabel, + InvalidName, +) + +default = object() + + +def Web3(): + from web3 import Web3 + return Web3 + + +def dict_copy(func): + "copy dict keyword args, to avoid modifying caller's copy" + @functools.wraps(func) + def wrapper(*args, **kwargs): + copied_kwargs = copy.deepcopy(kwargs) + return func(*args, **copied_kwargs) + return wrapper + + +def ensure_hex(data): + if not isinstance(data, str): + return Web3().toHex(data) + return data + + +def init_web3(providers=default): + from web3 import Web3 + + if providers is default: + w3 = Web3(ens=None) + else: + w3 = Web3(providers, ens=None) + + return customize_web3(w3) + + +def customize_web3(w3): + from web3.middleware import make_stalecheck_middleware + + w3.middleware_onion.remove('name_to_address') + w3.middleware_onion.add( + make_stalecheck_middleware(ACCEPTABLE_STALE_HOURS * 3600), + name='stalecheck', + ) + return w3 + + +def normalize_name(name): + """ + Clean the fully qualified name, as defined in ENS `EIP-137 + `_ + + This does *not* enforce whether ``name`` is a label or fully qualified domain. + + :param str name: the dot-separated ENS name + :raises InvalidName: if ``name`` has invalid syntax + """ + if not name: + return name + elif isinstance(name, (bytes, bytearray)): + name = name.decode('utf-8') + try: + return idna.decode(name, uts46=True, std3_rules=True) + except idna.IDNAError as exc: + raise InvalidName("%s is an invalid name, because %s" % (name, exc)) from exc + + +def is_valid_name(name): + """ + Validate whether the fully qualified name is valid, as defined in ENS `EIP-137 + `_ + + :param str name: the dot-separated ENS name + :returns: True if ``name`` is set, and :meth:`~ens.main.ENS.nameprep` will not raise InvalidName + """ + if not name: + return False + try: + normalize_name(name) + return True + except InvalidName: + return False + + +def name_to_label(name, registrar): + name = normalize_name(name) + if '.' not in name: + label = name + else: + name_pieces = name.split('.') + registrar_pieces = registrar.split('.') + if len(name_pieces) != len(registrar_pieces) + 1: + raise ValueError( + "You must specify a label, like 'tickets' " + "or a fully-qualified name, like 'tickets.%s'" % registrar + ) + label, *label_registrar = name_pieces + if label_registrar != registrar_pieces: + raise ValueError("This interface only manages names under .%s " % registrar) + return label + + +def dot_vns_label(name): + """ + Convert from a name, like 'ethfinex.vns', to a label, like 'ethfinex' + If name is already a label, this should be a noop, except for converting to a string + and validating the name syntax. + """ + label = name_to_label(name, registrar='vns') + if len(label) < MIN_ETH_LABEL_LENGTH: + raise InvalidLabel('name %r is too short' % label) + else: + return label + + +def to_utc_datetime(timestamp): + if timestamp: + return datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc) + else: + return None + + +def sha3_text(val): + if isinstance(val, str): + val = val.encode('utf-8') + return Web3().keccak(val) + + +def label_to_hash(label): + label = normalize_name(label) + if '.' in label: + raise ValueError("Cannot generate hash for label %r with a '.'" % label) + return Web3().keccak(text=label) + + +def normal_name_to_hash(name): + node = EMPTY_SHA3_BYTES + if name: + labels = name.split(".") + for label in reversed(labels): + labelhash = label_to_hash(label) + assert isinstance(labelhash, bytes) + assert isinstance(node, bytes) + node = Web3().keccak(node + labelhash) + return node + + +def raw_name_to_hash(name): + """ + Generate the namehash. This is also known as the ``node`` in ENS contracts. + + In normal operation, generating the namehash is handled + behind the scenes. For advanced usage, it is a helpful utility. + + This normalizes the name with `nameprep + `_ + before hashing. + + :param str name: ENS name to hash + :return: the namehash + :rtype: bytes + :raises InvalidName: if ``name`` has invalid syntax + """ + normalized_name = normalize_name(name) + return normal_name_to_hash(normalized_name) + + +def address_in(address, addresses): + return any(is_same_address(address, item) for item in addresses) + + +def address_to_reverse_domain(address): + lower_unprefixed_address = remove_0x_prefix(to_normalized_address(address)) + return lower_unprefixed_address + '.' + REVERSE_REGISTRAR_DOMAIN + + +def estimate_auction_start_gas(labels): + return AUCTION_START_GAS_CONSTANT + AUCTION_START_GAS_MARGINAL * len(labels) + + +def assert_signer_in_modifier_kwargs(modifier_kwargs): + ERR_MSG = "You must specify the sending account" + assert len(modifier_kwargs) == 1, ERR_MSG + + _modifier_type, modifier_dict = dict(modifier_kwargs).popitem() + if 'from' not in modifier_dict: + raise TypeError(ERR_MSG) + + return modifier_dict['from'] + + +def is_none_or_zero_address(addr): + return not addr or addr == '0x' + '00' * 20 diff --git a/pytest.ini b/pytest.ini index f5fdc0ec93..2c677dcae1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,7 @@ -[pytest] -addopts= -v --showlocals --durations 10 -python_paths= . -xfail_strict=true - -[pytest-watch] -runner= pytest --failed-first --maxfail=1 --no-success-flaky-report +[pytest] +addopts= -v --showlocals --durations 10 +python_paths= . +xfail_strict=true + +[pytest-watch] +runner= pytest --failed-first --maxfail=1 --no-success-flaky-report diff --git a/replace.py b/replace.py new file mode 100755 index 0000000000..55890d8637 --- /dev/null +++ b/replace.py @@ -0,0 +1,40 @@ +#! /usr/bin/python +#-*- coding: utf-8 -*- + +import os,sys + +def doReplace(fpath,src,dst): + newConent,bFlag = "",False + with open(fpath,"rb") as fin: + for line in fin : + #if len(line.strip()) == 0 : continue + if line.find(src) == -1 : + newLine = line + else: + bFlag = True + newLine = line.replace(src,dst) + newConent += newLine + if not bFlag : return None + print fpath + with open(fpath,"wb") as fout: + fout.write(newConent) + return None + +def replaceMain(dirName,src,dst): + for root, dirs, files in os.walk(dirName): + for name in files: + fpath = os.path.join(root, name) + doReplace(fpath,src,dst) + return None + +if __name__ == "__main__": + if len(sys.argv) < 3 : + print "usage : replaceMulti srcStr dstStr" + print "replace current dir files" + sys.exit(1) + srcStr = sys.argv[1] + dstStr = sys.argv[2] + dirName = "." + dirName = os.path.realpath(dirName) + print "working dir :",dirName + replaceMain(dirName,srcStr,dstStr) \ No newline at end of file diff --git a/requirements-docs.txt b/requirements-docs.txt index e9704b8ebf..4333084535 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1 +1 @@ -.[docs] +.[docs] diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 81ff676595..4b8977d9f1 --- a/setup.py +++ b/setup.py @@ -1,107 +1,99 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from setuptools import ( - find_packages, - setup, -) - -extras_require = { - 'tester': [ - "eth-tester[py-evm]==v0.2.0-beta.2", - "py-geth>=2.0.1,<3.0.0", - ], - 'linter': [ - "flake8==3.4.1", - "isort>=4.2.15,<4.3.5", - "mypy==0.730", - ], - 'docs': [ - "mock", - "sphinx-better-theme>=0.1.4", - "click>=5.1", - "configparser==3.5.0", - "contextlib2>=0.5.4", - "py-geth>=1.4.0", - "py-solc>=0.4.0", - "pytest>=4.4.0,<5.0.0", - "sphinx", - "sphinx_rtd_theme>=0.1.9", - "toposort>=1.4", - "towncrier>=19.2.0,<20", - "urllib3", - "web3>=2.1.0", - "wheel" - ], - 'dev': [ - "bumpversion", - "flaky>=3.3.0", - "hypothesis>=3.31.2", - "pytest>=4.4.0,<5.0.0", - "pytest-asyncio>=0.10.0,<0.11", - "pytest-mock>=1.10,<2", - "pytest-pythonpath>=0.3", - "pytest-watch>=4.2,<5", - "pytest-xdist>=1.29,<2", - "setuptools>=36.2.0", - "tox>=1.8.0", - "tqdm>4.32,<5", - "twine>=1.13,<2", - "when-changed>=0.3.0,<0.4" - ] -} - -extras_require['dev'] = ( - extras_require['tester'] + - extras_require['linter'] + - extras_require['docs'] + - extras_require['dev'] -) - -setup( - name='web3', - # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility. - version='5.3.0', - description="""Web3.py""", - long_description_markdown_filename='README.md', - author='Piper Merriam', - author_email='pipermerriam@gmail.com', - url='https://github.com/ethereum/web3.py', - include_package_data=True, - install_requires=[ - "eth-abi>=2.0.0b6,<3.0.0", - "eth-account>=0.4.0,<0.5.0", - "eth-hash[pycryptodome]>=0.2.0,<1.0.0", - "eth-typing>=2.0.0,<3.0.0", - "eth-utils>=1.8.1,<2.0.0", - "hexbytes>=0.1.0,<1.0.0", - "ipfshttpclient>=0.4.12,<1", - "jsonschema>=3.0.0,<4.0.0", - "lru-dict>=1.1.6,<2.0.0", - # remove mypy_extensions after python_requires>=3.8 - # see web3._utils.compat - "mypy_extensions>=0.4.1,<1.0.0", - "protobuf>=3.10.0,<4", - "pypiwin32>=223;platform_system=='Windows'", - "requests>=2.16.0,<3.0.0", - "websockets>=8.1.0,<9.0.0", - ], - setup_requires=['setuptools-markdown'], - python_requires='>=3.6,<4', - extras_require=extras_require, - py_modules=['web3', 'ens', 'ethpm'], - entry_points={"pytest11": ["pytest_ethereum = web3.tools.pytest_ethereum.plugins"]}, - license="MIT", - zip_safe=False, - keywords='ethereum', - packages=find_packages(exclude=["tests", "tests.*"]), - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - ], -) +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from setuptools import ( + find_packages, + setup, +) + +extras_require = { + 'tester': [ + "vns-tester[py-evm]==0.1.0-beta.39", + "py-geth>=2.0.1,<3.0.0", + "pytest-ethereum>=0.1.3a6,<1.0.0", + ], + 'linter': [ + "flake8==3.4.1", + "isort>=4.2.15,<4.3.5", + ], + 'docs': [ + "mock", + "sphinx-better-theme>=0.1.4", + "click>=5.1", + "configparser==3.5.0", + "contextlib2>=0.5.4", + "ethtoken", + "py-geth>=1.4.0", + "py-solc>=0.4.0", + "pytest>=4.4.0,<5.0.0", + "sphinx", + "sphinx_rtd_theme>=0.1.9", + "toposort>=1.4", + "urllib3", + "web3>=2.1.0", + "wheel" + ], + 'dev': [ + "bumpversion", + "flaky>=3.3.0", + "hypothesis>=3.31.2", + "pytest>=4.4.0,<5.0.0", + "pytest-mock==1.*", + "pytest-pythonpath>=0.3", + "pytest-watch==4.*", + "pytest-xdist==1.*", + "setuptools>=36.2.0", + "tox>=1.8.0", + "tqdm", + "twine", + "when-changed" + ] +} + +extras_require['dev'] = ( + extras_require['tester'] + + extras_require['linter'] + + extras_require['docs'] + + extras_require['dev'] +) + +setup( + name='web3', + # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility. + version='5.0.0-alpha.11', + description="""Web3.py""", + long_description_markdown_filename='README.md', + author='Piper Merriam', + author_email='pipermerriam@gmail.com', + url='https://github.com/ethereum/Web3.py', + include_package_data=True, + install_requires=[ + "vns-abi>=2.0.0b6,<3.0.0", + "vns-account>=0.2.1,<0.4.0", + "vns-hash[pycryptodome]>=0.2.0,<1.0.0", + "vns-typing>=2.0.0,<3.0.0", + "vns-utils>=1.4.0,<2.0.0", + "vnspm>=0.1.4a13,<1.0.0", + "hexbytes>=0.1.0,<1.0.0", + "lru-dict>=1.1.6,<2.0.0", + "requests>=2.16.0,<3.0.0", + "websockets>=7.0.0,<8.0.0", + "pypiwin32>=223;platform_system=='Windows'", + ], + setup_requires=['setuptools-markdown'], + python_requires='>=3.6,<4', + extras_require=extras_require, + py_modules=['web3', 'ens'], + license="MIT", + zip_safe=False, + keywords='ethereum', + packages=find_packages(exclude=["tests", "tests.*"]), + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Natural Language :: English', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ], +) diff --git a/tests/conftest.py b/tests/conftest.py index a7cf695c10..2fb87091b1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,22 +1,25 @@ -import pytest - -from eth_utils import ( - to_bytes, -) -from eth_utils.toolz import ( - identity, -) - -from .utils import ( - get_open_port, -) - - -@pytest.fixture(scope="module", params=[lambda x: to_bytes(hexstr=x), identity]) -def address_conversion_func(request): - return request.param - - -@pytest.fixture() -def open_port(): - return get_open_port() +import pytest + +from vnsutils import ( + to_bytes, +) + +from web3._utils.toolz import ( + identity, +) + +from .utils import ( + get_open_port, +) + +pytest_plugins = ["pytest_ethereum.plugins"] + + +@pytest.fixture(scope="module", params=[lambda x: to_bytes(hexstr=x), identity]) +def address_conversion_func(request): + return request.param + + +@pytest.fixture() +def open_port(): + return get_open_port() diff --git a/tests/core/admin-module/test_admin_addPeer.py b/tests/core/admin-module/test_admin_addPeer.py index 92b7be1505..69ff6f5dd0 100644 --- a/tests/core/admin-module/test_admin_addPeer.py +++ b/tests/core/admin-module/test_admin_addPeer.py @@ -1,20 +1,7 @@ -import pytest - - -def test_admin_addPeer(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning): - result = web3.geth.admin.addPeer( - 'enode://44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d@127.0.0.1:30304', # noqa: E501 - ) - assert result is True - - -def test_admin_add_peer(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - result = web3.geth.admin.add_peer( - 'enode://44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d@127.0.0.1:30304', # noqa: E501 - ) - assert result is True +def test_admin_addPeer(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + result = web3.geth.admin.addPeer( + 'enode://44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d@127.0.0.1:30304', # noqa: E501 + ) + assert result is True diff --git a/tests/core/admin-module/test_admin_nodeInfo.py b/tests/core/admin-module/test_admin_nodeInfo.py index 52977a76cb..014d1bcb20 100644 --- a/tests/core/admin-module/test_admin_nodeInfo.py +++ b/tests/core/admin-module/test_admin_nodeInfo.py @@ -1,20 +1,7 @@ -import pytest - - -def test_admin_nodeInfo(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning): - node_info = web3.geth.admin.nodeInfo - - assert 'enode' in node_info - assert 'id' in node_info - - -def test_admin_node_info(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - node_info = web3.geth.admin.node_info - - assert 'enode' in node_info - assert 'id' in node_info +def test_admin_nodeInfo(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + node_info = web3.geth.admin.nodeInfo + + assert 'enode' in node_info + assert 'id' in node_info diff --git a/tests/core/admin-module/test_admin_peers.py b/tests/core/admin-module/test_admin_peers.py index 38766a04e9..317d1a3356 100644 --- a/tests/core/admin-module/test_admin_peers.py +++ b/tests/core/admin-module/test_admin_peers.py @@ -1,4 +1,4 @@ -def test_admin_peers(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - assert web3.geth.admin.peers == [] +def test_admin_peers(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + assert web3.geth.admin.peers == [] diff --git a/tests/core/admin-module/test_admin_setSolc.py b/tests/core/admin-module/test_admin_setSolc.py new file mode 100644 index 0000000000..d0de61ebfc --- /dev/null +++ b/tests/core/admin-module/test_admin_setSolc.py @@ -0,0 +1,20 @@ +import pytest +import subprocess + +from vns_utils import ( + to_text, +) + + +def test_admin_setSolc(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + try: + solc_path = subprocess.check_output(['which', 'solc']).strip() + except subprocess.CalledProcessError: + pytest.skip('solc binary not found') + solc_version = subprocess.check_output(['solc', '--version']).strip() + + actual = web3.geth.admin.setSolc(solc_path) + assert to_text(solc_version) in actual + assert to_text(solc_path) in actual diff --git a/tests/core/caching-utils/test_generate_cache_key.py b/tests/core/caching-utils/test_generate_cache_key.py index 73c425dc3f..d48f5e85a9 100644 --- a/tests/core/caching-utils/test_generate_cache_key.py +++ b/tests/core/caching-utils/test_generate_cache_key.py @@ -1,51 +1,51 @@ -import random - -from eth_utils import ( - to_dict, -) -from hypothesis import ( - given, - strategies as st, -) - -from web3._utils.caching import ( - generate_cache_key, -) - - -@to_dict -def shuffle_dict(_dict): - keys = list(_dict.keys()) - random.shuffle(keys) - for key in keys: - yield key, _dict[key] - - -def extend_fn(children): - lists_st = st.lists(children) - dicts_st = st.dictionaries(st.text(), children) - return lists_st | dicts_st - - -all_st = st.recursive( - st.none() | st.integers() | st.booleans() | st.floats() | st.text() | st.binary(), - extend_fn, -) - - -def recursive_shuffle_dict(v): - if isinstance(v, dict): - return shuffle_dict(v) - elif isinstance(v, (list, tuple)): - return type(v)((recursive_shuffle_dict(_v) for _v in v)) - else: - return v - - -@given(value=all_st) -def test_key_generation_is_deterministic(value): - left = recursive_shuffle_dict(value) - right = recursive_shuffle_dict(value) - left_key = generate_cache_key(left) - right_key = generate_cache_key(right) - assert left_key == right_key +import random + +from vns_utils import ( + to_dict, +) +from hypothesis import ( + given, + strategies as st, +) + +from web3._utils.caching import ( + generate_cache_key, +) + + +@to_dict +def shuffle_dict(_dict): + keys = list(_dict.keys()) + random.shuffle(keys) + for key in keys: + yield key, _dict[key] + + +def extend_fn(children): + lists_st = st.lists(children) + dicts_st = st.dictionaries(st.text(), children) + return lists_st | dicts_st + + +all_st = st.recursive( + st.none() | st.integers() | st.booleans() | st.floats() | st.text() | st.binary(), + extend_fn, +) + + +def recursive_shuffle_dict(v): + if isinstance(v, dict): + return shuffle_dict(v) + elif isinstance(v, (list, tuple)): + return type(v)((recursive_shuffle_dict(_v) for _v in v)) + else: + return v + + +@given(value=all_st) +def test_key_generation_is_deterministic(value): + left = recursive_shuffle_dict(value) + right = recursive_shuffle_dict(value) + left_key = generate_cache_key(left) + right_key = generate_cache_key(right) + assert left_key == right_key diff --git a/tests/core/contracts/conftest.py b/tests/core/contracts/conftest.py index 717906880b..aa0eb8cc87 100644 --- a/tests/core/contracts/conftest.py +++ b/tests/core/contracts/conftest.py @@ -1,926 +1,775 @@ -import functools -import json -import pytest - -from eth_utils import ( - event_signature_to_log_topic, -) - -from web3._utils.module_testing.emitter_contract import ( - CONTRACT_EMITTER_ABI, - CONTRACT_EMITTER_CODE, - CONTRACT_EMITTER_RUNTIME, -) -from web3._utils.module_testing.event_contract import ( - EVNT_CONTRACT_ABI, - EVNT_CONTRACT_CODE, - EVNT_CONTRACT_RUNTIME, -) -from web3._utils.module_testing.indexed_event_contract import ( - IND_EVENT_CONTRACT_ABI, - IND_EVENT_CONTRACT_CODE, - IND_EVENT_CONTRACT_RUNTIME, -) - -CONTRACT_NESTED_TUPLE_SOURCE = """ -pragma solidity >=0.4.19 <0.6.0; -pragma experimental ABIEncoderV2; - -contract Tuple { - struct U { int x; int y; } - struct T { U[] u; } - struct S { T[] t; } - - function method(S memory s) public pure returns (S memory) { - return s; - } -} -""" -CONTRACT_NESTED_TUPLE_CODE = '0x608060405234801561001057600080fd5b50610575806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632655aef114610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906102a2565b610083565b60405161007a919061043e565b60405180910390f35b61008b610093565b819050919050565b602060405190810160405280606081525090565b600082601f83011215156100ba57600080fd5b81356100cd6100c88261048d565b610460565b9150818183526020840193506020810190508360005b8381101561011357813586016100f98882610206565b8452602084019350602083019250506001810190506100e3565b5050505092915050565b600082601f830112151561013057600080fd5b813561014361013e826104b5565b610460565b9150818183526020840193506020810190508385604084028201111561016857600080fd5b60005b83811015610198578161017e8882610256565b84526020840193506040830192505060018101905061016b565b5050505092915050565b60006101ae8235610531565b905092915050565b6000602082840312156101c857600080fd5b6101d26020610460565b9050600082013567ffffffffffffffff8111156101ee57600080fd5b6101fa848285016100a7565b60008301525092915050565b60006020828403121561021857600080fd5b6102226020610460565b9050600082013567ffffffffffffffff81111561023e57600080fd5b61024a8482850161011d565b60008301525092915050565b60006040828403121561026857600080fd5b6102726040610460565b90506000610282848285016101a2565b6000830152506020610296848285016101a2565b60208301525092915050565b6000602082840312156102b457600080fd5b600082013567ffffffffffffffff8111156102ce57600080fd5b6102da848285016101b6565b91505092915050565b60006102ee826104f7565b80845260208401935083602082028501610307856104dd565b60005b848110156103405783830388526103228383516103e5565b925061032d8261050d565b915060208801975060018101905061030a565b508196508694505050505092915050565b600061035c82610502565b80845260208401935061036e836104ea565b60005b828110156103a05761038486835161040f565b61038d8261051a565b9150604086019550600181019050610371565b50849250505092915050565b6103b581610527565b82525050565b600060208301600083015184820360008601526103d882826102e3565b9150508091505092915050565b600060208301600083015184820360008601526104028282610351565b9150508091505092915050565b60408201600082015161042560008501826103ac565b50602082015161043860208501826103ac565b50505050565b6000602082019050818103600083015261045881846103bb565b905092915050565b6000604051905081810181811067ffffffffffffffff8211171561048357600080fd5b8060405250919050565b600067ffffffffffffffff8211156104a457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff8211156104cc57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60008190509190505600a265627a7a7230582011dc176b6cffda1d2e58827bca1a16178776c2d8efcd6adf48bc0223f14f891a6c6578706572696d656e74616cf50037' # noqa: E501 -CONTRACT_NESTED_TUPLE_RUNTIME = '0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632655aef114610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906102a2565b610083565b60405161007a919061043e565b60405180910390f35b61008b610093565b819050919050565b602060405190810160405280606081525090565b600082601f83011215156100ba57600080fd5b81356100cd6100c88261048d565b610460565b9150818183526020840193506020810190508360005b8381101561011357813586016100f98882610206565b8452602084019350602083019250506001810190506100e3565b5050505092915050565b600082601f830112151561013057600080fd5b813561014361013e826104b5565b610460565b9150818183526020840193506020810190508385604084028201111561016857600080fd5b60005b83811015610198578161017e8882610256565b84526020840193506040830192505060018101905061016b565b5050505092915050565b60006101ae8235610531565b905092915050565b6000602082840312156101c857600080fd5b6101d26020610460565b9050600082013567ffffffffffffffff8111156101ee57600080fd5b6101fa848285016100a7565b60008301525092915050565b60006020828403121561021857600080fd5b6102226020610460565b9050600082013567ffffffffffffffff81111561023e57600080fd5b61024a8482850161011d565b60008301525092915050565b60006040828403121561026857600080fd5b6102726040610460565b90506000610282848285016101a2565b6000830152506020610296848285016101a2565b60208301525092915050565b6000602082840312156102b457600080fd5b600082013567ffffffffffffffff8111156102ce57600080fd5b6102da848285016101b6565b91505092915050565b60006102ee826104f7565b80845260208401935083602082028501610307856104dd565b60005b848110156103405783830388526103228383516103e5565b925061032d8261050d565b915060208801975060018101905061030a565b508196508694505050505092915050565b600061035c82610502565b80845260208401935061036e836104ea565b60005b828110156103a05761038486835161040f565b61038d8261051a565b9150604086019550600181019050610371565b50849250505092915050565b6103b581610527565b82525050565b600060208301600083015184820360008601526103d882826102e3565b9150508091505092915050565b600060208301600083015184820360008601526104028282610351565b9150508091505092915050565b60408201600082015161042560008501826103ac565b50602082015161043860208501826103ac565b50505050565b6000602082019050818103600083015261045881846103bb565b905092915050565b6000604051905081810181811067ffffffffffffffff8211171561048357600080fd5b8060405250919050565b600067ffffffffffffffff8211156104a457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff8211156104cc57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60008190509190505600a265627a7a7230582011dc176b6cffda1d2e58827bca1a16178776c2d8efcd6adf48bc0223f14f891a6c6578706572696d656e74616cf50037' # noqa: E501 -CONTRACT_NESTED_TUPLE_ABI = json.loads('[{"constant":true,"inputs":[{"components":[{"components":[{"components":[{"name":"x","type":"int256"},{"name":"y","type":"int256"}],"name":"u","type":"tuple[]"}],"name":"t","type":"tuple[]"}],"name":"s","type":"tuple"}],"name":"method","outputs":[{"components":[{"components":[{"components":[{"name":"x","type":"int256"},{"name":"y","type":"int256"}],"name":"u","type":"tuple[]"}],"name":"t","type":"tuple[]"}],"name":"","type":"tuple"}],"payable":false,"stateMutability":"pure","type":"function"}]') # noqa: E501 - - -@pytest.fixture() -def NESTED_TUPLE_CODE(): - return CONTRACT_NESTED_TUPLE_CODE - - -@pytest.fixture() -def NESTED_TUPLE_RUNTIME(): - return CONTRACT_NESTED_TUPLE_RUNTIME - - -@pytest.fixture() -def NESTED_TUPLE_ABI(): - return CONTRACT_NESTED_TUPLE_ABI - - -@pytest.fixture() -def NESTED_TUPLE_CONTRACT(NESTED_TUPLE_CODE, NESTED_TUPLE_RUNTIME, NESTED_TUPLE_ABI): - return { - 'bytecode': NESTED_TUPLE_CODE, - 'bytecode_runtime': NESTED_TUPLE_RUNTIME, - 'abi': NESTED_TUPLE_ABI, - } - - -@pytest.fixture() -def NestedTupleContract(web3, NESTED_TUPLE_CONTRACT): - return web3.eth.contract(**NESTED_TUPLE_CONTRACT) - - -CONTRACT_TUPLE_SOURCE = """ -pragma solidity >=0.4.19 <0.6.0; -pragma experimental ABIEncoderV2; - -contract Tuple { - struct T { int x; bool[2] y; address[] z; } - struct S { uint a; uint[] b; T[] c; } - - function method(S memory s) public pure returns (S memory) { - return s; - } -} -""" -CONTRACT_TUPLE_CODE = '0x608060405234801561001057600080fd5b506108ca806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638e1ae3c714610046575b600080fd5b34801561005257600080fd5b5061006d60048036036100689190810190610403565b610083565b60405161007a9190610696565b60405180910390f35b61008b610093565b819050919050565b6060604051908101604052806000815260200160608152602001606081525090565b60006100c18235610850565b905092915050565b600082601f83011215156100dc57600080fd5b81356100ef6100ea826106e5565b6106b8565b9150818183526020840193506020810190508385602084028201111561011457600080fd5b60005b83811015610144578161012a88826100b5565b845260208401935060208301925050600181019050610117565b5050505092915050565b600082601f830112151561016157600080fd5b600261017461016f8261070d565b6106b8565b9150818385602084028201111561018a57600080fd5b60005b838110156101ba57816101a088826102bf565b84526020840193506020830192505060018101905061018d565b5050505092915050565b600082601f83011215156101d757600080fd5b81356101ea6101e58261072f565b6106b8565b9150818183526020840193506020810190508360005b8381101561023057813586016102168882610377565b845260208401935060208301925050600181019050610200565b5050505092915050565b600082601f830112151561024d57600080fd5b813561026061025b82610757565b6106b8565b9150818183526020840193506020810190508385602084028201111561028557600080fd5b60005b838110156102b5578161029b88826103ef565b845260208401935060208301925050600181019050610288565b5050505092915050565b60006102cb8235610870565b905092915050565b60006102df823561087c565b905092915050565b6000606082840312156102f957600080fd5b61030360606106b8565b90506000610313848285016103ef565b600083015250602082013567ffffffffffffffff81111561033357600080fd5b61033f8482850161023a565b602083015250604082013567ffffffffffffffff81111561035f57600080fd5b61036b848285016101c4565b60408301525092915050565b60006080828403121561038957600080fd5b61039360606106b8565b905060006103a3848285016102d3565b60008301525060206103b78482850161014e565b602083015250606082013567ffffffffffffffff8111156103d757600080fd5b6103e3848285016100c9565b60408301525092915050565b60006103fb8235610886565b905092915050565b60006020828403121561041557600080fd5b600082013567ffffffffffffffff81111561042f57600080fd5b61043b848285016102e7565b91505092915050565b61044d81610810565b82525050565b600061045e826107b0565b8084526020840193506104708361077f565b60005b828110156104a257610486868351610444565b61048f826107dc565b9150602086019550600181019050610473565b50849250505092915050565b6104b7816107bb565b6104c08261078c565b60005b828110156104f2576104d68583516105c2565b6104df826107e9565b91506020850194506001810190506104c3565b5050505050565b6000610504826107c6565b8084526020840193508360208202850161051d85610796565b60005b84811015610556578383038852610538838351610637565b9250610543826107f6565b9150602088019750600181019050610520565b508196508694505050505092915050565b6000610572826107d1565b808452602084019350610584836107a3565b60005b828110156105b65761059a868351610687565b6105a382610803565b9150602086019550600181019050610587565b50849250505092915050565b6105cb81610830565b82525050565b6105da8161083c565b82525050565b60006060830160008301516105f86000860182610687565b50602083015184820360208601526106108282610567565b9150506040830151848203604086015261062a82826104f9565b9150508091505092915050565b600060808301600083015161064f60008601826105d1565b50602083015161066260208601826104ae565b506040830151848203606086015261067a8282610453565b9150508091505092915050565b61069081610846565b82525050565b600060208201905081810360008301526106b081846105e0565b905092915050565b6000604051905081810181811067ffffffffffffffff821117156106db57600080fd5b8060405250919050565b600067ffffffffffffffff8211156106fc57600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561072457600080fd5b602082029050919050565b600067ffffffffffffffff82111561074657600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561076e57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60008190509190505600a265627a7a72305820fb4abae14c4bcd2cd8a12f35862cb09a2e0f9004eb5fdbfdb76658a753570c8c6c6578706572696d656e74616cf50037' # noqa: E501 -CONTRACT_TUPLE_RUNTIME = '0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638e1ae3c714610046575b600080fd5b34801561005257600080fd5b5061006d60048036036100689190810190610403565b610083565b60405161007a9190610696565b60405180910390f35b61008b610093565b819050919050565b6060604051908101604052806000815260200160608152602001606081525090565b60006100c18235610850565b905092915050565b600082601f83011215156100dc57600080fd5b81356100ef6100ea826106e5565b6106b8565b9150818183526020840193506020810190508385602084028201111561011457600080fd5b60005b83811015610144578161012a88826100b5565b845260208401935060208301925050600181019050610117565b5050505092915050565b600082601f830112151561016157600080fd5b600261017461016f8261070d565b6106b8565b9150818385602084028201111561018a57600080fd5b60005b838110156101ba57816101a088826102bf565b84526020840193506020830192505060018101905061018d565b5050505092915050565b600082601f83011215156101d757600080fd5b81356101ea6101e58261072f565b6106b8565b9150818183526020840193506020810190508360005b8381101561023057813586016102168882610377565b845260208401935060208301925050600181019050610200565b5050505092915050565b600082601f830112151561024d57600080fd5b813561026061025b82610757565b6106b8565b9150818183526020840193506020810190508385602084028201111561028557600080fd5b60005b838110156102b5578161029b88826103ef565b845260208401935060208301925050600181019050610288565b5050505092915050565b60006102cb8235610870565b905092915050565b60006102df823561087c565b905092915050565b6000606082840312156102f957600080fd5b61030360606106b8565b90506000610313848285016103ef565b600083015250602082013567ffffffffffffffff81111561033357600080fd5b61033f8482850161023a565b602083015250604082013567ffffffffffffffff81111561035f57600080fd5b61036b848285016101c4565b60408301525092915050565b60006080828403121561038957600080fd5b61039360606106b8565b905060006103a3848285016102d3565b60008301525060206103b78482850161014e565b602083015250606082013567ffffffffffffffff8111156103d757600080fd5b6103e3848285016100c9565b60408301525092915050565b60006103fb8235610886565b905092915050565b60006020828403121561041557600080fd5b600082013567ffffffffffffffff81111561042f57600080fd5b61043b848285016102e7565b91505092915050565b61044d81610810565b82525050565b600061045e826107b0565b8084526020840193506104708361077f565b60005b828110156104a257610486868351610444565b61048f826107dc565b9150602086019550600181019050610473565b50849250505092915050565b6104b7816107bb565b6104c08261078c565b60005b828110156104f2576104d68583516105c2565b6104df826107e9565b91506020850194506001810190506104c3565b5050505050565b6000610504826107c6565b8084526020840193508360208202850161051d85610796565b60005b84811015610556578383038852610538838351610637565b9250610543826107f6565b9150602088019750600181019050610520565b508196508694505050505092915050565b6000610572826107d1565b808452602084019350610584836107a3565b60005b828110156105b65761059a868351610687565b6105a382610803565b9150602086019550600181019050610587565b50849250505092915050565b6105cb81610830565b82525050565b6105da8161083c565b82525050565b60006060830160008301516105f86000860182610687565b50602083015184820360208601526106108282610567565b9150506040830151848203604086015261062a82826104f9565b9150508091505092915050565b600060808301600083015161064f60008601826105d1565b50602083015161066260208601826104ae565b506040830151848203606086015261067a8282610453565b9150508091505092915050565b61069081610846565b82525050565b600060208201905081810360008301526106b081846105e0565b905092915050565b6000604051905081810181811067ffffffffffffffff821117156106db57600080fd5b8060405250919050565b600067ffffffffffffffff8211156106fc57600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561072457600080fd5b602082029050919050565b600067ffffffffffffffff82111561074657600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561076e57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60008190509190505600a265627a7a72305820fb4abae14c4bcd2cd8a12f35862cb09a2e0f9004eb5fdbfdb76658a753570c8c6c6578706572696d656e74616cf50037' # noqa: E501 -CONTRACT_TUPLE_ABI = json.loads('[{"constant":true,"inputs":[{"components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256[]"},{"components":[{"name":"x","type":"int256"},{"name":"y","type":"bool[2]"},{"name":"z","type":"address[]"}],"name":"c","type":"tuple[]"}],"name":"s","type":"tuple"}],"name":"method","outputs":[{"components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256[]"},{"components":[{"name":"x","type":"int256"},{"name":"y","type":"bool[2]"},{"name":"z","type":"address[]"}],"name":"c","type":"tuple[]"}],"name":"","type":"tuple"}],"payable":false,"stateMutability":"pure","type":"function"}]') # noqa: E501 - - -@pytest.fixture() -def TUPLE_CODE(): - return CONTRACT_TUPLE_CODE - - -@pytest.fixture() -def TUPLE_RUNTIME(): - return CONTRACT_TUPLE_RUNTIME - - -@pytest.fixture() -def TUPLE_ABI(): - return CONTRACT_TUPLE_ABI - - -@pytest.fixture() -def TUPLE_CONTRACT(TUPLE_CODE, TUPLE_RUNTIME, TUPLE_ABI): - return { - 'bytecode': TUPLE_CODE, - 'bytecode_runtime': TUPLE_RUNTIME, - 'abi': TUPLE_ABI, - } - - -@pytest.fixture() -def TupleContract(web3, TUPLE_CONTRACT): - return web3.eth.contract(**TUPLE_CONTRACT) - - -CONTRACT_CODE = "0x606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 - - -CONTRACT_RUNTIME = "0x60606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 - - -CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 - - -@pytest.fixture(scope="session") -def MATH_CODE(): - return CONTRACT_CODE - - -@pytest.fixture(scope="session") -def MATH_RUNTIME(): - return CONTRACT_RUNTIME - - -@pytest.fixture(scope="session") -def MATH_ABI(): - return CONTRACT_ABI - - -@pytest.fixture() -def MathContract(web3, MATH_ABI, MATH_CODE, MATH_RUNTIME): - return web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - - -CONTRACT_SIMPLE_CONSTRUCTOR_CODE = '0x60606040526003600055602c8060156000396000f3606060405260e060020a600035046373d4a13a8114601a575b005b602260005481565b6060908152602090f3' # noqa: E501 -CONTRACT_SIMPLE_CONSTRUCTOR_RUNTIME = '0x606060405260e060020a600035046373d4a13a8114601a575b005b602260005481565b6060908152602090f3' # noqa: E501 -CONTRACT_SIMPLE_CONSTRUCTOR_ABI = json.loads('[{"constant":true,"inputs":[],"name":"data","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[],"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture(scope="session") -def SIMPLE_CONSTRUCTOR_CODE(): - return CONTRACT_SIMPLE_CONSTRUCTOR_CODE - - -@pytest.fixture(scope="session") -def SIMPLE_CONSTRUCTOR_RUNTIME(): - return CONTRACT_SIMPLE_CONSTRUCTOR_RUNTIME - - -@pytest.fixture(scope="session") -def SIMPLE_CONSTRUCTOR_ABI(): - return CONTRACT_SIMPLE_CONSTRUCTOR_ABI - - -@pytest.fixture() -def SimpleConstructorContract(web3, - SIMPLE_CONSTRUCTOR_CODE, - SIMPLE_CONSTRUCTOR_RUNTIME, - SIMPLE_CONSTRUCTOR_ABI): - return web3.eth.contract( - abi=SIMPLE_CONSTRUCTOR_ABI, - bytecode=SIMPLE_CONSTRUCTOR_CODE, - bytecode_runtime=SIMPLE_CONSTRUCTOR_RUNTIME, - ) - - -CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_CODE = "0x60606040818152806066833960a09052516080516000918255600155603e908190602890396000f3606060405260e060020a600035046388ec134681146024578063d4c46c7614602c575b005b603460005481565b603460015481565b6060908152602090f3" # noqa: E501 -CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME = "0x606060405260e060020a600035046388ec134681146024578063d4c46c7614602c575b005b603460005481565b603460015481565b6060908152602090f3" # noqa: E501 -CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_ABI = json.loads('[{"constant":true,"inputs":[],"name":"data_a","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"data_b","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bytes32"}],"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ARGUMENTS_CODE(): - return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_CODE - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME(): - return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ARGUMENTS_ABI(): - return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_ABI - - -@pytest.fixture() -def WithConstructorArgumentsContract(web3, - WITH_CONSTRUCTOR_ARGUMENTS_CODE, - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, - WITH_CONSTRUCTOR_ARGUMENTS_ABI): - return web3.eth.contract( - abi=WITH_CONSTRUCTOR_ARGUMENTS_ABI, - bytecode=WITH_CONSTRUCTOR_ARGUMENTS_CODE, - bytecode_runtime=WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, - ) - - -@pytest.fixture() -def WithConstructorArgumentsContractStrict(w3_strict_abi, - WITH_CONSTRUCTOR_ARGUMENTS_CODE, - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, - WITH_CONSTRUCTOR_ARGUMENTS_ABI): - return w3_strict_abi.eth.contract( - abi=WITH_CONSTRUCTOR_ARGUMENTS_ABI, - bytecode=WITH_CONSTRUCTOR_ARGUMENTS_CODE, - bytecode_runtime=WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, - ) - - -CONTRACT_WITH_CONSTRUCTOR_ADDRESS_CODE = "0x6060604052604051602080607683395060806040525160008054600160a060020a031916821790555060428060346000396000f3606060405260e060020a600035046334664e3a8114601a575b005b603860005473ffffffffffffffffffffffffffffffffffffffff1681565b6060908152602090f3" # noqa: E501 -CONTRACT_WITH_CONSTRUCTOR_ADDRESS_RUNTIME = "0x606060405260e060020a600035046334664e3a8114601a575b005b603860005473ffffffffffffffffffffffffffffffffffffffff1681565b6060908152602090f3" # noqa: E501 -CONTRACT_WITH_CONSTRUCTOR_ADDRESS_ABI = json.loads('[{"constant":true,"inputs":[],"name":"testAddr","outputs":[{"name":"","type":"address"}],"type":"function"},{"inputs":[{"name":"_testAddr","type":"address"}],"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ADDRESS_CODE(): - return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_CODE - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ADDRESS_RUNTIME(): - return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_RUNTIME - - -@pytest.fixture() -def WITH_CONSTRUCTOR_ADDRESS_ABI(): - return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_ABI - - -@pytest.fixture() -def WithConstructorAddressArgumentsContract(web3, - WITH_CONSTRUCTOR_ADDRESS_CODE, - WITH_CONSTRUCTOR_ADDRESS_RUNTIME, - WITH_CONSTRUCTOR_ADDRESS_ABI): - return web3.eth.contract( - abi=WITH_CONSTRUCTOR_ADDRESS_ABI, - bytecode=WITH_CONSTRUCTOR_ADDRESS_CODE, - bytecode_runtime=WITH_CONSTRUCTOR_ADDRESS_RUNTIME, - ) - - -@pytest.fixture() -def AddressReflectorContract(web3): - return web3.eth.contract( - abi=json.loads('[{"constant":true,"inputs":[{"name":"arg","type":"address"}],"name":"reflect","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"arg","type":"address[]"}],"name":"reflect","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"pure","type":"function"}]'), # noqa: 501 - bytecode="6060604052341561000f57600080fd5b6101ca8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b816c1614610048578063c04d11fc146100c157600080fd5b341561005357600080fd5b61007f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610170565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100cc57600080fd5b61011960048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061017a565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561015c578082015181840152602081019050610141565b505050509050019250505060405180910390f35b6000819050919050565b61018261018a565b819050919050565b6020604051908101604052806000815250905600a165627a7a723058206b15d98a803b91327d94f943e9712291539701b2f7370e10f5873633941484930029", # noqa: 501 - bytecode_runtime="60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b816c1614610048578063c04d11fc146100c157600080fd5b341561005357600080fd5b61007f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610170565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100cc57600080fd5b61011960048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061017a565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561015c578082015181840152602081019050610141565b505050509050019250505060405180910390f35b6000819050919050565b61018261018a565b819050919050565b6020604051908101604052806000815250905600a165627a7a723058206b15d98a803b91327d94f943e9712291539701b2f7370e10f5873633941484930029", # noqa: 501 - ) - - -CONTRACT_STRING_CODE = "0x6060604052604051610496380380610496833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b5050506103d8806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a600035046320965255811461003c57806330de3cee1461009f5780633fa4f245146100c457806393a0935214610121575b005b6101c7600060608181528154602060026001831615610100026000190190921691909104601f810182900490910260a0908101604052608082815292939190828280156102605780601f1061023557610100808354040283529160200191610260565b6101c7600060609081526101a06040526101006080818152906102d860a03990505b90565b6101c760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102975780601f1061026c57610100808354040283529160200191610297565b60206004803580820135601f81018490049093026080908101604052606084815261003a946024939192918401918190838280828437509496505050505050508060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029f57805160ff19168380011785555b506102cf9291505b808211156102d4578381556001016101b4565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161024357829003601f168201915b505050505090506100c1565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b505050505081565b828001600101855582156101ac579182015b828111156101ac5782518260005055916020019190600101906102b1565b505050565b509056000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" # noqa: E501 - -CONTRACT_STRING_RUNTIME = "0x606060405260e060020a600035046320965255811461003c57806330de3cee1461009f5780633fa4f245146100c457806393a0935214610121575b005b6101c7600060608181528154602060026001831615610100026000190190921691909104601f810182900490910260a0908101604052608082815292939190828280156102605780601f1061023557610100808354040283529160200191610260565b6101c7600060609081526101a06040526101006080818152906102d860a03990505b90565b6101c760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102975780601f1061026c57610100808354040283529160200191610297565b60206004803580820135601f81018490049093026080908101604052606084815261003a946024939192918401918190838280828437509496505050505050508060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029f57805160ff19168380011785555b506102cf9291505b808211156102d4578381556001016101b4565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161024357829003601f168201915b505050505090506100c1565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b505050505081565b828001600101855582156101ac579182015b828111156101ac5782518260005055916020019190600101906102b1565b505050565b509056000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" # noqa: E501 - -CONTRACT_STRING_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"string"}],"name":"setValue","outputs":[],"type":"function"},{"inputs":[{"name":"_value","type":"string"}],"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture() -def STRING_CODE(): - return CONTRACT_STRING_CODE - - -@pytest.fixture() -def STRING_RUNTIME(): - return CONTRACT_STRING_RUNTIME - - -@pytest.fixture() -def STRING_ABI(): - return CONTRACT_STRING_ABI - - -@pytest.fixture() -def STRING_CONTRACT(STRING_CODE, STRING_RUNTIME, STRING_ABI): - return { - 'bytecode': STRING_CODE, - 'bytecode_runtime': STRING_RUNTIME, - 'abi': STRING_ABI, - } - - -@pytest.fixture() -def StringContract(web3, STRING_CONTRACT): - return web3.eth.contract(**STRING_CONTRACT) - - -CONTRACT_BYTES_CODE = "60606040526040805190810160405280600281526020017f01230000000000000000000000000000000000000000000000000000000000008152506000908051906020019061004f929190610096565b50341561005b57600080fd5b604051610723380380610723833981016040528080518201919050505b806001908051906020019061008e929190610116565b505b506101bb565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d757805160ff1916838001178555610105565b82800160010185558215610105579182015b828111156101045782518255916020019190600101906100e9565b5b5090506101129190610196565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015757805160ff1916838001178555610185565b82800160010185558215610185579182015b82811115610184578251825591602001919060010190610169565b5b5090506101929190610196565b5090565b6101b891905b808211156101b457600081600090555060010161019c565b5090565b90565b610559806101ca6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100ee5780633fa4f2451461017d578063439970aa1461020c575b600080fd5b341561006a57600080fd5b610072610269565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b35780820151818401525b602081019050610097565b50505050905090810190601f1680156100e05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f957600080fd5b610101610312565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101425780820151818401525b602081019050610126565b50505050905090810190601f16801561016f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561018857600080fd5b6101906103bb565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101d15780820151818401525b6020810190506101b5565b50505050905090810190601f1680156101fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561021757600080fd5b610267600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610459565b005b610271610474565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103075780601f106102dc57610100808354040283529160200191610307565b820191906000526020600020905b8154815290600101906020018083116102ea57829003601f168201915b505050505090505b90565b61031a610474565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103b05780601f10610385576101008083540402835291602001916103b0565b820191906000526020600020905b81548152906001019060200180831161039357829003601f168201915b505050505090505b90565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104515780601f1061042657610100808354040283529160200191610451565b820191906000526020600020905b81548152906001019060200180831161043457829003601f168201915b505050505081565b806001908051906020019061046f929190610488565b505b50565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104c957805160ff19168380011785556104f7565b828001600101855582156104f7579182015b828111156104f65782518255916020019190600101906104db565b5b5090506105049190610508565b5090565b61052a91905b8082111561052657600081600090555060010161050e565b5090565b905600a165627a7a723058203ff916ee91add6247b20793745d1c6a8d8dcaa49d8c84fbbabb5c966fd9b6fc90029" # noqa: E501 - -CONTRACT_BYTES_RUNTIME = "60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100ee5780633fa4f2451461017d578063439970aa1461020c575b600080fd5b341561006a57600080fd5b610072610269565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b35780820151818401525b602081019050610097565b50505050905090810190601f1680156100e05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f957600080fd5b610101610312565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101425780820151818401525b602081019050610126565b50505050905090810190601f16801561016f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561018857600080fd5b6101906103bb565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101d15780820151818401525b6020810190506101b5565b50505050905090810190601f1680156101fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561021757600080fd5b610267600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610459565b005b610271610474565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103075780601f106102dc57610100808354040283529160200191610307565b820191906000526020600020905b8154815290600101906020018083116102ea57829003601f168201915b505050505090505b90565b61031a610474565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103b05780601f10610385576101008083540402835291602001916103b0565b820191906000526020600020905b81548152906001019060200180831161039357829003601f168201915b505050505090505b90565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104515780601f1061042657610100808354040283529160200191610451565b820191906000526020600020905b81548152906001019060200180831161043457829003601f168201915b505050505081565b806001908051906020019061046f929190610488565b505b50565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104c957805160ff19168380011785556104f7565b828001600101855582156104f7579182015b828111156104f65782518255916020019190600101906104db565b5b5090506105049190610508565b5090565b61052a91905b8082111561052657600081600090555060010161050e565b5090565b905600a165627a7a723058203ff916ee91add6247b20793745d1c6a8d8dcaa49d8c84fbbabb5c966fd9b6fc90029" # noqa: E501 - -CONTRACT_BYTES_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"bytes"}],"name":"setValue","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_value","type":"bytes"}],"payable":false,"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture() -def BYTES_CODE(): - return CONTRACT_BYTES_CODE - - -@pytest.fixture() -def BYTES_RUNTIME(): - return CONTRACT_BYTES_RUNTIME - - -@pytest.fixture() -def BYTES_ABI(): - return CONTRACT_BYTES_ABI - - -@pytest.fixture() -def BYTES_CONTRACT(BYTES_CODE, BYTES_RUNTIME, BYTES_ABI): - return { - 'bytecode': BYTES_CODE, - 'bytecode_runtime': BYTES_RUNTIME, - 'abi': BYTES_ABI, - } - - -@pytest.fixture() -def BytesContract(web3, BYTES_CONTRACT): - return web3.eth.contract(**BYTES_CONTRACT) - - -CONTRACT_BYTES32_CODE = "60606040527f0123012301230123012301230123012301230123012301230123012301230123600090600019169055341561003957600080fd5b6040516020806101e2833981016040528080519060200190919050505b80600181600019169055505b505b61016f806100736000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100905780633fa4f245146100c157806358825b10146100f2575b600080fd5b341561006a57600080fd5b610072610119565b60405180826000191660001916815260200191505060405180910390f35b341561009b57600080fd5b6100a3610124565b60405180826000191660001916815260200191505060405180910390f35b34156100cc57600080fd5b6100d461012e565b60405180826000191660001916815260200191505060405180910390f35b34156100fd57600080fd5b610117600480803560001916906020019091905050610134565b005b600060015490505b90565b6000805490505b90565b60015481565b80600181600019169055505b505600a165627a7a7230582043b15c20378b1603d330561258ccf291d08923a4c25fa8af0d590a010a6322180029" # noqa: E501 - -CONTRACT_BYTES32_RUNTIME = "60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100905780633fa4f245146100c157806358825b10146100f2575b600080fd5b341561006a57600080fd5b610072610119565b60405180826000191660001916815260200191505060405180910390f35b341561009b57600080fd5b6100a3610124565b60405180826000191660001916815260200191505060405180910390f35b34156100cc57600080fd5b6100d461012e565b60405180826000191660001916815260200191505060405180910390f35b34156100fd57600080fd5b610117600480803560001916906020019091905050610134565b005b600060015490505b90565b6000805490505b90565b60015481565b80600181600019169055505b505600a165627a7a7230582043b15c20378b1603d330561258ccf291d08923a4c25fa8af0d590a010a6322180029" # noqa: E501 - -CONTRACT_BYTES32_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"bytes32"}],"name":"setValue","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_value","type":"bytes32"}],"payable":false,"type":"constructor"}]') # noqa: E501 - - -@pytest.fixture() -def BYTES32_CODE(): - return CONTRACT_BYTES32_CODE - - -@pytest.fixture() -def BYTES32_RUNTIME(): - return CONTRACT_BYTES32_RUNTIME - - -@pytest.fixture() -def BYTES32_ABI(): - return CONTRACT_BYTES32_ABI - - -@pytest.fixture() -def BYTES32_CONTRACT(BYTES32_CODE, BYTES32_RUNTIME, BYTES32_ABI): - return { - 'bytecode': BYTES32_CODE, - 'bytecode_runtime': BYTES32_RUNTIME, - 'abi': BYTES32_ABI, - } - - -@pytest.fixture() -def Bytes32Contract(web3, BYTES32_CONTRACT): - return web3.eth.contract(**BYTES32_CONTRACT) - - -@pytest.fixture() -def EMITTER_CODE(): - return CONTRACT_EMITTER_CODE - - -@pytest.fixture() -def EMITTER_RUNTIME(): - return CONTRACT_EMITTER_RUNTIME - - -@pytest.fixture() -def EMITTER_ABI(): - return CONTRACT_EMITTER_ABI - - -@pytest.fixture() -def EMITTER(EMITTER_CODE, - EMITTER_RUNTIME, - EMITTER_ABI): - return { - 'bytecode': EMITTER_CODE, - 'bytecode_runtime': EMITTER_RUNTIME, - 'abi': EMITTER_ABI, - } - - -@pytest.fixture() -def StrictEmitter(w3_strict_abi, EMITTER): - w3 = w3_strict_abi - return w3.eth.contract(**EMITTER) - - -@pytest.fixture() -def strict_emitter(w3_strict_abi, - StrictEmitter, - wait_for_transaction, - wait_for_block, - address_conversion_func): - w3 = w3_strict_abi - - wait_for_block(w3) - deploy_txn_hash = StrictEmitter.constructor().transact( - {'from': w3.eth.coinbase, 'gas': 1000000} - ) - deploy_receipt = wait_for_transaction(w3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = w3.eth.getCode(contract_address) - assert bytecode == StrictEmitter.bytecode_runtime - emitter_contract = StrictEmitter(address=contract_address) - assert emitter_contract.address == contract_address - return emitter_contract - - -@pytest.fixture() -def EVENT_CONTRACT_CODE(): - return EVNT_CONTRACT_CODE - - -@pytest.fixture() -def EVENT_CONTRACT_RUNTIME(): - return EVNT_CONTRACT_RUNTIME - - -@pytest.fixture() -def EVENT_CONTRACT_ABI(): - return EVNT_CONTRACT_ABI - - -@pytest.fixture() -def EVENT_CONTRACT( - EVENT_CONTRACT_CODE, - EVENT_CONTRACT_RUNTIME, - EVENT_CONTRACT_ABI): - return { - 'bytecode': EVENT_CONTRACT_CODE, - 'bytecode_runtime': EVENT_CONTRACT_RUNTIME, - 'abi': EVENT_CONTRACT_ABI, - } - - -@pytest.fixture() -def EventContract(web3_empty, EVENT_CONTRACT): - web3 = web3_empty - return web3.eth.contract(**EVENT_CONTRACT) - - -@pytest.fixture() -def event_contract( - web3_empty, - EventContract, - wait_for_transaction, - wait_for_block, - address_conversion_func): - - web3 = web3_empty - - wait_for_block(web3) - deploy_txn_hash = EventContract.constructor().transact({ - 'from': web3.eth.coinbase, 'gas': 1000000 - }) - deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == EventContract.bytecode_runtime - event_contract = EventContract(address=contract_address) - assert event_contract.address == contract_address - return event_contract - - -@pytest.fixture() -def INDEXED_EVENT_CONTRACT_CODE(): - return IND_EVENT_CONTRACT_CODE - - -@pytest.fixture() -def INDEXED_EVENT_CONTRACT_RUNTIME(): - return IND_EVENT_CONTRACT_RUNTIME - - -@pytest.fixture() -def INDEXED_EVENT_CONTRACT_ABI(): - return IND_EVENT_CONTRACT_ABI - - -@pytest.fixture() -def INDEXED_EVENT_CONTRACT( - INDEXED_EVENT_CONTRACT_CODE, - INDEXED_EVENT_CONTRACT_RUNTIME, - INDEXED_EVENT_CONTRACT_ABI): - return { - 'bytecode': INDEXED_EVENT_CONTRACT_CODE, - 'bytecode_runtime': INDEXED_EVENT_CONTRACT_RUNTIME, - 'abi': INDEXED_EVENT_CONTRACT_ABI, - } - - -@pytest.fixture() -def IndexedEventContract(web3_empty, INDEXED_EVENT_CONTRACT): - web3 = web3_empty - return web3.eth.contract(**INDEXED_EVENT_CONTRACT) - - -@pytest.fixture() -def indexed_event( - web3_empty, - IndexedEventContract, - wait_for_transaction, - wait_for_block, - address_conversion_func): - - web3 = web3_empty - - wait_for_block(web3) - deploy_txn_hash = IndexedEventContract.constructor().transact({ - 'from': web3.eth.coinbase, - 'gas': 1000000 - }) - deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == IndexedEventContract.bytecode_runtime - indexed_event_contract = IndexedEventContract(address=contract_address) - assert indexed_event_contract.address == contract_address - return indexed_event_contract - - -CONTRACT_ARRAYS_SOURCE = """ - contract ArraysContract { - - bytes32[] public bytes32Value; - bytes32[] public bytes32ConstValue; - byte[] public byteValue; - byte[] public byteConstValue; - - function ArraysContract(bytes32[] _bytes32Value, byte[] _byteValue) { - bytes32Value = _bytes32Value; - byteValue = _byteValue; - bytes32ConstValue = [keccak256('A'), keccak256('B')]; - byteConstValue = [bytes1(0), bytes1(1)]; - } - - function setBytes32Value(bytes32[] _bytes32Value) { - bytes32Value = _bytes32Value; - } - - function getBytes32Value() returns (bytes32[]) { - return bytes32Value; - } - - function getBytes32ConstValue() returns (bytes32[]) { - return bytes32ConstValue; - } - - function setByteValue(byte[] _byteValue) { - byteValue = _byteValue; - } - - function getByteValue() returns (byte[]) { - return byteValue; - } - - function getByteConstValue() returns (byte[]) { - return byteConstValue; - } -} -""" - - -CONTRACT_ARRAYS_CODE = "606060405234156200001057600080fd5b60405162000e6238038062000e628339810160405280805182019190602001805182019190505081600090805190602001906200004f92919062000209565b5080600290805190602001906200006892919062000261565b50604080519081016040528060405180807f4100000000000000000000000000000000000000000000000000000000000000815250600101905060405180910390206000191660001916815260200160405180807f420000000000000000000000000000000000000000000000000000000000000081525060010190506040518091039020600019166000191681525060019060026200010a9291906200032f565b50604080519081016040528060007f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160017f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681525060039060026200020092919062000387565b505050620004b0565b8280548282559060005260206000209081019282156200024e579160200282015b828111156200024d5782518290600019169055916020019190600101906200022a565b5b5090506200025d919062000455565b5090565b82805482825590600052602060002090601f016020900481019282156200031c5791602002820160005b83821115620002eb57835183826101000a81548160ff02191690837f01000000000000000000000000000000000000000000000000000000000000009004021790555092602001926001016020816000010492830192600103026200028b565b80156200031a5782816101000a81549060ff0219169055600101602081600001049283019260010302620002eb565b505b5090506200032b91906200047d565b5090565b82805482825590600052602060002090810192821562000374579160200282015b828111156200037357825182906000191690559160200191906001019062000350565b5b50905062000383919062000455565b5090565b82805482825590600052602060002090601f01602090048101928215620004425791602002820160005b838211156200041157835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302620003b1565b8015620004405782816101000a81549060ff021916905560010160208160000104928301926001030262000411565b505b5090506200045191906200047d565b5090565b6200047a91905b80821115620004765760008160009055506001016200045c565b5090565b90565b620004ad91905b80821115620004a957600081816101000a81549060ff02191690555060010162000484565b5090565b90565b6109a280620004c06000396000f300606060405236156100a2576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630afe5e33146100a757806312c9dcc8146101115780631579bf661461018c5780633ddcea2f146101f657806351b4878814610250578063542d83de1461028f578063605ba271146102ce5780638abe51fd14610338578063962e450c146103a2578063bb69679b1461041d575b600080fd5b34156100b257600080fd5b6100ba610477565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100fd5780820151818401526020810190506100e2565b505050509050019250505060405180910390f35b341561011c57600080fd5b61013260048080359060200190919050506104d9565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561019757600080fd5b61019f61052b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101e25780820151818401526020810190506101c7565b505050509050019250505060405180910390f35b341561020157600080fd5b61024e6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506105ed565b005b341561025b57600080fd5b6102716004808035906020019091905050610607565b60405180826000191660001916815260200191505060405180910390f35b341561029a57600080fd5b6102b0600480803590602001909190505061062b565b60405180826000191660001916815260200191505060405180910390f35b34156102d957600080fd5b6102e161064f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610324578082015181840152602081019050610309565b505050509050019250505060405180910390f35b341561034357600080fd5b61034b6106b1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561038e578082015181840152602081019050610373565b505050509050019250505060405180910390f35b34156103ad57600080fd5b6103c36004808035906020019091905050610773565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561042857600080fd5b6104756004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506107c5565b005b61047f6107df565b60018054806020026020016040519081016040528092919081815260200182805480156104cf57602002820191906000526020600020905b815460001916815260200190600101908083116104b7575b5050505050905090565b6002818154811015156104e857fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b6105336107f3565b60038054806020026020016040519081016040528092919081815260200182805480156105e357602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906001019060208260000104928301926001038202915080841161056e5790505b5050505050905090565b8060029080519060200190610603929190610807565b5050565b60018181548110151561061657fe5b90600052602060002090016000915090505481565b60008181548110151561063a57fe5b90600052602060002090016000915090505481565b6106576107df565b60008054806020026020016040519081016040528092919081815260200182805480156106a757602002820191906000526020600020905b8154600019168152602001906001019080831161068f575b5050505050905090565b6106b96107f3565b600280548060200260200160405190810160405280929190818152602001828054801561076957602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190600101906020826000010492830192600103820291508084116106f45790505b5050505050905090565b60038181548110151561078257fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b80600090805190602001906107db9291906108ce565b5050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b82805482825590600052602060002090601f016020900481019282156108bd5791602002820160005b8382111561088e57835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302610830565b80156108bb5782816101000a81549060ff021916905560010160208160000104928301926001030261088e565b505b5090506108ca9190610921565b5090565b828054828255906000526020600020908101928215610910579160200282015b8281111561090f5782518290600019169055916020019190600101906108ee565b5b50905061091d9190610951565b5090565b61094e91905b8082111561094a57600081816101000a81549060ff021916905550600101610927565b5090565b90565b61097391905b8082111561096f576000816000905550600101610957565b5090565b905600a165627a7a72305820d0caf89bd0d39b343a907ac9d0f0b9bcddb1b8dc910706160d9d26fdc552afa40029" # noqa: E501 - -CONTRACT_ARRAYS_RUNTIME = "0x606060405236156100a2576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630afe5e33146100a757806312c9dcc8146101115780631579bf661461018c5780633ddcea2f146101f657806351b4878814610250578063542d83de1461028f578063605ba271146102ce5780638abe51fd14610338578063962e450c146103a2578063bb69679b1461041d575b600080fd5b34156100b257600080fd5b6100ba610477565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100fd5780820151818401526020810190506100e2565b505050509050019250505060405180910390f35b341561011c57600080fd5b61013260048080359060200190919050506104d9565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561019757600080fd5b61019f61052b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101e25780820151818401526020810190506101c7565b505050509050019250505060405180910390f35b341561020157600080fd5b61024e6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506105ed565b005b341561025b57600080fd5b6102716004808035906020019091905050610607565b60405180826000191660001916815260200191505060405180910390f35b341561029a57600080fd5b6102b0600480803590602001909190505061062b565b60405180826000191660001916815260200191505060405180910390f35b34156102d957600080fd5b6102e161064f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610324578082015181840152602081019050610309565b505050509050019250505060405180910390f35b341561034357600080fd5b61034b6106b1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561038e578082015181840152602081019050610373565b505050509050019250505060405180910390f35b34156103ad57600080fd5b6103c36004808035906020019091905050610773565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561042857600080fd5b6104756004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506107c5565b005b61047f6107df565b60018054806020026020016040519081016040528092919081815260200182805480156104cf57602002820191906000526020600020905b815460001916815260200190600101908083116104b7575b5050505050905090565b6002818154811015156104e857fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b6105336107f3565b60038054806020026020016040519081016040528092919081815260200182805480156105e357602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906001019060208260000104928301926001038202915080841161056e5790505b5050505050905090565b8060029080519060200190610603929190610807565b5050565b60018181548110151561061657fe5b90600052602060002090016000915090505481565b60008181548110151561063a57fe5b90600052602060002090016000915090505481565b6106576107df565b60008054806020026020016040519081016040528092919081815260200182805480156106a757602002820191906000526020600020905b8154600019168152602001906001019080831161068f575b5050505050905090565b6106b96107f3565b600280548060200260200160405190810160405280929190818152602001828054801561076957602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190600101906020826000010492830192600103820291508084116106f45790505b5050505050905090565b60038181548110151561078257fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b80600090805190602001906107db9291906108ce565b5050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b82805482825590600052602060002090601f016020900481019282156108bd5791602002820160005b8382111561088e57835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302610830565b80156108bb5782816101000a81549060ff021916905560010160208160000104928301926001030261088e565b505b5090506108ca9190610921565b5090565b828054828255906000526020600020908101928215610910579160200282015b8281111561090f5782518290600019169055916020019190600101906108ee565b5b50905061091d9190610951565b5090565b61094e91905b8082111561094a57600081816101000a81549060ff021916905550600101610927565b5090565b90565b61097391905b8082111561096f576000816000905550600101610957565b5090565b905600a165627a7a72305820d0caf89bd0d39b343a907ac9d0f0b9bcddb1b8dc910706160d9d26fdc552afa40029" # noqa: E501 - -CONTRACT_ARRAYS_ABI = json.loads('[{"constant": false, "inputs": [], "name": "getBytes32ConstValue", "outputs": [{"name": "", "type": "bytes32[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "byteValue", "outputs": [{"name": "", "type": "bytes1"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "getByteConstValue", "outputs": [{"name": "", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": false, "inputs": [{"name": "_byteValue", "type": "bytes1[]"}], "name": "setByteValue", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "bytes32ConstValue", "outputs": [{"name": "", "type": "bytes32"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "bytes32Value", "outputs": [{"name": "", "type": "bytes32"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "getBytes32Value", "outputs": [{"name": "", "type": "bytes32[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": false, "inputs": [], "name": "getByteValue", "outputs": [{"name": "", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "byteConstValue", "outputs": [{"name": "", "type": "bytes1"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [{"name": "_bytes32Value", "type": "bytes32[]"}], "name": "setBytes32Value", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"inputs": [{"name": "_bytes32Value", "type": "bytes32[]"}, {"name": "_byteValue", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "constructor"}]') # noqa: E501 - - -@pytest.fixture() -def ARRAYS_CODE(): - return CONTRACT_ARRAYS_CODE - - -@pytest.fixture() -def ARRAYS_RUNTIME(): - return CONTRACT_ARRAYS_RUNTIME - - -@pytest.fixture() -def ARRAYS_ABI(): - return CONTRACT_ARRAYS_ABI - - -@pytest.fixture() -def ARRAYS_CONTRACT(ARRAYS_CODE, - ARRAYS_RUNTIME, - ARRAYS_ABI): - return { - 'bytecode': ARRAYS_CODE, - 'bytecode_runtime': ARRAYS_RUNTIME, - 'abi': ARRAYS_ABI, - } - - -@pytest.fixture() -def ArraysContract(web3, ARRAYS_CONTRACT): - return web3.eth.contract(**ARRAYS_CONTRACT) - - -@pytest.fixture() -def StrictArraysContract(w3_strict_abi, ARRAYS_CONTRACT): - return w3_strict_abi.eth.contract(**ARRAYS_CONTRACT) - - -CONTRACT_PAYABLE_TESTER_SOURCE = """ -contract PayableTester { - bool public wasCalled; - - function doNoValueCall() public { - wasCalled = true; - } -} -""" - -CONTRACT_PAYABLE_TESTER_CODE = "608060405234801561001057600080fd5b5060e88061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c680362214604e578063e4cb8f5c14607a575b600080fd5b348015605957600080fd5b506060608e565b604051808215151515815260200191505060405180910390f35b348015608557600080fd5b50608c60a0565b005b6000809054906101000a900460ff1681565b60016000806101000a81548160ff0219169083151502179055505600a165627a7a723058205362c7376eda918b0dc3a75d0ffab904a241c9b10b68d5268af6ca405242303e0029" # noqa: E501 - -CONTRACT_PAYABLE_TESTER_RUNTIME = "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c680362214604e578063e4cb8f5c14607a575b600080fd5b348015605957600080fd5b506060608e565b604051808215151515815260200191505060405180910390f35b348015608557600080fd5b50608c60a0565b005b6000809054906101000a900460ff1681565b60016000806101000a81548160ff0219169083151502179055505600a165627a7a723058205362c7376eda918b0dc3a75d0ffab904a241c9b10b68d5268af6ca405242303e0029" # noqa: E501 - -CONTRACT_PAYABLE_TESTER_ABI = json.loads('[{"constant": true, "inputs": [], "name": "wasCalled", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "doNoValueCall", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}]') # noqa: E501 - - -@pytest.fixture() -def PAYABLE_TESTER_CODE(): - return CONTRACT_PAYABLE_TESTER_CODE - - -@pytest.fixture() -def PAYABLE_TESTER_RUNTIME(): - return CONTRACT_PAYABLE_TESTER_RUNTIME - - -@pytest.fixture() -def PAYABLE_TESTER_ABI(): - return CONTRACT_PAYABLE_TESTER_ABI - - -@pytest.fixture() -def PAYABLE_TESTER_CONTRACT(PAYABLE_TESTER_CODE, - PAYABLE_TESTER_RUNTIME, - PAYABLE_TESTER_ABI): - return { - 'bytecode': PAYABLE_TESTER_CODE, - 'bytecode_runtime': PAYABLE_TESTER_RUNTIME, - 'abi': PAYABLE_TESTER_ABI, - } - - -@pytest.fixture() -def PayableTesterContract(web3, PAYABLE_TESTER_CONTRACT): - return web3.eth.contract(**PAYABLE_TESTER_CONTRACT) - - -# no matter the function selector, this will return back the 32 bytes of data supplied -CONTRACT_REFLECTION_CODE = ( - "0x610011566020600460003760206000f3005b61000461001103610004600039610004610011036000f3" -) - -# reference source used to generate it: -LLL_SOURCE = "['seq', ['return', 0, ['lll', ['seq', ['calldatacopy', 0, 4, 32], ['return', 0, 32], 'stop' ], 0]]])" # noqa: E501 - -CONTRACT_FIXED_ABI = [ - { - "type": "function", - "constant": False, - "inputs": [{"type": "fixed8x1"}], - "name": "reflect", - "outputs": [{"type": "fixed8x1"}], - }, - { - "type": "function", - "constant": False, - "inputs": [{"type": "ufixed256x80"}], - "name": "reflect", - "outputs": [{"type": "ufixed256x80"}], - }, - { - "type": "function", - "constant": False, - "inputs": [{"type": "ufixed256x1"}], - "name": "reflect", - "outputs": [{"type": "ufixed256x1"}], - }, - { - "type": "function", - "constant": False, - "inputs": [{"type": "ufixed8x1"}], - "name": "reflect_short_u", - "outputs": [{"type": "ufixed8x1"}], - }, -] - - -@pytest.fixture -def FixedReflectionContract(web3): - return web3.eth.contract(abi=CONTRACT_FIXED_ABI, bytecode=CONTRACT_REFLECTION_CODE) - - -CONTRACT_FALLBACK_FUNCTION_SOURCE = """ -contract A { - uint data; - function A() public payable { data = 0; } - function getData() returns (uint r) { return data; } - function() { data = 1; } -} -""" - -CONTRACT_FALLBACK_FUNCTION_CODE = "60606040526000808190555060ae806100196000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bc5de30146053575b3415604957600080fd5b6001600081905550005b3415605d57600080fd5b60636079565b6040518082815260200191505060405180910390f35b600080549050905600a165627a7a72305820045439389e4742569ec078687e6a0c81997709778a0097adbe07ccfd9f7b1a330029" # noqa: E501 - -CONTRACT_FALLBACK_FUNCTION_RUNTIME = "606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bc5de30146053575b3415604957600080fd5b6001600081905550005b3415605d57600080fd5b60636079565b6040518082815260200191505060405180910390f35b600080549050905600a165627a7a72305820045439389e4742569ec078687e6a0c81997709778a0097adbe07ccfd9f7b1a330029" # noqa: E501 - -CONTRACT_FALLBACK_FUNCTION_ABI = json.loads('[{"constant": false, "inputs": [], "name": "getData", "outputs": [{"name": "r", "type": "uint256"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"inputs": [], "payable": true, "stateMutability": "payable", "type": "constructor"}, {"payable": false, "stateMutability": "nonpayable", "type": "fallback"}]') # noqa: E501 - - -@pytest.fixture() -def FALLBACK_FUNCTION_CODE(): - return CONTRACT_FALLBACK_FUNCTION_CODE - - -@pytest.fixture() -def FALLBACK_FUNCTION_RUNTIME(): - return CONTRACT_FALLBACK_FUNCTION_RUNTIME - - -@pytest.fixture() -def FALLBACK_FUNCTION_ABI(): - return CONTRACT_FALLBACK_FUNCTION_ABI - - -@pytest.fixture() -def FALLBACK_FUNCTION_CONTRACT(FALLBACK_FUNCTION_CODE, - FALLBACK_FUNCTION_RUNTIME, - FALLBACK_FUNCTION_ABI): - return { - 'bytecode': FALLBACK_FUNCTION_CODE, - 'bytecode_runtime': FALLBACK_FUNCTION_RUNTIME, - 'abi': FALLBACK_FUNCTION_ABI, - } - - -@pytest.fixture() -def FallballFunctionContract(web3, FALLBACK_FUNCTION_CONTRACT): - return web3.eth.contract(**FALLBACK_FUNCTION_CONTRACT) - - -CONTRACT_CALLER_TESTER_SOURCE = """ -contract CallerTester { - int public count; - - function add(int256 a, int256 b) public payable returns (int256) { - return a + b; - } - - function increment() public returns (int256) { - return count += 1; - } - - function counter() public payable returns (int256) { - return count; - } - - function returnMeta() public payable returns (address, bytes memory, uint256, uint, uint) { - return (msg.sender, msg.data, gasleft(), msg.value, block.number); - } -} -""" - - -CONTRACT_CALLER_TESTER_CODE = "608060405234801561001057600080fd5b50610241806100206000396000f3fe608060405260043610610066577c0100000000000000000000000000000000000000000000000000000000600035046306661abd811461006b57806361bc221a14610092578063a5f3c23b1461009a578063c7fa7d66146100bd578063d09de08a14610185575b600080fd5b34801561007757600080fd5b5061008061019a565b60408051918252519081900360200190f35b6100806101a0565b610080600480360360408110156100b057600080fd5b50803590602001356101a6565b6100c56101aa565b604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001858152602001848152602001838152602001828103825286818151815260200191508051906020019080838360005b8381101561014657818101518382015260200161012e565b50505050905090810190601f1680156101735780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561019157600080fd5b50610080610207565b60005481565b60005490565b0190565b600060606000806000336000365a344385955084848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250989e929d50949b5092995090975095505050505050565b60008054600101908190559056fea165627a7a72305820ffe1620e420efa326b9c5e4ef9f93cac71cf986196246c7966d71a39259899b10029" # noqa: E501 - - -CONTRACT_CALLER_TESTER_RUNTIME = "608060405260043610610066577c0100000000000000000000000000000000000000000000000000000000600035046306661abd811461006b57806361bc221a14610092578063a5f3c23b1461009a578063c7fa7d66146100bd578063d09de08a14610185575b600080fd5b34801561007757600080fd5b5061008061019a565b60408051918252519081900360200190f35b6100806101a0565b610080600480360360408110156100b057600080fd5b50803590602001356101a6565b6100c56101aa565b604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001858152602001848152602001838152602001828103825286818151815260200191508051906020019080838360005b8381101561014657818101518382015260200161012e565b50505050905090810190601f1680156101735780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561019157600080fd5b50610080610207565b60005481565b60005490565b0190565b600060606000806000336000365a344385955084848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250989e929d50949b5092995090975095505050505050565b60008054600101908190559056fea165627a7a72305820ffe1620e420efa326b9c5e4ef9f93cac71cf986196246c7966d71a39259899b10029" # noqa: E501 - - -CONTRACT_CALLER_TESTER_ABI = json.loads('[ { "constant": true, "inputs": [], "name": "count", "outputs": [ { "name": "", "type": "int256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "counter", "outputs": [ { "name": "", "type": "int256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "a", "type": "int256" }, { "name": "b", "type": "int256" } ], "name": "add", "outputs": [ { "name": "", "type": "int256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [], "name": "returnMeta", "outputs": [ { "name": "", "type": "address" }, { "name": "", "type": "bytes" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [], "name": "increment", "outputs": [ { "name": "", "type": "int256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]') # noqa: E501 - - -@pytest.fixture() -def CALLER_TESTER_CODE(): - return CONTRACT_CALLER_TESTER_CODE - - -@pytest.fixture() -def CALLER_TESTER_RUNTIME(): - return CONTRACT_CALLER_TESTER_RUNTIME - - -@pytest.fixture() -def CALLER_TESTER_ABI(): - return CONTRACT_CALLER_TESTER_ABI - - -@pytest.fixture() -def CALLER_TESTER_CONTRACT(CALLER_TESTER_CODE, - CALLER_TESTER_RUNTIME, - CALLER_TESTER_ABI): - return { - 'bytecode': CALLER_TESTER_CODE, - 'bytecode_runtime': CALLER_TESTER_RUNTIME, - 'abi': CALLER_TESTER_ABI, - } - - -@pytest.fixture() -def CallerTesterContract(web3, CALLER_TESTER_CONTRACT): - return web3.eth.contract(**CALLER_TESTER_CONTRACT) - - -class LogFunctions: - LogAnonymous = 0 - LogNoArguments = 1 - LogSingleArg = 2 - LogDoubleArg = 3 - LogTripleArg = 4 - LogQuadrupleArg = 5 - LogSingleAnonymous = 6 - LogSingleWithIndex = 7 - LogDoubleAnonymous = 8 - LogDoubleWithIndex = 9 - LogTripleWithIndex = 10 - LogQuadrupleWithIndex = 11 - LogBytes = 12 - - -@pytest.fixture() -def emitter_event_ids(): - return LogFunctions - - -def _encode_to_topic(event_signature): - return event_signature_to_log_topic(event_signature) - - -class LogTopics: - LogAnonymous = _encode_to_topic("LogAnonymous()") - LogNoArguments = _encode_to_topic("LogNoArguments()") - LogSingleArg = _encode_to_topic("LogSingleArg(uint256)") - LogSingleAnonymous = _encode_to_topic("LogSingleAnonymous(uint256)") - LogSingleWithIndex = _encode_to_topic("LogSingleWithIndex(uint256)") - LogDoubleArg = _encode_to_topic("LogDoubleArg(uint256,uint256)") - LogDoubleAnonymous = _encode_to_topic("LogDoubleAnonymous(uint256,uint256)") - LogDoubleWithIndex = _encode_to_topic("LogDoubleWithIndex(uint256,uint256)") - LogTripleArg = _encode_to_topic("LogTripleArg(uint256,uint256,uint256)") - LogTripleWithIndex = _encode_to_topic("LogTripleWithIndex(uint256,uint256,uint256)") - LogQuadrupleArg = _encode_to_topic("LogQuadrupleArg(uint256,uint256,uint256,uint256)") - LogQuadrupleWithIndex = _encode_to_topic( - "LogQuadrupleWithIndex(uint256,uint256,uint256,uint256)", - ) - LogBytes = _encode_to_topic("LogBytes(bytes)") - LogString = _encode_to_topic("LogString(string)") - LogDynamicArgs = _encode_to_topic("LogDynamicArgs(string,string)") - LogListArgs = _encode_to_topic("LogListArgs(bytes2[],bytes2[])") - LogAddressIndexed = _encode_to_topic("LogAddressIndexed(address,address)") - LogAddressNotIndexed = _encode_to_topic("LogAddressNotIndexed(address,address)") - - -@pytest.fixture() -def emitter_log_topics(): - return LogTopics - - -@pytest.fixture() -def some_address(address_conversion_func): - return address_conversion_func('0x5B2063246F2191f18F2675ceDB8b28102e957458') - - -def invoke_contract(api_call_desig='call', - contract=None, - contract_function=None, - func_args=[], - func_kwargs={}, - tx_params={}): - allowable_call_desig = ['call', 'transact', 'estimateGas', 'buildTransaction'] - if api_call_desig not in allowable_call_desig: - raise ValueError("allowable_invoke_method must be one of: %s" % allowable_call_desig) - - function = contract.functions[contract_function] - result = getattr(function(*func_args, **func_kwargs), api_call_desig)(tx_params) - - return result - - -@pytest.fixture -def transact(request): - return functools.partial(invoke_contract, api_call_desig='transact') - - -@pytest.fixture -def call(request): - return functools.partial(invoke_contract, api_call_desig='call') - - -@pytest.fixture -def estimateGas(request): - return functools.partial(invoke_contract, api_call_desig='estimateGas') - - -@pytest.fixture -def buildTransaction(request): - return functools.partial(invoke_contract, api_call_desig='buildTransaction') +import functools +import json +import pytest + +from vns_utils import ( + event_signature_to_log_topic, +) + +CONTRACT_NESTED_TUPLE_SOURCE = """ +pragma solidity >=0.4.19 <0.6.0; +pragma experimental ABIEncoderV2; + +contract Tuple { + struct U { int x; int y; } + struct T { U[] u; } + struct S { T[] t; } + + function method(S memory s) public pure returns (S memory) { + return s; + } +} +""" +CONTRACT_NESTED_TUPLE_CODE = '0x608060405234801561001057600080fd5b50610575806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632655aef114610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906102a2565b610083565b60405161007a919061043e565b60405180910390f35b61008b610093565b819050919050565b602060405190810160405280606081525090565b600082601f83011215156100ba57600080fd5b81356100cd6100c88261048d565b610460565b9150818183526020840193506020810190508360005b8381101561011357813586016100f98882610206565b8452602084019350602083019250506001810190506100e3565b5050505092915050565b600082601f830112151561013057600080fd5b813561014361013e826104b5565b610460565b9150818183526020840193506020810190508385604084028201111561016857600080fd5b60005b83811015610198578161017e8882610256565b84526020840193506040830192505060018101905061016b565b5050505092915050565b60006101ae8235610531565b905092915050565b6000602082840312156101c857600080fd5b6101d26020610460565b9050600082013567ffffffffffffffff8111156101ee57600080fd5b6101fa848285016100a7565b60008301525092915050565b60006020828403121561021857600080fd5b6102226020610460565b9050600082013567ffffffffffffffff81111561023e57600080fd5b61024a8482850161011d565b60008301525092915050565b60006040828403121561026857600080fd5b6102726040610460565b90506000610282848285016101a2565b6000830152506020610296848285016101a2565b60208301525092915050565b6000602082840312156102b457600080fd5b600082013567ffffffffffffffff8111156102ce57600080fd5b6102da848285016101b6565b91505092915050565b60006102ee826104f7565b80845260208401935083602082028501610307856104dd565b60005b848110156103405783830388526103228383516103e5565b925061032d8261050d565b915060208801975060018101905061030a565b508196508694505050505092915050565b600061035c82610502565b80845260208401935061036e836104ea565b60005b828110156103a05761038486835161040f565b61038d8261051a565b9150604086019550600181019050610371565b50849250505092915050565b6103b581610527565b82525050565b600060208301600083015184820360008601526103d882826102e3565b9150508091505092915050565b600060208301600083015184820360008601526104028282610351565b9150508091505092915050565b60408201600082015161042560008501826103ac565b50602082015161043860208501826103ac565b50505050565b6000602082019050818103600083015261045881846103bb565b905092915050565b6000604051905081810181811067ffffffffffffffff8211171561048357600080fd5b8060405250919050565b600067ffffffffffffffff8211156104a457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff8211156104cc57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60008190509190505600a265627a7a7230582011dc176b6cffda1d2e58827bca1a16178776c2d8efcd6adf48bc0223f14f891a6c6578706572696d656e74616cf50037' # noqa: E501 +CONTRACT_NESTED_TUPLE_RUNTIME = '0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632655aef114610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906102a2565b610083565b60405161007a919061043e565b60405180910390f35b61008b610093565b819050919050565b602060405190810160405280606081525090565b600082601f83011215156100ba57600080fd5b81356100cd6100c88261048d565b610460565b9150818183526020840193506020810190508360005b8381101561011357813586016100f98882610206565b8452602084019350602083019250506001810190506100e3565b5050505092915050565b600082601f830112151561013057600080fd5b813561014361013e826104b5565b610460565b9150818183526020840193506020810190508385604084028201111561016857600080fd5b60005b83811015610198578161017e8882610256565b84526020840193506040830192505060018101905061016b565b5050505092915050565b60006101ae8235610531565b905092915050565b6000602082840312156101c857600080fd5b6101d26020610460565b9050600082013567ffffffffffffffff8111156101ee57600080fd5b6101fa848285016100a7565b60008301525092915050565b60006020828403121561021857600080fd5b6102226020610460565b9050600082013567ffffffffffffffff81111561023e57600080fd5b61024a8482850161011d565b60008301525092915050565b60006040828403121561026857600080fd5b6102726040610460565b90506000610282848285016101a2565b6000830152506020610296848285016101a2565b60208301525092915050565b6000602082840312156102b457600080fd5b600082013567ffffffffffffffff8111156102ce57600080fd5b6102da848285016101b6565b91505092915050565b60006102ee826104f7565b80845260208401935083602082028501610307856104dd565b60005b848110156103405783830388526103228383516103e5565b925061032d8261050d565b915060208801975060018101905061030a565b508196508694505050505092915050565b600061035c82610502565b80845260208401935061036e836104ea565b60005b828110156103a05761038486835161040f565b61038d8261051a565b9150604086019550600181019050610371565b50849250505092915050565b6103b581610527565b82525050565b600060208301600083015184820360008601526103d882826102e3565b9150508091505092915050565b600060208301600083015184820360008601526104028282610351565b9150508091505092915050565b60408201600082015161042560008501826103ac565b50602082015161043860208501826103ac565b50505050565b6000602082019050818103600083015261045881846103bb565b905092915050565b6000604051905081810181811067ffffffffffffffff8211171561048357600080fd5b8060405250919050565b600067ffffffffffffffff8211156104a457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff8211156104cc57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60008190509190505600a265627a7a7230582011dc176b6cffda1d2e58827bca1a16178776c2d8efcd6adf48bc0223f14f891a6c6578706572696d656e74616cf50037' # noqa: E501 +CONTRACT_NESTED_TUPLE_ABI = json.loads('[{"constant":true,"inputs":[{"components":[{"components":[{"components":[{"name":"x","type":"int256"},{"name":"y","type":"int256"}],"name":"u","type":"tuple[]"}],"name":"t","type":"tuple[]"}],"name":"s","type":"tuple"}],"name":"method","outputs":[{"components":[{"components":[{"components":[{"name":"x","type":"int256"},{"name":"y","type":"int256"}],"name":"u","type":"tuple[]"}],"name":"t","type":"tuple[]"}],"name":"","type":"tuple"}],"payable":false,"stateMutability":"pure","type":"function"}]') # noqa: E501 + + +@pytest.fixture() +def NESTED_TUPLE_CODE(): + return CONTRACT_NESTED_TUPLE_CODE + + +@pytest.fixture() +def NESTED_TUPLE_RUNTIME(): + return CONTRACT_NESTED_TUPLE_RUNTIME + + +@pytest.fixture() +def NESTED_TUPLE_ABI(): + return CONTRACT_NESTED_TUPLE_ABI + + +@pytest.fixture() +def NESTED_TUPLE_CONTRACT(NESTED_TUPLE_CODE, NESTED_TUPLE_RUNTIME, NESTED_TUPLE_ABI): + return { + 'bytecode': NESTED_TUPLE_CODE, + 'bytecode_runtime': NESTED_TUPLE_RUNTIME, + 'abi': NESTED_TUPLE_ABI, + } + + +@pytest.fixture() +def NestedTupleContract(web3, NESTED_TUPLE_CONTRACT): + return web3.vns.contract(**NESTED_TUPLE_CONTRACT) + + +CONTRACT_TUPLE_SOURCE = """ +pragma solidity >=0.4.19 <0.6.0; +pragma experimental ABIEncoderV2; + +contract Tuple { + struct T { int x; bool[2] y; address[] z; } + struct S { uint a; uint[] b; T[] c; } + + function method(S memory s) public pure returns (S memory) { + return s; + } +} +""" +CONTRACT_TUPLE_CODE = '0x608060405234801561001057600080fd5b506108ca806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638e1ae3c714610046575b600080fd5b34801561005257600080fd5b5061006d60048036036100689190810190610403565b610083565b60405161007a9190610696565b60405180910390f35b61008b610093565b819050919050565b6060604051908101604052806000815260200160608152602001606081525090565b60006100c18235610850565b905092915050565b600082601f83011215156100dc57600080fd5b81356100ef6100ea826106e5565b6106b8565b9150818183526020840193506020810190508385602084028201111561011457600080fd5b60005b83811015610144578161012a88826100b5565b845260208401935060208301925050600181019050610117565b5050505092915050565b600082601f830112151561016157600080fd5b600261017461016f8261070d565b6106b8565b9150818385602084028201111561018a57600080fd5b60005b838110156101ba57816101a088826102bf565b84526020840193506020830192505060018101905061018d565b5050505092915050565b600082601f83011215156101d757600080fd5b81356101ea6101e58261072f565b6106b8565b9150818183526020840193506020810190508360005b8381101561023057813586016102168882610377565b845260208401935060208301925050600181019050610200565b5050505092915050565b600082601f830112151561024d57600080fd5b813561026061025b82610757565b6106b8565b9150818183526020840193506020810190508385602084028201111561028557600080fd5b60005b838110156102b5578161029b88826103ef565b845260208401935060208301925050600181019050610288565b5050505092915050565b60006102cb8235610870565b905092915050565b60006102df823561087c565b905092915050565b6000606082840312156102f957600080fd5b61030360606106b8565b90506000610313848285016103ef565b600083015250602082013567ffffffffffffffff81111561033357600080fd5b61033f8482850161023a565b602083015250604082013567ffffffffffffffff81111561035f57600080fd5b61036b848285016101c4565b60408301525092915050565b60006080828403121561038957600080fd5b61039360606106b8565b905060006103a3848285016102d3565b60008301525060206103b78482850161014e565b602083015250606082013567ffffffffffffffff8111156103d757600080fd5b6103e3848285016100c9565b60408301525092915050565b60006103fb8235610886565b905092915050565b60006020828403121561041557600080fd5b600082013567ffffffffffffffff81111561042f57600080fd5b61043b848285016102e7565b91505092915050565b61044d81610810565b82525050565b600061045e826107b0565b8084526020840193506104708361077f565b60005b828110156104a257610486868351610444565b61048f826107dc565b9150602086019550600181019050610473565b50849250505092915050565b6104b7816107bb565b6104c08261078c565b60005b828110156104f2576104d68583516105c2565b6104df826107e9565b91506020850194506001810190506104c3565b5050505050565b6000610504826107c6565b8084526020840193508360208202850161051d85610796565b60005b84811015610556578383038852610538838351610637565b9250610543826107f6565b9150602088019750600181019050610520565b508196508694505050505092915050565b6000610572826107d1565b808452602084019350610584836107a3565b60005b828110156105b65761059a868351610687565b6105a382610803565b9150602086019550600181019050610587565b50849250505092915050565b6105cb81610830565b82525050565b6105da8161083c565b82525050565b60006060830160008301516105f86000860182610687565b50602083015184820360208601526106108282610567565b9150506040830151848203604086015261062a82826104f9565b9150508091505092915050565b600060808301600083015161064f60008601826105d1565b50602083015161066260208601826104ae565b506040830151848203606086015261067a8282610453565b9150508091505092915050565b61069081610846565b82525050565b600060208201905081810360008301526106b081846105e0565b905092915050565b6000604051905081810181811067ffffffffffffffff821117156106db57600080fd5b8060405250919050565b600067ffffffffffffffff8211156106fc57600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561072457600080fd5b602082029050919050565b600067ffffffffffffffff82111561074657600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561076e57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60008190509190505600a265627a7a72305820fb4abae14c4bcd2cd8a12f35862cb09a2e0f9004eb5fdbfdb76658a753570c8c6c6578706572696d656e74616cf50037' # noqa: E501 +CONTRACT_TUPLE_RUNTIME = '0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638e1ae3c714610046575b600080fd5b34801561005257600080fd5b5061006d60048036036100689190810190610403565b610083565b60405161007a9190610696565b60405180910390f35b61008b610093565b819050919050565b6060604051908101604052806000815260200160608152602001606081525090565b60006100c18235610850565b905092915050565b600082601f83011215156100dc57600080fd5b81356100ef6100ea826106e5565b6106b8565b9150818183526020840193506020810190508385602084028201111561011457600080fd5b60005b83811015610144578161012a88826100b5565b845260208401935060208301925050600181019050610117565b5050505092915050565b600082601f830112151561016157600080fd5b600261017461016f8261070d565b6106b8565b9150818385602084028201111561018a57600080fd5b60005b838110156101ba57816101a088826102bf565b84526020840193506020830192505060018101905061018d565b5050505092915050565b600082601f83011215156101d757600080fd5b81356101ea6101e58261072f565b6106b8565b9150818183526020840193506020810190508360005b8381101561023057813586016102168882610377565b845260208401935060208301925050600181019050610200565b5050505092915050565b600082601f830112151561024d57600080fd5b813561026061025b82610757565b6106b8565b9150818183526020840193506020810190508385602084028201111561028557600080fd5b60005b838110156102b5578161029b88826103ef565b845260208401935060208301925050600181019050610288565b5050505092915050565b60006102cb8235610870565b905092915050565b60006102df823561087c565b905092915050565b6000606082840312156102f957600080fd5b61030360606106b8565b90506000610313848285016103ef565b600083015250602082013567ffffffffffffffff81111561033357600080fd5b61033f8482850161023a565b602083015250604082013567ffffffffffffffff81111561035f57600080fd5b61036b848285016101c4565b60408301525092915050565b60006080828403121561038957600080fd5b61039360606106b8565b905060006103a3848285016102d3565b60008301525060206103b78482850161014e565b602083015250606082013567ffffffffffffffff8111156103d757600080fd5b6103e3848285016100c9565b60408301525092915050565b60006103fb8235610886565b905092915050565b60006020828403121561041557600080fd5b600082013567ffffffffffffffff81111561042f57600080fd5b61043b848285016102e7565b91505092915050565b61044d81610810565b82525050565b600061045e826107b0565b8084526020840193506104708361077f565b60005b828110156104a257610486868351610444565b61048f826107dc565b9150602086019550600181019050610473565b50849250505092915050565b6104b7816107bb565b6104c08261078c565b60005b828110156104f2576104d68583516105c2565b6104df826107e9565b91506020850194506001810190506104c3565b5050505050565b6000610504826107c6565b8084526020840193508360208202850161051d85610796565b60005b84811015610556578383038852610538838351610637565b9250610543826107f6565b9150602088019750600181019050610520565b508196508694505050505092915050565b6000610572826107d1565b808452602084019350610584836107a3565b60005b828110156105b65761059a868351610687565b6105a382610803565b9150602086019550600181019050610587565b50849250505092915050565b6105cb81610830565b82525050565b6105da8161083c565b82525050565b60006060830160008301516105f86000860182610687565b50602083015184820360208601526106108282610567565b9150506040830151848203604086015261062a82826104f9565b9150508091505092915050565b600060808301600083015161064f60008601826105d1565b50602083015161066260208601826104ae565b506040830151848203606086015261067a8282610453565b9150508091505092915050565b61069081610846565b82525050565b600060208201905081810360008301526106b081846105e0565b905092915050565b6000604051905081810181811067ffffffffffffffff821117156106db57600080fd5b8060405250919050565b600067ffffffffffffffff8211156106fc57600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561072457600080fd5b602082029050919050565b600067ffffffffffffffff82111561074657600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561076e57600080fd5b602082029050602081019050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60008190509190505600a265627a7a72305820fb4abae14c4bcd2cd8a12f35862cb09a2e0f9004eb5fdbfdb76658a753570c8c6c6578706572696d656e74616cf50037' # noqa: E501 +CONTRACT_TUPLE_ABI = json.loads('[{"constant":true,"inputs":[{"components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256[]"},{"components":[{"name":"x","type":"int256"},{"name":"y","type":"bool[2]"},{"name":"z","type":"address[]"}],"name":"c","type":"tuple[]"}],"name":"s","type":"tuple"}],"name":"method","outputs":[{"components":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256[]"},{"components":[{"name":"x","type":"int256"},{"name":"y","type":"bool[2]"},{"name":"z","type":"address[]"}],"name":"c","type":"tuple[]"}],"name":"","type":"tuple"}],"payable":false,"stateMutability":"pure","type":"function"}]') # noqa: E501 + + +@pytest.fixture() +def TUPLE_CODE(): + return CONTRACT_TUPLE_CODE + + +@pytest.fixture() +def TUPLE_RUNTIME(): + return CONTRACT_TUPLE_RUNTIME + + +@pytest.fixture() +def TUPLE_ABI(): + return CONTRACT_TUPLE_ABI + + +@pytest.fixture() +def TUPLE_CONTRACT(TUPLE_CODE, TUPLE_RUNTIME, TUPLE_ABI): + return { + 'bytecode': TUPLE_CODE, + 'bytecode_runtime': TUPLE_RUNTIME, + 'abi': TUPLE_ABI, + } + + +@pytest.fixture() +def TupleContract(web3, TUPLE_CONTRACT): + return web3.vns.contract(**TUPLE_CONTRACT) + + +CONTRACT_CODE = "0x606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 + + +CONTRACT_RUNTIME = "0x60606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 + + +CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 + + +@pytest.fixture(scope="session") +def MATH_CODE(): + return CONTRACT_CODE + + +@pytest.fixture(scope="session") +def MATH_RUNTIME(): + return CONTRACT_RUNTIME + + +@pytest.fixture(scope="session") +def MATH_ABI(): + return CONTRACT_ABI + + +@pytest.fixture() +def MathContract(web3, MATH_ABI, MATH_CODE, MATH_RUNTIME): + return web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + + +CONTRACT_SIMPLE_CONSTRUCTOR_CODE = '0x60606040526003600055602c8060156000396000f3606060405260e060020a600035046373d4a13a8114601a575b005b602260005481565b6060908152602090f3' # noqa: E501 +CONTRACT_SIMPLE_CONSTRUCTOR_RUNTIME = '0x606060405260e060020a600035046373d4a13a8114601a575b005b602260005481565b6060908152602090f3' # noqa: E501 +CONTRACT_SIMPLE_CONSTRUCTOR_ABI = json.loads('[{"constant":true,"inputs":[],"name":"data","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[],"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture(scope="session") +def SIMPLE_CONSTRUCTOR_CODE(): + return CONTRACT_SIMPLE_CONSTRUCTOR_CODE + + +@pytest.fixture(scope="session") +def SIMPLE_CONSTRUCTOR_RUNTIME(): + return CONTRACT_SIMPLE_CONSTRUCTOR_RUNTIME + + +@pytest.fixture(scope="session") +def SIMPLE_CONSTRUCTOR_ABI(): + return CONTRACT_SIMPLE_CONSTRUCTOR_ABI + + +@pytest.fixture() +def SimpleConstructorContract(web3, + SIMPLE_CONSTRUCTOR_CODE, + SIMPLE_CONSTRUCTOR_RUNTIME, + SIMPLE_CONSTRUCTOR_ABI): + return web3.vns.contract( + abi=SIMPLE_CONSTRUCTOR_ABI, + bytecode=SIMPLE_CONSTRUCTOR_CODE, + bytecode_runtime=SIMPLE_CONSTRUCTOR_RUNTIME, + ) + + +CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_CODE = "0x60606040818152806066833960a09052516080516000918255600155603e908190602890396000f3606060405260e060020a600035046388ec134681146024578063d4c46c7614602c575b005b603460005481565b603460015481565b6060908152602090f3" # noqa: E501 +CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME = "0x606060405260e060020a600035046388ec134681146024578063d4c46c7614602c575b005b603460005481565b603460015481565b6060908152602090f3" # noqa: E501 +CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_ABI = json.loads('[{"constant":true,"inputs":[],"name":"data_a","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"data_b","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bytes32"}],"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ARGUMENTS_CODE(): + return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_CODE + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME(): + return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ARGUMENTS_ABI(): + return CONTRACT_WITH_CONSTRUCTOR_ARGUMENTS_ABI + + +@pytest.fixture() +def WithConstructorArgumentsContract(web3, + WITH_CONSTRUCTOR_ARGUMENTS_CODE, + WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, + WITH_CONSTRUCTOR_ARGUMENTS_ABI): + return web3.vns.contract( + abi=WITH_CONSTRUCTOR_ARGUMENTS_ABI, + bytecode=WITH_CONSTRUCTOR_ARGUMENTS_CODE, + bytecode_runtime=WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, + ) + + +CONTRACT_WITH_CONSTRUCTOR_ADDRESS_CODE = "0x6060604052604051602080607683395060806040525160008054600160a060020a031916821790555060428060346000396000f3606060405260e060020a600035046334664e3a8114601a575b005b603860005473ffffffffffffffffffffffffffffffffffffffff1681565b6060908152602090f3" # noqa: E501 +CONTRACT_WITH_CONSTRUCTOR_ADDRESS_RUNTIME = "0x606060405260e060020a600035046334664e3a8114601a575b005b603860005473ffffffffffffffffffffffffffffffffffffffff1681565b6060908152602090f3" # noqa: E501 +CONTRACT_WITH_CONSTRUCTOR_ADDRESS_ABI = json.loads('[{"constant":true,"inputs":[],"name":"testAddr","outputs":[{"name":"","type":"address"}],"type":"function"},{"inputs":[{"name":"_testAddr","type":"address"}],"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ADDRESS_CODE(): + return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_CODE + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ADDRESS_RUNTIME(): + return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_RUNTIME + + +@pytest.fixture() +def WITH_CONSTRUCTOR_ADDRESS_ABI(): + return CONTRACT_WITH_CONSTRUCTOR_ADDRESS_ABI + + +@pytest.fixture() +def WithConstructorAddressArgumentsContract(web3, + WITH_CONSTRUCTOR_ADDRESS_CODE, + WITH_CONSTRUCTOR_ADDRESS_RUNTIME, + WITH_CONSTRUCTOR_ADDRESS_ABI): + return web3.vns.contract( + abi=WITH_CONSTRUCTOR_ADDRESS_ABI, + bytecode=WITH_CONSTRUCTOR_ADDRESS_CODE, + bytecode_runtime=WITH_CONSTRUCTOR_ADDRESS_RUNTIME, + ) + + +@pytest.fixture() +def AddressReflectorContract(web3): + return web3.vns.contract( + abi=json.loads('[{"constant":true,"inputs":[{"name":"arg","type":"address"}],"name":"reflect","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"arg","type":"address[]"}],"name":"reflect","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"pure","type":"function"}]'), # noqa: 501 + bytecode="6060604052341561000f57600080fd5b6101ca8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b816c1614610048578063c04d11fc146100c157600080fd5b341561005357600080fd5b61007f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610170565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100cc57600080fd5b61011960048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061017a565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561015c578082015181840152602081019050610141565b505050509050019250505060405180910390f35b6000819050919050565b61018261018a565b819050919050565b6020604051908101604052806000815250905600a165627a7a723058206b15d98a803b91327d94f943e9712291539701b2f7370e10f5873633941484930029", # noqa: 501 + bytecode_runtime="60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b816c1614610048578063c04d11fc146100c157600080fd5b341561005357600080fd5b61007f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610170565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100cc57600080fd5b61011960048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061017a565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561015c578082015181840152602081019050610141565b505050509050019250505060405180910390f35b6000819050919050565b61018261018a565b819050919050565b6020604051908101604052806000815250905600a165627a7a723058206b15d98a803b91327d94f943e9712291539701b2f7370e10f5873633941484930029", # noqa: 501 + ) + + +CONTRACT_STRING_CODE = "0x6060604052604051610496380380610496833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b5050506103d8806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a600035046320965255811461003c57806330de3cee1461009f5780633fa4f245146100c457806393a0935214610121575b005b6101c7600060608181528154602060026001831615610100026000190190921691909104601f810182900490910260a0908101604052608082815292939190828280156102605780601f1061023557610100808354040283529160200191610260565b6101c7600060609081526101a06040526101006080818152906102d860a03990505b90565b6101c760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102975780601f1061026c57610100808354040283529160200191610297565b60206004803580820135601f81018490049093026080908101604052606084815261003a946024939192918401918190838280828437509496505050505050508060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029f57805160ff19168380011785555b506102cf9291505b808211156102d4578381556001016101b4565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161024357829003601f168201915b505050505090506100c1565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b505050505081565b828001600101855582156101ac579182015b828111156101ac5782518260005055916020019190600101906102b1565b505050565b509056000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" # noqa: E501 + +CONTRACT_STRING_RUNTIME = "0x606060405260e060020a600035046320965255811461003c57806330de3cee1461009f5780633fa4f245146100c457806393a0935214610121575b005b6101c7600060608181528154602060026001831615610100026000190190921691909104601f810182900490910260a0908101604052608082815292939190828280156102605780601f1061023557610100808354040283529160200191610260565b6101c7600060609081526101a06040526101006080818152906102d860a03990505b90565b6101c760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102975780601f1061026c57610100808354040283529160200191610297565b60206004803580820135601f81018490049093026080908101604052606084815261003a946024939192918401918190838280828437509496505050505050508060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029f57805160ff19168380011785555b506102cf9291505b808211156102d4578381556001016101b4565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161024357829003601f168201915b505050505090506100c1565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b505050505081565b828001600101855582156101ac579182015b828111156101ac5782518260005055916020019190600101906102b1565b505050565b509056000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" # noqa: E501 + +CONTRACT_STRING_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"string"}],"name":"setValue","outputs":[],"type":"function"},{"inputs":[{"name":"_value","type":"string"}],"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture() +def STRING_CODE(): + return CONTRACT_STRING_CODE + + +@pytest.fixture() +def STRING_RUNTIME(): + return CONTRACT_STRING_RUNTIME + + +@pytest.fixture() +def STRING_ABI(): + return CONTRACT_STRING_ABI + + +@pytest.fixture() +def STRING_CONTRACT(STRING_CODE, STRING_RUNTIME, STRING_ABI): + return { + 'bytecode': STRING_CODE, + 'bytecode_runtime': STRING_RUNTIME, + 'abi': STRING_ABI, + } + + +@pytest.fixture() +def StringContract(web3, STRING_CONTRACT): + return web3.vns.contract(**STRING_CONTRACT) + + +CONTRACT_BYTES_CODE = "60606040526040805190810160405280600281526020017f01230000000000000000000000000000000000000000000000000000000000008152506000908051906020019061004f929190610096565b50341561005b57600080fd5b604051610723380380610723833981016040528080518201919050505b806001908051906020019061008e929190610116565b505b506101bb565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d757805160ff1916838001178555610105565b82800160010185558215610105579182015b828111156101045782518255916020019190600101906100e9565b5b5090506101129190610196565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015757805160ff1916838001178555610185565b82800160010185558215610185579182015b82811115610184578251825591602001919060010190610169565b5b5090506101929190610196565b5090565b6101b891905b808211156101b457600081600090555060010161019c565b5090565b90565b610559806101ca6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100ee5780633fa4f2451461017d578063439970aa1461020c575b600080fd5b341561006a57600080fd5b610072610269565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b35780820151818401525b602081019050610097565b50505050905090810190601f1680156100e05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f957600080fd5b610101610312565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101425780820151818401525b602081019050610126565b50505050905090810190601f16801561016f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561018857600080fd5b6101906103bb565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101d15780820151818401525b6020810190506101b5565b50505050905090810190601f1680156101fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561021757600080fd5b610267600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610459565b005b610271610474565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103075780601f106102dc57610100808354040283529160200191610307565b820191906000526020600020905b8154815290600101906020018083116102ea57829003601f168201915b505050505090505b90565b61031a610474565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103b05780601f10610385576101008083540402835291602001916103b0565b820191906000526020600020905b81548152906001019060200180831161039357829003601f168201915b505050505090505b90565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104515780601f1061042657610100808354040283529160200191610451565b820191906000526020600020905b81548152906001019060200180831161043457829003601f168201915b505050505081565b806001908051906020019061046f929190610488565b505b50565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104c957805160ff19168380011785556104f7565b828001600101855582156104f7579182015b828111156104f65782518255916020019190600101906104db565b5b5090506105049190610508565b5090565b61052a91905b8082111561052657600081600090555060010161050e565b5090565b905600a165627a7a723058203ff916ee91add6247b20793745d1c6a8d8dcaa49d8c84fbbabb5c966fd9b6fc90029" # noqa: E501 + +CONTRACT_BYTES_RUNTIME = "60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100ee5780633fa4f2451461017d578063439970aa1461020c575b600080fd5b341561006a57600080fd5b610072610269565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b35780820151818401525b602081019050610097565b50505050905090810190601f1680156100e05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f957600080fd5b610101610312565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101425780820151818401525b602081019050610126565b50505050905090810190601f16801561016f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561018857600080fd5b6101906103bb565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101d15780820151818401525b6020810190506101b5565b50505050905090810190601f1680156101fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561021757600080fd5b610267600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610459565b005b610271610474565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103075780601f106102dc57610100808354040283529160200191610307565b820191906000526020600020905b8154815290600101906020018083116102ea57829003601f168201915b505050505090505b90565b61031a610474565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103b05780601f10610385576101008083540402835291602001916103b0565b820191906000526020600020905b81548152906001019060200180831161039357829003601f168201915b505050505090505b90565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104515780601f1061042657610100808354040283529160200191610451565b820191906000526020600020905b81548152906001019060200180831161043457829003601f168201915b505050505081565b806001908051906020019061046f929190610488565b505b50565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104c957805160ff19168380011785556104f7565b828001600101855582156104f7579182015b828111156104f65782518255916020019190600101906104db565b5b5090506105049190610508565b5090565b61052a91905b8082111561052657600081600090555060010161050e565b5090565b905600a165627a7a723058203ff916ee91add6247b20793745d1c6a8d8dcaa49d8c84fbbabb5c966fd9b6fc90029" # noqa: E501 + +CONTRACT_BYTES_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"bytes"}],"name":"setValue","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_value","type":"bytes"}],"payable":false,"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture() +def BYTES_CODE(): + return CONTRACT_BYTES_CODE + + +@pytest.fixture() +def BYTES_RUNTIME(): + return CONTRACT_BYTES_RUNTIME + + +@pytest.fixture() +def BYTES_ABI(): + return CONTRACT_BYTES_ABI + + +@pytest.fixture() +def BYTES_CONTRACT(BYTES_CODE, BYTES_RUNTIME, BYTES_ABI): + return { + 'bytecode': BYTES_CODE, + 'bytecode_runtime': BYTES_RUNTIME, + 'abi': BYTES_ABI, + } + + +@pytest.fixture() +def BytesContract(web3, BYTES_CONTRACT): + return web3.vns.contract(**BYTES_CONTRACT) + + +CONTRACT_BYTES32_CODE = "60606040527f0123012301230123012301230123012301230123012301230123012301230123600090600019169055341561003957600080fd5b6040516020806101e2833981016040528080519060200190919050505b80600181600019169055505b505b61016f806100736000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100905780633fa4f245146100c157806358825b10146100f2575b600080fd5b341561006a57600080fd5b610072610119565b60405180826000191660001916815260200191505060405180910390f35b341561009b57600080fd5b6100a3610124565b60405180826000191660001916815260200191505060405180910390f35b34156100cc57600080fd5b6100d461012e565b60405180826000191660001916815260200191505060405180910390f35b34156100fd57600080fd5b610117600480803560001916906020019091905050610134565b005b600060015490505b90565b6000805490505b90565b60015481565b80600181600019169055505b505600a165627a7a7230582043b15c20378b1603d330561258ccf291d08923a4c25fa8af0d590a010a6322180029" # noqa: E501 + +CONTRACT_BYTES32_RUNTIME = "60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551461005f57806330de3cee146100905780633fa4f245146100c157806358825b10146100f2575b600080fd5b341561006a57600080fd5b610072610119565b60405180826000191660001916815260200191505060405180910390f35b341561009b57600080fd5b6100a3610124565b60405180826000191660001916815260200191505060405180910390f35b34156100cc57600080fd5b6100d461012e565b60405180826000191660001916815260200191505060405180910390f35b34156100fd57600080fd5b610117600480803560001916906020019091905050610134565b005b600060015490505b90565b6000805490505b90565b60015481565b80600181600019169055505b505600a165627a7a7230582043b15c20378b1603d330561258ccf291d08923a4c25fa8af0d590a010a6322180029" # noqa: E501 + +CONTRACT_BYTES32_ABI = json.loads('[{"constant":false,"inputs":[],"name":"getValue","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"constValue","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"bytes32"}],"name":"setValue","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_value","type":"bytes32"}],"payable":false,"type":"constructor"}]') # noqa: E501 + + +@pytest.fixture() +def BYTES32_CODE(): + return CONTRACT_BYTES32_CODE + + +@pytest.fixture() +def BYTES32_RUNTIME(): + return CONTRACT_BYTES32_RUNTIME + + +@pytest.fixture() +def BYTES32_ABI(): + return CONTRACT_BYTES32_ABI + + +@pytest.fixture() +def BYTES32_CONTRACT(BYTES32_CODE, BYTES32_RUNTIME, BYTES32_ABI): + return { + 'bytecode': BYTES32_CODE, + 'bytecode_runtime': BYTES32_RUNTIME, + 'abi': BYTES32_ABI, + } + + +@pytest.fixture() +def Bytes32Contract(web3, BYTES32_CONTRACT): + return web3.vns.contract(**BYTES32_CONTRACT) + + +CONTRACT_EMITTER_CODE = "6060604052341561000f57600080fd5b6107928061001e6000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806317c0c1801461007d57806320f0256e146100a357806390b41d8b146100ed5780639c37705314610125578063aa6fd82214610166578063acabb9ed14610195575b600080fd5b341561008857600080fd5b6100a1600480803560ff16906020019091905050610235565b005b34156100ae57600080fd5b6100eb600480803560ff169060200190919080359060200190919080359060200190919080359060200190919080359060200190919050506102bd565b005b34156100f857600080fd5b610123600480803560ff169060200190919080359060200190919080359060200190919050506103a2565b005b341561013057600080fd5b610164600480803560ff169060200190919080359060200190919080359060200190919080359060200190919050506104a8565b005b341561017157600080fd5b610193600480803560ff1690602001909190803590602001909190505061057c565b005b34156101a057600080fd5b610233600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610663565b005b6001600e81111561024257fe5b81600e81111561024e57fe5b1415610285577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60405160405180910390a16102ba565b6000600e81111561029257fe5b81600e81111561029e57fe5b14156102b45760405160405180910390a06102b9565b600080fd5b5b50565b6005600e8111156102ca57fe5b85600e8111156102d657fe5b1415610330577ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf9848484846040518085815260200184815260200183815260200182815260200194505050505060405180910390a161039b565b600b600e81111561033d57fe5b85600e81111561034957fe5b14156103955780827fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b58686604051808381526020018281526020019250505060405180910390a361039a565b600080fd5b5b5050505050565b6003600e8111156103af57fe5b83600e8111156103bb57fe5b1415610405577fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc58282604051808381526020018281526020019250505060405180910390a16104a3565b6009600e81111561041257fe5b83600e81111561041e57fe5b141561046157807f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca836040518082815260200191505060405180910390a26104a2565b6008600e81111561046e57fe5b83600e81111561047a57fe5b141561049c5780826040518082815260200191505060405180910390a16104a1565b600080fd5b5b5b505050565b6004600e8111156104b557fe5b84600e8111156104c157fe5b1415610513577f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f5506283838360405180848152602001838152602001828152602001935050505060405180910390a1610576565b600a600e81111561052057fe5b84600e81111561052c57fe5b14156105705780827ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec856040518082815260200191505060405180910390a3610575565b600080fd5b5b50505050565b6002600e81111561058957fe5b82600e81111561059557fe5b14156105d7577f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d4816040518082815260200191505060405180910390a161065f565b6007600e8111156105e457fe5b82600e8111156105f057fe5b141561062857807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560405160405180910390a261065e565b6006600e81111561063557fe5b82600e81111561064157fe5b1415610658578060405160405180910390a161065d565b600080fd5b5b5b5050565b816040518082805190602001908083835b6020831015156106995780518252602082019150602081019050602083039250610674565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207fe77cf33df73da7bc2e253a2dae617e6f15e4e337eaa462a108903af4643d1b75826040518080602001828103825283818151815260200191508051906020019080838360005b8381101561072857808201518184015260208101905061070d565b50505050905090810190601f1680156107555780820380516001836020036101000a031916815260200191505b509250505060405180910390a250505600a165627a7a723058209c8a4fb2bf8b853d1fb9ec7399bd5b69cab6b7fb351c1a81c39edcf6573180fb0029" # noqa: E501 + +CONTRACT_EMITTER_RUNTIME = "606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806317c0c1801461007d57806320f0256e146100a357806390b41d8b146100ed5780639c37705314610125578063aa6fd82214610166578063acabb9ed14610195575b600080fd5b341561008857600080fd5b6100a1600480803560ff16906020019091905050610235565b005b34156100ae57600080fd5b6100eb600480803560ff169060200190919080359060200190919080359060200190919080359060200190919080359060200190919050506102bd565b005b34156100f857600080fd5b610123600480803560ff169060200190919080359060200190919080359060200190919050506103a2565b005b341561013057600080fd5b610164600480803560ff169060200190919080359060200190919080359060200190919080359060200190919050506104a8565b005b341561017157600080fd5b610193600480803560ff1690602001909190803590602001909190505061057c565b005b34156101a057600080fd5b610233600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610663565b005b6001600e81111561024257fe5b81600e81111561024e57fe5b1415610285577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60405160405180910390a16102ba565b6000600e81111561029257fe5b81600e81111561029e57fe5b14156102b45760405160405180910390a06102b9565b600080fd5b5b50565b6005600e8111156102ca57fe5b85600e8111156102d657fe5b1415610330577ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf9848484846040518085815260200184815260200183815260200182815260200194505050505060405180910390a161039b565b600b600e81111561033d57fe5b85600e81111561034957fe5b14156103955780827fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b58686604051808381526020018281526020019250505060405180910390a361039a565b600080fd5b5b5050505050565b6003600e8111156103af57fe5b83600e8111156103bb57fe5b1415610405577fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc58282604051808381526020018281526020019250505060405180910390a16104a3565b6009600e81111561041257fe5b83600e81111561041e57fe5b141561046157807f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca836040518082815260200191505060405180910390a26104a2565b6008600e81111561046e57fe5b83600e81111561047a57fe5b141561049c5780826040518082815260200191505060405180910390a16104a1565b600080fd5b5b5b505050565b6004600e8111156104b557fe5b84600e8111156104c157fe5b1415610513577f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f5506283838360405180848152602001838152602001828152602001935050505060405180910390a1610576565b600a600e81111561052057fe5b84600e81111561052c57fe5b14156105705780827ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec856040518082815260200191505060405180910390a3610575565b600080fd5b5b50505050565b6002600e81111561058957fe5b82600e81111561059557fe5b14156105d7577f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d4816040518082815260200191505060405180910390a161065f565b6007600e8111156105e457fe5b82600e8111156105f057fe5b141561062857807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560405160405180910390a261065e565b6006600e81111561063557fe5b82600e81111561064157fe5b1415610658578060405160405180910390a161065d565b600080fd5b5b5b5050565b816040518082805190602001908083835b6020831015156106995780518252602082019150602081019050602083039250610674565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207fe77cf33df73da7bc2e253a2dae617e6f15e4e337eaa462a108903af4643d1b75826040518080602001828103825283818151815260200191508051906020019080838360005b8381101561072857808201518184015260208101905061070d565b50505050905090810190601f1680156107555780820380516001836020036101000a031916815260200191505b509250505060405180910390a250505600a165627a7a723058209c8a4fb2bf8b853d1fb9ec7399bd5b69cab6b7fb351c1a81c39edcf6573180fb0029" # noqa: E501 + +CONTRACT_EMITTER_ABI = json.loads('[{"constant":false,"inputs":[{"name":"v","type":"string"}],"name":"logString","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"}],"name":"logNoArgs","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"},{"name":"arg3","type":"uint256"}],"name":"logQuadruple","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"}],"name":"logDouble","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"}],"name":"logTriple","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"}],"name":"logSingle","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"arg0","type":"string"},{"name":"arg1","type":"string"}],"name":"logDynamicArgs","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"v","type":"bytes"}],"name":"logBytes","outputs":[],"payable":false,"type":"function"},{"anonymous":true,"inputs":[],"name":"LogAnonymous","type":"event"},{"anonymous":false,"inputs":[],"name":"LogNoArguments","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"}],"name":"LogSingleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"}],"name":"LogDoubleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"}],"name":"LogTripleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"},{"indexed":false,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleArg","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"name":"arg0","type":"uint256"}],"name":"LogSingleAnonymous","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"arg0","type":"uint256"}],"name":"LogSingleWithIndex","type":"event"},{"anonymous":true,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"}],"name":"LogDoubleAnonymous","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"}],"name":"LogDoubleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"}],"name":"LogTripleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"},{"indexed":true,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"arg0","type":"string"},{"indexed":false,"name":"arg1","type":"string"}],"name":"LogDynamicArgs","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"v","type":"bytes"}],"name":"LogBytes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"v","type":"string"}],"name":"LogString","type":"event"}]') # noqa: E501 + + +@pytest.fixture() +def EMITTER_CODE(): + return CONTRACT_EMITTER_CODE + + +@pytest.fixture() +def EMITTER_RUNTIME(): + return CONTRACT_EMITTER_RUNTIME + + +@pytest.fixture() +def EMITTER_ABI(): + return CONTRACT_EMITTER_ABI + + +@pytest.fixture() +def EMITTER(EMITTER_CODE, + EMITTER_RUNTIME, + EMITTER_ABI): + return { + 'bytecode': EMITTER_CODE, + 'bytecode_runtime': EMITTER_RUNTIME, + 'abi': EMITTER_ABI, + } + + +@pytest.fixture() +def Emitter(web3_empty, EMITTER): + web3 = web3_empty + return web3.vns.contract(**EMITTER) + + +@pytest.fixture() +def emitter(web3_empty, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): + web3 = web3_empty + + wait_for_block(web3) + deploy_txn_hash = Emitter.constructor().transact({'from': web3.vns.coinbase, 'gas': 1000000}) + deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + + bytecode = web3.vns.getCode(contract_address) + assert bytecode == Emitter.bytecode_runtime + emitter_contract = Emitter(address=contract_address) + assert emitter_contract.address == contract_address + return emitter_contract + + +CONTRACT_ARRAYS_SOURCE = """ + contract ArraysContract { + + bytes32[] public bytes32Value; + bytes32[] public bytes32ConstValue; + byte[] public byteValue; + byte[] public byteConstValue; + + function ArraysContract(bytes32[] _bytes32Value, byte[] _byteValue) { + bytes32Value = _bytes32Value; + byteValue = _byteValue; + bytes32ConstValue = [keccak256('A'), keccak256('B')]; + byteConstValue = [bytes1(0), bytes1(1)]; + } + + function setBytes32Value(bytes32[] _bytes32Value) { + bytes32Value = _bytes32Value; + } + + function getBytes32Value() returns (bytes32[]) { + return bytes32Value; + } + + function getBytes32ConstValue() returns (bytes32[]) { + return bytes32ConstValue; + } + + function setByteValue(byte[] _byteValue) { + byteValue = _byteValue; + } + + function getByteValue() returns (byte[]) { + return byteValue; + } + + function getByteConstValue() returns (byte[]) { + return byteConstValue; + } +} +""" + + +CONTRACT_ARRAYS_CODE = "606060405234156200001057600080fd5b60405162000e6238038062000e628339810160405280805182019190602001805182019190505081600090805190602001906200004f92919062000209565b5080600290805190602001906200006892919062000261565b50604080519081016040528060405180807f4100000000000000000000000000000000000000000000000000000000000000815250600101905060405180910390206000191660001916815260200160405180807f420000000000000000000000000000000000000000000000000000000000000081525060010190506040518091039020600019166000191681525060019060026200010a9291906200032f565b50604080519081016040528060007f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160017f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681525060039060026200020092919062000387565b505050620004b0565b8280548282559060005260206000209081019282156200024e579160200282015b828111156200024d5782518290600019169055916020019190600101906200022a565b5b5090506200025d919062000455565b5090565b82805482825590600052602060002090601f016020900481019282156200031c5791602002820160005b83821115620002eb57835183826101000a81548160ff02191690837f01000000000000000000000000000000000000000000000000000000000000009004021790555092602001926001016020816000010492830192600103026200028b565b80156200031a5782816101000a81549060ff0219169055600101602081600001049283019260010302620002eb565b505b5090506200032b91906200047d565b5090565b82805482825590600052602060002090810192821562000374579160200282015b828111156200037357825182906000191690559160200191906001019062000350565b5b50905062000383919062000455565b5090565b82805482825590600052602060002090601f01602090048101928215620004425791602002820160005b838211156200041157835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302620003b1565b8015620004405782816101000a81549060ff021916905560010160208160000104928301926001030262000411565b505b5090506200045191906200047d565b5090565b6200047a91905b80821115620004765760008160009055506001016200045c565b5090565b90565b620004ad91905b80821115620004a957600081816101000a81549060ff02191690555060010162000484565b5090565b90565b6109a280620004c06000396000f300606060405236156100a2576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630afe5e33146100a757806312c9dcc8146101115780631579bf661461018c5780633ddcea2f146101f657806351b4878814610250578063542d83de1461028f578063605ba271146102ce5780638abe51fd14610338578063962e450c146103a2578063bb69679b1461041d575b600080fd5b34156100b257600080fd5b6100ba610477565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100fd5780820151818401526020810190506100e2565b505050509050019250505060405180910390f35b341561011c57600080fd5b61013260048080359060200190919050506104d9565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561019757600080fd5b61019f61052b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101e25780820151818401526020810190506101c7565b505050509050019250505060405180910390f35b341561020157600080fd5b61024e6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506105ed565b005b341561025b57600080fd5b6102716004808035906020019091905050610607565b60405180826000191660001916815260200191505060405180910390f35b341561029a57600080fd5b6102b0600480803590602001909190505061062b565b60405180826000191660001916815260200191505060405180910390f35b34156102d957600080fd5b6102e161064f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610324578082015181840152602081019050610309565b505050509050019250505060405180910390f35b341561034357600080fd5b61034b6106b1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561038e578082015181840152602081019050610373565b505050509050019250505060405180910390f35b34156103ad57600080fd5b6103c36004808035906020019091905050610773565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561042857600080fd5b6104756004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506107c5565b005b61047f6107df565b60018054806020026020016040519081016040528092919081815260200182805480156104cf57602002820191906000526020600020905b815460001916815260200190600101908083116104b7575b5050505050905090565b6002818154811015156104e857fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b6105336107f3565b60038054806020026020016040519081016040528092919081815260200182805480156105e357602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906001019060208260000104928301926001038202915080841161056e5790505b5050505050905090565b8060029080519060200190610603929190610807565b5050565b60018181548110151561061657fe5b90600052602060002090016000915090505481565b60008181548110151561063a57fe5b90600052602060002090016000915090505481565b6106576107df565b60008054806020026020016040519081016040528092919081815260200182805480156106a757602002820191906000526020600020905b8154600019168152602001906001019080831161068f575b5050505050905090565b6106b96107f3565b600280548060200260200160405190810160405280929190818152602001828054801561076957602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190600101906020826000010492830192600103820291508084116106f45790505b5050505050905090565b60038181548110151561078257fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b80600090805190602001906107db9291906108ce565b5050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b82805482825590600052602060002090601f016020900481019282156108bd5791602002820160005b8382111561088e57835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302610830565b80156108bb5782816101000a81549060ff021916905560010160208160000104928301926001030261088e565b505b5090506108ca9190610921565b5090565b828054828255906000526020600020908101928215610910579160200282015b8281111561090f5782518290600019169055916020019190600101906108ee565b5b50905061091d9190610951565b5090565b61094e91905b8082111561094a57600081816101000a81549060ff021916905550600101610927565b5090565b90565b61097391905b8082111561096f576000816000905550600101610957565b5090565b905600a165627a7a72305820d0caf89bd0d39b343a907ac9d0f0b9bcddb1b8dc910706160d9d26fdc552afa40029" # noqa: E501 + +CONTRACT_ARRAYS_RUNTIME = "0x606060405236156100a2576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630afe5e33146100a757806312c9dcc8146101115780631579bf661461018c5780633ddcea2f146101f657806351b4878814610250578063542d83de1461028f578063605ba271146102ce5780638abe51fd14610338578063962e450c146103a2578063bb69679b1461041d575b600080fd5b34156100b257600080fd5b6100ba610477565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100fd5780820151818401526020810190506100e2565b505050509050019250505060405180910390f35b341561011c57600080fd5b61013260048080359060200190919050506104d9565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561019757600080fd5b61019f61052b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101e25780820151818401526020810190506101c7565b505050509050019250505060405180910390f35b341561020157600080fd5b61024e6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506105ed565b005b341561025b57600080fd5b6102716004808035906020019091905050610607565b60405180826000191660001916815260200191505060405180910390f35b341561029a57600080fd5b6102b0600480803590602001909190505061062b565b60405180826000191660001916815260200191505060405180910390f35b34156102d957600080fd5b6102e161064f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610324578082015181840152602081019050610309565b505050509050019250505060405180910390f35b341561034357600080fd5b61034b6106b1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561038e578082015181840152602081019050610373565b505050509050019250505060405180910390f35b34156103ad57600080fd5b6103c36004808035906020019091905050610773565b60405180827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b341561042857600080fd5b6104756004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506107c5565b005b61047f6107df565b60018054806020026020016040519081016040528092919081815260200182805480156104cf57602002820191906000526020600020905b815460001916815260200190600101908083116104b7575b5050505050905090565b6002818154811015156104e857fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b6105336107f3565b60038054806020026020016040519081016040528092919081815260200182805480156105e357602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906001019060208260000104928301926001038202915080841161056e5790505b5050505050905090565b8060029080519060200190610603929190610807565b5050565b60018181548110151561061657fe5b90600052602060002090016000915090505481565b60008181548110151561063a57fe5b90600052602060002090016000915090505481565b6106576107df565b60008054806020026020016040519081016040528092919081815260200182805480156106a757602002820191906000526020600020905b8154600019168152602001906001019080831161068f575b5050505050905090565b6106b96107f3565b600280548060200260200160405190810160405280929190818152602001828054801561076957602002820191906000526020600020906000905b82829054906101000a90047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190600101906020826000010492830192600103820291508084116106f45790505b5050505050905090565b60038181548110151561078257fe5b9060005260206000209060209182820401919006915054906101000a90047f01000000000000000000000000000000000000000000000000000000000000000281565b80600090805190602001906107db9291906108ce565b5050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b82805482825590600052602060002090601f016020900481019282156108bd5791602002820160005b8382111561088e57835183826101000a81548160ff02191690837f0100000000000000000000000000000000000000000000000000000000000000900402179055509260200192600101602081600001049283019260010302610830565b80156108bb5782816101000a81549060ff021916905560010160208160000104928301926001030261088e565b505b5090506108ca9190610921565b5090565b828054828255906000526020600020908101928215610910579160200282015b8281111561090f5782518290600019169055916020019190600101906108ee565b5b50905061091d9190610951565b5090565b61094e91905b8082111561094a57600081816101000a81549060ff021916905550600101610927565b5090565b90565b61097391905b8082111561096f576000816000905550600101610957565b5090565b905600a165627a7a72305820d0caf89bd0d39b343a907ac9d0f0b9bcddb1b8dc910706160d9d26fdc552afa40029" # noqa: E501 + +CONTRACT_ARRAYS_ABI = json.loads('[{"constant": false, "inputs": [], "name": "getBytes32ConstValue", "outputs": [{"name": "", "type": "bytes32[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "byteValue", "outputs": [{"name": "", "type": "bytes1"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "getByteConstValue", "outputs": [{"name": "", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": false, "inputs": [{"name": "_byteValue", "type": "bytes1[]"}], "name": "setByteValue", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "bytes32ConstValue", "outputs": [{"name": "", "type": "bytes32"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "bytes32Value", "outputs": [{"name": "", "type": "bytes32"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "getBytes32Value", "outputs": [{"name": "", "type": "bytes32[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": false, "inputs": [], "name": "getByteValue", "outputs": [{"name": "", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "", "type": "uint256"}], "name": "byteConstValue", "outputs": [{"name": "", "type": "bytes1"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [{"name": "_bytes32Value", "type": "bytes32[]"}], "name": "setBytes32Value", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"inputs": [{"name": "_bytes32Value", "type": "bytes32[]"}, {"name": "_byteValue", "type": "bytes1[]"}], "payable": false, "stateMutability": "nonpayable", "type": "constructor"}]') # noqa: E501 + + +@pytest.fixture() +def ARRAYS_CODE(): + return CONTRACT_ARRAYS_CODE + + +@pytest.fixture() +def ARRAYS_RUNTIME(): + return CONTRACT_ARRAYS_RUNTIME + + +@pytest.fixture() +def ARRAYS_ABI(): + return CONTRACT_ARRAYS_ABI + + +@pytest.fixture() +def ARRAYS_CONTRACT(ARRAYS_CODE, + ARRAYS_RUNTIME, + ARRAYS_ABI): + return { + 'bytecode': ARRAYS_CODE, + 'bytecode_runtime': ARRAYS_RUNTIME, + 'abi': ARRAYS_ABI, + } + + +@pytest.fixture() +def ArraysContract(web3, ARRAYS_CONTRACT): + return web3.vns.contract(**ARRAYS_CONTRACT) + + +CONTRACT_PAYABLE_TESTER_SOURCE = """ +contract PayableTester { + bool public wasCalled; + + function doNoValueCall() public { + wasCalled = true; + } +} +""" + +CONTRACT_PAYABLE_TESTER_CODE = "608060405234801561001057600080fd5b5060e88061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c680362214604e578063e4cb8f5c14607a575b600080fd5b348015605957600080fd5b506060608e565b604051808215151515815260200191505060405180910390f35b348015608557600080fd5b50608c60a0565b005b6000809054906101000a900460ff1681565b60016000806101000a81548160ff0219169083151502179055505600a165627a7a723058205362c7376eda918b0dc3a75d0ffab904a241c9b10b68d5268af6ca405242303e0029" # noqa: E501 + +CONTRACT_PAYABLE_TESTER_RUNTIME = "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c680362214604e578063e4cb8f5c14607a575b600080fd5b348015605957600080fd5b506060608e565b604051808215151515815260200191505060405180910390f35b348015608557600080fd5b50608c60a0565b005b6000809054906101000a900460ff1681565b60016000806101000a81548160ff0219169083151502179055505600a165627a7a723058205362c7376eda918b0dc3a75d0ffab904a241c9b10b68d5268af6ca405242303e0029" # noqa: E501 + +CONTRACT_PAYABLE_TESTER_ABI = json.loads('[{"constant": true, "inputs": [], "name": "wasCalled", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [], "name": "doNoValueCall", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}]') # noqa: E501 + + +@pytest.fixture() +def PAYABLE_TESTER_CODE(): + return CONTRACT_PAYABLE_TESTER_CODE + + +@pytest.fixture() +def PAYABLE_TESTER_RUNTIME(): + return CONTRACT_PAYABLE_TESTER_RUNTIME + + +@pytest.fixture() +def PAYABLE_TESTER_ABI(): + return CONTRACT_PAYABLE_TESTER_ABI + + +@pytest.fixture() +def PAYABLE_TESTER_CONTRACT(PAYABLE_TESTER_CODE, + PAYABLE_TESTER_RUNTIME, + PAYABLE_TESTER_ABI): + return { + 'bytecode': PAYABLE_TESTER_CODE, + 'bytecode_runtime': PAYABLE_TESTER_RUNTIME, + 'abi': PAYABLE_TESTER_ABI, + } + + +@pytest.fixture() +def PayableTesterContract(web3, PAYABLE_TESTER_CONTRACT): + return web3.vns.contract(**PAYABLE_TESTER_CONTRACT) + + +# no matter the function selector, this will return back the 32 bytes of data supplied +CONTRACT_REFLECTION_CODE = ( + "0x610011566020600460003760206000f3005b61000461001103610004600039610004610011036000f3" +) + +# reference source used to generate it: +LLL_SOURCE = "['seq', ['return', 0, ['lll', ['seq', ['calldatacopy', 0, 4, 32], ['return', 0, 32], 'stop' ], 0]]])" # noqa: E501 + +CONTRACT_FIXED_ABI = [ + { + "type": "function", + "constant": False, + "inputs": [{"type": "fixed8x1"}], + "name": "reflect", + "outputs": [{"type": "fixed8x1"}], + }, + { + "type": "function", + "constant": False, + "inputs": [{"type": "ufixed256x80"}], + "name": "reflect", + "outputs": [{"type": "ufixed256x80"}], + }, + { + "type": "function", + "constant": False, + "inputs": [{"type": "ufixed256x1"}], + "name": "reflect", + "outputs": [{"type": "ufixed256x1"}], + }, + { + "type": "function", + "constant": False, + "inputs": [{"type": "ufixed8x1"}], + "name": "reflect_short_u", + "outputs": [{"type": "ufixed8x1"}], + }, +] + + +@pytest.fixture +def FixedReflectionContract(web3): + return web3.vns.contract(abi=CONTRACT_FIXED_ABI, bytecode=CONTRACT_REFLECTION_CODE) + + +CONTRACT_FALLBACK_FUNCTION_SOURCE = """ +contract A { + uint data; + function A() public payable { data = 0; } + function getData() returns (uint r) { return data; } + function() { data = 1; } +} +""" + +CONTRACT_FALLBACK_FUNCTION_CODE = "60606040526000808190555060ae806100196000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bc5de30146053575b3415604957600080fd5b6001600081905550005b3415605d57600080fd5b60636079565b6040518082815260200191505060405180910390f35b600080549050905600a165627a7a72305820045439389e4742569ec078687e6a0c81997709778a0097adbe07ccfd9f7b1a330029" # noqa: E501 + +CONTRACT_FALLBACK_FUNCTION_RUNTIME = "606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bc5de30146053575b3415604957600080fd5b6001600081905550005b3415605d57600080fd5b60636079565b6040518082815260200191505060405180910390f35b600080549050905600a165627a7a72305820045439389e4742569ec078687e6a0c81997709778a0097adbe07ccfd9f7b1a330029" # noqa: E501 + +CONTRACT_FALLBACK_FUNCTION_ABI = json.loads('[{"constant": false, "inputs": [], "name": "getData", "outputs": [{"name": "r", "type": "uint256"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"inputs": [], "payable": true, "stateMutability": "payable", "type": "constructor"}, {"payable": false, "stateMutability": "nonpayable", "type": "fallback"}]') # noqa: E501 + + +@pytest.fixture() +def FALLBACK_FUNCTION_CODE(): + return CONTRACT_FALLBACK_FUNCTION_CODE + + +@pytest.fixture() +def FALLBACK_FUNCTION_RUNTIME(): + return CONTRACT_FALLBACK_FUNCTION_RUNTIME + + +@pytest.fixture() +def FALLBACK_FUNCTION_ABI(): + return CONTRACT_FALLBACK_FUNCTION_ABI + + +@pytest.fixture() +def FALLBACK_FUNCTION_CONTRACT(FALLBACK_FUNCTION_CODE, + FALLBACK_FUNCTION_RUNTIME, + FALLBACK_FUNCTION_ABI): + return { + 'bytecode': FALLBACK_FUNCTION_CODE, + 'bytecode_runtime': FALLBACK_FUNCTION_RUNTIME, + 'abi': FALLBACK_FUNCTION_ABI, + } + + +@pytest.fixture() +def FallballFunctionContract(web3, FALLBACK_FUNCTION_CONTRACT): + return web3.vns.contract(**FALLBACK_FUNCTION_CONTRACT) + + +CONTRACT_CALLER_TESTER_SOURCE = """ +contract CallerTester { + int public count; + + function add(int256 a, int256 b) public payable returns (int256) { + return a + b; + } + + function increment() public returns (int256) { + return count += 1; + } + + function counter() public payable returns (int256) { + return count; + } + + function returnMeta() public payable returns (address, bytes memory, uint256, uint, uint) { + return (msg.sender, msg.data, gasleft(), msg.value, block.number); + } +} +""" + + +CONTRACT_CALLER_TESTER_CODE = "608060405234801561001057600080fd5b50610241806100206000396000f3fe608060405260043610610066577c0100000000000000000000000000000000000000000000000000000000600035046306661abd811461006b57806361bc221a14610092578063a5f3c23b1461009a578063c7fa7d66146100bd578063d09de08a14610185575b600080fd5b34801561007757600080fd5b5061008061019a565b60408051918252519081900360200190f35b6100806101a0565b610080600480360360408110156100b057600080fd5b50803590602001356101a6565b6100c56101aa565b604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001858152602001848152602001838152602001828103825286818151815260200191508051906020019080838360005b8381101561014657818101518382015260200161012e565b50505050905090810190601f1680156101735780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561019157600080fd5b50610080610207565b60005481565b60005490565b0190565b600060606000806000336000365a344385955084848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250989e929d50949b5092995090975095505050505050565b60008054600101908190559056fea165627a7a72305820ffe1620e420efa326b9c5e4ef9f93cac71cf986196246c7966d71a39259899b10029" # noqa: E501 + + +CONTRACT_CALLER_TESTER_RUNTIME = "608060405260043610610066577c0100000000000000000000000000000000000000000000000000000000600035046306661abd811461006b57806361bc221a14610092578063a5f3c23b1461009a578063c7fa7d66146100bd578063d09de08a14610185575b600080fd5b34801561007757600080fd5b5061008061019a565b60408051918252519081900360200190f35b6100806101a0565b610080600480360360408110156100b057600080fd5b50803590602001356101a6565b6100c56101aa565b604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001858152602001848152602001838152602001828103825286818151815260200191508051906020019080838360005b8381101561014657818101518382015260200161012e565b50505050905090810190601f1680156101735780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561019157600080fd5b50610080610207565b60005481565b60005490565b0190565b600060606000806000336000365a344385955084848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250989e929d50949b5092995090975095505050505050565b60008054600101908190559056fea165627a7a72305820ffe1620e420efa326b9c5e4ef9f93cac71cf986196246c7966d71a39259899b10029" # noqa: E501 + + +CONTRACT_CALLER_TESTER_ABI = json.loads('[ { "constant": true, "inputs": [], "name": "count", "outputs": [ { "name": "", "type": "int256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "counter", "outputs": [ { "name": "", "type": "int256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "a", "type": "int256" }, { "name": "b", "type": "int256" } ], "name": "add", "outputs": [ { "name": "", "type": "int256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [], "name": "returnMeta", "outputs": [ { "name": "", "type": "address" }, { "name": "", "type": "bytes" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [], "name": "increment", "outputs": [ { "name": "", "type": "int256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]') # noqa: E501 + + +@pytest.fixture() +def CALLER_TESTER_CODE(): + return CONTRACT_CALLER_TESTER_CODE + + +@pytest.fixture() +def CALLER_TESTER_RUNTIME(): + return CONTRACT_CALLER_TESTER_RUNTIME + + +@pytest.fixture() +def CALLER_TESTER_ABI(): + return CONTRACT_CALLER_TESTER_ABI + + +@pytest.fixture() +def CALLER_TESTER_CONTRACT(CALLER_TESTER_CODE, + CALLER_TESTER_RUNTIME, + CALLER_TESTER_ABI): + return { + 'bytecode': CALLER_TESTER_CODE, + 'bytecode_runtime': CALLER_TESTER_RUNTIME, + 'abi': CALLER_TESTER_ABI, + } + + +@pytest.fixture() +def CallerTesterContract(web3, CALLER_TESTER_CONTRACT): + return web3.vns.contract(**CALLER_TESTER_CONTRACT) + + +class LogFunctions: + LogAnonymous = 0 + LogNoArguments = 1 + LogSingleArg = 2 + LogDoubleArg = 3 + LogTripleArg = 4 + LogQuadrupleArg = 5 + LogSingleAnonymous = 6 + LogSingleWithIndex = 7 + LogDoubleAnonymous = 8 + LogDoubleWithIndex = 9 + LogTripleWithIndex = 10 + LogQuadrupleWithIndex = 11 + + +@pytest.fixture() +def emitter_event_ids(): + return LogFunctions + + +def _encode_to_topic(event_signature): + return event_signature_to_log_topic(event_signature) + + +class LogTopics: + LogAnonymous = _encode_to_topic("LogAnonymous()") + LogNoArguments = _encode_to_topic("LogNoArguments()") + LogSingleArg = _encode_to_topic("LogSingleArg(uint256)") + LogSingleAnonymous = _encode_to_topic("LogSingleAnonymous(uint256)") + LogSingleWithIndex = _encode_to_topic("LogSingleWithIndex(uint256)") + LogDoubleArg = _encode_to_topic("LogDoubleArg(uint256,uint256)") + LogDoubleAnonymous = _encode_to_topic("LogDoubleAnonymous(uint256,uint256)") + LogDoubleWithIndex = _encode_to_topic("LogDoubleWithIndex(uint256,uint256)") + LogTripleArg = _encode_to_topic("LogTripleArg(uint256,uint256,uint256)") + LogTripleWithIndex = _encode_to_topic("LogTripleWithIndex(uint256,uint256,uint256)") + LogQuadrupleArg = _encode_to_topic("LogQuadrupleArg(uint256,uint256,uint256,uint256)") + LogQuadrupleWithIndex = _encode_to_topic( + "LogQuadrupleWithIndex(uint256,uint256,uint256,uint256)", + ) + LogBytes = _encode_to_topic("LogBytes(bytes)") + LogString = _encode_to_topic("LogString(string)") + LogDynamicArgs = _encode_to_topic("LogDynamicArgs(string,string)") + + +@pytest.fixture() +def emitter_log_topics(): + return LogTopics + + +@pytest.fixture() +def some_address(address_conversion_func): + return address_conversion_func('0x5B2063246F2191f18F2675ceDB8b28102e957458') + + +def invoke_contract(api_call_desig='call', + contract=None, + contract_function=None, + func_args=[], + func_kwargs={}, + tx_params={}): + allowable_call_desig = ['call', 'transact', 'estimateGas', 'buildTransaction'] + if api_call_desig not in allowable_call_desig: + raise ValueError("allowable_invoke_method must be one of: %s" % allowable_call_desig) + + function = contract.functions[contract_function] + result = getattr(function(*func_args, **func_kwargs), api_call_desig)(tx_params) + + return result + + +@pytest.fixture +def transact(request): + return functools.partial(invoke_contract, api_call_desig='transact') + + +@pytest.fixture +def call(request): + return functools.partial(invoke_contract, api_call_desig='call') + + +@pytest.fixture +def estimateGas(request): + return functools.partial(invoke_contract, api_call_desig='estimateGas') + + +@pytest.fixture +def buildTransaction(request): + return functools.partial(invoke_contract, api_call_desig='buildTransaction') diff --git a/tests/core/contracts/contract_sources/Emitter.sol b/tests/core/contracts/contract_sources/Emitter.sol index 5d38445e3a..7e47c2faf0 100644 --- a/tests/core/contracts/contract_sources/Emitter.sol +++ b/tests/core/contracts/contract_sources/Emitter.sol @@ -1,102 +1,90 @@ -pragma solidity ^0.4.21; - - -contract Emitter { - event LogAnonymous() anonymous; - event LogNoArguments(); - event LogSingleArg(uint arg0); - event LogDoubleArg(uint arg0, uint arg1); - event LogTripleArg(uint arg0, uint arg1, uint arg2); - event LogQuadrupleArg(uint arg0, uint arg1, uint arg2, uint arg3); - event LogString(string v); - event LogBytes(bytes v); - - // Indexed - event LogSingleWithIndex(uint indexed arg0); - event LogSingleAnonymous(uint indexed arg0) anonymous; - event LogDoubleWithIndex(uint arg0, uint indexed arg1); - event LogDoubleAnonymous(uint arg0, uint indexed arg1) anonymous; - event LogTripleWithIndex(uint arg0, uint indexed arg1, uint indexed arg2); - event LogQuadrupleWithIndex(uint arg0, uint arg1, uint indexed arg2, uint indexed arg3); - event LogDynamicArgs(string indexed arg0, string arg1); - event LogListArgs(bytes2[] indexed arg0, bytes2[] arg1); - event LogAddressIndexed(address indexed arg0, address arg1); - event LogAddressNotIndexed(address arg0, address arg1); - - enum WhichEvent { - LogAnonymous, - LogNoArguments, - LogSingleArg, - LogDoubleArg, - LogTripleArg, - LogQuadrupleArg, - LogSingleAnonymous, - LogSingleWithIndex, - LogDoubleAnonymous, - LogDoubleWithIndex, - LogTripleWithIndex, - LogQuadrupleWithIndex, - LogBytes, - LogString, - LogDynamicArgs, - LogListArgs, - LogAddressIndexed, - LogAddressNotIndexed - } - - function logNoArgs(WhichEvent which) public { - if (which == WhichEvent.LogNoArguments) emit LogNoArguments(); - else if (which == WhichEvent.LogAnonymous) emit LogAnonymous(); - else revert("Didn't match any allowable event index"); - } - - function logSingle(WhichEvent which, uint arg0) public { - if (which == WhichEvent.LogSingleArg) emit LogSingleArg(arg0); - else if (which == WhichEvent.LogSingleWithIndex) emit LogSingleWithIndex(arg0); - else if (which == WhichEvent.LogSingleAnonymous) emit LogSingleAnonymous(arg0); - else revert("Didn't match any allowable event index"); - } - - function logDouble(WhichEvent which, uint arg0, uint arg1) public { - if (which == WhichEvent.LogDoubleArg) emit LogDoubleArg(arg0, arg1); - else if (which == WhichEvent.LogDoubleWithIndex) emit LogDoubleWithIndex(arg0, arg1); - else if (which == WhichEvent.LogDoubleAnonymous) emit LogDoubleAnonymous(arg0, arg1); - else revert("Didn't match any allowable event index"); - } - - function logTriple(WhichEvent which, uint arg0, uint arg1, uint arg2) public { - if (which == WhichEvent.LogTripleArg) emit LogTripleArg(arg0, arg1, arg2); - else if (which == WhichEvent.LogTripleWithIndex) emit LogTripleWithIndex(arg0, arg1, arg2); - else revert("Didn't match any allowable event index"); - } - - function logQuadruple(WhichEvent which, uint arg0, uint arg1, uint arg2, uint arg3) public { - if (which == WhichEvent.LogQuadrupleArg) emit LogQuadrupleArg(arg0, arg1, arg2, arg3); - else if (which == WhichEvent.LogQuadrupleWithIndex) emit LogQuadrupleWithIndex(arg0, arg1, arg2, arg3); - else revert("Didn't match any allowable event index"); - } - - function logDynamicArgs(string arg0, string arg1) public { - emit LogDynamicArgs(arg0, arg1); - } - - function logListArgs(bytes2[] arg0, bytes2[] arg1) public { - emit LogListArgs(arg0, arg1); - } - - function logAddressIndexedArgs(address arg0, address arg1) public { - emit LogAddressIndexed(arg0, arg1); - } - - function logAddressNotIndexedArgs(address arg0, address arg1) public { - emit LogAddressNotIndexed(arg0, arg1); - } - - function logBytes(bytes v) public { - emit LogBytes(v); - } - - function logString(string v) public { - emit LogString(v); - } -} +pragma solidity ^0.4.21; + + +contract Emitter { + event LogAnonymous() anonymous; + event LogNoArguments(); + event LogSingleArg(uint arg0); + event LogDoubleArg(uint arg0, uint arg1); + event LogTripleArg(uint arg0, uint arg1, uint arg2); + event LogQuadrupleArg(uint arg0, uint arg1, uint arg2, uint arg3); + event LogString(string v); + event LogBytes(bytes v); + + // Indexed + event LogSingleWithIndex(uint indexed arg0); + event LogSingleAnonymous(uint indexed arg0) anonymous; + event LogDoubleWithIndex(uint arg0, uint indexed arg1); + event LogDoubleAnonymous(uint arg0, uint indexed arg1) anonymous; + event LogTripleWithIndex(uint arg0, uint indexed arg1, uint indexed arg2); + event LogQuadrupleWithIndex(uint arg0, uint arg1, uint indexed arg2, uint indexed arg3); + event LogDynamicArgs(string indexed arg0, string arg1); + event LogListArgs(bytes2[] indexed arg0, bytes2[] arg1); + event LogAddressIndexed(address indexed arg0, address arg1); + event LogAddressNotIndexed(address arg0, address arg1); + + enum WhichEvent { + LogAnonymous, + LogNoArguments, + LogSingleArg, + LogDoubleArg, + LogTripleArg, + LogQuadrupleArg, + LogSingleAnonymous, + LogSingleWithIndex, + LogDoubleAnonymous, + LogDoubleWithIndex, + LogTripleWithIndex, + LogQuadrupleWithIndex, + LogBytes, + LogString, + LogDynamicArgs, + LogListArgs, + LogAddressIndexed, + LogAddressNotIndexed + } + + function logNoArgs(WhichEvent which) public { + if (which == WhichEvent.LogNoArguments) emit LogNoArguments(); + else if (which == WhichEvent.LogAnonymous) emit LogAnonymous(); + else revert("Didn't match any allowable event index"); + } + + function logSingle(WhichEvent which, uint arg0) public { + if (which == WhichEvent.LogSingleArg) emit LogSingleArg(arg0); + else if (which == WhichEvent.LogSingleWithIndex) emit LogSingleWithIndex(arg0); + else if (which == WhichEvent.LogSingleAnonymous) emit LogSingleAnonymous(arg0); + else revert("Didn't match any allowable event index"); + } + + function logDouble(WhichEvent which, uint arg0, uint arg1) public { + if (which == WhichEvent.LogDoubleArg) emit LogDoubleArg(arg0, arg1); + else if (which == WhichEvent.LogDoubleWithIndex) emit LogDoubleWithIndex(arg0, arg1); + else if (which == WhichEvent.LogDoubleAnonymous) emit LogDoubleAnonymous(arg0, arg1); + else revert("Didn't match any allowable event index"); + } + + function logTriple(WhichEvent which, uint arg0, uint arg1, uint arg2) public { + if (which == WhichEvent.LogTripleArg) emit LogTripleArg(arg0, arg1, arg2); + else if (which == WhichEvent.LogTripleWithIndex) emit LogTripleWithIndex(arg0, arg1, arg2); + else revert("Didn't match any allowable event index"); + } + + function logQuadruple(WhichEvent which, uint arg0, uint arg1, uint arg2, uint arg3) public { + if (which == WhichEvent.LogQuadrupleArg) emit LogQuadrupleArg(arg0, arg1, arg2, arg3); + else if (which == WhichEvent.LogQuadrupleWithIndex) emit LogQuadrupleWithIndex(arg0, arg1, arg2, arg3); + else revert("Didn't match any allowable event index"); + } + function logDynamicArgs(string arg0, string arg1) public { + emit LogDynamicArgs(arg0, arg1); + } + function logListArgs(bytes2[] arg0, bytes2[] arg1) public { + emit LogListArgs(arg0, arg1); + } + function logAddressIndexedArgs(address arg0, address arg1) public { + emit LogAddressIndexed(arg0, arg1); + } + function logAddressNotIndexedArgs(address arg0, address arg1) public { + emit LogAddressNotIndexed(arg0, arg1); + } +} diff --git a/tests/core/contracts/test_args_and_kwargs_merger.py b/tests/core/contracts/test_args_and_kwargs_merger.py index eeb9876702..a51a19121f 100644 --- a/tests/core/contracts/test_args_and_kwargs_merger.py +++ b/tests/core/contracts/test_args_and_kwargs_merger.py @@ -1,102 +1,102 @@ -import pytest - -from web3._utils.abi import ( - merge_args_and_kwargs, -) - -FUNCTION_ABI = { - "constant": False, - "inputs": [ - {"name": "a", "type": "int256"}, - {"name": "b", "type": "int256"}, - {"name": "c", "type": "int256"}, - {"name": "d", "type": "int256"}, - ], - "name": "testFn", - "outputs": [], - "type": "function", -} - - -def test_error_when_invalid_args_kwargs_combo_provided(): - with pytest.raises(TypeError): - merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2,), {'a': 1, 'b': 2}) - - -@pytest.mark.parametrize( - 'args,kwargs,expected_args', - ( - ((1, 4, 2, 3), {}, (1, 4, 2, 3)), - ((1, 4, 2), {'d': 3}, (1, 4, 2, 3)), - ((1, 4), {'d': 3, 'c': 2}, (1, 4, 2, 3)), - ((1,), {'d': 3, 'b': 4, 'c': 2}, (1, 4, 2, 3)), - (tuple(), {'d': 3, 'b': 4, 'a': 1, 'c': 2}, (1, 4, 2, 3)), - ), -) -def test_merging_of_args_and_kwargs(args, kwargs, expected_args): - actual_args = merge_args_and_kwargs(FUNCTION_ABI, args, kwargs) - assert actual_args == expected_args - - -NO_INPUTS_FUNCTION_ABI = { - "constant": False, - "inputs": [], - "name": "testFn", - "outputs": [], - "type": "function", -} - - -def test_merging_of_args_and_kwargs_with_no_inputs(): - actual = merge_args_and_kwargs(NO_INPUTS_FUNCTION_ABI, tuple(), {}) - assert actual == tuple() - - -GENERATED_FUNCTION_ABI = { - "constant": False, - "inputs": [ - {"name": "", "type": "int256"}, - {"name": "", "type": "int256"}, - ], - "name": "testFn", - "outputs": [], - "type": "function", -} - - -def test_kwargs_is_disallowed_when_merging_with_unnamed_inputs(): - with pytest.raises(TypeError): - merge_args_and_kwargs(GENERATED_FUNCTION_ABI, tuple(), {'x': 1, 'y': 2}) - - -def test_args_works_when_merging_with_unnamed_inputs(): - actual = merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2), {}) - assert actual == (1, 2) - - -DUPLICATE_NAMES_FUNCTION_ABI = { - "constant": False, - "inputs": [ - {"name": "b", "type": "int256"}, - {"name": "b", "type": "int256"}, - {"name": "a", "type": "int256"}, - ], - "name": "testFn", - "outputs": [], - "type": "function", -} - - -def test_args_allowed_when_duplicate_named_inputs(): - actual = merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1, 2, 3), {}) - assert actual == (1, 2, 3) - - -def test_kwargs_not_allowed_for_duplicate_input_names(): - with pytest.raises(TypeError): - merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1,), {'a': 2, 'b': 3}) - - -def test_kwargs_allowed_if_no_intersections_with_duplicate_input_names(): - with pytest.raises(TypeError): - merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1,), {'a': 2, 'b': 3}) +import pytest + +from web3._utils.abi import ( + merge_args_and_kwargs, +) + +FUNCTION_ABI = { + "constant": False, + "inputs": [ + {"name": "a", "type": "int256"}, + {"name": "b", "type": "int256"}, + {"name": "c", "type": "int256"}, + {"name": "d", "type": "int256"}, + ], + "name": "testFn", + "outputs": [], + "type": "function", +} + + +def test_error_when_invalid_args_kwargs_combo_provided(): + with pytest.raises(TypeError): + merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2,), {'a': 1, 'b': 2}) + + +@pytest.mark.parametrize( + 'args,kwargs,expected_args', + ( + ((1, 4, 2, 3), {}, (1, 4, 2, 3)), + ((1, 4, 2), {'d': 3}, (1, 4, 2, 3)), + ((1, 4), {'d': 3, 'c': 2}, (1, 4, 2, 3)), + ((1,), {'d': 3, 'b': 4, 'c': 2}, (1, 4, 2, 3)), + (tuple(), {'d': 3, 'b': 4, 'a': 1, 'c': 2}, (1, 4, 2, 3)), + ), +) +def test_merging_of_args_and_kwargs(args, kwargs, expected_args): + actual_args = merge_args_and_kwargs(FUNCTION_ABI, args, kwargs) + assert actual_args == expected_args + + +NO_INPUTS_FUNCTION_ABI = { + "constant": False, + "inputs": [], + "name": "testFn", + "outputs": [], + "type": "function", +} + + +def test_merging_of_args_and_kwargs_with_no_inputs(): + actual = merge_args_and_kwargs(NO_INPUTS_FUNCTION_ABI, tuple(), {}) + assert actual == tuple() + + +GENERATED_FUNCTION_ABI = { + "constant": False, + "inputs": [ + {"name": "", "type": "int256"}, + {"name": "", "type": "int256"}, + ], + "name": "testFn", + "outputs": [], + "type": "function", +} + + +def test_kwargs_is_disallowed_when_merging_with_unnamed_inputs(): + with pytest.raises(TypeError): + merge_args_and_kwargs(GENERATED_FUNCTION_ABI, tuple(), {'x': 1, 'y': 2}) + + +def test_args_works_when_merging_with_unnamed_inputs(): + actual = merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2), {}) + assert actual == (1, 2) + + +DUPLICATE_NAMES_FUNCTION_ABI = { + "constant": False, + "inputs": [ + {"name": "b", "type": "int256"}, + {"name": "b", "type": "int256"}, + {"name": "a", "type": "int256"}, + ], + "name": "testFn", + "outputs": [], + "type": "function", +} + + +def test_args_allowed_when_duplicate_named_inputs(): + actual = merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1, 2, 3), {}) + assert actual == (1, 2, 3) + + +def test_kwargs_not_allowed_for_duplicate_input_names(): + with pytest.raises(TypeError): + merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1,), {'a': 2, 'b': 3}) + + +def test_kwargs_allowed_if_no_intersections_with_duplicate_input_names(): + with pytest.raises(TypeError): + merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1,), {'a': 2, 'b': 3}) diff --git a/tests/core/contracts/test_concise_contract.py b/tests/core/contracts/test_concise_contract.py index 9d3d1ee8c1..1a6063601c 100644 --- a/tests/core/contracts/test_concise_contract.py +++ b/tests/core/contracts/test_concise_contract.py @@ -1,144 +1,141 @@ -import pytest -from unittest.mock import ( - Mock, -) - -from eth_utils import ( - decode_hex, -) - -from web3.contract import ( - CONCISE_NORMALIZERS, - ConciseContract, - ConciseMethod, -) - - -def deploy(web3, Contract, args=None): - args = args or [] - deploy_txn = Contract.constructor(*args).transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - contract = Contract(address=deploy_receipt['contractAddress']) - assert len(web3.eth.getCode(contract.address)) > 0 - return contract - - -@pytest.fixture() -def EMPTY_ADDR(address_conversion_func): - addr = '0x' + '00' * 20 - return address_conversion_func(addr) - - -@pytest.fixture() -def zero_address_contract(web3, WithConstructorAddressArgumentsContract, EMPTY_ADDR): - deploy_txn = WithConstructorAddressArgumentsContract.constructor( - EMPTY_ADDR, - ).transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - _address_contract = WithConstructorAddressArgumentsContract( - address=deploy_receipt['contractAddress'], - ) - with pytest.warns(DeprecationWarning, match='deprecated in favor of contract.caller'): - return ConciseContract(_address_contract) - - -def test_concisecontract_call_default(): - mock = Mock() - sweet_method = ConciseMethod(mock.functions.grail) - sweet_method(1, 2) - mock.functions.grail.assert_called_once_with(1, 2) - # Checking in return_value, ie the function instance - mock.functions.grail.return_value.call.assert_called_once_with({}) - - -def test_concisecontract_custom_transact(): - mock = Mock() - sweet_method = ConciseMethod(mock.functions.grail) - sweet_method(1, 2, transact={'holy': 3}) - mock.functions.grail.assert_called_once_with(1, 2) - # Checking in return_value, ie the function instance - mock.functions.grail.return_value.transact.assert_called_once_with({'holy': 3}) - - -def test_concisecontract_two_keywords_fail(): - mock = Mock() - sweet_method = ConciseMethod(mock) - with pytest.raises(TypeError): - sweet_method(1, 2, transact={'holy': 3}, call={'count_to': 4}) - - -def test_concisecontract_unknown_keyword_fails(): - contract = Mock() - sweet_method = ConciseMethod(contract.functions.grail) - with pytest.raises(TypeError): - sweet_method(1, 2, count={'to': 5}) - - -def test_concisecontract_returns_none_for_0addr(zero_address_contract): - result = zero_address_contract.testAddr() - assert result is None - - -def test_class_construction_sets_class_vars(web3, - MATH_ABI, - MATH_CODE, - MATH_RUNTIME, - some_address, - ): - MathContract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - - classic = MathContract(some_address) - assert classic.web3 == web3 - assert classic.bytecode == decode_hex(MATH_CODE) - assert classic.bytecode_runtime == decode_hex(MATH_RUNTIME) - - -def test_conciscecontract_keeps_custom_normalizers_on_base(web3, MATH_ABI): - base_contract = web3.eth.contract(abi=MATH_ABI) - # give different normalizers to this base instance - base_contract._return_data_normalizers = base_contract._return_data_normalizers + tuple([None]) - - # create concisce contract with custom contract - new_normalizers_size = len(base_contract._return_data_normalizers) - with pytest.warns(DeprecationWarning, match='deprecated in favor of contract.caller'): - concise = ConciseContract(base_contract) - - # check that concise contract includes the new normalizers - concise_normalizers_size = len(concise._classic_contract._return_data_normalizers) - assert concise_normalizers_size == new_normalizers_size + len(CONCISE_NORMALIZERS) - assert concise._classic_contract._return_data_normalizers[0] is None - - -def test_conciscecontract_function_collision( - web3, - StringContract): - - contract = deploy(web3, StringContract, args=["blarg"]) - - def getValue(): - assert 'getValue' in [ - item['name'] for item - in StringContract.abi - if 'name' in item] - - setattr(ConciseContract, 'getValue', getValue) - - with pytest.warns(DeprecationWarning, match='deprecated in favor of contract.caller'): - concise_contract = ConciseContract(contract) - - assert isinstance(concise_contract, ConciseContract) - - with pytest.raises(AttributeError, match=r'Namespace collision .* with ConciseContract API.'): - concise_contract.getValue() - - -def test_concisecontract_deprecation_warning(web3, StringContract): - contract = deploy(web3, StringContract, args=["blarg"]) - with pytest.warns(DeprecationWarning): - ConciseContract(contract) +import pytest +from unittest.mock import ( + Mock, +) + +from vns_utils import ( + decode_hex, +) + +from web3.contract import ( + CONCISE_NORMALIZERS, + ConciseContract, + ConciseMethod, +) + + +def deploy(web3, Contract, args=None): + args = args or [] + deploy_txn = Contract.constructor(*args).transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + contract = Contract(address=deploy_receipt['contractAddress']) + assert len(web3.vns.getCode(contract.address)) > 0 + return contract + + +@pytest.fixture() +def EMPTY_ADDR(address_conversion_func): + addr = '0x' + '00' * 20 + return address_conversion_func(addr) + + +@pytest.fixture() +def zero_address_contract(web3, WithConstructorAddressArgumentsContract, EMPTY_ADDR): + deploy_txn = WithConstructorAddressArgumentsContract.constructor( + EMPTY_ADDR, + ).transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + _address_contract = WithConstructorAddressArgumentsContract( + address=deploy_receipt['contractAddress'], + ) + return ConciseContract(_address_contract) + + +def test_concisecontract_call_default(): + mock = Mock() + sweet_method = ConciseMethod(mock.functions.grail) + sweet_method(1, 2) + mock.functions.grail.assert_called_once_with(1, 2) + # Checking in return_value, ie the function instance + mock.functions.grail.return_value.call.assert_called_once_with({}) + + +def test_concisecontract_custom_transact(): + mock = Mock() + sweet_method = ConciseMethod(mock.functions.grail) + sweet_method(1, 2, transact={'holy': 3}) + mock.functions.grail.assert_called_once_with(1, 2) + # Checking in return_value, ie the function instance + mock.functions.grail.return_value.transact.assert_called_once_with({'holy': 3}) + + +def test_concisecontract_two_keywords_fail(): + mock = Mock() + sweet_method = ConciseMethod(mock) + with pytest.raises(TypeError): + sweet_method(1, 2, transact={'holy': 3}, call={'count_to': 4}) + + +def test_concisecontract_unknown_keyword_fails(): + contract = Mock() + sweet_method = ConciseMethod(contract.functions.grail) + with pytest.raises(TypeError): + sweet_method(1, 2, count={'to': 5}) + + +def test_concisecontract_returns_none_for_0addr(zero_address_contract): + result = zero_address_contract.testAddr() + assert result is None + + +def test_class_construction_sets_class_vars(web3, + MATH_ABI, + MATH_CODE, + MATH_RUNTIME, + some_address, + ): + MathContract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + + classic = MathContract(some_address) + assert classic.web3 == web3 + assert classic.bytecode == decode_hex(MATH_CODE) + assert classic.bytecode_runtime == decode_hex(MATH_RUNTIME) + + +def test_conciscecontract_keeps_custom_normalizers_on_base(web3, MATH_ABI): + base_contract = web3.vns.contract(abi=MATH_ABI) + # give different normalizers to this base instance + base_contract._return_data_normalizers = base_contract._return_data_normalizers + tuple([None]) + + # create concisce contract with custom contract + new_normalizers_size = len(base_contract._return_data_normalizers) + concise = ConciseContract(base_contract) + + # check that concise contract includes the new normalizers + concise_normalizers_size = len(concise._classic_contract._return_data_normalizers) + assert concise_normalizers_size == new_normalizers_size + len(CONCISE_NORMALIZERS) + assert concise._classic_contract._return_data_normalizers[0] is None + + +def test_conciscecontract_function_collision( + web3, + StringContract): + + contract = deploy(web3, StringContract, args=["blarg"]) + + def getValue(): + assert 'getValue' in [ + item['name'] for item + in StringContract.abi + if 'name' in item] + + setattr(ConciseContract, 'getValue', getValue) + + concise_contract = ConciseContract(contract) + + assert isinstance(concise_contract, ConciseContract) + + with pytest.raises(AttributeError, match=r'Namespace collision .* with ConciseContract API.'): + concise_contract.getValue() + + +def test_concisecontract_deprecation_warning(web3, StringContract): + contract = deploy(web3, StringContract, args=["blarg"]) + with pytest.warns(DeprecationWarning): + ConciseContract(contract) diff --git a/tests/core/contracts/test_contract_ambiguous_functions.py b/tests/core/contracts/test_contract_ambiguous_functions.py index acc4b29e15..6c2609f456 100644 --- a/tests/core/contracts/test_contract_ambiguous_functions.py +++ b/tests/core/contracts/test_contract_ambiguous_functions.py @@ -1,193 +1,194 @@ -import pytest - -from eth_utils.toolz import ( - compose, - curry, -) -from hexbytes import ( - HexBytes, -) - -AMBIGUOUS_CONTRACT_ABI = [ - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}], - 'name': 'blockHashAmphithyronVersify', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}, {'name': 'uselessFlag', 'type': 'bool'}], - 'name': 'identity', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'int256'}, {'name': 'uselessFlag', 'type': 'bool'}], - 'name': 'identity', - 'outputs': [{'name': '', 'type': 'int256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - } -] - - -@pytest.fixture() -def string_contract(web3, StringContract, address_conversion_func): - deploy_txn = StringContract.constructor("Caqalai").transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - contract = StringContract(address=contract_address) - assert contract.address == contract_address - assert len(web3.eth.getCode(contract.address)) > 0 - return contract - - -map_repr = compose(list, curry(map, repr)) - - -@pytest.mark.parametrize( - 'method,args,repr_func,expected', - ( - ( - 'all_functions', - (), - map_repr, - [ - '', - '', - '' - ] - ), - ( - 'get_function_by_signature', - ('identity(uint256,bool)',), - repr, - '' - ), - ( - 'find_functions_by_name', - ('identity',), - map_repr, - [ - '', - '' - ] - ), - ( - 'get_function_by_name', - ('blockHashAmphithyronVersify',), - repr, - '', - ), - ( - 'get_function_by_selector', - (b'\x00\x00\x00\x00',), - repr, - '', - ), - ( - 'get_function_by_selector', - (0x00000000,), - repr, - '', - ), - ( - 'get_function_by_selector', - ('0x00000000',), - repr, - '', - ), - ( - 'find_functions_by_args', - (1, True), - map_repr, - [ - '', - '' - ] - ), - ( - 'get_function_by_args', - (1,), - repr, - '', - ), - ), -) -def test_find_or_get_functions_by_type(web3, method, args, repr_func, expected): - contract = web3.eth.contract(abi=AMBIGUOUS_CONTRACT_ABI) - function = getattr(contract, method)(*args) - assert repr_func(function) == expected - - -@pytest.mark.parametrize( - 'method,args,expected_message,expected_error', - ( - ( - 'get_function_by_signature', - ('identity(uint256, bool)', ), - r'Function signature should not contain any spaces.*', - ValueError - ), - ( - 'get_function_by_name', - ('identity', ), - r'Found multiple functions with matching name*', - ValueError - ), - ( - 'get_function_by_name', - ('undefined_function', ), - r'Could not find any function with matching name', - ValueError - ), - ( - 'get_function_by_selector', - (b'\x00' * (4 + 1), ), - r'expected value of size 4 bytes. Got: %s bytes' % (4 + 1), - ValueError - ), - ( - 'get_function_by_args', - (1, True), - r'Found multiple functions with matching args*', - ValueError - ), - ( - 'get_function_by_args', - (1,) * 50, - r'Could not find any function with matching args', - ValueError - ), - ) -) -def test_functions_error_messages(web3, method, args, expected_message, expected_error): - contract = web3.eth.contract(abi=AMBIGUOUS_CONTRACT_ABI) - with pytest.raises(expected_error, match=expected_message): - getattr(contract, method)(*args) - - -def test_contract_function_methods(string_contract): - set_value_func = string_contract.get_function_by_signature('setValue(string)') - get_value_func = string_contract.get_function_by_signature('getValue()') - assert isinstance(set_value_func('Hello').transact(), HexBytes) - assert get_value_func().call() == 'Hello' - assert isinstance(set_value_func('Hello World').estimateGas(), int) - assert isinstance(set_value_func('Hello World').buildTransaction(), dict) - - -def test_diff_between_fn_and_fn_called(string_contract): - get_value_func = string_contract.get_function_by_signature('getValue()') - get_value_func_called = get_value_func() - assert get_value_func is not get_value_func_called - assert repr(get_value_func) == '' - assert repr(get_value_func_called) == '' +import pytest + +from hexbytes import ( + HexBytes, +) + +from web3._utils.toolz import ( + compose, + curry, +) + +AMBIGUOUS_CONTRACT_ABI = [ + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}], + 'name': 'blockHashAmphithyronVersify', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}, {'name': 'uselessFlag', 'type': 'bool'}], + 'name': 'identity', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'int256'}, {'name': 'uselessFlag', 'type': 'bool'}], + 'name': 'identity', + 'outputs': [{'name': '', 'type': 'int256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + } +] + + +@pytest.fixture() +def string_contract(web3, StringContract, address_conversion_func): + deploy_txn = StringContract.constructor("Caqalai").transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + contract = StringContract(address=contract_address) + assert contract.address == contract_address + assert len(web3.vns.getCode(contract.address)) > 0 + return contract + + +map_repr = compose(list, curry(map, repr)) + + +@pytest.mark.parametrize( + 'method,args,repr_func,expected', + ( + ( + 'all_functions', + (), + map_repr, + [ + '', + '', + '' + ] + ), + ( + 'get_function_by_signature', + ('identity(uint256,bool)',), + repr, + '' + ), + ( + 'find_functions_by_name', + ('identity',), + map_repr, + [ + '', + '' + ] + ), + ( + 'get_function_by_name', + ('blockHashAmphithyronVersify',), + repr, + '', + ), + ( + 'get_function_by_selector', + (b'\x00\x00\x00\x00',), + repr, + '', + ), + ( + 'get_function_by_selector', + (0x00000000,), + repr, + '', + ), + ( + 'get_function_by_selector', + ('0x00000000',), + repr, + '', + ), + ( + 'find_functions_by_args', + (1, True), + map_repr, + [ + '', + '' + ] + ), + ( + 'get_function_by_args', + (1,), + repr, + '', + ), + ), +) +def test_find_or_get_functions_by_type(web3, method, args, repr_func, expected): + contract = web3.vns.contract(abi=AMBIGUOUS_CONTRACT_ABI) + function = getattr(contract, method)(*args) + assert repr_func(function) == expected + + +@pytest.mark.parametrize( + 'method,args,expected_message,expected_error', + ( + ( + 'get_function_by_signature', + ('identity(uint256, bool)', ), + r'Function signature should not contain any spaces.*', + ValueError + ), + ( + 'get_function_by_name', + ('identity', ), + r'Found multiple functions with matching name*', + ValueError + ), + ( + 'get_function_by_name', + ('undefined_function', ), + r'Could not find any function with matching name', + ValueError + ), + ( + 'get_function_by_selector', + (b'\x00' * (4 + 1), ), + r'expected value of size 4 bytes. Got: %s bytes' % (4 + 1), + ValueError + ), + ( + 'get_function_by_args', + (1, True), + r'Found multiple functions with matching args*', + ValueError + ), + ( + 'get_function_by_args', + (1,) * 50, + r'Could not find any function with matching args', + ValueError + ), + ) +) +def test_functions_error_messages(web3, method, args, expected_message, expected_error): + contract = web3.vns.contract(abi=AMBIGUOUS_CONTRACT_ABI) + with pytest.raises(expected_error, match=expected_message): + getattr(contract, method)(*args) + + +def test_contract_function_methods(string_contract): + set_value_func = string_contract.get_function_by_signature('setValue(string)') + get_value_func = string_contract.get_function_by_signature('getValue()') + assert isinstance(set_value_func('Hello').transact(), HexBytes) + assert get_value_func().call() == 'Hello' + assert isinstance(set_value_func('Hello World').estimateGas(), int) + assert isinstance(set_value_func('Hello World').buildTransaction(), dict) + + +def test_diff_between_fn_and_fn_called(string_contract): + get_value_func = string_contract.get_function_by_signature('getValue()') + get_value_func_called = get_value_func() + assert get_value_func is not get_value_func_called + assert repr(get_value_func) == '' + assert repr(get_value_func_called) == '' diff --git a/tests/core/contracts/test_contract_buildTransaction.py b/tests/core/contracts/test_contract_buildTransaction.py index 039aa02d46..d07ec59528 100644 --- a/tests/core/contracts/test_contract_buildTransaction.py +++ b/tests/core/contracts/test_contract_buildTransaction.py @@ -1,225 +1,224 @@ -# -*- coding: utf-8 -*- - -import pytest - -from eth_utils.toolz import ( - dissoc, -) - -from web3.exceptions import ( - ValidationError, -) - -# Ignore warning in pyethereum 1.6 - will go away with the upgrade -pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") - - -@pytest.fixture() -def math_contract(web3, MathContract, address_conversion_func): - deploy_txn = MathContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - math_contract_address = address_conversion_func(deploy_receipt['contractAddress']) - _math_contract = MathContract(address=math_contract_address) - assert _math_contract.address == math_contract_address - return _math_contract - - -@pytest.fixture() -def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): - deploy_txn = FallballFunctionContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - fallback_contract_address = address_conversion_func(deploy_receipt['contractAddress']) - _fallback_contract = FallballFunctionContract(address=fallback_contract_address) - assert _fallback_contract.address == fallback_contract_address - return _fallback_contract - - -@pytest.fixture() -def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): - deploy_txn = PayableTesterContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - payable_tester_address = address_conversion_func(deploy_receipt['contractAddress']) - _payable_tester = PayableTesterContract(address=payable_tester_address) - assert _payable_tester.address == payable_tester_address - return _payable_tester - - -def test_build_transaction_not_paying_to_nonpayable_function( - web3, - payable_tester_contract, - buildTransaction): - txn = buildTransaction(contract=payable_tester_contract, - contract_function='doNoValueCall') - assert dissoc(txn, 'gas') == { - 'to': payable_tester_contract.address, - 'data': '0xe4cb8f5c', - 'value': 0, - 'gasPrice': 1, - 'chainId': 61, - } - - -def test_build_transaction_paying_to_nonpayable_function( - web3, - payable_tester_contract, - buildTransaction): - with pytest.raises(ValidationError): - buildTransaction(contract=payable_tester_contract, - contract_function='doNoValueCall', - tx_params={'value': 1}) - - -def test_build_transaction_with_contract_no_arguments(web3, math_contract, buildTransaction): - txn = buildTransaction(contract=math_contract, contract_function='increment') - assert dissoc(txn, 'gas') == { - 'to': math_contract.address, - 'data': '0xd09de08a', - 'value': 0, - 'gasPrice': 1, - 'chainId': 61, - } - - -def test_build_transaction_with_contract_fallback_function(web3, fallback_function_contract): - txn = fallback_function_contract.fallback.buildTransaction() - assert dissoc(txn, 'gas') == { - 'to': fallback_function_contract.address, - 'data': '0x', - 'value': 0, - 'gasPrice': 1, - 'chainId': 61, - } - - -def test_build_transaction_with_contract_class_method( - web3, - MathContract, - math_contract, - buildTransaction): - txn = buildTransaction( - contract=MathContract, - contract_function='increment', - tx_params={'to': math_contract.address}, - ) - assert dissoc(txn, 'gas') == { - 'to': math_contract.address, - 'data': '0xd09de08a', - 'value': 0, - 'gasPrice': 1, - 'chainId': 61, - } - - -def test_build_transaction_with_contract_default_account_is_set( - web3, - math_contract, - buildTransaction): - txn = buildTransaction(contract=math_contract, contract_function='increment') - assert dissoc(txn, 'gas') == { - 'to': math_contract.address, - 'data': '0xd09de08a', - 'value': 0, - 'gasPrice': 1, - 'chainId': 61, - } - - -def test_build_transaction_with_gas_price_strategy_set(web3, math_contract, buildTransaction): - def my_gas_price_strategy(web3, transaction_params): - return 5 - web3.eth.setGasPriceStrategy(my_gas_price_strategy) - txn = buildTransaction(contract=math_contract, contract_function='increment') - assert dissoc(txn, 'gas') == { - 'to': math_contract.address, - 'data': '0xd09de08a', - 'value': 0, - 'gasPrice': 5, - 'chainId': 61, - } - - -def test_build_transaction_with_contract_data_supplied_errors(web3, - math_contract, - buildTransaction): - with pytest.raises(ValueError): - buildTransaction(contract=math_contract, - contract_function='increment', - tx_params={'data': '0x000'}) - - -def test_build_transaction_with_contract_to_address_supplied_errors(web3, - math_contract, - buildTransaction): - with pytest.raises(ValueError): - buildTransaction(contract=math_contract, - contract_function='increment', - tx_params={'to': '0xb2930B35844a230f00E51431aCAe96Fe543a0347'}) - - -@pytest.mark.parametrize( - 'transaction_args,method_args,method_kwargs,expected,skip_testrpc', - ( - ( - {}, (5,), {}, { - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 - 'value': 0, 'gasPrice': 1, 'chainId': 61, - }, False - ), - ( - {'gas': 800000}, (5,), {}, { - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 - 'value': 0, 'gasPrice': 1, 'chainId': 61, - }, False - ), - ( - {'gasPrice': 21000000000}, (5,), {}, { - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 - 'value': 0, 'gasPrice': 21000000000, 'chainId': 61, - }, False - ), - ( - {'nonce': 7}, (5,), {}, { - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 - 'value': 0, 'gasPrice': 1, 'nonce': 7, 'chainId': 61, - }, True - ), - ( - {'value': 20000}, (5,), {}, { - 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 - 'value': 20000, 'gasPrice': 1, 'chainId': 61, - }, False - ), - ), - ids=[ - 'Standard', - 'Explicit Gas', - 'Explicit Gas Price', - 'Explicit Nonce', - 'With Value', - ] -) -def test_build_transaction_with_contract_with_arguments(web3, skip_if_testrpc, math_contract, - transaction_args, - method_args, - method_kwargs, - expected, - skip_testrpc, - buildTransaction): - if skip_testrpc: - skip_if_testrpc(web3) - - txn = buildTransaction(contract=math_contract, - contract_function='increment', - func_args=method_args, - func_kwargs=method_kwargs, - tx_params=transaction_args) - expected['to'] = math_contract.address - assert txn is not None - if 'gas' in transaction_args: - assert txn['gas'] == transaction_args['gas'] - else: - assert 'gas' in txn - assert dissoc(txn, 'gas') == expected +# -*- coding: utf-8 -*- + +import pytest + +from web3._utils.toolz import ( + dissoc, +) +from web3.exceptions import ( + ValidationError, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +@pytest.fixture() +def math_contract(web3, MathContract, address_conversion_func): + deploy_txn = MathContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + math_contract_address = address_conversion_func(deploy_receipt['contractAddress']) + _math_contract = MathContract(address=math_contract_address) + assert _math_contract.address == math_contract_address + return _math_contract + + +@pytest.fixture() +def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): + deploy_txn = FallballFunctionContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + fallback_contract_address = address_conversion_func(deploy_receipt['contractAddress']) + _fallback_contract = FallballFunctionContract(address=fallback_contract_address) + assert _fallback_contract.address == fallback_contract_address + return _fallback_contract + + +@pytest.fixture() +def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): + deploy_txn = PayableTesterContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + payable_tester_address = address_conversion_func(deploy_receipt['contractAddress']) + _payable_tester = PayableTesterContract(address=payable_tester_address) + assert _payable_tester.address == payable_tester_address + return _payable_tester + + +def test_build_transaction_not_paying_to_nonpayable_function( + web3, + payable_tester_contract, + buildTransaction): + txn = buildTransaction(contract=payable_tester_contract, + contract_function='doNoValueCall') + assert dissoc(txn, 'gas') == { + 'to': payable_tester_contract.address, + 'data': '0xe4cb8f5c', + 'value': 0, + 'gasPrice': 1, + 'chainId': 1, + } + + +def test_build_transaction_paying_to_nonpayable_function( + web3, + payable_tester_contract, + buildTransaction): + with pytest.raises(ValidationError): + buildTransaction(contract=payable_tester_contract, + contract_function='doNoValueCall', + tx_params={'value': 1}) + + +def test_build_transaction_with_contract_no_arguments(web3, math_contract, buildTransaction): + txn = buildTransaction(contract=math_contract, contract_function='increment') + assert dissoc(txn, 'gas') == { + 'to': math_contract.address, + 'data': '0xd09de08a', + 'value': 0, + 'gasPrice': 1, + 'chainId': 1, + } + + +def test_build_transaction_with_contract_fallback_function(web3, fallback_function_contract): + txn = fallback_function_contract.fallback.buildTransaction() + assert dissoc(txn, 'gas') == { + 'to': fallback_function_contract.address, + 'data': '0x', + 'value': 0, + 'gasPrice': 1, + 'chainId': 1, + } + + +def test_build_transaction_with_contract_class_method( + web3, + MathContract, + math_contract, + buildTransaction): + txn = buildTransaction( + contract=MathContract, + contract_function='increment', + tx_params={'to': math_contract.address}, + ) + assert dissoc(txn, 'gas') == { + 'to': math_contract.address, + 'data': '0xd09de08a', + 'value': 0, + 'gasPrice': 1, + 'chainId': 1, + } + + +def test_build_transaction_with_contract_default_account_is_set( + web3, + math_contract, + buildTransaction): + txn = buildTransaction(contract=math_contract, contract_function='increment') + assert dissoc(txn, 'gas') == { + 'to': math_contract.address, + 'data': '0xd09de08a', + 'value': 0, + 'gasPrice': 1, + 'chainId': 1, + } + + +def test_build_transaction_with_gas_price_strategy_set(web3, math_contract, buildTransaction): + def my_gas_price_strategy(web3, transaction_params): + return 5 + web3.vns.setGasPriceStrategy(my_gas_price_strategy) + txn = buildTransaction(contract=math_contract, contract_function='increment') + assert dissoc(txn, 'gas') == { + 'to': math_contract.address, + 'data': '0xd09de08a', + 'value': 0, + 'gasPrice': 5, + 'chainId': 1, + } + + +def test_build_transaction_with_contract_data_supplied_errors(web3, + math_contract, + buildTransaction): + with pytest.raises(ValueError): + buildTransaction(contract=math_contract, + contract_function='increment', + tx_params={'data': '0x000'}) + + +def test_build_transaction_with_contract_to_address_supplied_errors(web3, + math_contract, + buildTransaction): + with pytest.raises(ValueError): + buildTransaction(contract=math_contract, + contract_function='increment', + tx_params={'to': '0xb2930B35844a230f00E51431aCAe96Fe543a0347'}) + + +@pytest.mark.parametrize( + 'transaction_args,method_args,method_kwargs,expected,skip_testrpc', + ( + ( + {}, (5,), {}, { + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 + 'value': 0, 'gasPrice': 1, 'chainId': 1, + }, False + ), + ( + {'gas': 800000}, (5,), {}, { + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 + 'value': 0, 'gasPrice': 1, 'chainId': 1, + }, False + ), + ( + {'gasPrice': 21000000000}, (5,), {}, { + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 + 'value': 0, 'gasPrice': 21000000000, 'chainId': 1, + }, False + ), + ( + {'nonce': 7}, (5,), {}, { + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 + 'value': 0, 'gasPrice': 1, 'nonce': 7, 'chainId': 1, + }, True + ), + ( + {'value': 20000}, (5,), {}, { + 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005', # noqa: E501 + 'value': 20000, 'gasPrice': 1, 'chainId': 1, + }, False + ), + ), + ids=[ + 'Standard', + 'Explicit Gas', + 'Explicit Gas Price', + 'Explicit Nonce', + 'With Value', + ] +) +def test_build_transaction_with_contract_with_arguments(web3, skip_if_testrpc, math_contract, + transaction_args, + method_args, + method_kwargs, + expected, + skip_testrpc, + buildTransaction): + if skip_testrpc: + skip_if_testrpc(web3) + + txn = buildTransaction(contract=math_contract, + contract_function='increment', + func_args=method_args, + func_kwargs=method_kwargs, + tx_params=transaction_args) + expected['to'] = math_contract.address + assert txn is not None + if 'gas' in transaction_args: + assert txn['gas'] == transaction_args['gas'] + else: + assert 'gas' in txn + assert dissoc(txn, 'gas') == expected diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index 221a272577..c09ce6a350 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -1,821 +1,750 @@ -from decimal import ( - Decimal, - getcontext, -) -from distutils.version import ( - LooseVersion, -) -import json -import pytest - -import eth_abi -from eth_utils import ( - is_text, -) -from eth_utils.toolz import ( - identity, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.ens import ( - contract_ens_addresses, -) -from web3.exceptions import ( - BadFunctionCallOutput, - BlockNumberOutofRange, - InvalidAddress, - MismatchedABI, - NoABIFound, - NoABIFunctionsFound, - ValidationError, -) - -# Ignore warning in pyethereum 1.6 - will go away with the upgrade -pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") - - -def deploy(web3, Contract, apply_func=identity, args=None): - args = args or [] - deploy_txn = Contract.constructor(*args).transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = apply_func(deploy_receipt['contractAddress']) - contract = Contract(address=address) - assert contract.address == address - assert len(web3.eth.getCode(contract.address)) > 0 - return contract - - -@pytest.fixture() -def address_reflector_contract(web3, AddressReflectorContract, address_conversion_func): - return deploy(web3, AddressReflectorContract, address_conversion_func) - - -@pytest.fixture() -def math_contract(web3, MathContract, address_conversion_func): - return deploy(web3, MathContract, address_conversion_func) - - -@pytest.fixture() -def string_contract(web3, StringContract, address_conversion_func): - return deploy(web3, StringContract, address_conversion_func, args=["Caqalai"]) - - -@pytest.fixture() -def arrays_contract(web3, ArraysContract, address_conversion_func): - # bytes_32 = [keccak('0'), keccak('1')] - bytes32_array = [ - b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 - b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 - ] - byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] - return deploy(web3, ArraysContract, address_conversion_func, args=[bytes32_array, byte_arr]) - - -@pytest.fixture() -def strict_arrays_contract(w3_strict_abi, StrictArraysContract, address_conversion_func): - # bytes_32 = [keccak('0'), keccak('1')] - bytes32_array = [ - b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 - b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 - ] - byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] - return deploy( - w3_strict_abi, - StrictArraysContract, - address_conversion_func, - args=[bytes32_array, byte_arr] - ) - - -@pytest.fixture() -def address_contract(web3, WithConstructorAddressArgumentsContract, address_conversion_func): - return deploy( - web3, - WithConstructorAddressArgumentsContract, - address_conversion_func, - args=["0xd3CdA913deB6f67967B99D67aCDFa1712C293601"] - ) - - -@pytest.fixture(params=[b'\x04\x06', '0x0406', '0406']) -def bytes_contract(web3, BytesContract, request, address_conversion_func): - if is_text(request.param) and request.param[:2] != '0x': - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the "0x" prefix' - ): - return deploy(web3, BytesContract, address_conversion_func, args=[request.param]) - else: - return deploy(web3, BytesContract, address_conversion_func, args=[request.param]) - - -@pytest.fixture() -def fixed_reflection_contract(web3, FixedReflectionContract, address_conversion_func): - return deploy(web3, FixedReflectionContract, address_conversion_func) - - -@pytest.fixture() -def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): - return deploy(web3, PayableTesterContract, address_conversion_func) - - -@pytest.fixture() -def call_transaction(): - return { - 'data': '0x61bc221a', - 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9' - } - - -@pytest.fixture(params=[ - '0x0406040604060406040604060406040604060406040604060406040604060406', - '0406040604060406040604060406040604060406040604060406040604060406', - HexBytes('0406040604060406040604060406040604060406040604060406040604060406'), -]) -def bytes32_contract(web3, Bytes32Contract, request, address_conversion_func): - if is_text(request.param) and request.param[:2] != '0x': - with pytest.warns(DeprecationWarning): - return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param]) - else: - return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param]) - - -@pytest.fixture() -def undeployed_math_contract(web3, MathContract, address_conversion_func): - empty_address = address_conversion_func("0x000000000000000000000000000000000000dEaD") - _undeployed_math_contract = MathContract(address=empty_address) - return _undeployed_math_contract - - -@pytest.fixture() -def mismatched_math_contract(web3, StringContract, MathContract, address_conversion_func): - deploy_txn = StringContract.constructor("Caqalai").transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _mismatched_math_contract = MathContract(address=address) - return _mismatched_math_contract - - -@pytest.fixture() -def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): - return deploy(web3, FallballFunctionContract, address_conversion_func) - - -@pytest.fixture() -def tuple_contract(web3, TupleContract, address_conversion_func): - return deploy(web3, TupleContract, address_conversion_func) - - -@pytest.fixture() -def nested_tuple_contract(web3, NestedTupleContract, address_conversion_func): - return deploy(web3, NestedTupleContract, address_conversion_func) - - -def test_invalid_address_in_deploy_arg(web3, WithConstructorAddressArgumentsContract): - with pytest.raises(InvalidAddress): - WithConstructorAddressArgumentsContract.constructor( - "0xd3cda913deb6f67967b99d67acdfa1712c293601", - ).transact() - - -def test_call_with_no_arguments(math_contract, call): - result = call(contract=math_contract, - contract_function='return13') - assert result == 13 - - -def test_call_with_one_argument(math_contract, call): - result = call(contract=math_contract, - contract_function='multiply7', - func_args=[3]) - assert result == 21 - - -@pytest.mark.parametrize( - 'call_args,call_kwargs', - ( - ((9, 7), {}), - ((9,), {'b': 7}), - (tuple(), {'a': 9, 'b': 7}), - ), -) -def test_call_with_multiple_arguments(math_contract, call, call_args, call_kwargs): - result = call(contract=math_contract, - contract_function='add', - func_args=call_args, - func_kwargs=call_kwargs) - assert result == 16 - - -@pytest.mark.parametrize( - 'call_args,call_kwargs', - ( - ((9, 7), {}), - ((9,), {'b': 7}), - (tuple(), {'a': 9, 'b': 7}), - ), -) -def test_saved_method_call_with_multiple_arguments(math_contract, call_args, call_kwargs): - math_contract_add = math_contract.functions.add(*call_args, **call_kwargs) - result = math_contract_add.call() - assert result == 16 - - -def test_call_get_string_value(string_contract, call): - result = call(contract=string_contract, - contract_function='getValue') - # eth_abi.decode_abi() does not assume implicit utf-8 - # encoding of string return values. Thus, we need to decode - # ourselves for fair comparison. - assert result == "Caqalai" - - -@pytest.mark.skipif( - LooseVersion(eth_abi.__version__) >= LooseVersion("2"), - reason="eth-abi >=2 does utf-8 string decoding") -def test_call_read_string_variable(string_contract, call): - result = call(contract=string_contract, - contract_function='constValue') - assert result == b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff".decode(errors='backslashreplace') # noqa: E501 - - -@pytest.mark.skipif( - LooseVersion(eth_abi.__version__) < LooseVersion("2"), - reason="eth-abi does not raise exception on undecodable bytestrings") -def test_call_on_undecodable_string(string_contract, call): - with pytest.raises(BadFunctionCallOutput): - call( - contract=string_contract, - contract_function='constValue') - - -def test_call_get_bytes32_array(arrays_contract, call): - result = call(contract=arrays_contract, - contract_function='getBytes32Value') - # expected_bytes32_array = [keccak('0'), keccak('1')] - expected_bytes32_array = [ - b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 - b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 - ] - assert result == expected_bytes32_array - - -def test_call_get_bytes32_const_array(arrays_contract, call): - result = call(contract=arrays_contract, - contract_function='getBytes32ConstValue') - # expected_bytes32_array = [keccak('A'), keccak('B')] - expected_bytes32_array = [ - b'\x03x?\xac.\xfe\xd8\xfb\xc9\xadD>Y.\xe3\x0ea\xd6_G\x11@\xc1\x0c\xa1U\xe97\xb45\xb7`', - b'\x1fg[\xff\x07Q_]\xf9g7\x19N\xa9E\xc3lA\xe7\xb4\xfc\xef0{|\xd4\xd0\xe6\x02\xa6\x91\x11', - ] - assert result == expected_bytes32_array - - -def test_call_get_byte_array(arrays_contract, call): - result = call(contract=arrays_contract, - contract_function='getByteValue') - expected_byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] - assert result == expected_byte_arr - - -@pytest.mark.parametrize('args,expected', [([b''], [b'\x00']), (['0x'], [b'\x00'])]) -def test_set_byte_array(arrays_contract, call, transact, args, expected): - transact( - contract=arrays_contract, - contract_function='setByteValue', - func_args=[args] - ) - result = call(contract=arrays_contract, - contract_function='getByteValue') - - assert result == expected - - -@pytest.mark.parametrize( - 'args,expected', [ - ([b'1'], [b'1']), - (['0xDe'], [b'\xDe']) - ] -) -def test_set_strict_byte_array(strict_arrays_contract, call, transact, args, expected): - transact( - contract=strict_arrays_contract, - contract_function='setByteValue', - func_args=[args] - ) - result = call(contract=strict_arrays_contract, - contract_function='getByteValue') - - assert result == expected - - -@pytest.mark.parametrize('args', ([''], ['s'])) -def test_set_strict_byte_array_with_invalid_args(strict_arrays_contract, transact, args): - with pytest.raises(ValidationError): - transact( - contract=strict_arrays_contract, - contract_function='setByteValue', - func_args=[args] - ) - - -def test_call_get_byte_const_array(arrays_contract, call): - result = call(contract=arrays_contract, - contract_function='getByteConstValue') - expected_byte_arr = [b'\x00', b'\x01'] - assert result == expected_byte_arr - - -def test_call_read_address_variable(address_contract, call): - result = call(contract=address_contract, - contract_function='testAddr') - assert result == "0xd3CdA913deB6f67967B99D67aCDFa1712C293601" - - -def test_init_with_ens_name_arg(web3, WithConstructorAddressArgumentsContract, call): - with contract_ens_addresses( - WithConstructorAddressArgumentsContract, - [("arg-name.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], - ): - address_contract = deploy(web3, WithConstructorAddressArgumentsContract, args=[ - "arg-name.eth", - ]) - - result = call(contract=address_contract, - contract_function='testAddr') - assert result == "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413" - - -def test_call_read_bytes_variable(bytes_contract, call): - result = call(contract=bytes_contract, contract_function='constValue') - assert result == b"\x01\x23" - - -def test_call_get_bytes_value(bytes_contract, call): - result = call(contract=bytes_contract, contract_function='getValue') - assert result == b'\x04\x06' - - -def test_call_read_bytes32_variable(bytes32_contract, call): - result = call(contract=bytes32_contract, contract_function='constValue') - assert result == b"\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23" # noqa - - -def test_call_get_bytes32_value(bytes32_contract, call): - result = call(contract=bytes32_contract, contract_function='getValue') - assert result == b'\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06' # noqa - - -@pytest.mark.parametrize( - 'value, expected', - [ - ( - '0x' + '11' * 20, - '0x' + '11' * 20, - ), - ( - '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', - InvalidAddress, - ), - ( - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - ), - ] -) -def test_call_address_reflector_with_address(address_reflector_contract, value, expected, call): - if not isinstance(expected, str): - with pytest.raises(expected): - call(contract=address_reflector_contract, - contract_function='reflect', - func_args=[value]) - else: - assert call(contract=address_reflector_contract, - contract_function='reflect', - func_args=[value]) == expected - - -@pytest.mark.parametrize( - 'value, expected', - [ - ( - ['0x' + '11' * 20, '0x' + '22' * 20], - ['0x' + '11' * 20, '0x' + '22' * 20], - ), - ( - ['0x' + '11' * 20, '0x' + 'aa' * 20], - InvalidAddress - ), - ( - [ - '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - ], - [ - '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - ], - ), - ] -) -def test_call_address_list_reflector_with_address(address_reflector_contract, - value, - expected, - call): - if not isinstance(expected, list): - with pytest.raises(expected): - call(contract=address_reflector_contract, - contract_function='reflect', - func_args=[value]) - else: - assert call(contract=address_reflector_contract, - contract_function='reflect', - func_args=[value]) == expected - - -def test_call_address_reflector_single_name(address_reflector_contract, call): - with contract_ens_addresses( - address_reflector_contract, - [("dennisthepeasant.eth", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], - ): - result = call(contract=address_reflector_contract, - contract_function='reflect', - func_args=['dennisthepeasant.eth']) - assert result == '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413' - - -def test_call_address_reflector_name_array(address_reflector_contract, call): - names = [ - 'autonomouscollective.eth', - 'wedonthavealord.eth', - ] - addresses = [ - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', - ] - - with contract_ens_addresses(address_reflector_contract, zip(names, addresses)): - result = call(contract=address_reflector_contract, - contract_function='reflect', - func_args=[names]) - - assert addresses == result - - -def test_call_reject_invalid_ens_name(address_reflector_contract, call): - with contract_ens_addresses(address_reflector_contract, []): - with pytest.raises(ValueError): - call(contract=address_reflector_contract, - contract_function='reflect', - func_args=['type0.eth']) - - -def test_call_missing_function(mismatched_math_contract, call): - expected_missing_function_error_message = "Could not decode contract function call" - with pytest.raises(BadFunctionCallOutput) as exception_info: - call(contract=mismatched_math_contract, contract_function='return13') - assert expected_missing_function_error_message in str(exception_info.value) - - -def test_call_undeployed_contract(undeployed_math_contract, call): - expected_undeployed_call_error_message = "Could not transact with/call contract function" - with pytest.raises(BadFunctionCallOutput) as exception_info: - call(contract=undeployed_math_contract, contract_function='return13') - assert expected_undeployed_call_error_message in str(exception_info.value) - - -def test_call_fallback_function(fallback_function_contract): - result = fallback_function_contract.fallback.call() - assert result == [] - - -def test_throws_error_if_block_out_of_range(web3, math_contract): - web3.provider.make_request(method='evm_mine', params=[20]) - with pytest.raises(BlockNumberOutofRange): - math_contract.functions.counter().call(block_identifier=-50) - - -def test_accepts_latest_block(web3, math_contract): - web3.provider.make_request(method='evm_mine', params=[5]) - math_contract.functions.increment().transact() - - late = math_contract.functions.counter().call(block_identifier='latest') - pend = math_contract.functions.counter().call(block_identifier='pending') - - assert late == 1 - assert pend == 1 - - -def test_accepts_block_hash_as_identifier(web3, math_contract): - blocks = web3.provider.make_request(method='evm_mine', params=[5]) - math_contract.functions.increment().transact() - more_blocks = web3.provider.make_request(method='evm_mine', params=[5]) - - old = math_contract.functions.counter().call(block_identifier=blocks['result'][2]) - new = math_contract.functions.counter().call(block_identifier=more_blocks['result'][2]) - - assert old == 0 - assert new == 1 - - -def test_neg_block_indexes_from_the_end(web3, math_contract): - web3.provider.make_request(method='evm_mine', params=[5]) - math_contract.functions.increment().transact() - math_contract.functions.increment().transact() - web3.provider.make_request(method='evm_mine', params=[5]) - - output1 = math_contract.functions.counter().call(block_identifier=-7) - output2 = math_contract.functions.counter().call(block_identifier=-6) - - assert output1 == 1 - assert output2 == 2 - - -def test_returns_data_from_specified_block(web3, math_contract): - start_num = web3.eth.getBlock('latest').number - web3.provider.make_request(method='evm_mine', params=[5]) - math_contract.functions.increment().transact() - math_contract.functions.increment().transact() - - output1 = math_contract.functions.counter().call(block_identifier=start_num + 6) - output2 = math_contract.functions.counter().call(block_identifier=start_num + 7) - - assert output1 == 1 - assert output2 == 2 - - -message_regex = ( - r"\nCould not identify the intended function with name `.*`, " - r"positional argument\(s\) of type `.*` and " - r"keyword argument\(s\) of type `.*`." - r"\nFound .* function\(s\) with the name `.*`: .*" -) -diagnosis_arg_regex = ( - r"\nFunction invocation failed due to improper number of arguments." -) -diagnosis_encoding_regex = ( - r"\nFunction invocation failed due to no matching argument types." -) -diagnosis_ambiguous_encoding = ( - r"\nAmbiguous argument encoding. " - r"Provided arguments can be encoded to multiple functions matching this call." -) - - -def test_no_functions_match_identifier(arrays_contract): - with pytest.raises(MismatchedABI): - arrays_contract.functions.thisFunctionDoesNotExist().call() - - -def test_function_1_match_identifier_wrong_number_of_args(arrays_contract): - regex = message_regex + diagnosis_arg_regex - with pytest.raises(ValidationError, match=regex): - arrays_contract.functions.setBytes32Value().call() - - -def test_function_1_match_identifier_wrong_args_encoding(arrays_contract): - regex = message_regex + diagnosis_encoding_regex - with pytest.raises(ValidationError, match=regex): - arrays_contract.functions.setBytes32Value('dog').call() - - -def test_function_multiple_match_identifiers_no_correct_number_of_args(web3): - MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - regex = message_regex + diagnosis_arg_regex - with pytest.raises(ValidationError, match=regex): - Contract.functions.a(100, 'dog').call() - - -def test_function_multiple_match_identifiers_no_correct_encoding_of_args(web3): - MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - regex = message_regex + diagnosis_encoding_regex - with pytest.raises(ValidationError, match=regex): - Contract.functions.a('dog').call() - - -def test_function_multiple_possible_encodings(web3): - MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - regex = message_regex + diagnosis_ambiguous_encoding - with pytest.raises(ValidationError, match=regex): - Contract.functions.a(100).call() - - -def test_function_no_abi(web3): - contract = web3.eth.contract() - with pytest.raises(NoABIFound): - contract.functions.thisFunctionDoesNotExist().call() - - -def test_call_abi_no_functions(web3): - contract = web3.eth.contract(abi=[]) - with pytest.raises(NoABIFunctionsFound): - contract.functions.thisFunctionDoesNotExist().call() - - -def test_call_not_sending_ether_to_nonpayable_function(payable_tester_contract, call): - result = call(contract=payable_tester_contract, - contract_function='doNoValueCall') - assert result == [] - - -def test_call_sending_ether_to_nonpayable_function(payable_tester_contract, call): - with pytest.raises(ValidationError): - call(contract=payable_tester_contract, - contract_function='doNoValueCall', - tx_params={'value': 1}) - - -@pytest.mark.parametrize( - 'function, value', - ( - # minimum positive unambiguous value (larger than fixed8x1) - ('reflect', Decimal('12.8')), - # maximum value (for ufixed256x1) - ('reflect', Decimal(2 ** 256 - 1) / 10), - # maximum negative unambiguous value (less than 0 from ufixed*) - ('reflect', Decimal('-0.1')), - # minimum value (for fixed8x1) - ('reflect', Decimal('-12.8')), - # only ufixed256x80 type supports 2-80 decimals - ('reflect', Decimal(2 ** 256 - 1) / 10 ** 80), # maximum allowed value - ('reflect', Decimal(1) / 10 ** 80), # smallest non-zero value - # minimum value (for ufixed8x1) - ('reflect_short_u', 0), - # maximum value (for ufixed8x1) - ('reflect_short_u', Decimal('25.5')), - ), -) -def test_reflect_fixed_value(web3, fixed_reflection_contract, function, value): - contract_func = fixed_reflection_contract.functions[function] - reflected = contract_func(value).call({'gas': 420000}) - assert reflected == value - - -DEFAULT_DECIMALS = getcontext().prec - - -@pytest.mark.parametrize( - 'function, value, error', - ( - # out of range - ('reflect_short_u', Decimal('25.6'), "no matching argument types"), - ('reflect_short_u', Decimal('-.1'), "no matching argument types"), - # too many digits for *x1, too large for 256x80 - ('reflect', Decimal('0.01'), "no matching argument types"), - - # too many digits - ('reflect_short_u', Decimal('0.01'), "no matching argument types"), - ( - 'reflect_short_u', - Decimal('1e-%d' % (DEFAULT_DECIMALS + 1)), - "no matching argument types", - ), - ('reflect_short_u', Decimal('25.4' + '9' * DEFAULT_DECIMALS), "no matching argument types"), - ('reflect', Decimal(1) / 10 ** 81, "no matching argument types"), - - # floats not accepted, for floating point error concerns - ('reflect_short_u', 0.1, "no matching argument types"), - - # ambiguous - ('reflect', Decimal('12.7'), "Ambiguous argument encoding"), - ('reflect', Decimal(0), "Ambiguous argument encoding"), - ('reflect', 0, "Ambiguous argument encoding"), - ), -) -def test_invalid_fixed_value_reflections(web3, fixed_reflection_contract, function, value, error): - contract_func = fixed_reflection_contract.functions[function] - with pytest.raises(ValidationError, match=error): - contract_func(value).call({'gas': 420000}) - - -@pytest.mark.parametrize( - 'method_input, expected', - ( - ( - {'a': 123, 'b': [1, 2], 'c': [ - {'x': 234, 'y': [True, False], 'z': [ - '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - ]}, - {'x': 345, 'y': [False, False], 'z': [ - '0xefd1FF70c185A1C0b125939815225199079096Ee', - '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', - ]}, - ]}, - (123, [1, 2], [ - (234, [True, False], [ - '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - ]), - (345, [False, False], [ - '0xefd1FF70c185A1C0b125939815225199079096Ee', - '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', - ]), - ]), - ), - ( - (123, [1, 2], [ - (234, [True, False], [ - '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - ]), - (345, [False, False], [ - '0xefd1FF70c185A1C0b125939815225199079096Ee', - '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', - ]), - ]), - (123, [1, 2], [ - (234, [True, False], [ - '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', - ]), - (345, [False, False], [ - '0xefd1FF70c185A1C0b125939815225199079096Ee', - '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', - ]), - ]), - ), - ), -) -def test_call_tuple_contract(tuple_contract, method_input, expected): - result = tuple_contract.functions.method(method_input).call() - assert result == expected - - -@pytest.mark.parametrize( - 'method_input, expected', - ( - ( - {'t': [ - {'u': [ - {'x': 1, 'y': 2}, - {'x': 3, 'y': 4}, - {'x': 5, 'y': 6}, - ]}, - {'u': [ - {'x': 7, 'y': 8}, - {'x': 9, 'y': 10}, - {'x': 11, 'y': 12}, - ]}, - ]}, - ( - [ - ([ - (1, 2), - (3, 4), - (5, 6), - ],), - ([ - (7, 8), - (9, 10), - (11, 12), - ],), - ], - ), - ), - ( - ( - [ - ([ - (1, 2), - (3, 4), - (5, 6), - ],), - ([ - (7, 8), - (9, 10), - (11, 12), - ],), - ], - ), - ( - [ - ([ - (1, 2), - (3, 4), - (5, 6), - ],), - ([ - (7, 8), - (9, 10), - (11, 12), - ],), - ], - ), - ), - ), -) -def test_call_nested_tuple_contract(nested_tuple_contract, method_input, expected): - result = nested_tuple_contract.functions.method(method_input).call() - assert result == expected +from decimal import ( + Decimal, + getcontext, +) +from distutils.version import ( + LooseVersion, +) +import json +import pytest + +import vns_abi +from hexbytes import ( + HexBytes, +) + +from web3._utils.ens import ( + contract_ens_addresses, +) +from web3._utils.toolz import ( + identity, +) +from web3.exceptions import ( + BadFunctionCallOutput, + BlockNumberOutofRange, + InvalidAddress, + MismatchedABI, + NoABIFound, + NoABIFunctionsFound, + ValidationError, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +def deploy(web3, Contract, apply_func=identity, args=None): + args = args or [] + deploy_txn = Contract.constructor(*args).transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = apply_func(deploy_receipt['contractAddress']) + contract = Contract(address=address) + assert contract.address == address + assert len(web3.vns.getCode(contract.address)) > 0 + return contract + + +@pytest.fixture() +def address_reflector_contract(web3, AddressReflectorContract, address_conversion_func): + return deploy(web3, AddressReflectorContract, address_conversion_func) + + +@pytest.fixture() +def math_contract(web3, MathContract, address_conversion_func): + return deploy(web3, MathContract, address_conversion_func) + + +@pytest.fixture() +def string_contract(web3, StringContract, address_conversion_func): + return deploy(web3, StringContract, address_conversion_func, args=["Caqalai"]) + + +@pytest.fixture() +def arrays_contract(web3, ArraysContract, address_conversion_func): + # bytes_32 = [keccak('0'), keccak('1')] + bytes32_array = [ + b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 + b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 + ] + byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] + return deploy(web3, ArraysContract, address_conversion_func, args=[bytes32_array, byte_arr]) + + +@pytest.fixture() +def address_contract(web3, WithConstructorAddressArgumentsContract, address_conversion_func): + return deploy( + web3, + WithConstructorAddressArgumentsContract, + address_conversion_func, + args=["0xd3CdA913deB6f67967B99D67aCDFa1712C293601"] + ) + + +@pytest.fixture(params=[b'\x04\x06', '0x0406', '0406']) +def bytes_contract(web3, BytesContract, request, address_conversion_func): + return deploy(web3, BytesContract, address_conversion_func, args=[request.param]) + + +@pytest.fixture() +def fixed_reflection_contract(web3, FixedReflectionContract, address_conversion_func): + return deploy(web3, FixedReflectionContract, address_conversion_func) + + +@pytest.fixture() +def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): + return deploy(web3, PayableTesterContract, address_conversion_func) + + +@pytest.fixture() +def call_transaction(): + return { + 'data': '0x61bc221a', + 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9' + } + + +@pytest.fixture(params=[ + '0x0406040604060406040604060406040604060406040604060406040604060406', + '0406040604060406040604060406040604060406040604060406040604060406', + HexBytes('0406040604060406040604060406040604060406040604060406040604060406'), +]) +def bytes32_contract(web3, Bytes32Contract, request, address_conversion_func): + return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param]) + + +@pytest.fixture() +def undeployed_math_contract(web3, MathContract, address_conversion_func): + empty_address = address_conversion_func("0x000000000000000000000000000000000000dEaD") + _undeployed_math_contract = MathContract(address=empty_address) + return _undeployed_math_contract + + +@pytest.fixture() +def mismatched_math_contract(web3, StringContract, MathContract, address_conversion_func): + deploy_txn = StringContract.constructor("Caqalai").transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _mismatched_math_contract = MathContract(address=address) + return _mismatched_math_contract + + +@pytest.fixture() +def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): + return deploy(web3, FallballFunctionContract, address_conversion_func) + + +@pytest.fixture() +def tuple_contract(web3, TupleContract, address_conversion_func): + return deploy(web3, TupleContract, address_conversion_func) + + +@pytest.fixture() +def nested_tuple_contract(web3, NestedTupleContract, address_conversion_func): + return deploy(web3, NestedTupleContract, address_conversion_func) + + +def test_invalid_address_in_deploy_arg(web3, WithConstructorAddressArgumentsContract): + with pytest.raises(InvalidAddress): + WithConstructorAddressArgumentsContract.constructor( + "0xd3cda913deb6f67967b99d67acdfa1712c293601", + ).transact() + + +def test_call_with_no_arguments(math_contract, call): + result = call(contract=math_contract, + contract_function='return13') + assert result == 13 + + +def test_call_with_one_argument(math_contract, call): + result = call(contract=math_contract, + contract_function='multiply7', + func_args=[3]) + assert result == 21 + + +@pytest.mark.parametrize( + 'call_args,call_kwargs', + ( + ((9, 7), {}), + ((9,), {'b': 7}), + (tuple(), {'a': 9, 'b': 7}), + ), +) +def test_call_with_multiple_arguments(math_contract, call, call_args, call_kwargs): + result = call(contract=math_contract, + contract_function='add', + func_args=call_args, + func_kwargs=call_kwargs) + assert result == 16 + + +@pytest.mark.parametrize( + 'call_args,call_kwargs', + ( + ((9, 7), {}), + ((9,), {'b': 7}), + (tuple(), {'a': 9, 'b': 7}), + ), +) +def test_saved_method_call_with_multiple_arguments(math_contract, call_args, call_kwargs): + math_contract_add = math_contract.functions.add(*call_args, **call_kwargs) + result = math_contract_add.call() + assert result == 16 + + +def test_call_get_string_value(string_contract, call): + result = call(contract=string_contract, + contract_function='getValue') + # vns_abi.decode_abi() does not assume implicit utf-8 + # encoding of string return values. Thus, we need to decode + # ourselves for fair comparison. + assert result == "Caqalai" + + +@pytest.mark.skipif( + LooseVersion(vns_abi.__version__) >= LooseVersion("2"), + reason="vns-abi >=2 does utf-8 string decoding") +def test_call_read_string_variable(string_contract, call): + result = call(contract=string_contract, + contract_function='constValue') + assert result == b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff".decode(errors='backslashreplace') # noqa: E501 + + +@pytest.mark.skipif( + LooseVersion(vns_abi.__version__) < LooseVersion("2"), + reason="vns-abi does not raise exception on undecodable bytestrings") +def test_call_on_undecodable_string(string_contract, call): + with pytest.raises(BadFunctionCallOutput): + call( + contract=string_contract, + contract_function='constValue') + + +def test_call_get_bytes32_array(arrays_contract, call): + result = call(contract=arrays_contract, + contract_function='getBytes32Value') + # expected_bytes32_array = [keccak('0'), keccak('1')] + expected_bytes32_array = [ + b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 + b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 + ] + assert result == expected_bytes32_array + + +def test_call_get_bytes32_const_array(arrays_contract, call): + result = call(contract=arrays_contract, + contract_function='getBytes32ConstValue') + # expected_bytes32_array = [keccak('A'), keccak('B')] + expected_bytes32_array = [ + b'\x03x?\xac.\xfe\xd8\xfb\xc9\xadD>Y.\xe3\x0ea\xd6_G\x11@\xc1\x0c\xa1U\xe97\xb45\xb7`', + b'\x1fg[\xff\x07Q_]\xf9g7\x19N\xa9E\xc3lA\xe7\xb4\xfc\xef0{|\xd4\xd0\xe6\x02\xa6\x91\x11', + ] + assert result == expected_bytes32_array + + +def test_call_get_byte_array(arrays_contract, call): + result = call(contract=arrays_contract, + contract_function='getByteValue') + expected_byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] + assert result == expected_byte_arr + + +def test_call_get_byte_const_array(arrays_contract, call): + result = call(contract=arrays_contract, + contract_function='getByteConstValue') + expected_byte_arr = [b'\x00', b'\x01'] + assert result == expected_byte_arr + + +def test_call_read_address_variable(address_contract, call): + result = call(contract=address_contract, + contract_function='testAddr') + assert result == "0xd3CdA913deB6f67967B99D67aCDFa1712C293601" + + +def test_init_with_ens_name_arg(web3, WithConstructorAddressArgumentsContract, call): + with contract_ens_addresses( + WithConstructorAddressArgumentsContract, + [("arg-name.vns", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], + ): + address_contract = deploy(web3, WithConstructorAddressArgumentsContract, args=[ + "arg-name.vns", + ]) + + result = call(contract=address_contract, + contract_function='testAddr') + assert result == "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413" + + +def test_call_read_bytes_variable(bytes_contract, call): + result = call(contract=bytes_contract, contract_function='constValue') + assert result == b"\x01\x23" + + +def test_call_get_bytes_value(bytes_contract, call): + result = call(contract=bytes_contract, contract_function='getValue') + assert result == b'\x04\x06' + + +def test_call_read_bytes32_variable(bytes32_contract, call): + result = call(contract=bytes32_contract, contract_function='constValue') + assert result == b"\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23\x01\x23" # noqa + + +def test_call_get_bytes32_value(bytes32_contract, call): + result = call(contract=bytes32_contract, contract_function='getValue') + assert result == b'\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06\x04\x06' # noqa + + +@pytest.mark.parametrize( + 'value, expected', + [ + ( + '0x' + '11' * 20, + '0x' + '11' * 20, + ), + ( + '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', + InvalidAddress, + ), + ( + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + ), + ] +) +def test_call_address_reflector_with_address(address_reflector_contract, value, expected, call): + if not isinstance(expected, str): + with pytest.raises(expected): + call(contract=address_reflector_contract, + contract_function='reflect', + func_args=[value]) + else: + assert call(contract=address_reflector_contract, + contract_function='reflect', + func_args=[value]) == expected + + +@pytest.mark.parametrize( + 'value, expected', + [ + ( + ['0x' + '11' * 20, '0x' + '22' * 20], + ['0x' + '11' * 20, '0x' + '22' * 20], + ), + ( + ['0x' + '11' * 20, '0x' + 'aa' * 20], + InvalidAddress + ), + ( + [ + '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + ], + [ + '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + ], + ), + ] +) +def test_call_address_list_reflector_with_address(address_reflector_contract, + value, + expected, + call): + if not isinstance(expected, list): + with pytest.raises(expected): + call(contract=address_reflector_contract, + contract_function='reflect', + func_args=[value]) + else: + assert call(contract=address_reflector_contract, + contract_function='reflect', + func_args=[value]) == expected + + +def test_call_address_reflector_single_name(address_reflector_contract, call): + with contract_ens_addresses( + address_reflector_contract, + [("dennisthepeasant.vns", "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")], + ): + result = call(contract=address_reflector_contract, + contract_function='reflect', + func_args=['dennisthepeasant.vns']) + assert result == '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413' + + +def test_call_address_reflector_name_array(address_reflector_contract, call): + names = [ + 'autonomouscollective.vns', + 'wedonthavealord.vns', + ] + addresses = [ + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4', + ] + + with contract_ens_addresses(address_reflector_contract, zip(names, addresses)): + result = call(contract=address_reflector_contract, + contract_function='reflect', + func_args=[names]) + + assert addresses == result + + +def test_call_reject_invalid_ens_name(address_reflector_contract, call): + with contract_ens_addresses(address_reflector_contract, []): + with pytest.raises(ValueError): + call(contract=address_reflector_contract, + contract_function='reflect', + func_args=['type0.vns']) + + +def test_call_missing_function(mismatched_math_contract, call): + expected_missing_function_error_message = "Could not decode contract function call" + with pytest.raises(BadFunctionCallOutput) as exception_info: + call(contract=mismatched_math_contract, contract_function='return13') + assert expected_missing_function_error_message in str(exception_info.value) + + +def test_call_undeployed_contract(undeployed_math_contract, call): + expected_undeployed_call_error_message = "Could not transact with/call contract function" + with pytest.raises(BadFunctionCallOutput) as exception_info: + call(contract=undeployed_math_contract, contract_function='return13') + assert expected_undeployed_call_error_message in str(exception_info.value) + + +def test_call_fallback_function(fallback_function_contract): + result = fallback_function_contract.fallback.call() + assert result == [] + + +def test_throws_error_if_block_out_of_range(web3, math_contract): + web3.provider.make_request(method='evm_mine', params=[20]) + with pytest.raises(BlockNumberOutofRange): + math_contract.functions.counter().call(block_identifier=-50) + + +def test_accepts_latest_block(web3, math_contract): + web3.provider.make_request(method='evm_mine', params=[5]) + math_contract.functions.increment().transact() + + late = math_contract.functions.counter().call(block_identifier='latest') + pend = math_contract.functions.counter().call(block_identifier='pending') + + assert late == 1 + assert pend == 1 + + +def test_accepts_block_hash_as_identifier(web3, math_contract): + blocks = web3.provider.make_request(method='evm_mine', params=[5]) + math_contract.functions.increment().transact() + more_blocks = web3.provider.make_request(method='evm_mine', params=[5]) + + old = math_contract.functions.counter().call(block_identifier=blocks['result'][2]) + new = math_contract.functions.counter().call(block_identifier=more_blocks['result'][2]) + + assert old == 0 + assert new == 1 + + +def test_neg_block_indexes_from_the_end(web3, math_contract): + web3.provider.make_request(method='evm_mine', params=[5]) + math_contract.functions.increment().transact() + math_contract.functions.increment().transact() + web3.provider.make_request(method='evm_mine', params=[5]) + + output1 = math_contract.functions.counter().call(block_identifier=-7) + output2 = math_contract.functions.counter().call(block_identifier=-6) + + assert output1 == 1 + assert output2 == 2 + + +def test_returns_data_from_specified_block(web3, math_contract): + start_num = web3.vns.getBlock('latest').number + web3.provider.make_request(method='evm_mine', params=[5]) + math_contract.functions.increment().transact() + math_contract.functions.increment().transact() + + output1 = math_contract.functions.counter().call(block_identifier=start_num + 6) + output2 = math_contract.functions.counter().call(block_identifier=start_num + 7) + + assert output1 == 1 + assert output2 == 2 + + +message_regex = ( + r"\nCould not identify the intended function with name `.*`, " + r"positional argument\(s\) of type `.*` and " + r"keyword argument\(s\) of type `.*`." + r"\nFound .* function\(s\) with the name `.*`: .*" +) +diagnosis_arg_regex = ( + r"\nFunction invocation failed due to improper number of arguments." +) +diagnosis_encoding_regex = ( + r"\nFunction invocation failed due to no matching argument types." +) +diagnosis_ambiguous_encoding = ( + r"\nAmbiguous argument encoding. " + r"Provided arguments can be encoded to multiple functions matching this call." +) + + +def test_no_functions_match_identifier(arrays_contract): + with pytest.raises(MismatchedABI): + arrays_contract.functions.thisFunctionDoesNotExist().call() + + +def test_function_1_match_identifier_wrong_number_of_args(arrays_contract): + regex = message_regex + diagnosis_arg_regex + with pytest.raises(ValidationError, match=regex): + arrays_contract.functions.setBytes32Value().call() + + +def test_function_1_match_identifier_wrong_args_encoding(arrays_contract): + regex = message_regex + diagnosis_encoding_regex + with pytest.raises(ValidationError, match=regex): + arrays_contract.functions.setBytes32Value('dog').call() + + +def test_function_multiple_match_identifiers_no_correct_number_of_args(web3): + MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 + Contract = web3.vns.contract(abi=MULTIPLE_FUNCTIONS) + regex = message_regex + diagnosis_arg_regex + with pytest.raises(ValidationError, match=regex): + Contract.functions.a(100, 'dog').call() + + +def test_function_multiple_match_identifiers_no_correct_encoding_of_args(web3): + MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 + Contract = web3.vns.contract(abi=MULTIPLE_FUNCTIONS) + regex = message_regex + diagnosis_encoding_regex + with pytest.raises(ValidationError, match=regex): + Contract.functions.a('dog').call() + + +def test_function_multiple_possible_encodings(web3): + MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint8"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"int8"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 + Contract = web3.vns.contract(abi=MULTIPLE_FUNCTIONS) + regex = message_regex + diagnosis_ambiguous_encoding + with pytest.raises(ValidationError, match=regex): + Contract.functions.a(100).call() + + +def test_function_no_abi(web3): + contract = web3.vns.contract() + with pytest.raises(NoABIFound): + contract.functions.thisFunctionDoesNotExist().call() + + +def test_call_abi_no_functions(web3): + contract = web3.vns.contract(abi=[]) + with pytest.raises(NoABIFunctionsFound): + contract.functions.thisFunctionDoesNotExist().call() + + +def test_call_not_sending_ether_to_nonpayable_function(payable_tester_contract, call): + result = call(contract=payable_tester_contract, + contract_function='doNoValueCall') + assert result == [] + + +def test_call_sending_ether_to_nonpayable_function(payable_tester_contract, call): + with pytest.raises(ValidationError): + call(contract=payable_tester_contract, + contract_function='doNoValueCall', + tx_params={'value': 1}) + + +@pytest.mark.parametrize( + 'function, value', + ( + # minimum positive unambiguous value (larger than fixed8x1) + ('reflect', Decimal('12.8')), + # maximum value (for ufixed256x1) + ('reflect', Decimal(2 ** 256 - 1) / 10), + # maximum negative unambiguous value (less than 0 from ufixed*) + ('reflect', Decimal('-0.1')), + # minimum value (for fixed8x1) + ('reflect', Decimal('-12.8')), + # only ufixed256x80 type supports 2-80 decimals + ('reflect', Decimal(2 ** 256 - 1) / 10 ** 80), # maximum allowed value + ('reflect', Decimal(1) / 10 ** 80), # smallest non-zero value + # minimum value (for ufixed8x1) + ('reflect_short_u', 0), + # maximum value (for ufixed8x1) + ('reflect_short_u', Decimal('25.5')), + ), +) +def test_reflect_fixed_value(web3, fixed_reflection_contract, function, value): + contract_func = fixed_reflection_contract.functions[function] + reflected = contract_func(value).call({'gas': 420000}) + assert reflected == value + + +DEFAULT_DECIMALS = getcontext().prec + + +@pytest.mark.parametrize( + 'function, value, error', + ( + # out of range + ('reflect_short_u', Decimal('25.6'), "no matching argument types"), + ('reflect_short_u', Decimal('-.1'), "no matching argument types"), + # too many digits for *x1, too large for 256x80 + ('reflect', Decimal('0.01'), "no matching argument types"), + + # too many digits + ('reflect_short_u', Decimal('0.01'), "no matching argument types"), + ( + 'reflect_short_u', + Decimal('1e-%d' % (DEFAULT_DECIMALS + 1)), + "no matching argument types", + ), + ('reflect_short_u', Decimal('25.4' + '9' * DEFAULT_DECIMALS), "no matching argument types"), + ('reflect', Decimal(1) / 10 ** 81, "no matching argument types"), + + # floats not accepted, for floating point error concerns + ('reflect_short_u', 0.1, "no matching argument types"), + + # ambiguous + ('reflect', Decimal('12.7'), "Ambiguous argument encoding"), + ('reflect', Decimal(0), "Ambiguous argument encoding"), + ('reflect', 0, "Ambiguous argument encoding"), + ), +) +def test_invalid_fixed_value_reflections(web3, fixed_reflection_contract, function, value, error): + contract_func = fixed_reflection_contract.functions[function] + with pytest.raises(ValidationError, match=error): + contract_func(value).call({'gas': 420000}) + + +@pytest.mark.parametrize( + 'method_input, expected', + ( + ( + {'a': 123, 'b': [1, 2], 'c': [ + {'x': 234, 'y': [True, False], 'z': [ + '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + ]}, + {'x': 345, 'y': [False, False], 'z': [ + '0xefd1FF70c185A1C0b125939815225199079096Ee', + '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', + ]}, + ]}, + (123, [1, 2], [ + (234, [True, False], [ + '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + ]), + (345, [False, False], [ + '0xefd1FF70c185A1C0b125939815225199079096Ee', + '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', + ]), + ]), + ), + ( + (123, [1, 2], [ + (234, [True, False], [ + '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + ]), + (345, [False, False], [ + '0xefd1FF70c185A1C0b125939815225199079096Ee', + '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', + ]), + ]), + (123, [1, 2], [ + (234, [True, False], [ + '0x4AD7E79d88650B01EEA2B1f069f01EE9db343d5c', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + '0xfdF1946A9b40245224488F1a36f4A9ed4844a523', + ]), + (345, [False, False], [ + '0xefd1FF70c185A1C0b125939815225199079096Ee', + '0xf35C0784794F3Cd935F5754d3a0EbcE95bEf851e', + ]), + ]), + ), + ), +) +def test_call_tuple_contract(tuple_contract, method_input, expected): + result = tuple_contract.functions.method(method_input).call() + assert result == expected + + +@pytest.mark.parametrize( + 'method_input, expected', + ( + ( + {'t': [ + {'u': [ + {'x': 1, 'y': 2}, + {'x': 3, 'y': 4}, + {'x': 5, 'y': 6}, + ]}, + {'u': [ + {'x': 7, 'y': 8}, + {'x': 9, 'y': 10}, + {'x': 11, 'y': 12}, + ]}, + ]}, + ( + [ + ([ + (1, 2), + (3, 4), + (5, 6), + ],), + ([ + (7, 8), + (9, 10), + (11, 12), + ],), + ], + ), + ), + ( + ( + [ + ([ + (1, 2), + (3, 4), + (5, 6), + ],), + ([ + (7, 8), + (9, 10), + (11, 12), + ],), + ], + ), + ( + [ + ([ + (1, 2), + (3, 4), + (5, 6), + ],), + ([ + (7, 8), + (9, 10), + (11, 12), + ],), + ], + ), + ), + ), +) +def test_call_nested_tuple_contract(nested_tuple_contract, method_input, expected): + result = nested_tuple_contract.functions.method(method_input).call() + assert result == expected diff --git a/tests/core/contracts/test_contract_caller_interface.py b/tests/core/contracts/test_contract_caller_interface.py index b73d4150e2..8c6d19cdb2 100644 --- a/tests/core/contracts/test_contract_caller_interface.py +++ b/tests/core/contracts/test_contract_caller_interface.py @@ -1,171 +1,170 @@ -import pytest - -from eth_utils.toolz import ( - identity, -) - -from web3.exceptions import ( - MismatchedABI, - NoABIFound, - NoABIFunctionsFound, -) - - -def deploy(web3, Contract, apply_func=identity, args=None): - args = args or [] - deploy_txn = Contract.constructor(*args).transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = apply_func(deploy_receipt['contractAddress']) - contract = Contract(address=address) - assert contract.address == address - assert len(web3.eth.getCode(contract.address)) > 0 - return contract - - -@pytest.fixture() -def address(web3): - return web3.eth.accounts[1] - - -@pytest.fixture() -def math_contract(web3, MathContract, address_conversion_func): - return deploy(web3, MathContract, address_conversion_func) - - -@pytest.fixture() -def caller_tester_contract(web3, CallerTesterContract, address_conversion_func): - return deploy(web3, CallerTesterContract, address_conversion_func) - - -@pytest.fixture() -def transaction_dict(web3, address): - return { - 'from': address, - 'gas': 210000, - 'gasPrice': web3.toWei(.001, 'ether'), - 'value': 12345, - } - - -def test_caller_default(math_contract): - result = math_contract.caller.add(3, 5) - assert result == 8 - - -def test_caller_with_parens(math_contract): - result = math_contract.caller().add(3, 5) - assert result == 8 - - -def test_caller_with_no_abi(web3): - contract = web3.eth.contract() - with pytest.raises(NoABIFound): - contract.caller.thisFunctionDoesNotExist() - - -def test_caller_with_no_abi_and_parens(web3): - contract = web3.eth.contract() - with pytest.raises(NoABIFound): - contract.caller().thisFunctionDoesNotExist() - - -def test_caller_with_empty_abi_and_parens(web3): - contract = web3.eth.contract(abi=[]) - with pytest.raises(NoABIFunctionsFound): - contract.caller().thisFunctionDoesNotExist() - - -def test_caller_with_empty_abi(web3): - contract = web3.eth.contract(abi=[]) - with pytest.raises(NoABIFunctionsFound): - contract.caller.thisFunctionDoesNotExist() - - -def test_caller_with_a_nonexistent_function(math_contract): - contract = math_contract - with pytest.raises(MismatchedABI): - contract.caller.thisFunctionDoesNotExist() - - -def test_caller_with_block_identifier(web3, math_contract): - start_num = web3.eth.getBlock('latest').number - assert math_contract.caller.counter() == 0 - - web3.provider.make_request(method='evm_mine', params=[5]) - math_contract.functions.increment().transact() - math_contract.functions.increment().transact() - - output1 = math_contract.caller(block_identifier=start_num + 6).counter() - output2 = math_contract.caller(block_identifier=start_num + 7).counter() - - assert output1 == 1 - assert output2 == 2 - - -def test_caller_with_block_identifier_and_transaction_dict(web3, - caller_tester_contract, - transaction_dict, - address): - start_num = web3.eth.getBlock('latest').number - assert caller_tester_contract.caller.counter() == 0 - - web3.provider.make_request(method='evm_mine', params=[5]) - caller_tester_contract.functions.increment().transact() - - block_id = start_num + 6 - contract = caller_tester_contract.caller( - transaction=transaction_dict, - block_identifier=block_id - ) - - sender, _, gasLeft, value, block_num = contract.returnMeta() - counter = contract.counter() - - assert sender == address - assert gasLeft <= transaction_dict['gas'] - assert value == transaction_dict['value'] - assert block_num == block_id - assert counter == 1 - - -def test_caller_with_transaction_keyword(web3, - caller_tester_contract, - transaction_dict, - address): - contract = caller_tester_contract.caller(transaction=transaction_dict) - - sender, _, gasLeft, value, _ = contract.returnMeta() - - assert address == sender - assert gasLeft <= transaction_dict['gas'] - assert value == transaction_dict['value'] - - -def test_caller_with_dict_but_no_transaction_keyword(web3, - caller_tester_contract, - transaction_dict, - address): - contract = caller_tester_contract.caller(transaction_dict) - - sender, _, gasLeft, value, _ = contract.returnMeta() - - assert address == sender - assert gasLeft <= transaction_dict['gas'] - assert value == transaction_dict['value'] - - -def test_caller_with_args_and_no_transaction_keyword(web3, - caller_tester_contract, - transaction_dict, - address): - contract = caller_tester_contract.caller(transaction_dict) - - sender, _, gasLeft, value, _ = contract.returnMeta() - - assert address == sender - assert gasLeft <= transaction_dict['gas'] - assert value == transaction_dict['value'] - - add_result = contract.add(3, 5) - assert add_result == 8 +import pytest + +from web3._utils.toolz import ( + identity, +) +from web3.exceptions import ( + MismatchedABI, + NoABIFound, + NoABIFunctionsFound, +) + + +def deploy(web3, Contract, apply_func=identity, args=None): + args = args or [] + deploy_txn = Contract.constructor(*args).transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = apply_func(deploy_receipt['contractAddress']) + contract = Contract(address=address) + assert contract.address == address + assert len(web3.vns.getCode(contract.address)) > 0 + return contract + + +@pytest.fixture() +def address(web3): + return web3.vns.accounts[1] + + +@pytest.fixture() +def math_contract(web3, MathContract, address_conversion_func): + return deploy(web3, MathContract, address_conversion_func) + + +@pytest.fixture() +def caller_tester_contract(web3, CallerTesterContract, address_conversion_func): + return deploy(web3, CallerTesterContract, address_conversion_func) + + +@pytest.fixture() +def transaction_dict(web3, address): + return { + 'from': address, + 'gas': 210000, + 'gasPrice': web3.toWei(.001, 'ether'), + 'value': 12345, + } + + +def test_caller_default(math_contract): + result = math_contract.caller.add(3, 5) + assert result == 8 + + +def test_caller_with_parens(math_contract): + result = math_contract.caller().add(3, 5) + assert result == 8 + + +def test_caller_with_no_abi(web3): + contract = web3.vns.contract() + with pytest.raises(NoABIFound): + contract.caller.thisFunctionDoesNotExist() + + +def test_caller_with_no_abi_and_parens(web3): + contract = web3.vns.contract() + with pytest.raises(NoABIFound): + contract.caller().thisFunctionDoesNotExist() + + +def test_caller_with_empty_abi_and_parens(web3): + contract = web3.vns.contract(abi=[]) + with pytest.raises(NoABIFunctionsFound): + contract.caller().thisFunctionDoesNotExist() + + +def test_caller_with_empty_abi(web3): + contract = web3.vns.contract(abi=[]) + with pytest.raises(NoABIFunctionsFound): + contract.caller.thisFunctionDoesNotExist() + + +def test_caller_with_a_nonexistent_function(math_contract): + contract = math_contract + with pytest.raises(MismatchedABI): + contract.caller.thisFunctionDoesNotExist() + + +def test_caller_with_block_identifier(web3, math_contract): + start_num = web3.vns.getBlock('latest').number + assert math_contract.caller.counter() == 0 + + web3.provider.make_request(method='evm_mine', params=[5]) + math_contract.functions.increment().transact() + math_contract.functions.increment().transact() + + output1 = math_contract.caller(block_identifier=start_num + 6).counter() + output2 = math_contract.caller(block_identifier=start_num + 7).counter() + + assert output1 == 1 + assert output2 == 2 + + +def test_caller_with_block_identifier_and_transaction_dict(web3, + caller_tester_contract, + transaction_dict, + address): + start_num = web3.vns.getBlock('latest').number + assert caller_tester_contract.caller.counter() == 0 + + web3.provider.make_request(method='evm_mine', params=[5]) + caller_tester_contract.functions.increment().transact() + + block_id = start_num + 6 + contract = caller_tester_contract.caller( + transaction=transaction_dict, + block_identifier=block_id + ) + + sender, _, gasLeft, value, block_num = contract.returnMeta() + counter = contract.counter() + + assert sender == address + assert gasLeft <= transaction_dict['gas'] + assert value == transaction_dict['value'] + assert block_num == block_id + assert counter == 1 + + +def test_caller_with_transaction_keyword(web3, + caller_tester_contract, + transaction_dict, + address): + contract = caller_tester_contract.caller(transaction=transaction_dict) + + sender, _, gasLeft, value, _ = contract.returnMeta() + + assert address == sender + assert gasLeft <= transaction_dict['gas'] + assert value == transaction_dict['value'] + + +def test_caller_with_dict_but_no_transaction_keyword(web3, + caller_tester_contract, + transaction_dict, + address): + contract = caller_tester_contract.caller(transaction_dict) + + sender, _, gasLeft, value, _ = contract.returnMeta() + + assert address == sender + assert gasLeft <= transaction_dict['gas'] + assert value == transaction_dict['value'] + + +def test_caller_with_args_and_no_transaction_keyword(web3, + caller_tester_contract, + transaction_dict, + address): + contract = caller_tester_contract.caller(transaction_dict) + + sender, _, gasLeft, value, _ = contract.returnMeta() + + assert address == sender + assert gasLeft <= transaction_dict['gas'] + assert value == transaction_dict['value'] + + add_result = contract.add(3, 5) + assert add_result == 8 diff --git a/tests/core/contracts/test_contract_class_construction.py b/tests/core/contracts/test_contract_class_construction.py index 1f5557081d..046c1dad20 100644 --- a/tests/core/contracts/test_contract_class_construction.py +++ b/tests/core/contracts/test_contract_class_construction.py @@ -1,56 +1,56 @@ -import json -import pytest - -from eth_utils import ( - decode_hex, -) - -from web3.contract import ( - Contract, -) -from web3.exceptions import ( - FallbackNotFound, -) - - -def test_class_construction_sets_class_vars(web3, - MATH_ABI, - MATH_CODE, - MATH_RUNTIME): - MathContract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - - assert MathContract.web3 == web3 - assert MathContract.bytecode == decode_hex(MATH_CODE) - assert MathContract.bytecode_runtime == decode_hex(MATH_RUNTIME) - - -def test_error_to_instantiate_base_class(): - with pytest.raises(AttributeError): - Contract() - - -def test_abi_as_json_string(web3, MATH_ABI, some_address): - abi_str = json.dumps(MATH_ABI) - - MathContract = web3.eth.contract(abi=abi_str) - assert MathContract.abi == MATH_ABI - - math = MathContract(some_address) - assert math.abi == MATH_ABI - - -def test_error_to_call_non_existent_fallback(web3, - MATH_ABI, - MATH_CODE, - MATH_RUNTIME): - math_contract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - with pytest.raises(FallbackNotFound): - math_contract.fallback.estimateGas() +import json +import pytest + +from vns_utils import ( + decode_hex, +) + +from web3.contract import ( + Contract, +) +from web3.exceptions import ( + FallbackNotFound, +) + + +def test_class_construction_sets_class_vars(web3, + MATH_ABI, + MATH_CODE, + MATH_RUNTIME): + MathContract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + + assert MathContract.web3 == web3 + assert MathContract.bytecode == decode_hex(MATH_CODE) + assert MathContract.bytecode_runtime == decode_hex(MATH_RUNTIME) + + +def test_error_to_instantiate_base_class(): + with pytest.raises(AttributeError): + Contract() + + +def test_abi_as_json_string(web3, MATH_ABI, some_address): + abi_str = json.dumps(MATH_ABI) + + MathContract = web3.vns.contract(abi=abi_str) + assert MathContract.abi == MATH_ABI + + math = MathContract(some_address) + assert math.abi == MATH_ABI + + +def test_error_to_call_non_existent_fallback(web3, + MATH_ABI, + MATH_CODE, + MATH_RUNTIME): + math_contract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + with pytest.raises(FallbackNotFound): + math_contract.fallback.estimateGas() diff --git a/tests/core/contracts/test_contract_constructor.py b/tests/core/contracts/test_contract_constructor.py index 62995de406..dd62f7b3b2 100644 --- a/tests/core/contracts/test_contract_constructor.py +++ b/tests/core/contracts/test_contract_constructor.py @@ -1,234 +1,234 @@ -import pytest - -from eth_utils import ( - decode_hex, -) - -TEST_ADDRESS = '0x16D9983245De15E7A9A73bC586E01FF6E08dE737' -EXPECTED_DATA_A = 1234 -EXPECTED_DATA_B = (b'abcd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') - - -def test_contract_constructor_abi_encoding_with_no_constructor_fn(MathContract, MATH_CODE): - deploy_data = MathContract.constructor()._encode_data_in_transaction() - assert deploy_data == MATH_CODE - - -def test_contract_constructor_gas_estimate_no_constructor(web3, MathContract): - gas_estimate = MathContract.constructor().estimateGas() - - deploy_txn = MathContract.constructor().transact() - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_contract_constructor_gas_estimate_with_constructor_without_arguments( - web3, - SimpleConstructorContract): - gas_estimate = SimpleConstructorContract.constructor().estimateGas() - - deploy_txn = SimpleConstructorContract.constructor().transact() - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -@pytest.mark.parametrize( - 'constructor_args,constructor_kwargs', - ( - ([1234, b'abcd'], {}), - ([1234], {'b': b'abcd'}), - ([], {'a': 1234, 'b': b'abcd'}), - ([], {'b': b'abcd', 'a': 1234}), - ), -) -def test_contract_constructor_gas_estimate_with_constructor_with_arguments( - web3, - WithConstructorArgumentsContract, - constructor_args, - constructor_kwargs): - gas_estimate = WithConstructorArgumentsContract.constructor( - *constructor_args, **constructor_kwargs).estimateGas() - - deploy_txn = WithConstructorArgumentsContract.constructor( - *constructor_args, **constructor_kwargs).transact() - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_contract_constructor_gas_estimate_with_constructor_with_address_argument( - web3, - WithConstructorAddressArgumentsContract, - address_conversion_func): - gas_estimate = WithConstructorAddressArgumentsContract.constructor( - address_conversion_func("0x16D9983245De15E7A9A73bC586E01FF6E08dE737")).estimateGas() - - deploy_txn = WithConstructorAddressArgumentsContract.constructor( - address_conversion_func("0x16D9983245De15E7A9A73bC586E01FF6E08dE737")).transact() - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_contract_constructor_transact_no_constructor( - web3, - MathContract, - MATH_RUNTIME, - address_conversion_func): - deploy_txn = MathContract.constructor().transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = address_conversion_func(txn_receipt['contractAddress']) - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(MATH_RUNTIME) - - -def test_contract_constructor_transact_with_constructor_without_arguments( - web3, - SimpleConstructorContract, - SIMPLE_CONSTRUCTOR_RUNTIME, - address_conversion_func): - deploy_txn = SimpleConstructorContract.constructor().transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = address_conversion_func(txn_receipt['contractAddress']) - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(SIMPLE_CONSTRUCTOR_RUNTIME) - - -@pytest.mark.parametrize( - 'constructor_args,constructor_kwargs, expected_a, expected_b', - ( - ([1234, b'abcd'], {}, EXPECTED_DATA_A, EXPECTED_DATA_B), - ([1234], {'b': b'abcd'}, EXPECTED_DATA_A, EXPECTED_DATA_B), - ([], {'a': 1234, 'b': b'abcd'}, EXPECTED_DATA_A, EXPECTED_DATA_B), - ([], {'b': b'abcd', 'a': 1234}, EXPECTED_DATA_A, EXPECTED_DATA_B), - ), -) -def test_contract_constructor_transact_with_constructor_with_arguments( - web3, - WithConstructorArgumentsContract, - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, - constructor_args, - constructor_kwargs, - expected_a, - expected_b, - address_conversion_func): - deploy_txn = WithConstructorArgumentsContract.constructor( - *constructor_args, **constructor_kwargs).transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = address_conversion_func(txn_receipt['contractAddress']) - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME) - assert expected_a == WithConstructorArgumentsContract( - address=contract_address).functions.data_a().call() - assert expected_b == WithConstructorArgumentsContract( - address=contract_address).functions.data_b().call() - - -def test_contract_constructor_transact_with_constructor_with_address_arguments( - web3, - WithConstructorAddressArgumentsContract, - WITH_CONSTRUCTOR_ADDRESS_RUNTIME, - address_conversion_func): - deploy_txn = WithConstructorAddressArgumentsContract.constructor(TEST_ADDRESS).transact() - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - assert txn_receipt['contractAddress'] - contract_address = address_conversion_func(txn_receipt['contractAddress']) - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ADDRESS_RUNTIME) - assert TEST_ADDRESS == WithConstructorAddressArgumentsContract( - address=contract_address).functions.testAddr().call() - - -def test_contract_constructor_build_transaction_to_field_error(MathContract): - with pytest.raises(ValueError): - MathContract.constructor().buildTransaction({'to': '123'}) - - -def test_contract_constructor_build_transaction_no_constructor( - web3, - MathContract, - address_conversion_func): - txn_hash = MathContract.constructor().transact( - {'from': address_conversion_func(web3.eth.accounts[0])} - ) - txn = web3.eth.getTransaction(txn_hash) - nonce = web3.eth.getTransactionCount(web3.eth.coinbase) - unsent_txn = MathContract.constructor().buildTransaction({'nonce': nonce}) - assert txn['data'] == unsent_txn['data'] - - new_txn_hash = web3.eth.sendTransaction(unsent_txn) - new_txn = web3.eth.getTransaction(new_txn_hash) - assert new_txn['data'] == unsent_txn['data'] - assert new_txn['nonce'] == nonce - - -def test_contract_constructor_build_transaction_with_constructor_without_argument( - web3, - MathContract, - address_conversion_func): - txn_hash = MathContract.constructor().transact( - {'from': address_conversion_func(web3.eth.accounts[0])} - ) - txn = web3.eth.getTransaction(txn_hash) - nonce = web3.eth.getTransactionCount(web3.eth.coinbase) - unsent_txn = MathContract.constructor().buildTransaction({'nonce': nonce}) - assert txn['data'] == unsent_txn['data'] - - new_txn_hash = web3.eth.sendTransaction(unsent_txn) - new_txn = web3.eth.getTransaction(new_txn_hash) - assert new_txn['data'] == unsent_txn['data'] - assert new_txn['nonce'] == nonce - - -@pytest.mark.parametrize( - 'constructor_args,constructor_kwargs', - ( - ([1234, b'abcd'], {}), - ([1234], {'b': b'abcd'}), - ([], {'a': 1234, 'b': b'abcd'}), - ([], {'b': b'abcd', 'a': 1234}), - ), -) -def test_contract_constructor_build_transaction_with_constructor_with_argument( - web3, - WithConstructorArgumentsContract, - constructor_args, - constructor_kwargs, - address_conversion_func): - txn_hash = WithConstructorArgumentsContract.constructor( - *constructor_args, **constructor_kwargs).transact( - {'from': address_conversion_func(web3.eth.accounts[0])} - ) - txn = web3.eth.getTransaction(txn_hash) - nonce = web3.eth.getTransactionCount(web3.eth.coinbase) - unsent_txn = WithConstructorArgumentsContract.constructor( - *constructor_args, **constructor_kwargs).buildTransaction({'nonce': nonce}) - assert txn['data'] == unsent_txn['data'] - - new_txn_hash = web3.eth.sendTransaction(unsent_txn) - new_txn = web3.eth.getTransaction(new_txn_hash) - assert new_txn['data'] == unsent_txn['data'] - assert new_txn['nonce'] == nonce +import pytest + +from vns_utils import ( + decode_hex, +) + +TEST_ADDRESS = '0x16D9983245De15E7A9A73bC586E01FF6E08dE737' +EXPECTED_DATA_A = 1234 +EXPECTED_DATA_B = (b'abcd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + + +def test_contract_constructor_abi_encoding_with_no_constructor_fn(MathContract, MATH_CODE): + deploy_data = MathContract.constructor()._encode_data_in_transaction() + assert deploy_data == MATH_CODE + + +def test_contract_constructor_gas_estimate_no_constructor(web3, MathContract): + gas_estimate = MathContract.constructor().estimateGas() + + deploy_txn = MathContract.constructor().transact() + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_contract_constructor_gas_estimate_with_constructor_without_arguments( + web3, + SimpleConstructorContract): + gas_estimate = SimpleConstructorContract.constructor().estimateGas() + + deploy_txn = SimpleConstructorContract.constructor().transact() + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +@pytest.mark.parametrize( + 'constructor_args,constructor_kwargs', + ( + ([1234, b'abcd'], {}), + ([1234], {'b': b'abcd'}), + ([], {'a': 1234, 'b': b'abcd'}), + ([], {'b': b'abcd', 'a': 1234}), + ), +) +def test_contract_constructor_gas_estimate_with_constructor_with_arguments( + web3, + WithConstructorArgumentsContract, + constructor_args, + constructor_kwargs): + gas_estimate = WithConstructorArgumentsContract.constructor( + *constructor_args, **constructor_kwargs).estimateGas() + + deploy_txn = WithConstructorArgumentsContract.constructor( + *constructor_args, **constructor_kwargs).transact() + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_contract_constructor_gas_estimate_with_constructor_with_address_argument( + web3, + WithConstructorAddressArgumentsContract, + address_conversion_func): + gas_estimate = WithConstructorAddressArgumentsContract.constructor( + address_conversion_func("0x16D9983245De15E7A9A73bC586E01FF6E08dE737")).estimateGas() + + deploy_txn = WithConstructorAddressArgumentsContract.constructor( + address_conversion_func("0x16D9983245De15E7A9A73bC586E01FF6E08dE737")).transact() + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_contract_constructor_transact_no_constructor( + web3, + MathContract, + MATH_RUNTIME, + address_conversion_func): + deploy_txn = MathContract.constructor().transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = address_conversion_func(txn_receipt['contractAddress']) + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(MATH_RUNTIME) + + +def test_contract_constructor_transact_with_constructor_without_arguments( + web3, + SimpleConstructorContract, + SIMPLE_CONSTRUCTOR_RUNTIME, + address_conversion_func): + deploy_txn = SimpleConstructorContract.constructor().transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = address_conversion_func(txn_receipt['contractAddress']) + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(SIMPLE_CONSTRUCTOR_RUNTIME) + + +@pytest.mark.parametrize( + 'constructor_args,constructor_kwargs, expected_a, expected_b', + ( + ([1234, b'abcd'], {}, EXPECTED_DATA_A, EXPECTED_DATA_B), + ([1234], {'b': b'abcd'}, EXPECTED_DATA_A, EXPECTED_DATA_B), + ([], {'a': 1234, 'b': b'abcd'}, EXPECTED_DATA_A, EXPECTED_DATA_B), + ([], {'b': b'abcd', 'a': 1234}, EXPECTED_DATA_A, EXPECTED_DATA_B), + ), +) +def test_contract_constructor_transact_with_constructor_with_arguments( + web3, + WithConstructorArgumentsContract, + WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, + constructor_args, + constructor_kwargs, + expected_a, + expected_b, + address_conversion_func): + deploy_txn = WithConstructorArgumentsContract.constructor( + *constructor_args, **constructor_kwargs).transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = address_conversion_func(txn_receipt['contractAddress']) + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME) + assert expected_a == WithConstructorArgumentsContract( + address=contract_address).functions.data_a().call() + assert expected_b == WithConstructorArgumentsContract( + address=contract_address).functions.data_b().call() + + +def test_contract_constructor_transact_with_constructor_with_address_arguments( + web3, + WithConstructorAddressArgumentsContract, + WITH_CONSTRUCTOR_ADDRESS_RUNTIME, + address_conversion_func): + deploy_txn = WithConstructorAddressArgumentsContract.constructor(TEST_ADDRESS).transact() + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + assert txn_receipt['contractAddress'] + contract_address = address_conversion_func(txn_receipt['contractAddress']) + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ADDRESS_RUNTIME) + assert TEST_ADDRESS == WithConstructorAddressArgumentsContract( + address=contract_address).functions.testAddr().call() + + +def test_contract_constructor_build_transaction_to_field_error(MathContract): + with pytest.raises(ValueError): + MathContract.constructor().buildTransaction({'to': '123'}) + + +def test_contract_constructor_build_transaction_no_constructor( + web3, + MathContract, + address_conversion_func): + txn_hash = MathContract.constructor().transact( + {'from': address_conversion_func(web3.vns.accounts[0])} + ) + txn = web3.vns.getTransaction(txn_hash) + nonce = web3.vns.getTransactionCount(web3.vns.coinbase) + unsent_txn = MathContract.constructor().buildTransaction({'nonce': nonce}) + assert txn['data'] == unsent_txn['data'] + + new_txn_hash = web3.vns.sendTransaction(unsent_txn) + new_txn = web3.vns.getTransaction(new_txn_hash) + assert new_txn['data'] == unsent_txn['data'] + assert new_txn['nonce'] == nonce + + +def test_contract_constructor_build_transaction_with_constructor_without_argument( + web3, + MathContract, + address_conversion_func): + txn_hash = MathContract.constructor().transact( + {'from': address_conversion_func(web3.vns.accounts[0])} + ) + txn = web3.vns.getTransaction(txn_hash) + nonce = web3.vns.getTransactionCount(web3.vns.coinbase) + unsent_txn = MathContract.constructor().buildTransaction({'nonce': nonce}) + assert txn['data'] == unsent_txn['data'] + + new_txn_hash = web3.vns.sendTransaction(unsent_txn) + new_txn = web3.vns.getTransaction(new_txn_hash) + assert new_txn['data'] == unsent_txn['data'] + assert new_txn['nonce'] == nonce + + +@pytest.mark.parametrize( + 'constructor_args,constructor_kwargs', + ( + ([1234, b'abcd'], {}), + ([1234], {'b': b'abcd'}), + ([], {'a': 1234, 'b': b'abcd'}), + ([], {'b': b'abcd', 'a': 1234}), + ), +) +def test_contract_constructor_build_transaction_with_constructor_with_argument( + web3, + WithConstructorArgumentsContract, + constructor_args, + constructor_kwargs, + address_conversion_func): + txn_hash = WithConstructorArgumentsContract.constructor( + *constructor_args, **constructor_kwargs).transact( + {'from': address_conversion_func(web3.vns.accounts[0])} + ) + txn = web3.vns.getTransaction(txn_hash) + nonce = web3.vns.getTransactionCount(web3.vns.coinbase) + unsent_txn = WithConstructorArgumentsContract.constructor( + *constructor_args, **constructor_kwargs).buildTransaction({'nonce': nonce}) + assert txn['data'] == unsent_txn['data'] + + new_txn_hash = web3.vns.sendTransaction(unsent_txn) + new_txn = web3.vns.getTransaction(new_txn_hash) + assert new_txn['data'] == unsent_txn['data'] + assert new_txn['nonce'] == nonce diff --git a/tests/core/contracts/test_contract_constructor_encoding.py b/tests/core/contracts/test_contract_constructor_encoding.py index 3711dfaf9a..1accac1385 100644 --- a/tests/core/contracts/test_contract_constructor_encoding.py +++ b/tests/core/contracts/test_contract_constructor_encoding.py @@ -1,118 +1,65 @@ -import pytest - -from eth_utils import ( - encode_hex, - remove_0x_prefix, -) - - -def test_contract_constructor_abi_encoding_with_no_constructor_fn(MathContract, MATH_CODE): - deploy_data = MathContract._encode_constructor_data() - assert deploy_data == MATH_CODE - - -def test_contract_constructor_abi_encoding_with_constructor_with_no_args(SimpleConstructorContract, - SIMPLE_CONSTRUCTOR_CODE): - deploy_data = SimpleConstructorContract._encode_constructor_data() - assert deploy_data == SIMPLE_CONSTRUCTOR_CODE - - -@pytest.mark.parametrize( - 'args,kwargs', - ( - (None, 'kwarg-is-ignored'), - ('arg-is-ignored', None), - ), -) -def test_contract_error_if_additional_args_are_supplied_with_no_constructor_fn(MathContract, - args, kwargs): - with pytest.raises(TypeError, match="Constructor args"): - MathContract._encode_constructor_data(args, kwargs) - - -@pytest.mark.parametrize( - 'arguments', - ( - [], - [1234], - ['abcd', 1234], # wrong order - [1234, 'abcd', 'extra-arg'], # extra arguments - [-1234, 'abcd'], # wrong types - ['1234', 'abcd'], # wrong types - ), -) -def test_error_if_invalid_arguments_supplied(WithConstructorArgumentsContract, arguments): - with pytest.raises(TypeError): - WithConstructorArgumentsContract._encode_constructor_data(arguments) - - -@pytest.mark.parametrize( - 'bytes_arg', - ( - b'abcd', - '0x61626364', - ), -) -def test_contract_constructor_encoding_encoding(web3, WithConstructorArgumentsContract, bytes_arg): - deploy_data = WithConstructorArgumentsContract._encode_constructor_data([1234, bytes_arg]) - encoded_args = '0x00000000000000000000000000000000000000000000000000000000000004d26162636400000000000000000000000000000000000000000000000000000000' # noqa: E501 - expected_ending = encode_hex(web3.codec.encode_abi(['uint256', 'bytes32'], [1234, b'abcd'])) - assert expected_ending == encoded_args - assert deploy_data.endswith(remove_0x_prefix(expected_ending)) - - -def test_contract_constructor_encoding_encoding_warning(web3, WithConstructorArgumentsContract): - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the \"0x\" prefix' - ): - deploy_data = WithConstructorArgumentsContract._encode_constructor_data([1234, '61626364']) - encoded_args = '0x00000000000000000000000000000000000000000000000000000000000004d26162636400000000000000000000000000000000000000000000000000000000' # noqa: E501 - - expected_ending = encode_hex( - web3.codec.encode_abi(['uint256', 'bytes32'], [1234, b'abcd']) - ) - assert expected_ending == encoded_args - assert deploy_data.endswith(remove_0x_prefix(expected_ending)) - - -@pytest.mark.parametrize( - 'bytes_arg,encoded_args', - ( - ('0x' + '00' * 32, '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 - (b'1' * 32, '0x00000000000000000000000000000000000000000000000000000000000004d23131313131313131313131313131313131313131313131313131313131313131'), # noqa: E501 - ), -) -def test_contract_constructor_encoding_encoding_strict( - w3_strict_abi, - WithConstructorArgumentsContractStrict, - encoded_args, - bytes_arg): - - deploy_data = WithConstructorArgumentsContractStrict._encode_constructor_data([1234, bytes_arg]) - - expected_ending = encode_hex( - w3_strict_abi.codec.encode_abi(['uint256', 'bytes32'], [1234, bytes_arg]) - ) - assert expected_ending == encoded_args - assert deploy_data.endswith(remove_0x_prefix(expected_ending)) - - -@pytest.mark.parametrize( - 'bytes_arg', - ( - b'abcd', - '0x61626364', - '', - '61626364', - ), -) -def test_contract_constructor_encoding_encoding_strict_errors( - w3_strict_abi, - WithConstructorArgumentsContractStrict, - bytes_arg): - with pytest.raises( - TypeError, - match="One or more arguments could not be encoded to the necessary ABI type." - ): - WithConstructorArgumentsContractStrict._encode_constructor_data([1234, bytes_arg]) +import pytest + +from vns_abi import ( + encode_abi, +) +from vns_utils import ( + encode_hex, + remove_0x_prefix, +) + + +def test_contract_constructor_abi_encoding_with_no_constructor_fn(MathContract, MATH_CODE): + deploy_data = MathContract._encode_constructor_data() + assert deploy_data == MATH_CODE + + +def test_contract_constructor_abi_encoding_with_constructor_with_no_args(SimpleConstructorContract, + SIMPLE_CONSTRUCTOR_CODE): + deploy_data = SimpleConstructorContract._encode_constructor_data() + assert deploy_data == SIMPLE_CONSTRUCTOR_CODE + + +@pytest.mark.parametrize( + 'args,kwargs', + ( + (None, 'kwarg-is-ignored'), + ('arg-is-ignored', None), + ), +) +def test_contract_error_if_additional_args_are_supplied_with_no_constructor_fn(MathContract, + args, kwargs): + with pytest.raises(TypeError, match="Constructor args"): + MathContract._encode_constructor_data(args, kwargs) + + +@pytest.mark.parametrize( + 'arguments', + ( + [], + [1234], + ['abcd', 1234], # wrong order + [1234, 'abcd', 'extra-arg'], # extra arguments + [-1234, 'abcd'], # wrong types + ['1234', 'abcd'], # wrong types + ), +) +def test_error_if_invalid_arguments_supplied(WithConstructorArgumentsContract, arguments): + with pytest.raises(TypeError): + WithConstructorArgumentsContract._encode_constructor_data(arguments) + + +@pytest.mark.parametrize( + 'bytes_arg', + ( + b'abcd', + '61626364', + '0x61626364', + ), +) +def test_contract_constructor_encoding_encoding(WithConstructorArgumentsContract, bytes_arg): + deploy_data = WithConstructorArgumentsContract._encode_constructor_data([1234, bytes_arg]) + encoded_args = '0x00000000000000000000000000000000000000000000000000000000000004d26162636400000000000000000000000000000000000000000000000000000000' # noqa: E501 + expected_ending = encode_hex(encode_abi(['uint256', 'bytes32'], [1234, b'abcd'])) + assert expected_ending == encoded_args + assert deploy_data.endswith(remove_0x_prefix(expected_ending)) diff --git a/tests/core/contracts/test_contract_deployment.py b/tests/core/contracts/test_contract_deployment.py index da7767c64c..053102b09b 100644 --- a/tests/core/contracts/test_contract_deployment.py +++ b/tests/core/contracts/test_contract_deployment.py @@ -1,104 +1,70 @@ - -import pytest - -from eth_utils import ( - decode_hex, -) - - -def test_contract_deployment_no_constructor(web3, MathContract, - MATH_RUNTIME): - deploy_txn = MathContract.constructor().transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(MATH_RUNTIME) - - -def test_contract_deployment_with_constructor_without_args(web3, - SimpleConstructorContract, - SIMPLE_CONSTRUCTOR_RUNTIME): - deploy_txn = SimpleConstructorContract.constructor().transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(SIMPLE_CONSTRUCTOR_RUNTIME) - - -def test_contract_deployment_with_constructor_with_arguments(web3, - WithConstructorArgumentsContract, - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME): - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the "0x" prefix' - ): - deploy_txn = WithConstructorArgumentsContract.constructor(1234, 'abcd').transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME) - - -@pytest.mark.parametrize('constructor_arg', ( - b'1234\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', # noqa: E501 - '0x0000000000000000000000000000000000000000000000000000000000000000' - ) -) -def test_contract_deployment_with_constructor_with_arguments_strict(w3_strict_abi, - WithConstructorArgumentsContractStrict, # noqa: E501 - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, # noqa: E501 - constructor_arg): - deploy_txn = WithConstructorArgumentsContractStrict.constructor( - 1234, constructor_arg - ).transact() - - txn_receipt = w3_strict_abi.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - - blockchain_code = w3_strict_abi.eth.getCode(contract_address) - assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME) - - -def test_contract_deployment_with_constructor_with_arguments_strict_error(w3_strict_abi, - WithConstructorArgumentsContractStrict, # noqa: E501 - WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME): # noqa: E501 - with pytest.raises( - TypeError, - match="One or more arguments could not be encoded to the necessary ABI type. Expected types are: uint256, bytes32" # noqa: E501 - ): - WithConstructorArgumentsContractStrict.constructor(1234, 'abcd').transact() - - -def test_contract_deployment_with_constructor_with_address_argument(web3, - WithConstructorAddressArgumentsContract, # noqa: E501 - WITH_CONSTRUCTOR_ADDRESS_RUNTIME): # noqa: E501 - deploy_txn = WithConstructorAddressArgumentsContract.constructor( - "0x16D9983245De15E7A9A73bC586E01FF6E08dE737", - ).transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert txn_receipt is not None - - assert txn_receipt['contractAddress'] - contract_address = txn_receipt['contractAddress'] - - blockchain_code = web3.eth.getCode(contract_address) - assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ADDRESS_RUNTIME) + +import pytest + +from vns_utils import ( + decode_hex, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +def test_contract_deployment_no_constructor(web3, MathContract, + MATH_RUNTIME): + deploy_txn = MathContract.constructor().transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = txn_receipt['contractAddress'] + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(MATH_RUNTIME) + + +def test_contract_deployment_with_constructor_without_args(web3, + SimpleConstructorContract, + SIMPLE_CONSTRUCTOR_RUNTIME): + deploy_txn = SimpleConstructorContract.constructor().transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = txn_receipt['contractAddress'] + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(SIMPLE_CONSTRUCTOR_RUNTIME) + + +def test_contract_deployment_with_constructor_with_arguments(web3, + WithConstructorArgumentsContract, + WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME): + deploy_txn = WithConstructorArgumentsContract.constructor(1234, 'abcd').transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = txn_receipt['contractAddress'] + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME) + + +def test_contract_deployment_with_constructor_with_address_argument(web3, + WithConstructorAddressArgumentsContract, # noqa: E501 + WITH_CONSTRUCTOR_ADDRESS_RUNTIME): # noqa: E501 + deploy_txn = WithConstructorAddressArgumentsContract.constructor( + "0x16D9983245De15E7A9A73bC586E01FF6E08dE737", + ).transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert txn_receipt is not None + + assert txn_receipt['contractAddress'] + contract_address = txn_receipt['contractAddress'] + + blockchain_code = web3.vns.getCode(contract_address) + assert blockchain_code == decode_hex(WITH_CONSTRUCTOR_ADDRESS_RUNTIME) diff --git a/tests/core/contracts/test_contract_estimateGas.py b/tests/core/contracts/test_contract_estimateGas.py index 79e1aac3b3..f7c46df38d 100644 --- a/tests/core/contracts/test_contract_estimateGas.py +++ b/tests/core/contracts/test_contract_estimateGas.py @@ -1,139 +1,139 @@ -import pytest - -from web3.exceptions import ( - ValidationError, -) - - -@pytest.fixture(autouse=True) -def wait_for_first_block(web3, wait_for_block): - wait_for_block(web3) - - -@pytest.fixture() -def math_contract(web3, - MATH_ABI, - MATH_CODE, - MATH_RUNTIME, - wait_for_transaction, - address_conversion_func): - MathContract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - deploy_txn = MathContract.constructor().transact({'from': web3.eth.coinbase}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - - assert deploy_receipt is not None - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - web3.isAddress(contract_address) - - _math_contract = MathContract(address=contract_address) - assert _math_contract.address == contract_address - return _math_contract - - -@pytest.fixture() -def fallback_function_contract(web3, - FALLBACK_FUNCTION_ABI, - FALLBACK_FUNCTION_CODE, - FALLBACK_FUNCTION_RUNTIME, - wait_for_transaction, - address_conversion_func): - fallback_contract = web3.eth.contract( - abi=FALLBACK_FUNCTION_ABI, - bytecode=FALLBACK_FUNCTION_CODE, - bytecode_runtime=FALLBACK_FUNCTION_RUNTIME - ) - deploy_txn = fallback_contract.constructor().transact({'from': web3.eth.coinbase}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - - assert deploy_receipt is not None - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - web3.isAddress(contract_address) - - _fallback_function_contract = fallback_contract(address=contract_address) - assert _fallback_function_contract.address == contract_address - return _fallback_function_contract - - -@pytest.fixture() -def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): - deploy_txn = PayableTesterContract.constructor().transact({'from': web3.eth.coinbase}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - - assert deploy_receipt is not None - payable_tester_address = address_conversion_func(deploy_receipt['contractAddress']) - - _payable_tester = PayableTesterContract(address=payable_tester_address) - assert _payable_tester.address == payable_tester_address - return _payable_tester - - -def test_contract_estimateGas(web3, math_contract, estimateGas, transact): - gas_estimate = estimateGas(contract=math_contract, - contract_function='increment') - - txn_hash = transact( - contract=math_contract, - contract_function='increment') - - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_contract_fallback_estimateGas(web3, fallback_function_contract): - gas_estimate = fallback_function_contract.fallback.estimateGas() - - txn_hash = fallback_function_contract.fallback.transact() - - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_contract_estimateGas_with_arguments(web3, math_contract, estimateGas, transact): - gas_estimate = estimateGas(contract=math_contract, - contract_function='add', - func_args=[5, 6]) - - txn_hash = transact( - contract=math_contract, - contract_function='add', - func_args=[5, 6]) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_estimateGas_not_sending_ether_to_nonpayable_function( - web3, - payable_tester_contract, - estimateGas, - transact): - gas_estimate = estimateGas(contract=payable_tester_contract, - contract_function='doNoValueCall') - - txn_hash = transact( - contract=payable_tester_contract, - contract_function='doNoValueCall') - - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - gas_used = txn_receipt.get('gasUsed') - - assert abs(gas_estimate - gas_used) < 21000 - - -def test_estimateGas_sending_ether_to_nonpayable_function( - web3, - payable_tester_contract, - estimateGas): - with pytest.raises(ValidationError): - estimateGas(contract=payable_tester_contract, - contract_function='doNoValueCall', - tx_params={'value': 1}) +import pytest + +from web3.exceptions import ( + ValidationError, +) + + +@pytest.fixture(autouse=True) +def wait_for_first_block(web3, wait_for_block): + wait_for_block(web3) + + +@pytest.fixture() +def math_contract(web3, + MATH_ABI, + MATH_CODE, + MATH_RUNTIME, + wait_for_transaction, + address_conversion_func): + MathContract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + deploy_txn = MathContract.constructor().transact({'from': web3.vns.coinbase}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + + assert deploy_receipt is not None + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + web3.isAddress(contract_address) + + _math_contract = MathContract(address=contract_address) + assert _math_contract.address == contract_address + return _math_contract + + +@pytest.fixture() +def fallback_function_contract(web3, + FALLBACK_FUNCTION_ABI, + FALLBACK_FUNCTION_CODE, + FALLBACK_FUNCTION_RUNTIME, + wait_for_transaction, + address_conversion_func): + fallback_contract = web3.vns.contract( + abi=FALLBACK_FUNCTION_ABI, + bytecode=FALLBACK_FUNCTION_CODE, + bytecode_runtime=FALLBACK_FUNCTION_RUNTIME + ) + deploy_txn = fallback_contract.constructor().transact({'from': web3.vns.coinbase}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + + assert deploy_receipt is not None + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + web3.isAddress(contract_address) + + _fallback_function_contract = fallback_contract(address=contract_address) + assert _fallback_function_contract.address == contract_address + return _fallback_function_contract + + +@pytest.fixture() +def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): + deploy_txn = PayableTesterContract.constructor().transact({'from': web3.vns.coinbase}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + + assert deploy_receipt is not None + payable_tester_address = address_conversion_func(deploy_receipt['contractAddress']) + + _payable_tester = PayableTesterContract(address=payable_tester_address) + assert _payable_tester.address == payable_tester_address + return _payable_tester + + +def test_contract_estimateGas(web3, math_contract, estimateGas, transact): + gas_estimate = estimateGas(contract=math_contract, + contract_function='increment') + + txn_hash = transact( + contract=math_contract, + contract_function='increment') + + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_contract_fallback_estimateGas(web3, fallback_function_contract): + gas_estimate = fallback_function_contract.fallback.estimateGas() + + txn_hash = fallback_function_contract.fallback.transact() + + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_contract_estimateGas_with_arguments(web3, math_contract, estimateGas, transact): + gas_estimate = estimateGas(contract=math_contract, + contract_function='add', + func_args=[5, 6]) + + txn_hash = transact( + contract=math_contract, + contract_function='add', + func_args=[5, 6]) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_estimateGas_not_sending_ether_to_nonpayable_function( + web3, + payable_tester_contract, + estimateGas, + transact): + gas_estimate = estimateGas(contract=payable_tester_contract, + contract_function='doNoValueCall') + + txn_hash = transact( + contract=payable_tester_contract, + contract_function='doNoValueCall') + + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + gas_used = txn_receipt.get('gasUsed') + + assert abs(gas_estimate - gas_used) < 21000 + + +def test_estimateGas_sending_ether_to_nonpayable_function( + web3, + payable_tester_contract, + estimateGas): + with pytest.raises(ValidationError): + estimateGas(contract=payable_tester_contract, + contract_function='doNoValueCall', + tx_params={'value': 1}) diff --git a/tests/core/contracts/test_contract_events_buildFilter.py b/tests/core/contracts/test_contract_events_buildFilter.py index 02fade01c3..1bc186c673 100644 --- a/tests/core/contracts/test_contract_events_buildFilter.py +++ b/tests/core/contracts/test_contract_events_buildFilter.py @@ -1,70 +1,70 @@ -import json -import pytest - -from eth_utils import ( - keccak, -) -from hexbytes import ( - HexBytes, -) - -CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 - - -def test_build_filter_topic_signature(web3): - contract = web3.eth.contract(abi=CONTRACT_ABI) - filter_builder = contract.events.Increased.build_filter() - filter_builder.args['value'].match_any(100, 200, 300) - _filter = filter_builder.deploy(web3) - assert _filter.filter_params == { - 'topics': ( - HexBytes(keccak(text="Increased(uint256)")).hex(),)} - assert _filter.data_filter_set == (('uint256', (100, 200, 300)),) - - -def test_build_filter_resetting_build_filter_properties(web3): - contract = web3.eth.contract(abi=CONTRACT_ABI) - filter_builder = contract.events.Increased.build_filter() - # Address is setable from undeployed contract class - filter_builder.address = b'\x10' * 40 - filter_builder.fromBlock = 0 - filter_builder.toBlock = 'latest' - # Test that all filter properties can only set values once - with pytest.raises(ValueError): - filter_builder.address = b'\x00' * 40 - with pytest.raises(ValueError): - filter_builder.fromBlock = 1 - with pytest.raises(ValueError): - filter_builder.toBlock = 50 - - -def test_build_filter_argument_match_single_can_only_be_set_once(web3): - contract = web3.eth.contract(abi=CONTRACT_ABI) - filter_builder = contract.events.Increased.build_filter() - filter_builder.args['value'].match_single(100) - with pytest.raises(ValueError): - filter_builder.args['value'].match_single(200) - - -def test_build_filter_argument_match_any_can_only_be_set_once(web3): - contract = web3.eth.contract(abi=CONTRACT_ABI) - filter_builder = contract.events.Increased.build_filter() - filter_builder.args['value'].match_any(100, 200) - with pytest.raises(ValueError): - filter_builder.args['value'].match_any(200, 300) - - -def test_deployed_build_filter_can_have_no_values_set(web3): - contract = web3.eth.contract(abi=CONTRACT_ABI) - filter_builder = contract.events.Increased.build_filter() - filter_builder.deploy(web3) - with pytest.raises(ValueError): - filter_builder.address = b'\x00' * 40 - with pytest.raises(ValueError): - filter_builder.fromBlock = 1 - with pytest.raises(ValueError): - filter_builder.toBlock = 50 - with pytest.raises(ValueError): - filter_builder.args['value'].match_single(200) - with pytest.raises(ValueError): - filter_builder.args['value'].match_any(200, 300) +import json +import pytest + +from vns_utils import ( + keccak, +) +from hexbytes import ( + HexBytes, +) + +CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 + + +def test_build_filter_topic_signature(web3): + contract = web3.vns.contract(abi=CONTRACT_ABI) + filter_builder = contract.events.Increased.build_filter() + filter_builder.args['value'].match_any(100, 200, 300) + _filter = filter_builder.deploy(web3) + assert _filter.filter_params == { + 'topics': ( + HexBytes(keccak(text="Increased(uint256)")).hex(),)} + assert _filter.data_filter_set == (('uint256', (100, 200, 300)),) + + +def test_build_filter_resetting_build_filter_properties(web3): + contract = web3.vns.contract(abi=CONTRACT_ABI) + filter_builder = contract.events.Increased.build_filter() + # Address is setable from undeployed contract class + filter_builder.address = b'\x10' * 40 + filter_builder.fromBlock = 0 + filter_builder.toBlock = 'latest' + # Test that all filter properties can only set values once + with pytest.raises(ValueError): + filter_builder.address = b'\x00' * 40 + with pytest.raises(ValueError): + filter_builder.fromBlock = 1 + with pytest.raises(ValueError): + filter_builder.toBlock = 50 + + +def test_build_filter_argument_match_single_can_only_be_set_once(web3): + contract = web3.vns.contract(abi=CONTRACT_ABI) + filter_builder = contract.events.Increased.build_filter() + filter_builder.args['value'].match_single(100) + with pytest.raises(ValueError): + filter_builder.args['value'].match_single(200) + + +def test_build_filter_argument_match_any_can_only_be_set_once(web3): + contract = web3.vns.contract(abi=CONTRACT_ABI) + filter_builder = contract.events.Increased.build_filter() + filter_builder.args['value'].match_any(100, 200) + with pytest.raises(ValueError): + filter_builder.args['value'].match_any(200, 300) + + +def test_deployed_build_filter_can_have_no_values_set(web3): + contract = web3.vns.contract(abi=CONTRACT_ABI) + filter_builder = contract.events.Increased.build_filter() + filter_builder.deploy(web3) + with pytest.raises(ValueError): + filter_builder.address = b'\x00' * 40 + with pytest.raises(ValueError): + filter_builder.fromBlock = 1 + with pytest.raises(ValueError): + filter_builder.toBlock = 50 + with pytest.raises(ValueError): + filter_builder.args['value'].match_single(200) + with pytest.raises(ValueError): + filter_builder.args['value'].match_any(200, 300) diff --git a/tests/core/contracts/test_contract_example.py b/tests/core/contracts/test_contract_example.py index 43427e1561..4849235061 100644 --- a/tests/core/contracts/test_contract_example.py +++ b/tests/core/contracts/test_contract_example.py @@ -1,103 +1,103 @@ -# This file is used by the documentation as an example of how to write unit tests with web3.py -import pytest - -from web3 import ( - EthereumTesterProvider, - Web3, -) - - -@pytest.fixture -def tester_provider(): - return EthereumTesterProvider() - - -@pytest.fixture -def eth_tester(tester_provider): - return tester_provider.ethereum_tester - - -@pytest.fixture -def w3(tester_provider): - return Web3(tester_provider) - - -@pytest.fixture -def foo_contract(eth_tester, w3): - # For simplicity of this example we statically define the - # contract code here. You might read your contracts from a - # file, or something else to test with in your own code - # - # pragma solidity^0.5.3; - # - # contract Foo { - # - # string public bar; - # event barred(string _bar); - # - # constructor() public { - # bar = "hello world"; - # } - # - # function setBar(string memory _bar) public { - # bar = _bar; - # emit barred(_bar); - # } - # - # } - - deploy_address = eth_tester.get_accounts()[0] - - abi = """[{"anonymous":false,"inputs":[{"indexed":false,"name":"_bar","type":"string"}],"name":"barred","type":"event"},{"constant":false,"inputs":[{"name":"_bar","type":"string"}],"name":"setBar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"bar","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]""" # noqa: E501 - # This bytecode is the output of compiling with - # solc version:0.5.3+commit.10d17f24.Emscripten.clang - bytecode = """608060405234801561001057600080fd5b506040805190810160405280600b81526020017f68656c6c6f20776f726c640000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6103bb806101166000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c01000000000000000000000000000000000000000000000000000000009004806397bc14aa14610058578063febb0f7e14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b61024c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac9291906102ea565b507f5f71ad82e16f082de5ff496b140e2fbc8621eeb37b36d59b185c3f1364bbd529816040518080602001828103825283818151815260200191508051906020019080838360005b8381101561020f5780820151818401526020810190506101f4565b50505050905090810190601f16801561023c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a150565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102e25780601f106102b7576101008083540402835291602001916102e2565b820191906000526020600020905b8154815290600101906020018083116102c557829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032b57805160ff1916838001178555610359565b82800160010185558215610359579182015b8281111561035857825182559160200191906001019061033d565b5b509050610366919061036a565b5090565b61038c91905b80821115610388576000816000905550600101610370565b5090565b9056fea165627a7a72305820ae6ca683d45ee8a71bba45caee29e4815147cd308f772c853a20dfe08214dbb50029""" # noqa: E501 - - # Create our contract class. - FooContract = w3.eth.contract(abi=abi, bytecode=bytecode) - # issue a transaction to deploy the contract. - tx_hash = FooContract.constructor().transact({ - 'from': deploy_address, - }) - # wait for the transaction to be mined - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash, 180) - # instantiate and return an instance of our contract. - return FooContract(tx_receipt.contractAddress) - - -def test_initial_greeting(foo_contract): - hw = foo_contract.caller.bar() - assert hw == "hello world" - - -def test_can_update_greeting(w3, foo_contract): - # send transaction that updates the greeting - tx_hash = foo_contract.functions.setBar( - "testing contracts is easy", - ).transact({ - 'from': w3.eth.accounts[1], - }) - w3.eth.waitForTransactionReceipt(tx_hash, 180) - - # verify that the contract is now using the updated greeting - hw = foo_contract.caller.bar() - assert hw == "testing contracts is easy" - - -def test_updating_greeting_emits_event(w3, foo_contract): - # send transaction that updates the greeting - tx_hash = foo_contract.functions.setBar( - "testing contracts is easy", - ).transact({ - 'from': w3.eth.accounts[1], - }) - receipt = w3.eth.waitForTransactionReceipt(tx_hash, 180) - - # get all of the `barred` logs for the contract - logs = foo_contract.events.barred.getLogs() - assert len(logs) == 1 - - # verify that the log's data matches the expected value - event = logs[0] - assert event.blockHash == receipt.blockHash - assert event.args._bar == "testing contracts is easy" +# This file is used by the documentation as an example of how to write unit tests with web3.py +import pytest + +from web3 import ( + EthereumTesterProvider, + Web3, +) + + +@pytest.fixture +def tester_provider(): + return EthereumTesterProvider() + + +@pytest.fixture +def vns_tester(tester_provider): + return tester_provider.ethereum_tester + + +@pytest.fixture +def w3(tester_provider): + return Web3(tester_provider) + + +@pytest.fixture +def foo_contract(vns_tester, w3): + # For simplicity of this example we statically define the + # contract code here. You might read your contracts from a + # file, or something else to test with in your own code + # + # pragma solidity^0.5.3; + # + # contract Foo { + # + # string public bar; + # event barred(string _bar); + # + # constructor() public { + # bar = "hello world"; + # } + # + # function setBar(string memory _bar) public { + # bar = _bar; + # emit barred(_bar); + # } + # + # } + + deploy_address = vns_tester.get_accounts()[0] + + abi = """[{"anonymous":false,"inputs":[{"indexed":false,"name":"_bar","type":"string"}],"name":"barred","type":"event"},{"constant":false,"inputs":[{"name":"_bar","type":"string"}],"name":"setBar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"bar","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]""" # noqa: E501 + # This bytecode is the output of compiling with + # solc version:0.5.3+commit.10d17f24.Emscripten.clang + bytecode = """608060405234801561001057600080fd5b506040805190810160405280600b81526020017f68656c6c6f20776f726c640000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6103bb806101166000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c01000000000000000000000000000000000000000000000000000000009004806397bc14aa14610058578063febb0f7e14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b61024c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac9291906102ea565b507f5f71ad82e16f082de5ff496b140e2fbc8621eeb37b36d59b185c3f1364bbd529816040518080602001828103825283818151815260200191508051906020019080838360005b8381101561020f5780820151818401526020810190506101f4565b50505050905090810190601f16801561023c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a150565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102e25780601f106102b7576101008083540402835291602001916102e2565b820191906000526020600020905b8154815290600101906020018083116102c557829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032b57805160ff1916838001178555610359565b82800160010185558215610359579182015b8281111561035857825182559160200191906001019061033d565b5b509050610366919061036a565b5090565b61038c91905b80821115610388576000816000905550600101610370565b5090565b9056fea165627a7a72305820ae6ca683d45ee8a71bba45caee29e4815147cd308f772c853a20dfe08214dbb50029""" # noqa: E501 + + # Create our contract class. + FooContract = w3.vns.contract(abi=abi, bytecode=bytecode) + # issue a transaction to deploy the contract. + tx_hash = FooContract.constructor().transact({ + 'from': deploy_address, + }) + # wait for the transaction to be mined + tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash, 180) + # instantiate and return an instance of our contract. + return FooContract(tx_receipt.contractAddress) + + +def test_initial_greeting(foo_contract): + hw = foo_contract.caller.bar() + assert hw == "hello world" + + +def test_can_update_greeting(w3, foo_contract): + # send transaction that updates the greeting + tx_hash = foo_contract.functions.setBar( + "testing contracts is easy", + ).transact({ + 'from': w3.vns.accounts[1], + }) + w3.vns.waitForTransactionReceipt(tx_hash, 180) + + # verify that the contract is now using the updated greeting + hw = foo_contract.caller.bar() + assert hw == "testing contracts is easy" + + +def test_updating_greeting_emits_event(w3, foo_contract): + # send transaction that updates the greeting + tx_hash = foo_contract.functions.setBar( + "testing contracts is easy", + ).transact({ + 'from': w3.vns.accounts[1], + }) + receipt = w3.vns.waitForTransactionReceipt(tx_hash, 180) + + # get all of the `barred` logs for the contract + logs = foo_contract.events.barred.getLogs() + assert len(logs) == 1 + + # verify that the log's data matches the expected value + event = logs[0] + assert event.blockHash == receipt.blockHash + assert event.args._bar == "testing contracts is easy" diff --git a/tests/core/contracts/test_contract_init.py b/tests/core/contracts/test_contract_init.py index faaa46af60..27ffca38c3 100644 --- a/tests/core/contracts/test_contract_init.py +++ b/tests/core/contracts/test_contract_init.py @@ -1,76 +1,76 @@ -import pytest - -from web3._utils.ens import ( - contract_ens_addresses, - ens_addresses, -) -from web3.exceptions import ( - BadFunctionCallOutput, - NameNotFound, -) - - -@pytest.fixture() -def math_addr(MathContract, address_conversion_func): - web3 = MathContract.web3 - deploy_txn = MathContract.constructor().transact({'from': web3.eth.coinbase}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - return address_conversion_func(deploy_receipt['contractAddress']) - - -def test_contract_with_unset_address(MathContract): - with contract_ens_addresses(MathContract, []): - with pytest.raises(NameNotFound): - MathContract(address='unsetname.eth') - - -def test_contract_with_name_address(MathContract, math_addr): - with contract_ens_addresses(MathContract, [('thedao.eth', math_addr)]): - mc = MathContract(address='thedao.eth') - caller = mc.web3.eth.coinbase - assert mc.address == 'thedao.eth' - assert mc.functions.return13().call({'from': caller}) == 13 - - -def test_contract_with_name_address_from_eth_contract( - web3, - MATH_ABI, - MATH_CODE, - MATH_RUNTIME, - math_addr, -): - with ens_addresses(web3, [('thedao.eth', math_addr)]): - mc = web3.eth.contract( - address='thedao.eth', - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - - caller = mc.web3.eth.coinbase - assert mc.address == 'thedao.eth' - assert mc.functions.return13().call({'from': caller}) == 13 - - -def test_contract_with_name_address_changing(MathContract, math_addr): - # Contract address is validated once on creation - with contract_ens_addresses(MathContract, [('thedao.eth', math_addr)]): - mc = MathContract(address='thedao.eth') - - caller = mc.web3.eth.coinbase - assert mc.address == 'thedao.eth' - - # what happen when name returns no address at all - with contract_ens_addresses(mc, []): - with pytest.raises(NameNotFound): - mc.functions.return13().call({'from': caller}) - - # what happen when name returns address to different contract - with contract_ens_addresses(mc, [('thedao.eth', '0x' + '11' * 20)]): - with pytest.raises(BadFunctionCallOutput): - mc.functions.return13().call({'from': caller}) - - # contract works again when name resolves correctly - with contract_ens_addresses(mc, [('thedao.eth', math_addr)]): - assert mc.functions.return13().call({'from': caller}) == 13 +import pytest + +from web3._utils.ens import ( + contract_ens_addresses, + ens_addresses, +) +from web3.exceptions import ( + BadFunctionCallOutput, + NameNotFound, +) + + +@pytest.fixture() +def math_addr(MathContract, address_conversion_func): + web3 = MathContract.web3 + deploy_txn = MathContract.constructor().transact({'from': web3.vns.coinbase}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + return address_conversion_func(deploy_receipt['contractAddress']) + + +def test_contract_with_unset_address(MathContract): + with contract_ens_addresses(MathContract, []): + with pytest.raises(NameNotFound): + MathContract(address='unsetname.vns') + + +def test_contract_with_name_address(MathContract, math_addr): + with contract_ens_addresses(MathContract, [('thedao.vns', math_addr)]): + mc = MathContract(address='thedao.vns') + caller = mc.web3.vns.coinbase + assert mc.address == 'thedao.vns' + assert mc.functions.return13().call({'from': caller}) == 13 + + +def test_contract_with_name_address_from_vns_contract( + web3, + MATH_ABI, + MATH_CODE, + MATH_RUNTIME, + math_addr, +): + with ens_addresses(web3, [('thedao.vns', math_addr)]): + mc = web3.vns.contract( + address='thedao.vns', + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + + caller = mc.web3.vns.coinbase + assert mc.address == 'thedao.vns' + assert mc.functions.return13().call({'from': caller}) == 13 + + +def test_contract_with_name_address_changing(MathContract, math_addr): + # Contract address is validated once on creation + with contract_ens_addresses(MathContract, [('thedao.vns', math_addr)]): + mc = MathContract(address='thedao.vns') + + caller = mc.web3.vns.coinbase + assert mc.address == 'thedao.vns' + + # what happen when name returns no address at all + with contract_ens_addresses(mc, []): + with pytest.raises(NameNotFound): + mc.functions.return13().call({'from': caller}) + + # what happen when name returns address to different contract + with contract_ens_addresses(mc, [('thedao.vns', '0x' + '11' * 20)]): + with pytest.raises(BadFunctionCallOutput): + mc.functions.return13().call({'from': caller}) + + # contract works again when name resolves correctly + with contract_ens_addresses(mc, [('thedao.vns', math_addr)]): + assert mc.functions.return13().call({'from': caller}) == 13 diff --git a/tests/core/contracts/test_contract_method_abi_decoding.py b/tests/core/contracts/test_contract_method_abi_decoding.py index fafe12a9bb..03d711d1e1 100644 --- a/tests/core/contracts/test_contract_method_abi_decoding.py +++ b/tests/core/contracts/test_contract_method_abi_decoding.py @@ -1,119 +1,105 @@ -from binascii import ( - unhexlify, -) -import json -import pytest - -ABI_A = json.loads('[{"constant":false,"inputs":[],"name":"noargfunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_B = json.loads('[{"constant":false,"inputs":[{"name":"uintarg","type":"uint256"}],"name":"uintfunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_C = json.loads('[{"constant":false,"inputs":[],"name":"namesakefunc","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"bytesarg","type":"bytes32"}],"name":"namesakefunc","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"uintarg","type":"uint256"}],"name":"namesakefunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_D = json.loads('[{ "constant": false, "inputs": [ { "name": "b", "type": "bytes32[]" } ], "name": "byte_array", "outputs": [], "payable": false, "type": "function" }]') # noqa: E501 -ABI_BYTES = json.loads('[{"constant":false,"inputs":[{"name":"bytesarg","type":"bytes"}],"name":"bytesfunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_STRING = json.loads('[{"constant":false,"inputs":[{"name":"stringarg","type":"string"}],"name":"stringfunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_ADDRESS = json.loads('[{"constant":false,"inputs":[{"name":"addressarg","type":"address"}],"name":"addressfunc","outputs":[],"type":"function"}]') # noqa: E501 -ABI_TUPLE = json.loads('[{"constant":false,"inputs":[{"components":[{"name":"owner","type":"address"},{"name":"number","type":"uint256"}],"name":"fromAccount","type":"tuple"},{"components":[{"name":"owner","type":"address"},{"name":"number","type":"uint256"}],"name":"liquidAccount","type":"tuple"},{"components":[{"name":"value","type":"uint256"}],"name":"minLiquidatorRatio","type":"tuple"},{"name":"minValueLiquidated","type":"uint256"},{"name":"owedPreferences","type":"uint256[]"},{"name":"heldPreferences","type":"uint256[]"}],"name":"liquidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]') # noqa: E501 -a32bytes = b'a'.ljust(32, b'\x00') - - -@pytest.mark.parametrize( - 'abi,data,method,expected', - ( - ( - ABI_A, - '0xc4c1a40b', - 'noargfunc', - {}, - ), - ( - ABI_B, - '0xcc6820de0000000000000000000000000000000000000000000000000000000000000001', - 'uintfunc', - {'uintarg': 1}, - ), - ( - ABI_C, - '0x22d86fa3', - 'namesakefunc', - {}, - ), - ( - ABI_C, - '0x40c05b2f0000000000000000000000000000000000000000000000000000000000000001', - 'namesakefunc', - {'uintarg': 1}, - ), - ( - ABI_C, - '0xf931d77c6100000000000000000000000000000000000000000000000000000000000000', - 'namesakefunc', - {'bytesarg': a32bytes}, - ), - ( - ABI_BYTES, - '0xb606a9f6000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000', # noqa: E501 - 'bytesfunc', - {'bytesarg': b'a'}, - ), - ( - ABI_STRING, - '0x33b4005f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000', # noqa: E501 - 'stringfunc', - {'stringarg': 'a'}, - ), - ( - ABI_ADDRESS, - '0x4767be6c000000000000000000000000ffffffffffffffffffffffffffffffffffffffff', - 'addressfunc', - {'addressarg': '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'}, - ), - ( - ABI_TUPLE, - '0xc29a4b71000000000000000000000000bfae42a79ff045659dd0f84e65534f5c4c8100230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db3d3af153cb02f0bc44621db82289280e93500f94a7d1598c397f6b49ecd5ccbc2b464259b96870063493b0dc7409d0fd9fb9860000000000000000000000000000000000000000000000000429d069189e00000000000000000000000000000000000178287f49c4a1d6622fb2ab40000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', # noqa: E501 - 'liquidate', - { - 'fromAccount': ('0xBfae42A79FF045659DD0F84e65534f5c4c810023', 0), - 'liquidAccount': ('0xdb3d3AF153cB02f0Bc44621Db82289280e93500F', 67238809929330522294664880975001390268660278453875034113630810005818923006342), # noqa: E501 - 'minLiquidatorRatio': (300000000000000000,), - 'minValueLiquidated': 500000000000000000000000000000000000000, - 'owedPreferences': [0, 1, 2], - 'heldPreferences': [2, 0, 1], - }, - ), - ), -) -def test_contract_abi_decoding(web3, abi, data, method, expected): - contract = web3.eth.contract(abi=abi) - func, params = contract.decode_function_input(data) - assert func.fn_name == method - assert params == expected - - reinvoke_func = contract.functions[func.fn_name](**params) - rebuild_txn = reinvoke_func.buildTransaction({'gas': 0, 'nonce': 0, 'to': '\x00' * 20}) - assert rebuild_txn['data'] == data - - -@pytest.mark.parametrize( - 'abi,method,expected,data', - ( - ( - ABI_D, - 'byte_array', - { - 'b': [ - unhexlify('5595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa809'), - unhexlify('6f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125'), - ], - }, - '0xf166d6f8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa8096f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125', # noqa: E501 - ), - ), -) -def test_contract_abi_encoding_kwargs(web3, abi, method, expected, data): - contract = web3.eth.contract(abi=abi) - func, params = contract.decode_function_input(data) - assert func.fn_name == method - assert params == expected - - reinvoke_func = contract.functions[func.fn_name](**params) - rebuild_txn = reinvoke_func.buildTransaction({'gas': 0, 'nonce': 0, 'to': '\x00' * 20}) - assert rebuild_txn['data'] == data +from binascii import ( + unhexlify, +) +import json +import pytest + +ABI_A = json.loads('[{"constant":false,"inputs":[],"name":"noargfunc","outputs":[],"type":"function"}]') # noqa: E501 +ABI_B = json.loads('[{"constant":false,"inputs":[{"name":"uintarg","type":"uint256"}],"name":"uintfunc","outputs":[],"type":"function"}]') # noqa: E501 +ABI_C = json.loads('[{"constant":false,"inputs":[],"name":"namesakefunc","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"bytesarg","type":"bytes32"}],"name":"namesakefunc","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"uintarg","type":"uint256"}],"name":"namesakefunc","outputs":[],"type":"function"}]') # noqa: E501 +ABI_D = json.loads('[{ "constant": false, "inputs": [ { "name": "b", "type": "bytes32[]" } ], "name": "byte_array", "outputs": [], "payable": false, "type": "function" }]') # noqa: E501 +ABI_BYTES = json.loads('[{"constant":false,"inputs":[{"name":"bytesarg","type":"bytes"}],"name":"bytesfunc","outputs":[],"type":"function"}]') # noqa: E501 +ABI_STRING = json.loads('[{"constant":false,"inputs":[{"name":"stringarg","type":"string"}],"name":"stringfunc","outputs":[],"type":"function"}]') # noqa: E501 +ABI_ADDRESS = json.loads('[{"constant":false,"inputs":[{"name":"addressarg","type":"address"}],"name":"addressfunc","outputs":[],"type":"function"}]') # noqa: E501 +a32bytes = b'a'.ljust(32, b'\x00') + + +@pytest.mark.parametrize( + 'abi,data,method,expected', + ( + ( + ABI_A, + '0xc4c1a40b', + 'noargfunc', + {}, + ), + ( + ABI_B, + '0xcc6820de0000000000000000000000000000000000000000000000000000000000000001', + 'uintfunc', + {'uintarg': 1}, + ), + ( + ABI_C, + '0x22d86fa3', + 'namesakefunc', + {}, + ), + ( + ABI_C, + '0x40c05b2f0000000000000000000000000000000000000000000000000000000000000001', + 'namesakefunc', + {'uintarg': 1}, + ), + ( + ABI_C, + '0xf931d77c6100000000000000000000000000000000000000000000000000000000000000', + 'namesakefunc', + {'bytesarg': a32bytes}, + ), + ( + ABI_BYTES, + '0xb606a9f6000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000', # noqa: E501 + 'bytesfunc', + {'bytesarg': b'a'}, + ), + ( + ABI_STRING, + '0x33b4005f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000', # noqa: E501 + 'stringfunc', + {'stringarg': 'a'}, + ), + ( + ABI_ADDRESS, + '0x4767be6c000000000000000000000000ffffffffffffffffffffffffffffffffffffffff', + 'addressfunc', + {'addressarg': '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'}, + ), + ), +) +def test_contract_abi_decoding(web3, abi, data, method, expected): + contract = web3.vns.contract(abi=abi) + func, params = contract.decode_function_input(data) + assert func.fn_name == method + assert params == expected + + reinvoke_func = contract.functions[func.fn_name](**params) + rebuild_txn = reinvoke_func.buildTransaction({'gas': 0, 'nonce': 0, 'to': '\x00' * 20}) + assert rebuild_txn['data'] == data + + +@pytest.mark.parametrize( + 'abi,method,expected,data', + ( + ( + ABI_D, + 'byte_array', + { + 'b': [ + unhexlify('5595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa809'), + unhexlify('6f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125'), + ], + }, + '0xf166d6f8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa8096f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125', # noqa: E501 + ), + ), +) +def test_contract_abi_encoding_kwargs(web3, abi, method, expected, data): + contract = web3.vns.contract(abi=abi) + func, params = contract.decode_function_input(data) + assert func.fn_name == method + assert params == expected + + reinvoke_func = contract.functions[func.fn_name](**params) + rebuild_txn = reinvoke_func.buildTransaction({'gas': 0, 'nonce': 0, 'to': '\x00' * 20}) + assert rebuild_txn['data'] == data diff --git a/tests/core/contracts/test_contract_method_abi_encoding.py b/tests/core/contracts/test_contract_method_abi_encoding.py index c5e4540e93..ca2721aa21 100644 --- a/tests/core/contracts/test_contract_method_abi_encoding.py +++ b/tests/core/contracts/test_contract_method_abi_encoding.py @@ -1,138 +1,84 @@ -import json -import pytest - -from web3.exceptions import ( - ValidationError, -) - -ABI_A = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"}]') -ABI_B = json.loads('[{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 -ABI_C = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 -ABI_D = json.loads('[{ "constant": false, "inputs": [ { "name": "b", "type": "bytes32[]" } ], "name": "byte_array", "outputs": [], "payable": false, "type": "function" }]') # noqa: E501 - - -@pytest.mark.parametrize( - 'abi,arguments,data,expected', - ( - pytest.param(ABI_A, [], None, '0x0dbe671f', id="ABI_A, no args, no data"), - pytest.param(ABI_A, [], '0x12345678', '0x12345678', id="ABI_A, no args, some data"), - pytest.param( - ABI_B, - [0], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000000', - id='ABI_B, valid int args, no data' - ), - pytest.param( - ABI_B, - [1], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', - id='ABI_B, valid int args, no data' - ), - pytest.param( - ABI_C, - [1], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', - id='ABI_B, valid int args, no data' - ), - pytest.param( - ABI_C, - [b'a'], - None, - '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', - id='ABI_C, valid byte args, no data' - ), - pytest.param( - ABI_C, - ['0x61'], - None, - '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', - id='ABI_C, valid hex args, no data' - ), - ), -) -def test_contract_abi_encoding(web3, abi, arguments, data, expected): - contract = web3.eth.contract(abi=abi) - actual = contract.encodeABI('a', arguments, data=data) - assert actual == expected - - -def test_contract_abi_encoding_warning(web3): - contract = web3.eth.contract(abi=ABI_C) - - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the "0x" prefix' - ): - - actual = contract.encodeABI('a', ['61'], data=None) - assert actual == '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000' # noqa: E501 - - -def test_contract_abi_encoding_kwargs(web3): - contract = web3.eth.contract(abi=ABI_D) - kwargs = { - 'b': [ - '0x5595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa809', - '0x6f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125', - ], - } - actual = contract.encodeABI('byte_array', kwargs=kwargs) - assert actual == '0xf166d6f8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa8096f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125' # noqa: E501 - - -@pytest.mark.parametrize('arguments', ([b'a'], ['0x61'], ['61'],)) -def test_contract_abi_encoding_strict_with_error(w3_strict_abi, arguments): - contract = w3_strict_abi.eth.contract(abi=ABI_C) - with pytest.raises(ValidationError): - contract.encodeABI('a', arguments, data=None) - - -@pytest.mark.parametrize( - 'abi,arguments,data,expected', - ( - pytest.param(ABI_A, [], None, '0x0dbe671f', id="ABI_A, no args, no data"), - pytest.param(ABI_A, [], '0x12345678', '0x12345678', id="ABI_A, no args, some data"), - pytest.param( - ABI_B, - [0], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000000', - id='ABI_B, valid int args, no data' - ), - pytest.param( - ABI_B, - [1], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', - id='ABI_B, valid int args, no data' - ), - pytest.param( - ABI_C, - [1], - None, - '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', - id='ABI_C, valid int args, no data' - ), - pytest.param( - ABI_C, - [b'00000000000000000000000000000000'], - None, - '0x9f3fab583030303030303030303030303030303030303030303030303030303030303030', - id='ABI_C, valid bytestring args, no data' - ), - pytest.param( - ABI_C, - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - None, - '0x9f3fab580000000000000000000000000000000000000000000000000000000000000000', - id='ABI_C, valid hexstring args, no data' - ), - ), -) -def test_contract_abi_encoding_strict(w3_strict_abi, abi, arguments, data, expected): - contract = w3_strict_abi.eth.contract(abi=abi) - actual = contract.encodeABI('a', arguments, data=data) - assert actual == expected +import json +import pytest + +ABI_A = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"}]') +ABI_B = json.loads('[{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 +ABI_C = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"a","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 +ABI_D = json.loads('[{ "constant": false, "inputs": [ { "name": "b", "type": "bytes32[]" } ], "name": "byte_array", "outputs": [], "payable": false, "type": "function" }]') # noqa: E501 + + +@pytest.mark.parametrize( + 'abi,method,arguments,data,expected', + ( + (ABI_A, 'a', [], None, '0x0dbe671f'), + (ABI_A, 'a', [], '0x12345678', '0x12345678'), + ( + ABI_B, + 'a', + [0], + None, + '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000000', + ), + ( + ABI_B, + 'a', + [1], + None, + '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + ), + ( + ABI_C, + 'a', + [1], + None, + '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + ), + ( + ABI_C, + 'a', + [b'a'], + None, + '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', + ), + ( + ABI_C, + 'a', + ['0x61'], + None, + '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', + ), + ( + ABI_C, + 'a', + ['61'], + None, + '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', + ), + ), +) +def test_contract_abi_encoding(web3, abi, method, arguments, data, expected): + contract = web3.vns.contract(abi=abi) + actual = contract.encodeABI(method, arguments, data=data) + assert actual == expected + + +@pytest.mark.parametrize( + 'abi,method,kwargs,expected', + ( + ( + ABI_D, + 'byte_array', + { + 'b': [ + '0x5595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa809', + '0x6f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125', + ], + }, + '0xf166d6f8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025595c210956e7721f9b692e702708556aa9aabb14ea163e96afa56ffbe9fa8096f8d2fa18448afbfe4f82143c384484ad09a0271f3a3c0eb9f629e703f883125', # noqa: E501 + ), + ), +) +def test_contract_abi_encoding_kwargs(web3, abi, method, kwargs, expected): + contract = web3.vns.contract(abi=abi) + actual = contract.encodeABI(method, kwargs=kwargs) + assert actual == expected diff --git a/tests/core/contracts/test_contract_method_to_argument_matching.py b/tests/core/contracts/test_contract_method_to_argument_matching.py index 353157aa43..1c4a432541 100644 --- a/tests/core/contracts/test_contract_method_to_argument_matching.py +++ b/tests/core/contracts/test_contract_method_to_argument_matching.py @@ -1,187 +1,150 @@ -import json -import pytest - -from web3._utils.abi import ( - get_abi_input_types, -) -from web3._utils.function_identifiers import ( - FallbackFn, -) -from web3.exceptions import ( - ValidationError, -) - -SINGLE_FN_NO_ARGS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 -SINGLE_FN_ONE_ARG = json.loads('[{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 -FALLBACK_FUNCTION = json.loads('[{"constant": false, "inputs": [], "name": "getData", "outputs": [{"name": "r", "type": "uint256"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"payable": false, "stateMutability": "nonpayable", "type": "fallback"}]') # noqa: E501 -MULTIPLE_FUNCTIONS = json.loads(''' -[ - { - "constant": false, - "inputs": [], - "name": "a", - "outputs": [], - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "a", - "outputs": [], - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "a", - "outputs": [], - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "", - "type": "uint8" - } - ], - "name": "a", - "outputs": [], - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "", - "type": "int8" - } - ], - "name": "a", - "outputs": [], - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "", - "type": "tuple[]", - "components": [ - {"name": "", "type": "int256"}, - {"name": "", "type": "bool"} - ] - } - ], - "name": "a", - "outputs": [], - "type": "function" - } -] -''') - - -def test_finds_single_function_without_args(web3): - Contract = web3.eth.contract(abi=SINGLE_FN_NO_ARGS) - - abi = Contract._find_matching_fn_abi('a', []) - assert abi['name'] == 'a' - assert abi['inputs'] == [] - - -def test_finds_single_function_with_args(web3): - Contract = web3.eth.contract(abi=SINGLE_FN_ONE_ARG) - - abi = Contract._find_matching_fn_abi('a', [1234]) - assert abi['name'] == 'a' - assert len(abi['inputs']) == 1 - assert abi['inputs'][0]['type'] == 'uint256' - - -def test_finds_fallback_function(web3): - Contract = web3.eth.contract(abi=FALLBACK_FUNCTION) - - abi = Contract._find_matching_fn_abi(FallbackFn, []) - assert abi['type'] == 'fallback' - - -def test_error_when_no_function_name_match(web3): - Contract = web3.eth.contract(abi=SINGLE_FN_NO_ARGS) - - with pytest.raises(ValidationError): - Contract._find_matching_fn_abi('no_function_name', [1234]) - - -@pytest.mark.parametrize( - 'arguments,expected_types', - ( - ([], []), - ([b'arst'], ['bytes32']), - (['0xf00b47'], ['bytes32']), - (['0x'], ['bytes32']), - ([1234567890], ['uint256']), - # ([255], ['uint8']), # TODO: enable - ([-1], ['int8']), - ([[(-1, True), (2, False)]], ['(int256,bool)[]']), - ) -) -def test_finds_function_with_matching_args(web3, arguments, expected_types): - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - - abi = Contract._find_matching_fn_abi('a', arguments) - assert abi['name'] == 'a' - assert len(abi['inputs']) == len(expected_types) - assert set(get_abi_input_types(abi)) == set(expected_types) - - -def test_finds_function_with_matching_args_deprecation_warning(web3): - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - - with pytest.warns(DeprecationWarning): - abi = Contract._find_matching_fn_abi('a', ['']) - assert abi['name'] == 'a' - assert len(abi['inputs']) == len(['bytes32']) - assert set(get_abi_input_types(abi)) == set(['bytes32']) - - -def test_error_when_duplicate_match(web3): - Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS) - - with pytest.raises(ValidationError): - Contract._find_matching_fn_abi('a', [100]) - - -@pytest.mark.parametrize('arguments', (['0xf00b47'], [b''], [''], ['00' * 16])) -def test_strict_errors_if_type_is_wrong(w3_strict_abi, arguments): - Contract = w3_strict_abi.eth.contract(abi=MULTIPLE_FUNCTIONS) - - with pytest.raises(ValidationError): - Contract._find_matching_fn_abi('a', arguments) - - -@pytest.mark.parametrize( - 'arguments,expected_types', - ( - ([], []), - ([1234567890], ['uint256']), - ([-1], ['int8']), - ([[(-1, True), (2, False)]], ['(int256,bool)[]']), - ) -) -def test_strict_finds_function_with_matching_args(w3_strict_abi, arguments, expected_types): - Contract = w3_strict_abi.eth.contract(abi=MULTIPLE_FUNCTIONS) - - abi = Contract._find_matching_fn_abi('a', arguments) - assert abi['name'] == 'a' - assert len(abi['inputs']) == len(expected_types) - assert set(get_abi_input_types(abi)) == set(expected_types) +import json +import pytest + +from web3._utils.abi import ( + get_abi_input_types, +) +from web3._utils.function_identifiers import ( + FallbackFn, +) +from web3.exceptions import ( + ValidationError, +) + +SINGLE_FN_NO_ARGS = json.loads('[{"constant":false,"inputs":[],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 +SINGLE_FN_ONE_ARG = json.loads('[{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"a","outputs":[],"type":"function"}]') # noqa: E501 +FALLBACK_FUNCTION = json.loads('[{"constant": false, "inputs": [], "name": "getData", "outputs": [{"name": "r", "type": "uint256"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"payable": false, "stateMutability": "nonpayable", "type": "fallback"}]') # noqa: E501 +MULTIPLE_FUNCTIONS = json.loads(''' +[ + { + "constant": false, + "inputs": [], + "name": "a", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "a", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "a", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "uint8" + } + ], + "name": "a", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "int8" + } + ], + "name": "a", + "outputs": [], + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "tuple[]", + "components": [ + {"name": "", "type": "int256"}, + {"name": "", "type": "bool"} + ] + } + ], + "name": "a", + "outputs": [], + "type": "function" + } +] +''') + + +def test_finds_single_function_without_args(web3): + Contract = web3.vns.contract(abi=SINGLE_FN_NO_ARGS) + + abi = Contract._find_matching_fn_abi('a', []) + assert abi['name'] == 'a' + assert abi['inputs'] == [] + + +def test_finds_single_function_with_args(web3): + Contract = web3.vns.contract(abi=SINGLE_FN_ONE_ARG) + + abi = Contract._find_matching_fn_abi('a', [1234]) + assert abi['name'] == 'a' + assert len(abi['inputs']) == 1 + assert abi['inputs'][0]['type'] == 'uint256' + + +def test_finds_fallback_function(web3): + Contract = web3.vns.contract(abi=FALLBACK_FUNCTION) + + abi = Contract._find_matching_fn_abi(FallbackFn, []) + assert abi['type'] == 'fallback' + + +def test_error_when_no_function_name_match(web3): + Contract = web3.vns.contract(abi=SINGLE_FN_NO_ARGS) + + with pytest.raises(ValidationError): + Contract._find_matching_fn_abi('no_function_name', [1234]) + + +@pytest.mark.parametrize( + 'arguments,expected_types', + ( + ([], []), + ([b'arst'], ['bytes32']), + (['0xf00b47'], ['bytes32']), + ([1234567890], ['uint256']), + # ([255], ['uint8']), # TODO: enable + ([-1], ['int8']), + ([[(-1, True), (2, False)]], ['(int256,bool)[]']), + ) +) +def test_finds_function_with_matching_args(web3, arguments, expected_types): + Contract = web3.vns.contract(abi=MULTIPLE_FUNCTIONS) + + abi = Contract._find_matching_fn_abi('a', arguments) + assert abi['name'] == 'a' + assert len(abi['inputs']) == len(expected_types) + assert set(get_abi_input_types(abi)) == set(expected_types) + + +def test_error_when_duplicate_match(web3): + Contract = web3.vns.contract(abi=MULTIPLE_FUNCTIONS) + + with pytest.raises(ValidationError): + Contract._find_matching_fn_abi('a', [100]) diff --git a/tests/core/contracts/test_contract_transact_interface.py b/tests/core/contracts/test_contract_transact_interface.py index e86435c59d..f3e5b34961 100644 --- a/tests/core/contracts/test_contract_transact_interface.py +++ b/tests/core/contracts/test_contract_transact_interface.py @@ -1,323 +1,323 @@ -# -*- coding: utf-8 -*- - -import pytest - -from eth_utils import ( - to_bytes, -) - -from web3._utils.empty import ( - empty, -) -from web3.exceptions import ( - ValidationError, -) - -# Ignore warning in pyethereum 1.6 - will go away with the upgrade -pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") - - -@pytest.fixture() -def math_contract(web3, MathContract, address_conversion_func): - deploy_txn = MathContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _math_contract = MathContract(address=address) - assert _math_contract.address == address - return _math_contract - - -@pytest.fixture() -def string_contract(web3, StringContract, address_conversion_func): - deploy_txn = StringContract.constructor("Caqalai").transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _string_contract = StringContract(address=address) - assert _string_contract.address == address - return _string_contract - - -@pytest.fixture() -def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): - deploy_txn = FallballFunctionContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _fallback_contract = FallballFunctionContract(address=address) - assert _fallback_contract.address == address - return _fallback_contract - - -@pytest.fixture() -def arrays_contract(web3, ArraysContract, address_conversion_func): - # bytes_32 = [keccak('0'), keccak('1')] - bytes32_array = [ - b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 - b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 - ] - byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] - deploy_txn = ArraysContract.constructor(bytes32_array, byte_arr).transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _arrays_contract = ArraysContract(address=address) - return _arrays_contract - - -@pytest.fixture() -def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): - deploy_txn = PayableTesterContract.constructor().transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - address = address_conversion_func(deploy_receipt['contractAddress']) - _payable_tester = PayableTesterContract(address=address) - assert _payable_tester.address == address - return _payable_tester - - -def test_transacting_with_contract_no_arguments(web3, math_contract, transact, call): - initial_value = call(contract=math_contract, - contract_function='counter') - - txn_hash = transact(contract=math_contract, - contract_function='increment') - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=math_contract, - contract_function='counter') - - assert final_value - initial_value == 1 - - -def test_transact_not_sending_ether_to_nonpayable_function( - web3, - payable_tester_contract, - transact, - call): - initial_value = call(contract=payable_tester_contract, - contract_function='wasCalled') - - assert initial_value is False - txn_hash = transact(contract=payable_tester_contract, - contract_function='doNoValueCall') - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=payable_tester_contract, - contract_function='wasCalled') - - assert final_value is True - - -def test_transact_sending_ether_to_nonpayable_function( - web3, - payable_tester_contract, - transact, - call): - initial_value = call(contract=payable_tester_contract, - contract_function='wasCalled') - - assert initial_value is False - with pytest.raises(ValidationError): - txn_hash = transact(contract=payable_tester_contract, - contract_function='doNoValueCall', - tx_params={'value': 1}) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=payable_tester_contract, - contract_function='wasCalled') - - assert final_value is False - - -@pytest.mark.parametrize( - 'transact_args,transact_kwargs', - ( - ((5,), {}), - (tuple(), {'amt': 5}), - ), -) -def test_transacting_with_contract_with_arguments(web3, - math_contract, - transact, - call, - transact_args, - transact_kwargs): - initial_value = call(contract=math_contract, - contract_function='counter') - - txn_hash = transact(contract=math_contract, - contract_function='increment', - func_args=transact_args, - func_kwargs=transact_kwargs) - - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=math_contract, - contract_function='counter') - - assert final_value - initial_value == 5 - - -def test_deploy_when_default_account_is_set(web3, - wait_for_transaction, - STRING_CONTRACT): - web3.eth.defaultAccount = web3.eth.accounts[1] - assert web3.eth.defaultAccount is not empty - - StringContract = web3.eth.contract(**STRING_CONTRACT) - - deploy_txn = StringContract.constructor("Caqalai").transact() - web3.eth.waitForTransactionReceipt(deploy_txn) - txn_after = web3.eth.getTransaction(deploy_txn) - assert txn_after['from'] == web3.eth.defaultAccount - - -def test_transact_when_default_account_is_set(web3, - wait_for_transaction, - math_contract, - transact): - web3.eth.defaultAccount = web3.eth.accounts[1] - assert web3.eth.defaultAccount is not empty - - txn_hash = transact(contract=math_contract, - contract_function='increment') - wait_for_transaction(web3, txn_hash) - txn_after = web3.eth.getTransaction(txn_hash) - assert txn_after['from'] == web3.eth.defaultAccount - - -def test_transacting_with_contract_with_string_argument(web3, string_contract, transact, call): - # eth_abi will pass as raw bytes, no encoding - # unless we encode ourselves - txn_hash = transact(contract=string_contract, - contract_function='setValue', - func_args=["ÄLÄMÖLÖ".encode('utf8')]) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=string_contract, - contract_function='getValue') - - assert final_value == "ÄLÄMÖLÖ" - - -def test_transacting_with_contract_with_bytes32_array_argument(web3, - arrays_contract, - transact, - call): - # new_bytes32_array = [keccak('1'), keccak('2'), keccak('3')] - new_bytes32_array = [ - b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 - b'\xad|[\xef\x02x\x16\xa8\x00\xda\x176DO\xb5\x8a\x80~\xf4\xc9`;xHg?~:h\xeb\x14\xa5', - b"*\x80\xe1\xef\x1dxB\xf2\x7f.k\xe0\x97+\xb7\x08\xb9\xa15\xc3\x88`\xdb\xe7<'\xc3Hl4\xf4\xde", # noqa: E501 - ] - txn_hash = transact(contract=arrays_contract, - contract_function="setBytes32Value", - func_args=[new_bytes32_array]) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=arrays_contract, - contract_function="getBytes32Value") - assert final_value == new_bytes32_array - - -def test_transacting_with_contract_with_byte_array_argument(web3, arrays_contract, transact, call): - new_byte_array = [b'\x03', b'\x03', b'\x03', b'\x03', b'\x03', b'\x03'] - txn_hash = transact(contract=arrays_contract, - contract_function='setByteValue', - func_args=[new_byte_array]) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=arrays_contract, - contract_function='getByteValue') - assert final_value == new_byte_array - - -def test_transacting_with_contract_respects_explicit_gas(web3, - STRING_CONTRACT, - skip_if_testrpc, - wait_for_block, - call, - transact): - skip_if_testrpc(web3) - - wait_for_block(web3) - - StringContract = web3.eth.contract(**STRING_CONTRACT) - - deploy_txn = StringContract.constructor("Caqalai").transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn, 30) - assert deploy_receipt is not None - string_contract = StringContract(address=deploy_receipt['contractAddress']) - - # eth_abi will pass as raw bytes, no encoding - # unless we encode ourselves - txn_hash = transact(contract=string_contract, - contract_function='setValue', - func_args=[to_bytes(text="ÄLÄMÖLÖ")], - tx_kwargs={'gas': 200000}) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash, 30) - assert txn_receipt is not None - - final_value = call(contract=string_contract, - contract_function='getValue') - assert to_bytes(text=final_value) == to_bytes(text="ÄLÄMÖLÖ") - - txn = web3.eth.getTransaction(txn_hash) - assert txn['gas'] == 200000 - - -def test_auto_gas_computation_when_transacting(web3, - STRING_CONTRACT, - skip_if_testrpc, - wait_for_block, - call, - transact): - skip_if_testrpc(web3) - - wait_for_block(web3) - - StringContract = web3.eth.contract(**STRING_CONTRACT) - - deploy_txn = StringContract.constructor("Caqalai").transact() - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn, 30) - assert deploy_receipt is not None - string_contract = StringContract(address=deploy_receipt['contractAddress']) - - gas_estimate = string_contract.functions.setValue(to_bytes(text="ÄLÄMÖLÖ")).estimateGas() - - # eth_abi will pass as raw bytes, no encoding - # unless we encode ourselves - txn_hash = transact(contract=string_contract, - contract_function="setValue", - func_args=[to_bytes(text="ÄLÄMÖLÖ")]) - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash, 30) - assert txn_receipt is not None - - final_value = call(contract=string_contract, - contract_function='getValue') - assert to_bytes(text=final_value) == to_bytes(text="ÄLÄMÖLÖ") - - txn = web3.eth.getTransaction(txn_hash) - assert txn['gas'] == gas_estimate + 100000 - - -def test_fallback_transacting_with_contract(web3, fallback_function_contract, call): - initial_value = call(contract=fallback_function_contract, - contract_function='getData') - txn_hash = fallback_function_contract.fallback.transact() - txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash) - assert txn_receipt is not None - - final_value = call(contract=fallback_function_contract, - contract_function='getData') - - assert final_value - initial_value == 1 +# -*- coding: utf-8 -*- + +import pytest + +from vns_utils import ( + to_bytes, +) + +from web3._utils.empty import ( + empty, +) +from web3.exceptions import ( + ValidationError, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +@pytest.fixture() +def math_contract(web3, MathContract, address_conversion_func): + deploy_txn = MathContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _math_contract = MathContract(address=address) + assert _math_contract.address == address + return _math_contract + + +@pytest.fixture() +def string_contract(web3, StringContract, address_conversion_func): + deploy_txn = StringContract.constructor("Caqalai").transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _string_contract = StringContract(address=address) + assert _string_contract.address == address + return _string_contract + + +@pytest.fixture() +def fallback_function_contract(web3, FallballFunctionContract, address_conversion_func): + deploy_txn = FallballFunctionContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _fallback_contract = FallballFunctionContract(address=address) + assert _fallback_contract.address == address + return _fallback_contract + + +@pytest.fixture() +def arrays_contract(web3, ArraysContract, address_conversion_func): + # bytes_32 = [keccak('0'), keccak('1')] + bytes32_array = [ + b'\x04HR\xb2\xa6p\xad\xe5@~x\xfb(c\xc5\x1d\xe9\xfc\xb9eB\xa0q\x86\xfe:\xed\xa6\xbb\x8a\x11m', # noqa: E501 + b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 + ] + byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] + deploy_txn = ArraysContract.constructor(bytes32_array, byte_arr).transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _arrays_contract = ArraysContract(address=address) + return _arrays_contract + + +@pytest.fixture() +def payable_tester_contract(web3, PayableTesterContract, address_conversion_func): + deploy_txn = PayableTesterContract.constructor().transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + address = address_conversion_func(deploy_receipt['contractAddress']) + _payable_tester = PayableTesterContract(address=address) + assert _payable_tester.address == address + return _payable_tester + + +def test_transacting_with_contract_no_arguments(web3, math_contract, transact, call): + initial_value = call(contract=math_contract, + contract_function='counter') + + txn_hash = transact(contract=math_contract, + contract_function='increment') + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=math_contract, + contract_function='counter') + + assert final_value - initial_value == 1 + + +def test_transact_not_sending_ether_to_nonpayable_function( + web3, + payable_tester_contract, + transact, + call): + initial_value = call(contract=payable_tester_contract, + contract_function='wasCalled') + + assert initial_value is False + txn_hash = transact(contract=payable_tester_contract, + contract_function='doNoValueCall') + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=payable_tester_contract, + contract_function='wasCalled') + + assert final_value is True + + +def test_transact_sending_ether_to_nonpayable_function( + web3, + payable_tester_contract, + transact, + call): + initial_value = call(contract=payable_tester_contract, + contract_function='wasCalled') + + assert initial_value is False + with pytest.raises(ValidationError): + txn_hash = transact(contract=payable_tester_contract, + contract_function='doNoValueCall', + tx_params={'value': 1}) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=payable_tester_contract, + contract_function='wasCalled') + + assert final_value is False + + +@pytest.mark.parametrize( + 'transact_args,transact_kwargs', + ( + ((5,), {}), + (tuple(), {'amt': 5}), + ), +) +def test_transacting_with_contract_with_arguments(web3, + math_contract, + transact, + call, + transact_args, + transact_kwargs): + initial_value = call(contract=math_contract, + contract_function='counter') + + txn_hash = transact(contract=math_contract, + contract_function='increment', + func_args=transact_args, + func_kwargs=transact_kwargs) + + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=math_contract, + contract_function='counter') + + assert final_value - initial_value == 5 + + +def test_deploy_when_default_account_is_set(web3, + wait_for_transaction, + STRING_CONTRACT): + web3.vns.defaultAccount = web3.vns.accounts[1] + assert web3.vns.defaultAccount is not empty + + StringContract = web3.vns.contract(**STRING_CONTRACT) + + deploy_txn = StringContract.constructor("Caqalai").transact() + web3.vns.waitForTransactionReceipt(deploy_txn) + txn_after = web3.vns.getTransaction(deploy_txn) + assert txn_after['from'] == web3.vns.defaultAccount + + +def test_transact_when_default_account_is_set(web3, + wait_for_transaction, + math_contract, + transact): + web3.vns.defaultAccount = web3.vns.accounts[1] + assert web3.vns.defaultAccount is not empty + + txn_hash = transact(contract=math_contract, + contract_function='increment') + wait_for_transaction(web3, txn_hash) + txn_after = web3.vns.getTransaction(txn_hash) + assert txn_after['from'] == web3.vns.defaultAccount + + +def test_transacting_with_contract_with_string_argument(web3, string_contract, transact, call): + # vns_abi will pass as raw bytes, no encoding + # unless we encode ourselves + txn_hash = transact(contract=string_contract, + contract_function='setValue', + func_args=["ÄLÄMÖLÖ".encode('utf8')]) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=string_contract, + contract_function='getValue') + + assert final_value == "ÄLÄMÖLÖ" + + +def test_transacting_with_contract_with_bytes32_array_argument(web3, + arrays_contract, + transact, + call): + # new_bytes32_array = [keccak('1'), keccak('2'), keccak('3')] + new_bytes32_array = [ + b'\xc8\x9e\xfd\xaaT\xc0\xf2\x0cz\xdfa(\x82\xdf\tP\xf5\xa9Qc~\x03\x07\xcd\xcbLg/)\x8b\x8b\xc6', # noqa: E501 + b'\xad|[\xef\x02x\x16\xa8\x00\xda\x176DO\xb5\x8a\x80~\xf4\xc9`;xHg?~:h\xeb\x14\xa5', + b"*\x80\xe1\xef\x1dxB\xf2\x7f.k\xe0\x97+\xb7\x08\xb9\xa15\xc3\x88`\xdb\xe7<'\xc3Hl4\xf4\xde", # noqa: E501 + ] + txn_hash = transact(contract=arrays_contract, + contract_function="setBytes32Value", + func_args=[new_bytes32_array]) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=arrays_contract, + contract_function="getBytes32Value") + assert final_value == new_bytes32_array + + +def test_transacting_with_contract_with_byte_array_argument(web3, arrays_contract, transact, call): + new_byte_array = [b'\x03', b'\x03', b'\x03', b'\x03', b'\x03', b'\x03'] + txn_hash = transact(contract=arrays_contract, + contract_function='setByteValue', + func_args=[new_byte_array]) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=arrays_contract, + contract_function='getByteValue') + assert final_value == new_byte_array + + +def test_transacting_with_contract_respects_explicit_gas(web3, + STRING_CONTRACT, + skip_if_testrpc, + wait_for_block, + call, + transact): + skip_if_testrpc(web3) + + wait_for_block(web3) + + StringContract = web3.vns.contract(**STRING_CONTRACT) + + deploy_txn = StringContract.constructor("Caqalai").transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn, 30) + assert deploy_receipt is not None + string_contract = StringContract(address=deploy_receipt['contractAddress']) + + # vns_abi will pass as raw bytes, no encoding + # unless we encode ourselves + txn_hash = transact(contract=string_contract, + contract_function='setValue', + func_args=[to_bytes(text="ÄLÄMÖLÖ")], + tx_kwargs={'gas': 200000}) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash, 30) + assert txn_receipt is not None + + final_value = call(contract=string_contract, + contract_function='getValue') + assert to_bytes(text=final_value) == to_bytes(text="ÄLÄMÖLÖ") + + txn = web3.vns.getTransaction(txn_hash) + assert txn['gas'] == 200000 + + +def test_auto_gas_computation_when_transacting(web3, + STRING_CONTRACT, + skip_if_testrpc, + wait_for_block, + call, + transact): + skip_if_testrpc(web3) + + wait_for_block(web3) + + StringContract = web3.vns.contract(**STRING_CONTRACT) + + deploy_txn = StringContract.constructor("Caqalai").transact() + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn, 30) + assert deploy_receipt is not None + string_contract = StringContract(address=deploy_receipt['contractAddress']) + + gas_estimate = string_contract.functions.setValue(to_bytes(text="ÄLÄMÖLÖ")).estimateGas() + + # vns_abi will pass as raw bytes, no encoding + # unless we encode ourselves + txn_hash = transact(contract=string_contract, + contract_function="setValue", + func_args=[to_bytes(text="ÄLÄMÖLÖ")]) + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash, 30) + assert txn_receipt is not None + + final_value = call(contract=string_contract, + contract_function='getValue') + assert to_bytes(text=final_value) == to_bytes(text="ÄLÄMÖLÖ") + + txn = web3.vns.getTransaction(txn_hash) + assert txn['gas'] == gas_estimate + 100000 + + +def test_fallback_transacting_with_contract(web3, fallback_function_contract, call): + initial_value = call(contract=fallback_function_contract, + contract_function='getData') + txn_hash = fallback_function_contract.fallback.transact() + txn_receipt = web3.vns.waitForTransactionReceipt(txn_hash) + assert txn_receipt is not None + + final_value = call(contract=fallback_function_contract, + contract_function='getData') + + assert final_value - initial_value == 1 diff --git a/tests/core/contracts/test_contract_util_functions.py b/tests/core/contracts/test_contract_util_functions.py index c73f8aa66f..bd7761c090 100644 --- a/tests/core/contracts/test_contract_util_functions.py +++ b/tests/core/contracts/test_contract_util_functions.py @@ -1,13 +1,13 @@ -from web3.contract import ( - parse_block_identifier_int, -) - - -# This tests negative block number identifiers, which behave like python -# list slices, with -1 being the latest block and -2 being the block before that. -# This test is necessary because transaction calls allow negative block indexes, although -# getBlock() does not allow negative block identifiers. Support for negative block identifier -# will likely be removed in v5. -def test_parse_block_identifier_int(web3): - last_num = web3.eth.getBlock('latest').number - assert 0 == parse_block_identifier_int(web3, -1 - last_num) +from web3.contract import ( + parse_block_identifier_int, +) + + +# This tests negative block number identifiers, which behave like python +# list slices, with -1 being the latest block and -2 being the block before that. +# This test is necessary because transaction calls allow negative block indexes, although +# getBlock() does not allow negative block identifiers. Support for negative block identifier +# will likely be removed in v5. +def test_parse_block_identifier_int(web3): + last_num = web3.vns.getBlock('latest').number + assert 0 == parse_block_identifier_int(web3, -1 - last_num) diff --git a/tests/core/contracts/test_extracting_event_data.py b/tests/core/contracts/test_extracting_event_data.py index fd1a8de286..3894e8425c 100644 --- a/tests/core/contracts/test_extracting_event_data.py +++ b/tests/core/contracts/test_extracting_event_data.py @@ -1,739 +1,216 @@ -import pytest -import re - -from eth_utils import ( - is_same_address, -) -from eth_utils.toolz import ( - dissoc, -) - -from web3._utils.events import ( - get_event_data, -) -from web3.exceptions import ( - LogTopicError, - ValidationError, -) -from web3.logs import ( - DISCARD, - IGNORE, - STRICT, - WARN, -) - - -@pytest.fixture() -def Emitter(web3, EMITTER): - return web3.eth.contract(**EMITTER) - - -@pytest.fixture() -def emitter(web3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): - wait_for_block(web3) - deploy_txn_hash = Emitter.constructor().transact({'from': web3.eth.coinbase, 'gas': 1000000}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == Emitter.bytecode_runtime - _emitter = Emitter(address=contract_address) - assert _emitter.address == contract_address - return _emitter - - -@pytest.fixture() -def EventContract(web3, EVENT_CONTRACT): - return web3.eth.contract(**EVENT_CONTRACT) - - -@pytest.fixture() -def event_contract( - web3, - EventContract, - wait_for_transaction, - wait_for_block, - address_conversion_func): - - wait_for_block(web3) - deploy_txn_hash = EventContract.constructor().transact({ - 'from': web3.eth.coinbase, 'gas': 1000000 - }) - deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == EventContract.bytecode_runtime - event_contract = EventContract(address=contract_address) - assert event_contract.address == contract_address - return event_contract - - -@pytest.fixture() -def IndexedEventContract(web3, INDEXED_EVENT_CONTRACT): - return web3.eth.contract(**INDEXED_EVENT_CONTRACT) - - -@pytest.fixture() -def indexed_event_contract( - web3, - IndexedEventContract, - wait_for_transaction, - wait_for_block, - address_conversion_func): - - wait_for_block(web3) - deploy_txn_hash = IndexedEventContract.constructor().transact({ - 'from': web3.eth.coinbase, 'gas': 1000000 - }) - deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == IndexedEventContract.bytecode_runtime - indexed_event_contract = IndexedEventContract(address=contract_address) - assert indexed_event_contract.address == contract_address - return indexed_event_contract - - -@pytest.fixture() -def dup_txn_receipt( - web3, - indexed_event_contract, - wait_for_transaction, - event_contract): - - emitter_fn = indexed_event_contract.functions.logTwoEvents - - txn_hash = emitter_fn(12345).transact() - wait_for_transaction(web3, txn_hash) - - event_contract_fn = event_contract.functions.logTwoEvents - dup_txn_hash = event_contract_fn(12345).transact() - return wait_for_transaction(web3, dup_txn_hash) - - -@pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args', - ( - ('logNoArgs', 'LogAnonymous', [], {}), - ('logNoArgs', 'LogNoArguments', [], {}), - ('logSingle', 'LogSingleArg', [12345], {'arg0': 12345}), - ('logSingle', 'LogSingleWithIndex', [12345], {'arg0': 12345}), - ('logSingle', 'LogSingleAnonymous', [12345], {'arg0': 12345}), - ('logDouble', 'LogDoubleArg', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ('logDouble', 'LogDoubleAnonymous', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ('logDouble', 'LogDoubleWithIndex', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ( - 'logTriple', - 'LogTripleArg', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - ), - ( - 'logTriple', - 'LogTripleWithIndex', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - ), - ( - 'logQuadruple', - 'LogQuadrupleArg', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - ), - ( - 'logQuadruple', - 'LogQuadrupleWithIndex', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - ), - ) -) -def test_event_data_extraction(web3, - emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids, - contract_fn, - event_name, - call_args, - expected_args): - emitter_fn = emitter.functions[contract_fn] - event_id = getattr(emitter_event_ids, event_name) - txn_hash = emitter_fn(event_id, *call_args).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_abi = emitter._find_matching_event_abi(event_name) - - event_topic = getattr(emitter_log_topics, event_name) - is_anonymous = event_abi['anonymous'] - - if is_anonymous: - assert event_topic not in log_entry['topics'] - else: - assert event_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == event_name - - -@pytest.mark.parametrize( - 'call_args,expected_args', - ( - ( - [[b'13'], [b'54']], - { - 'arg0': b'H\x7f\xad\xb3\x16zAS7\xa5\x0c\xfe\xe2%T\xb7\x17\x81p\xf04~\x8d(\x93\x8e\x19\x97k\xd9"1', # noqa: E501 - 'arg1': [b'54'] - } - ), - ( - [[b'1'], [b'5']], - { - 'arg0': b' F=9\n\x03\xb6\xe1\x00\xc5\xb7\xce\xf5\xa5\xac\x08\x08\xb8\xaf\xc4d=\xdb\xda\xf1\x05|a\x0f.\xa1!', # noqa: E501 - 'arg1': [b'5\x00']} - ), - ) -) -def test_event_data_extraction_bytes(web3, - emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids, - call_args, - expected_args): - emitter_fn = emitter.functions.logListArgs - txn_hash = emitter_fn(*call_args).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_name = 'LogListArgs' - event_abi = emitter._find_matching_event_abi(event_name) - - event_topic = getattr(emitter_log_topics, event_name) - - assert event_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == event_name - - -def test_event_data_extraction_bytes_with_warning(web3, - emitter, - wait_for_transaction, - emitter_log_topics): - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the "0x" prefix' - ): - txn_hash = emitter.functions.logListArgs(['13'], ['54']).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_name = 'LogListArgs' - event_abi = emitter._find_matching_event_abi(event_name) - - event_topic = getattr(emitter_log_topics, event_name) - - assert event_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - expected_args = { - 'arg0': b']\x0b\xf6sp\xbe\xa2L\xa9is\xe4\xab\xb7\xfa+nVJpgt\xa7\x8f:\xa4\x9f\xdb\x93\xf0\x8f\xae', # noqa: E501 - 'arg1': [b'T\x00'] - } - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == event_name - - -@pytest.mark.parametrize( - 'call_args', - ( - ( - [[b'1312'], [b'4354']], - ), - ( - [[b'1'], [b'5']], - ), - ) -) -def test_event_data_extraction_bytes_strict_with_errors(strict_emitter, - call_args): - emitter_fn = strict_emitter.functions.logListArgs - with pytest.raises(ValidationError): - emitter_fn(*call_args).transact() - - -def test_dynamic_length_argument_extraction(web3, - emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids): - string_0 = "this-is-the-first-string-which-exceeds-32-bytes-in-length" - string_1 = "this-is-the-second-string-which-exceeds-32-bytes-in-length" - txn_hash = emitter.functions.logDynamicArgs(string_0, string_1).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_abi = emitter._find_matching_event_abi('LogDynamicArgs') - - event_topic = emitter_log_topics.LogDynamicArgs - assert event_topic in log_entry['topics'] - - string_0_topic = web3.keccak(text=string_0) - assert string_0_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - - expected_args = { - "arg0": string_0_topic, - "arg1": string_1, - } - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == 'LogDynamicArgs' - - -def test_argument_extraction_strict_bytes_types(w3_strict_abi, - strict_emitter, - wait_for_transaction, - emitter_log_topics): - arg_0 = [b'12'] - arg_1 = [b'12'] - txn_hash = strict_emitter.functions.logListArgs(arg_0, arg_1).transact() - txn_receipt = wait_for_transaction(w3_strict_abi, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - assert len(log_entry['topics']) == 2 - - event_abi = strict_emitter._find_matching_event_abi('LogListArgs') - - event_topic = emitter_log_topics.LogListArgs - assert event_topic in log_entry['topics'] - - encoded_arg_0 = w3_strict_abi.codec.encode_abi(['bytes2'], arg_0) - padded_arg_0 = encoded_arg_0.ljust(32, b'\x00') - arg_0_topic = w3_strict_abi.keccak(padded_arg_0) - assert arg_0_topic in log_entry['topics'] - - event_data = get_event_data(w3_strict_abi.codec, event_abi, log_entry) - - expected_args = { - "arg0": arg_0_topic, - "arg1": arg_1 - } - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], strict_emitter.address) - assert event_data['event'] == 'LogListArgs' - - -@pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args,warning_msg,process_receipt', - ( - ( - 'logNoArgs', - 'LogAnonymous', - [], - {}, - 'Expected non-anonymous event to have 1 or more topics', - True - ), - ( - 'logNoArgs', - 'LogAnonymous', - [], - {}, - 'Expected non-anonymous event to have 1 or more topics', - False - ), - ( - 'logNoArgs', - 'LogNoArguments', - [], - {}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logNoArgs', - 'LogNoArguments', - [], - {}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logSingle', - 'LogSingleArg', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logSingle', - 'LogSingleArg', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logSingle', - 'LogSingleWithIndex', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logSingle', - 'LogSingleWithIndex', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logSingle', - 'LogSingleAnonymous', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logSingle', - 'LogSingleAnonymous', - [12345], - {'arg0': 12345}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logDouble', - 'LogDoubleArg', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logDouble', - 'LogDoubleArg', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logDouble', - 'LogDoubleAnonymous', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logDouble', - 'LogDoubleAnonymous', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logDouble', - 'LogDoubleWithIndex', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - True - ), - ( - 'logDouble', - 'LogDoubleWithIndex', - [12345, 54321], - {'arg0': 12345, 'arg1': 54321}, - 'The event signature did not match the provided ABI', - False - ), - ( - 'logTriple', - 'LogTripleArg', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - 'The event signature did not match the provided ABI', - True, - ), - ( - 'logTriple', - 'LogTripleArg', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - 'The event signature did not match the provided ABI', - False, - ), - ( - 'logTriple', - 'LogTripleWithIndex', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - 'The event signature did not match the provided ABI', - True, - ), - ( - 'logTriple', - 'LogTripleWithIndex', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - 'The event signature did not match the provided ABI', - False, - ), - ( - 'logQuadruple', - 'LogQuadrupleArg', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - 'The event signature did not match the provided ABI', - True, - ), - ( - 'logQuadruple', - 'LogQuadrupleArg', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - 'The event signature did not match the provided ABI', - False, - ), - ( - 'logQuadruple', - 'LogQuadrupleWithIndex', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - 'The event signature did not match the provided ABI', - True, - ), - ( - 'logQuadruple', - 'LogQuadrupleWithIndex', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - 'The event signature did not match the provided ABI', - False, - ), - ) -) -def test_event_rich_log( - web3, - emitter, - emitter_event_ids, - wait_for_transaction, - contract_fn, - event_name, - warning_msg, - call_args, - process_receipt, - expected_args): - - emitter_fn = emitter.functions[contract_fn] - event_id = getattr(emitter_event_ids, event_name) - txn_hash = emitter_fn(event_id, *call_args).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - event_instance = emitter.events[event_name]() - - if process_receipt: - processed_logs = event_instance.processReceipt(txn_receipt) - assert len(processed_logs) == 1 - rich_log = processed_logs[0] - elif not process_receipt: - rich_log = event_instance.processLog(txn_receipt['logs'][0]) - else: - raise Exception('Unreachable!') - - assert rich_log['args'] == expected_args - assert rich_log.args == expected_args - for arg in expected_args: - assert getattr(rich_log.args, arg) == expected_args[arg] - assert rich_log['blockHash'] == txn_receipt['blockHash'] - assert rich_log['blockNumber'] == txn_receipt['blockNumber'] - assert rich_log['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(rich_log['address'], emitter.address) - assert rich_log['event'] == event_name - - quiet_event = emitter.events['LogBytes'] - with pytest.warns(UserWarning, match=warning_msg): - empty_rich_log = quiet_event().processReceipt(txn_receipt) - assert empty_rich_log == tuple() - - -@pytest.mark.parametrize('process_receipt', (True, False)) -def test_event_rich_log_with_byte_args( - web3, - emitter, - emitter_event_ids, - wait_for_transaction, - process_receipt): - - txn_hash = emitter.functions.logListArgs([b'13'], [b'54']).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - event_instance = emitter.events.LogListArgs() - - if process_receipt: - processed_logs = event_instance.processReceipt(txn_receipt) - assert len(processed_logs) == 1 - rich_log = processed_logs[0] - elif not process_receipt: - rich_log = event_instance.processLog(txn_receipt['logs'][0]) - else: - raise Exception('Unreachable!') - - expected_args = { - 'arg0': b'H\x7f\xad\xb3\x16zAS7\xa5\x0c\xfe\xe2%T\xb7\x17\x81p\xf04~\x8d(\x93\x8e\x19\x97k\xd9"1', # noqa: E501 - 'arg1': [b'54'] - } - assert rich_log['args'] == expected_args - assert rich_log.args == expected_args - for arg in expected_args: - assert getattr(rich_log.args, arg) == expected_args[arg] - assert rich_log['blockHash'] == txn_receipt['blockHash'] - assert rich_log['blockNumber'] == txn_receipt['blockNumber'] - assert rich_log['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(rich_log['address'], emitter.address) - assert rich_log['event'] == 'LogListArgs' - - -def test_receipt_processing_with_discard_flag( - web3, - event_contract, - indexed_event_contract, - dup_txn_receipt, - wait_for_transaction): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - returned_logs = event_instance.processReceipt(dup_txn_receipt, errors=DISCARD) - assert returned_logs == () - - -def test_receipt_processing_with_ignore_flag( - web3, - event_contract, - indexed_event_contract, - dup_txn_receipt, - wait_for_transaction): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - returned_logs = event_instance.processReceipt(dup_txn_receipt, errors=IGNORE) - assert len(returned_logs) == 2 - - # Check that the correct error is appended to the log - first_log = returned_logs[0] - log_error = re.compile("Expected 1 log topics. Got 0") - assert log_error.search(str(first_log.errors)) is not None - - # Then, do the same with the other log: - second_log = returned_logs[1] - abi_error = re.compile("The event signature did not match the provided ABI") - assert abi_error.search(str(second_log.errors)) is not None - - for log in returned_logs: - # Check that the returned log is the same as what got sent in, - # except for the added errors field - orig_log = dissoc(dict(log), 'errors') - assert orig_log in dup_txn_receipt['logs'] - assert is_same_address(log['address'], event_contract.address) - - -def test_receipt_processing_with_warn_flag( - web3, - indexed_event_contract, - dup_txn_receipt): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - with pytest.warns(UserWarning, match='Expected 1 log topics. Got 0'): - returned_logs = event_instance.processReceipt(dup_txn_receipt, errors=WARN) - assert len(returned_logs) == 0 - - -def test_receipt_processing_with_strict_flag( - web3, - indexed_event_contract, - dup_txn_receipt): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - with pytest.raises(LogTopicError, match="Expected 1 log topics. Got 0"): - event_instance.processReceipt(dup_txn_receipt, errors=STRICT) - - -def test_receipt_processing_with_invalid_flag( - web3, - indexed_event_contract, - dup_txn_receipt): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - with pytest.raises(AttributeError, match=f"Error flag must be one of: "): - event_instance.processReceipt(dup_txn_receipt, errors='not-a-flag') - - -def test_receipt_processing_with_no_flag( - web3, - indexed_event_contract, - dup_txn_receipt): - - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - with pytest.warns(UserWarning, match='Expected 1 log topics. Got 0'): - returned_log = event_instance.processReceipt(dup_txn_receipt) - assert len(returned_log) == 0 - - -def test_single_log_processing_with_errors( - web3, - indexed_event_contract, - dup_txn_receipt): - event_instance = indexed_event_contract.events.LogSingleWithIndex() - - with pytest.raises(LogTopicError, match="Expected 1 log topics. Got 0"): - event_instance.processLog(dup_txn_receipt['logs'][0]) +import pytest + +from vns_utils import ( + is_same_address, +) + +from web3._utils.events import ( + get_event_data, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +@pytest.fixture() +def Emitter(web3, EMITTER): + return web3.vns.contract(**EMITTER) + + +@pytest.fixture() +def emitter(web3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): + wait_for_block(web3) + deploy_txn_hash = Emitter.constructor().transact({'from': web3.vns.coinbase, 'gas': 1000000}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn_hash) + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + + bytecode = web3.vns.getCode(contract_address) + assert bytecode == Emitter.bytecode_runtime + _emitter = Emitter(address=contract_address) + assert _emitter.address == contract_address + return _emitter + + +@pytest.mark.parametrize( + 'contract_fn,event_name,call_args,expected_args', + ( + ('logNoArgs', 'LogAnonymous', [], {}), + ('logNoArgs', 'LogNoArguments', [], {}), + ('logSingle', 'LogSingleArg', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleWithIndex', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleAnonymous', [12345], {'arg0': 12345}), + ('logDouble', 'LogDoubleArg', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleAnonymous', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleWithIndex', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ( + 'logTriple', + 'LogTripleArg', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logTriple', + 'LogTripleWithIndex', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logQuadruple', + 'LogQuadrupleArg', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ( + 'logQuadruple', + 'LogQuadrupleWithIndex', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ) +) +def test_event_data_extraction(web3, + emitter, + wait_for_transaction, + emitter_log_topics, + emitter_event_ids, + contract_fn, + event_name, + call_args, + expected_args): + emitter_fn = emitter.functions[contract_fn] + event_id = getattr(emitter_event_ids, event_name) + txn_hash = emitter_fn(event_id, *call_args).transact() + txn_receipt = wait_for_transaction(web3, txn_hash) + + assert len(txn_receipt['logs']) == 1 + log_entry = txn_receipt['logs'][0] + + event_abi = emitter._find_matching_event_abi(event_name) + + event_topic = getattr(emitter_log_topics, event_name) + is_anonymous = event_abi['anonymous'] + + if is_anonymous: + assert event_topic not in log_entry['topics'] + else: + assert event_topic in log_entry['topics'] + + event_data = get_event_data(event_abi, log_entry) + + assert event_data['args'] == expected_args + assert event_data['blockHash'] == txn_receipt['blockHash'] + assert event_data['blockNumber'] == txn_receipt['blockNumber'] + assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] + assert is_same_address(event_data['address'], emitter.address) + assert event_data['event'] == event_name + + +def test_dynamic_length_argument_extraction(web3, + emitter, + wait_for_transaction, + emitter_log_topics, + emitter_event_ids): + string_0 = "this-is-the-first-string-which-exceeds-32-bytes-in-length" + string_1 = "this-is-the-second-string-which-exceeds-32-bytes-in-length" + txn_hash = emitter.functions.logDynamicArgs(string_0, string_1).transact() + txn_receipt = wait_for_transaction(web3, txn_hash) + + assert len(txn_receipt['logs']) == 1 + log_entry = txn_receipt['logs'][0] + + event_abi = emitter._find_matching_event_abi('LogDynamicArgs') + + event_topic = emitter_log_topics.LogDynamicArgs + assert event_topic in log_entry['topics'] + + string_0_topic = web3.keccak(text=string_0) + assert string_0_topic in log_entry['topics'] + + event_data = get_event_data(event_abi, log_entry) + + expected_args = { + "arg0": string_0_topic, + "arg1": string_1, + } + + assert event_data['args'] == expected_args + assert event_data['blockHash'] == txn_receipt['blockHash'] + assert event_data['blockNumber'] == txn_receipt['blockNumber'] + assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] + assert is_same_address(event_data['address'], emitter.address) + assert event_data['event'] == 'LogDynamicArgs' + + +@pytest.mark.parametrize( + 'contract_fn,event_name,call_args,expected_args', + ( + ('logNoArgs', 'LogAnonymous', [], {}), + ('logNoArgs', 'LogNoArguments', [], {}), + ('logSingle', 'LogSingleArg', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleWithIndex', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleAnonymous', [12345], {'arg0': 12345}), + ('logDouble', 'LogDoubleArg', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleAnonymous', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleWithIndex', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ( + 'logTriple', + 'LogTripleArg', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logTriple', + 'LogTripleWithIndex', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logQuadruple', + 'LogQuadrupleArg', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ( + 'logQuadruple', + 'LogQuadrupleWithIndex', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ) +) +def test_event_rich_log( + web3, + emitter, + emitter_event_ids, + wait_for_transaction, + contract_fn, + event_name, + call_args, + expected_args): + + emitter_fn = emitter.functions[contract_fn] + event_id = getattr(emitter_event_ids, event_name) + txn_hash = emitter_fn(event_id, *call_args).transact() + txn_receipt = wait_for_transaction(web3, txn_hash) + + event_instance = emitter.events[event_name]() + + rich_logs = event_instance.processReceipt(txn_receipt) + + assert len(rich_logs) == 1 + + rich_log = rich_logs[0] + + assert rich_log['args'] == expected_args + assert rich_log.args == expected_args + for arg in expected_args: + assert getattr(rich_log.args, arg) == expected_args[arg] + assert rich_log['blockHash'] == txn_receipt['blockHash'] + assert rich_log['blockNumber'] == txn_receipt['blockNumber'] + assert rich_log['transactionIndex'] == txn_receipt['transactionIndex'] + assert is_same_address(rich_log['address'], emitter.address) + assert rich_log['event'] == event_name + + quiet_event = emitter.events['LogBytes'] + empty_rich_log = quiet_event().processReceipt(txn_receipt) + assert empty_rich_log == tuple() diff --git a/tests/core/contracts/test_extracting_event_data_old.py b/tests/core/contracts/test_extracting_event_data_old.py index e100762125..462059a486 100644 --- a/tests/core/contracts/test_extracting_event_data_old.py +++ b/tests/core/contracts/test_extracting_event_data_old.py @@ -1,141 +1,141 @@ -import pytest - -from eth_utils import ( - is_same_address, -) - -from web3._utils.events import ( - get_event_data, -) - -# Ignore warning in pyethereum 1.6 - will go away with the upgrade -pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") - - -@pytest.fixture() -def Emitter(web3, EMITTER): - return web3.eth.contract(**EMITTER) - - -@pytest.fixture() -def emitter(web3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): - wait_for_block(web3) - deploy_txn_hash = Emitter.constructor().transact({'from': web3.eth.coinbase, 'gas': 1000000}) - deploy_receipt = web3.eth.waitForTransactionReceipt(deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3.eth.getCode(contract_address) - assert bytecode == Emitter.bytecode_runtime - _emitter = Emitter(address=contract_address) - assert _emitter.address == contract_address - return _emitter - - -@pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args', - ( - ('logNoArgs', 'LogAnonymous', [], {}), - ('logNoArgs', 'LogNoArguments', [], {}), - ('logSingle', 'LogSingleArg', [12345], {'arg0': 12345}), - ('logSingle', 'LogSingleWithIndex', [12345], {'arg0': 12345}), - ('logSingle', 'LogSingleAnonymous', [12345], {'arg0': 12345}), - ('logDouble', 'LogDoubleArg', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ('logDouble', 'LogDoubleAnonymous', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ('logDouble', 'LogDoubleWithIndex', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), - ( - 'logTriple', - 'LogTripleArg', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - ), - ( - 'logTriple', - 'LogTripleWithIndex', - [12345, 54321, 98765], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, - ), - ( - 'logQuadruple', - 'LogQuadrupleArg', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - ), - ( - 'logQuadruple', - 'LogQuadrupleWithIndex', - [12345, 54321, 98765, 56789], - {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, - ), - ) -) -def test_event_data_extraction(web3, - emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids, - contract_fn, - event_name, - call_args, - expected_args): - function = getattr(emitter.functions, contract_fn) - event_id = getattr(emitter_event_ids, event_name) - txn_hash = function(event_id, *call_args).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_abi = emitter._find_matching_event_abi(event_name) - - event_topic = getattr(emitter_log_topics, event_name) - is_anonymous = event_abi['anonymous'] - - if is_anonymous: - assert event_topic not in log_entry['topics'] - else: - assert event_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == event_name - - -def test_dynamic_length_argument_extraction(web3, - emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids): - string_0 = "this-is-the-first-string-which-exceeds-32-bytes-in-length" - string_1 = "this-is-the-second-string-which-exceeds-32-bytes-in-length" - txn_hash = emitter.functions.logDynamicArgs(string_0, string_1).transact() - txn_receipt = wait_for_transaction(web3, txn_hash) - - assert len(txn_receipt['logs']) == 1 - log_entry = txn_receipt['logs'][0] - - event_abi = emitter._find_matching_event_abi('LogDynamicArgs') - - event_topic = emitter_log_topics.LogDynamicArgs - assert event_topic in log_entry['topics'] - - string_0_topic = web3.keccak(text=string_0) - assert string_0_topic in log_entry['topics'] - - event_data = get_event_data(web3.codec, event_abi, log_entry) - - expected_args = { - "arg0": string_0_topic, - "arg1": string_1, - } - - assert event_data['args'] == expected_args - assert event_data['blockHash'] == txn_receipt['blockHash'] - assert event_data['blockNumber'] == txn_receipt['blockNumber'] - assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] - assert is_same_address(event_data['address'], emitter.address) - assert event_data['event'] == 'LogDynamicArgs' +import pytest + +from vns_utils import ( + is_same_address, +) + +from web3._utils.events import ( + get_event_data, +) + +# Ignore warning in pyethereum 1.6 - will go away with the upgrade +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +@pytest.fixture() +def Emitter(web3, EMITTER): + return web3.vns.contract(**EMITTER) + + +@pytest.fixture() +def emitter(web3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func): + wait_for_block(web3) + deploy_txn_hash = Emitter.constructor().transact({'from': web3.vns.coinbase, 'gas': 1000000}) + deploy_receipt = web3.vns.waitForTransactionReceipt(deploy_txn_hash) + contract_address = address_conversion_func(deploy_receipt['contractAddress']) + + bytecode = web3.vns.getCode(contract_address) + assert bytecode == Emitter.bytecode_runtime + _emitter = Emitter(address=contract_address) + assert _emitter.address == contract_address + return _emitter + + +@pytest.mark.parametrize( + 'contract_fn,event_name,call_args,expected_args', + ( + ('logNoArgs', 'LogAnonymous', [], {}), + ('logNoArgs', 'LogNoArguments', [], {}), + ('logSingle', 'LogSingleArg', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleWithIndex', [12345], {'arg0': 12345}), + ('logSingle', 'LogSingleAnonymous', [12345], {'arg0': 12345}), + ('logDouble', 'LogDoubleArg', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleAnonymous', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ('logDouble', 'LogDoubleWithIndex', [12345, 54321], {'arg0': 12345, 'arg1': 54321}), + ( + 'logTriple', + 'LogTripleArg', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logTriple', + 'LogTripleWithIndex', + [12345, 54321, 98765], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765}, + ), + ( + 'logQuadruple', + 'LogQuadrupleArg', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ( + 'logQuadruple', + 'LogQuadrupleWithIndex', + [12345, 54321, 98765, 56789], + {'arg0': 12345, 'arg1': 54321, 'arg2': 98765, 'arg3': 56789}, + ), + ) +) +def test_event_data_extraction(web3, + emitter, + wait_for_transaction, + emitter_log_topics, + emitter_event_ids, + contract_fn, + event_name, + call_args, + expected_args): + function = getattr(emitter.functions, contract_fn) + event_id = getattr(emitter_event_ids, event_name) + txn_hash = function(event_id, *call_args).transact() + txn_receipt = wait_for_transaction(web3, txn_hash) + + assert len(txn_receipt['logs']) == 1 + log_entry = txn_receipt['logs'][0] + + event_abi = emitter._find_matching_event_abi(event_name) + + event_topic = getattr(emitter_log_topics, event_name) + is_anonymous = event_abi['anonymous'] + + if is_anonymous: + assert event_topic not in log_entry['topics'] + else: + assert event_topic in log_entry['topics'] + + event_data = get_event_data(event_abi, log_entry) + + assert event_data['args'] == expected_args + assert event_data['blockHash'] == txn_receipt['blockHash'] + assert event_data['blockNumber'] == txn_receipt['blockNumber'] + assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] + assert is_same_address(event_data['address'], emitter.address) + assert event_data['event'] == event_name + + +def test_dynamic_length_argument_extraction(web3, + emitter, + wait_for_transaction, + emitter_log_topics, + emitter_event_ids): + string_0 = "this-is-the-first-string-which-exceeds-32-bytes-in-length" + string_1 = "this-is-the-second-string-which-exceeds-32-bytes-in-length" + txn_hash = emitter.functions.logDynamicArgs(string_0, string_1).transact() + txn_receipt = wait_for_transaction(web3, txn_hash) + + assert len(txn_receipt['logs']) == 1 + log_entry = txn_receipt['logs'][0] + + event_abi = emitter._find_matching_event_abi('LogDynamicArgs') + + event_topic = emitter_log_topics.LogDynamicArgs + assert event_topic in log_entry['topics'] + + string_0_topic = web3.keccak(text=string_0) + assert string_0_topic in log_entry['topics'] + + event_data = get_event_data(event_abi, log_entry) + + expected_args = { + "arg0": string_0_topic, + "arg1": string_1, + } + + assert event_data['args'] == expected_args + assert event_data['blockHash'] == txn_receipt['blockHash'] + assert event_data['blockNumber'] == txn_receipt['blockNumber'] + assert event_data['transactionIndex'] == txn_receipt['transactionIndex'] + assert is_same_address(event_data['address'], emitter.address) + assert event_data['event'] == 'LogDynamicArgs' diff --git a/tests/core/contracts/test_implicit_contract.py b/tests/core/contracts/test_implicit_contract.py index 41cd405cbb..90b1283cbf 100644 --- a/tests/core/contracts/test_implicit_contract.py +++ b/tests/core/contracts/test_implicit_contract.py @@ -1,119 +1,106 @@ -import pytest - -from eth_utils import ( - is_integer, -) - -from web3.contract import ( - ImplicitContract, -) - - -@pytest.fixture() -def math_contract(web3, MATH_ABI, MATH_CODE, MATH_RUNTIME, address_conversion_func): - # Deploy math contract - # NOTE Must use non-specialized contract factory or else deploy() doesn't work - MathContract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ) - tx_hash = MathContract.constructor().transact() - tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash) - math_address = address_conversion_func(tx_receipt['contractAddress']) - # Return interactive contract instance at deployed address - # TODO Does parent class not implement 'deploy()' for a reason? - MathContract = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_CODE, - bytecode_runtime=MATH_RUNTIME, - ContractFactoryClass=ImplicitContract, - ) - with pytest.warns(DeprecationWarning, match='deprecated in favor of contract.caller'): - contract = MathContract(math_address) - assert contract.address == math_address - return contract - - -@pytest.fixture() -def get_transaction_count(web3): - def get_transaction_count(blocknum_or_label): - block = web3.eth.getBlock(blocknum_or_label) - # Return the blocknum if we requested this via labels - # so we can directly query the block next time (using the same API call) - # Either way, return the number of transactions in the given block - if blocknum_or_label in ["pending", "latest", "earliest"]: - return block.number, len(block.transactions) - else: - return len(block.transactions) - return get_transaction_count - - -def test_implicitcontract_call_default(math_contract, get_transaction_count): - # When a function is called that defaults to call - blocknum, starting_txns = get_transaction_count("pending") - with pytest.warns(DeprecationWarning, match='deprecated in favor of classic contract syntax'): - start_count = math_contract.counter() - assert is_integer(start_count) - # Check that a call was made and not a transact - # (Auto-mining is enabled, so query by block number) - assert get_transaction_count(blocknum) == starting_txns - # Check that no blocks were mined - assert get_transaction_count("pending") == (blocknum, 0) - - -def test_implicitcontract_transact_default(web3, math_contract, get_transaction_count): - # Use to verify correct operation later on - with pytest.warns(DeprecationWarning, match='deprecated in favor of classic contract syntax'): - start_count = math_contract.counter() - - assert is_integer(start_count) # Verify correct type - # When a function is called that defaults to transact - blocknum, starting_txns = get_transaction_count("pending") - with pytest.warns(DeprecationWarning, - match='deprecated in favor of classic contract syntax') as warnings: - math_contract.increment(transact={}) - # Check that a transaction was made and not a call - assert math_contract.counter() - start_count == 1 - # Check that the correct number of warnings are raised - assert len(warnings) == 2 - # (Auto-mining is enabled, so query by block number) - assert get_transaction_count(blocknum) == starting_txns + 1 - # Check that only one block was mined - assert get_transaction_count("pending") == (blocknum + 1, 0) - - -def test_implicitcontract_call_override(math_contract, get_transaction_count): - # When a function is called with transact override that defaults to call - blocknum, starting_txns = get_transaction_count("pending") - with pytest.warns(DeprecationWarning, match='deprecated in favor of classic contract syntax'): - math_contract.counter(transact={}) - # Check that a transaction was made and not a call - # (Auto-mining is enabled, so query by block number) - assert get_transaction_count(blocknum) == starting_txns + 1 - # Check that only one block was mined - assert get_transaction_count("pending") == (blocknum + 1, 0) - - -def test_implicitcontract_transact_override(math_contract, get_transaction_count): - # Use to verify correct operation later on - with pytest.warns(DeprecationWarning, match='deprecated in favor of classic contract syntax'): - start_count = math_contract.counter() - assert is_integer(start_count) # Verify correct type - # When a function is called with call override that defaults to transact - blocknum, starting_txns = get_transaction_count("pending") - with pytest.warns(DeprecationWarning, - match='deprecated in favor of classic contract syntax') as warnings: - math_contract.increment(call={}) - # Check that a call was made and not a transact - assert math_contract.counter() - start_count == 0 - assert len(warnings) == 2 - # (Auto-mining is enabled, so query by block number) - assert get_transaction_count(blocknum) == starting_txns - # Check that no blocks were mined - assert get_transaction_count("pending") == (blocknum, 0) - - -def test_implicitcontract_deprecation_warning(math_contract): - with pytest.warns(DeprecationWarning, match='deprecated in favor of classic contract syntax'): - math_contract.counter(transact={}) +import pytest + +from vns_utils import ( + is_integer, +) + +from web3.contract import ( + ImplicitContract, +) + + +@pytest.fixture() +def math_contract(web3, MATH_ABI, MATH_CODE, MATH_RUNTIME, address_conversion_func): + # Deploy math contract + # NOTE Must use non-specialized contract factory or else deploy() doesn't work + MathContract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ) + tx_hash = MathContract.constructor().transact() + tx_receipt = web3.vns.waitForTransactionReceipt(tx_hash) + math_address = address_conversion_func(tx_receipt['contractAddress']) + # Return interactive contract instance at deployed address + # TODO Does parent class not implement 'deploy()' for a reason? + MathContract = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_CODE, + bytecode_runtime=MATH_RUNTIME, + ContractFactoryClass=ImplicitContract, + ) + contract = MathContract(math_address) + assert contract.address == math_address + return contract + + +@pytest.fixture() +def get_transaction_count(web3): + def get_transaction_count(blocknum_or_label): + block = web3.vns.getBlock(blocknum_or_label) + # Return the blocknum if we requested this via labels + # so we can directly query the block next time (using the same API call) + # Either way, return the number of transactions in the given block + if blocknum_or_label in ["pending", "latest", "earliest"]: + return block.number, len(block.transactions) + else: + return len(block.transactions) + return get_transaction_count + + +def test_implicitcontract_call_default(math_contract, get_transaction_count): + # When a function is called that defaults to call + blocknum, starting_txns = get_transaction_count("pending") + start_count = math_contract.counter() + assert is_integer(start_count) + # Check that a call was made and not a transact + # (Auto-mining is enabled, so query by block number) + assert get_transaction_count(blocknum) == starting_txns + # Check that no blocks were mined + assert get_transaction_count("pending") == (blocknum, 0) + + +def test_implicitcontract_transact_default(web3, math_contract, get_transaction_count): + # Use to verify correct operation later on + start_count = math_contract.counter() + assert is_integer(start_count) # Verify correct type + # When a function is called that defaults to transact + blocknum, starting_txns = get_transaction_count("pending") + math_contract.increment(transact={}) + # Check that a transaction was made and not a call + assert math_contract.counter() - start_count == 1 + # (Auto-mining is enabled, so query by block number) + assert get_transaction_count(blocknum) == starting_txns + 1 + # Check that only one block was mined + assert get_transaction_count("pending") == (blocknum + 1, 0) + + +def test_implicitcontract_call_override(math_contract, get_transaction_count): + # When a function is called with transact override that defaults to call + blocknum, starting_txns = get_transaction_count("pending") + math_contract.counter(transact={}) + # Check that a transaction was made and not a call + # (Auto-mining is enabled, so query by block number) + assert get_transaction_count(blocknum) == starting_txns + 1 + # Check that only one block was mined + assert get_transaction_count("pending") == (blocknum + 1, 0) + + +def test_implicitcontract_transact_override(math_contract, get_transaction_count): + # Use to verify correct operation later on + start_count = math_contract.counter() + assert is_integer(start_count) # Verify correct type + # When a function is called with call override that defaults to transact + blocknum, starting_txns = get_transaction_count("pending") + math_contract.increment(call={}) + # Check that a call was made and not a transact + assert math_contract.counter() - start_count == 0 + # (Auto-mining is enabled, so query by block number) + assert get_transaction_count(blocknum) == starting_txns + # Check that no blocks were mined + assert get_transaction_count("pending") == (blocknum, 0) + + +def test_implicitcontract_deprecation_warning(math_contract): + with pytest.warns(DeprecationWarning): + math_contract.counter(transact={}) diff --git a/tests/core/core/block-utils/test_select_method_for_block_identifier.py b/tests/core/core/block-utils/test_select_method_for_block_identifier.py index 8bcebb37f4..d833702f45 100644 --- a/tests/core/core/block-utils/test_select_method_for_block_identifier.py +++ b/tests/core/core/block-utils/test_select_method_for_block_identifier.py @@ -1,43 +1,42 @@ -import pytest - -from eth_utils.toolz import ( - partial, -) - -from web3._utils.blocks import ( - select_method_for_block_identifier, -) - -selector_fn = partial( - select_method_for_block_identifier, - if_hash='test_hash', - if_number='test_number', - if_predefined='test_predefined', -) - - -@pytest.mark.parametrize( - 'input,expected', - ( - ('latest', 'test_predefined'), - ('pending', 'test_predefined'), - ('earliest', 'test_predefined'), - (-1, ValueError), - (0, 'test_number'), - (1, 'test_number'), - (4000000, 'test_number'), - ('0x0', 'test_number'), - ('0x00', 'test_number'), - ('0x1', 'test_number'), - ('0x01', 'test_number'), - (hex(4000000), 'test_number'), - ('0x' + ''.zfill(64), 'test_hash'), - ), -) -def test_select_method_for_block_identifier(input, expected): - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - selector_fn(input) - else: - actual = selector_fn(input) - assert actual == expected +import pytest + +from web3._utils.blocks import ( + select_method_for_block_identifier, +) +from web3._utils.toolz import ( + partial, +) + +selector_fn = partial( + select_method_for_block_identifier, + if_hash='test_hash', + if_number='test_number', + if_predefined='test_predefined', +) + + +@pytest.mark.parametrize( + 'input,expected', + ( + ('latest', 'test_predefined'), + ('pending', 'test_predefined'), + ('earliest', 'test_predefined'), + (-1, ValueError), + (0, 'test_number'), + (1, 'test_number'), + (4000000, 'test_number'), + ('0x0', 'test_number'), + ('0x00', 'test_number'), + ('0x1', 'test_number'), + ('0x01', 'test_number'), + (hex(4000000), 'test_number'), + ('0x' + ''.zfill(64), 'test_hash'), + ), +) +def test_select_method_for_block_identifier(input, expected): + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + selector_fn(input) + else: + actual = selector_fn(input) + assert actual == expected diff --git a/tests/core/empty-object/test_empty_object_is_falsy.py b/tests/core/empty-object/test_empty_object_is_falsy.py index e9aaaf26b8..8567588b77 100644 --- a/tests/core/empty-object/test_empty_object_is_falsy.py +++ b/tests/core/empty-object/test_empty_object_is_falsy.py @@ -1,8 +1,8 @@ -from web3._utils.empty import ( - empty, -) - - -def test_empty_object_is_falsy(): - assert bool(empty) is False - assert not empty +from web3._utils.empty import ( + empty, +) + + +def test_empty_object_is_falsy(): + assert bool(empty) is False + assert not empty diff --git a/tests/core/eth-module/conftest.py b/tests/core/eth-module/conftest.py index 1da778510f..d17a1f68b9 100644 --- a/tests/core/eth-module/conftest.py +++ b/tests/core/eth-module/conftest.py @@ -1,41 +1,41 @@ -import json -import pytest - - -@pytest.fixture -def account_password(): - return "this-is-not-a-secure-password" - - -@pytest.fixture -def extra_accounts(web3, account_password): - num_accounts_to_create = 10 - len(web3.eth.accounts) - - for i in range(num_accounts_to_create): - web3.personal.newAccount(account_password) - - return web3.eth.accounts - - -CONTRACT_CODE = b"606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 - - -CONTRACT_RUNTIME = b"0x60606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 - - -CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 - - -@pytest.fixture(scope="session") -def MATH_CODE(): - return CONTRACT_CODE - - -@pytest.fixture(scope="session") -def MATH_RUNTIME(): - return CONTRACT_RUNTIME - - -@pytest.fixture(scope="session") -def MATH_ABI(): - return CONTRACT_ABI +import json +import pytest + + +@pytest.fixture +def account_password(): + return "this-is-not-a-secure-password" + + +@pytest.fixture +def extra_accounts(web3, account_password): + num_accounts_to_create = 10 - len(web3.vns.accounts) + + for i in range(num_accounts_to_create): + web3.personal.newAccount(account_password) + + return web3.vns.accounts + + +CONTRACT_CODE = b"606060405261022e806100126000396000f360606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 + + +CONTRACT_RUNTIME = b"0x60606040523615610074576000357c01000000000000000000000000000000000000000000000000000000009004806316216f391461007657806361bc221a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d578063dcf537b11461014057610074565b005b610083600480505061016c565b6040518082815260200191505060405180910390f35b6100a6600480505061017f565b6040518082815260200191505060405180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b6040518082815260200191505060405180910390f35b6101566004808035906020019091905050610217565b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90565b60006000505481565b6000816000600082828250540192505081905550600060005054905080507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b60006007820290508050809050610229565b91905056" # noqa: E501 + + +CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501 + + +@pytest.fixture(scope="session") +def MATH_CODE(): + return CONTRACT_CODE + + +@pytest.fixture(scope="session") +def MATH_RUNTIME(): + return CONTRACT_RUNTIME + + +@pytest.fixture(scope="session") +def MATH_ABI(): + return CONTRACT_ABI diff --git a/tests/core/eth-module/test_accounts.py b/tests/core/eth-module/test_accounts.py index 97ed78cd5c..827453639d 100644 --- a/tests/core/eth-module/test_accounts.py +++ b/tests/core/eth-module/test_accounts.py @@ -1,417 +1,404 @@ -# coding=utf-8 - -import pytest - -from eth_account.messages import ( - encode_defunct, -) -from eth_utils import ( - is_checksum_address, -) -from eth_utils.toolz import ( - dissoc, -) -from hexbytes import ( - HexBytes, -) - -from web3 import ( - Account, - Web3, -) -from web3._utils.encoding import ( - to_bytes, - to_hex, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - -# from https://github.com/ethereum/tests/blob/3930ca3a9a377107d5792b3e7202f79c688f1a67/BasicTests/txtest.json # noqa: 501 -ETH_TEST_TRANSACTIONS = [ - { - "chainId": None, - "key": "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", - "nonce": 0, - "gasPrice": 1000000000000, - "gas": 10000, - "to": "0x13978aee95f38490e9769C39B2773Ed763d9cd5F", - "value": 10000000000000000, - "data": "", - "unsigned": "eb8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc1000080808080", # noqa: 501 - "signed": "f86b8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc10000801ba0eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4a014a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1" # noqa: 501 - }, - { - "chainId": None, - "key": "c87f65ff3f271bf5dc8643484f66b200109caffe4bf98c4cb393dc35740b28c0", - "nonce": 0, - "gasPrice": 1000000000000, - "gas": 10000, - "to": "", - "value": 0, - "data": "6025515b525b600a37f260003556601b596020356000355760015b525b54602052f260255860005b525b54602052f2", # noqa: 501 - "unsigned": "f83f8085e8d4a510008227108080af6025515b525b600a37f260003556601b596020356000355760015b525b54602052f260255860005b525b54602052f2808080", # noqa: 501 - "signed": "f87f8085e8d4a510008227108080af6025515b525b600a37f260003556601b596020356000355760015b525b54602052f260255860005b525b54602052f21ba05afed0244d0da90b67cf8979b0f246432a5112c0d31e8d5eedd2bc17b171c694a0bb1035c834677c2e1185b8dc90ca6d1fa585ab3d7ef23707e1a497a98e752d1b" # noqa: 501 - } -] - - -@pytest.fixture -def PRIVATE_BYTES(): - return b'unicorns' * 4 - - -@pytest.fixture -def PRIVATE_BYTES_ALT(PRIVATE_BYTES): - return b'rainbows' * 4 - - -@pytest.fixture -def web3js_key(): - return '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' - - -@pytest.fixture -def web3js_password(): - return 'test!' - - -@pytest.fixture(params=['instance', 'class']) -def acct(request, web3): - if request.param == 'instance': - return web3.eth.account - elif request.param == 'class': - return Account - raise Exception('Unreachable!') - - -@pytest.fixture() -def w3(): - return Web3(EthereumTesterProvider()) - - -def test_eth_account_create_variation(acct): - account1 = acct.create() - account2 = acct.create() - assert account1 != account2 - - -def test_eth_account_from_key_reproducible(acct, PRIVATE_BYTES): - account1 = acct.from_key(PRIVATE_BYTES) - account2 = acct.from_key(PRIVATE_BYTES) - assert bytes(account1) == PRIVATE_BYTES - assert bytes(account1) == bytes(account2) - assert isinstance(str(account1), str) - - -def test_eth_account_from_key_diverge(acct, PRIVATE_BYTES, PRIVATE_BYTES_ALT): - account1 = acct.from_key(PRIVATE_BYTES) - account2 = acct.from_key(PRIVATE_BYTES_ALT) - assert bytes(account2) == PRIVATE_BYTES_ALT - assert bytes(account1) != bytes(account2) - - -def test_eth_account_from_key_seed_restrictions(acct): - with pytest.raises(ValueError): - acct.from_key(b'') - with pytest.raises(ValueError): - acct.from_key(b'\xff' * 31) - with pytest.raises(ValueError): - acct.from_key(b'\xff' * 33) - - -def test_eth_account_from_key_properties(acct, PRIVATE_BYTES): - account = acct.from_key(PRIVATE_BYTES) - assert callable(account.signHash) - assert callable(account.sign_transaction) - assert is_checksum_address(account.address) - assert account.address == '0xa79F6f349C853F9Ea0B29636779ae3Cb4E3BA729' - assert account.key == PRIVATE_BYTES - - -def test_eth_account_create_properties(acct): - account = acct.create() - assert callable(account.signHash) - assert callable(account.sign_transaction) - assert is_checksum_address(account.address) - assert isinstance(account.key, bytes) and len(account.key) == 32 - - -def test_eth_account_recover_transaction_example(acct): - raw_tx_hex = '0xf8640d843b9aca00830e57e0945b2063246f2191f18f2675cedb8b28102e957458018025a00c753084e5a8290219324c1a3a86d4064ded2d15979b1ea790734aaa2ceaafc1a0229ca4538106819fd3a5509dd383e8fe4b731c6870339556a5c06feb9cf330bb' # noqa: E501 - from_account = acct.recover_transaction(raw_tx_hex) - assert from_account == '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4' - - -def test_eth_account_recover_transaction_with_literal(acct): - raw_tx = 0xf8640d843b9aca00830e57e0945b2063246f2191f18f2675cedb8b28102e957458018025a00c753084e5a8290219324c1a3a86d4064ded2d15979b1ea790734aaa2ceaafc1a0229ca4538106819fd3a5509dd383e8fe4b731c6870339556a5c06feb9cf330bb # noqa: E501 - from_account = acct.recover_transaction(raw_tx) - assert from_account == '0xFeC2079e80465cc8C687fFF9EE6386ca447aFec4' - - -def test_eth_account_recover_message(acct): - v, r, s = ( - 28, - '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', - '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce', - ) - message_text = "I♥SF" - message = encode_defunct(text=message_text) - from_account = acct.recover_message(message, vrs=(v, r, s)) - assert from_account == '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' - - -@pytest.mark.parametrize( - 'signature_bytes', - [ - # test signature bytes with standard v (0 in this case) - b'\x0cu0\x84\xe5\xa8)\x02\x192L\x1a:\x86\xd4\x06M\xed-\x15\x97\x9b\x1e\xa7\x90sJ\xaa,\xea\xaf\xc1"\x9c\xa4S\x81\x06\x81\x9f\xd3\xa5P\x9d\xd3\x83\xe8\xfeKs\x1chp3\x95V\xa5\xc0o\xeb\x9c\xf30\xbb\x00', # noqa: E501 - # test signature bytes with chain-naive v (27 in this case) - b'\x0cu0\x84\xe5\xa8)\x02\x192L\x1a:\x86\xd4\x06M\xed-\x15\x97\x9b\x1e\xa7\x90sJ\xaa,\xea\xaf\xc1"\x9c\xa4S\x81\x06\x81\x9f\xd3\xa5P\x9d\xd3\x83\xe8\xfeKs\x1chp3\x95V\xa5\xc0o\xeb\x9c\xf30\xbb\x1b', # noqa: E501 - ], - ids=['test_sig_bytes_standard_v', 'test_sig_bytes_chain_naive_v'] -) -def test_eth_account_recover_signature_bytes(acct, signature_bytes): - msg = encode_defunct(b'\xbb\r\x8a\xba\x9f\xf7\xa1= len(block_info['blocks']): - raise ValueError("no more blocks to mine") - - block_info['head_block_number'] += num_blocks - - def _get_block_by_number(method, params, block_info=_block_info): - block_id = params[0] - blocks = block_info['blocks'] - head_block_number = block_info['head_block_number'] - - if block_id == 'latest': - return blocks[head_block_number] - elif block_id == 'pending': - if head_block_number + 1 >= len(blocks): - raise ValueError("no pending block") - return blocks[head_block_number + 1] - elif block_id == 'earliest': - return blocks[0] - elif is_integer(block_id): - if block_id <= head_block_number: - return blocks[block_id] - else: - return None - else: - raise TypeError('Invalid type for block_id') - - def _get_block_by_hash(method, params, block_info=_block_info): - block_hash = params[0] - blocks = block_info['blocks'] - head_block_number = block_info['head_block_number'] - - blocks_by_hash = { - block['hash']: block - for block - in blocks - } - try: - block = blocks_by_hash[block_hash] - if block['number'] <= head_block_number: - return block - else: - return None - except KeyError: - return None - - return construct_result_generator_middleware({ - 'eth_getBlockByNumber': _get_block_by_number, - 'eth_getBlockByHash': _get_block_by_hash, - 'evm_mine': _evm_mine, - }) - return _construct_block_data_middleware - - -@pytest.fixture -def block_data_middleware(construct_block_data_middleware): - return construct_block_data_middleware(5) - - -@pytest.fixture -def result_generator_middleware(): - return construct_result_generator_middleware({ - 'fake_endpoint': lambda *_: str(uuid.uuid4()), - 'not_whitelisted': lambda *_: str(uuid.uuid4()), - }) - - -@pytest.fixture -def latest_block_based_cache_middleware(): - return construct_latest_block_based_cache_middleware( - cache_class=dict, - average_block_time_sample_size=1, - default_average_block_time=0.1, - rpc_whitelist={'fake_endpoint'}, - ) - - -@pytest.fixture -def w3(w3_base, - result_generator_middleware, - block_data_middleware, - latest_block_based_cache_middleware): - w3_base.middleware_onion.add(block_data_middleware) - w3_base.middleware_onion.add(result_generator_middleware) - w3_base.middleware_onion.add(latest_block_based_cache_middleware) - return w3_base - - -def test_latest_block_based_cache_middleware_pulls_from_cache( - w3_base, - block_data_middleware, - result_generator_middleware): - w3 = w3_base - w3.middleware_onion.add(block_data_middleware) - w3.middleware_onion.add(result_generator_middleware) - - current_block_hash = w3.eth.getBlock('latest')['hash'] - - def cache_class(): - return { - generate_cache_key( - (current_block_hash, 'fake_endpoint', [1]) - ): {'result': 'value-a'}, - } - - w3.middleware_onion.add(construct_latest_block_based_cache_middleware( - cache_class=cache_class, - rpc_whitelist={'fake_endpoint'}, - )) - - assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' - - -def test_latest_block_based_cache_middleware_populates_cache(w3): - result = w3.manager.request_blocking('fake_endpoint', []) - - assert w3.manager.request_blocking('fake_endpoint', []) == result - assert w3.manager.request_blocking('fake_endpoint', [1]) != result - - -def test_latest_block_based_cache_middleware_busts_cache(w3, mocker): - result = w3.manager.request_blocking('fake_endpoint', []) - - assert w3.manager.request_blocking('fake_endpoint', []) == result - w3.testing.mine() - - # should still be cached for at least 1 second. This also verifies that - # the middleware caches the latest block based on the block time. - assert w3.manager.request_blocking('fake_endpoint', []) == result - - mocker.patch('time.time', return_value=time.time() + 5) - - assert w3.manager.request_blocking('fake_endpoint', []) != result - - -def test_latest_block_cache_middleware_does_not_cache_bad_responses( - w3_base, - block_data_middleware, - latest_block_based_cache_middleware): - counter = itertools.count() - w3 = w3_base - - def result_cb(method, params): - next(counter) - return None - - w3 = w3_base - w3.middleware_onion.add(block_data_middleware) - w3.middleware_onion.add(construct_result_generator_middleware({ - 'fake_endpoint': result_cb, - })) - w3.middleware_onion.add(latest_block_based_cache_middleware) - - w3.manager.request_blocking('fake_endpoint', []) - w3.manager.request_blocking('fake_endpoint', []) - - assert next(counter) == 2 - - -def test_latest_block_cache_middleware_does_not_cache_error_response( - w3_base, - block_data_middleware, - latest_block_based_cache_middleware): - counter = itertools.count() - w3 = w3_base - - def error_cb(method, params): - next(counter) - return "the error message" - - w3.middleware_onion.add(block_data_middleware) - w3.middleware_onion.add(construct_error_generator_middleware({ - 'fake_endpoint': error_cb, - })) - w3.middleware_onion.add(latest_block_based_cache_middleware) - - with pytest.raises(ValueError): - w3.manager.request_blocking('fake_endpoint', []) - with pytest.raises(ValueError): - w3.manager.request_blocking('fake_endpoint', []) - - assert next(counter) == 2 +import codecs +import itertools +import pytest +import time +import uuid + +from vns_utils import ( + is_integer, + to_tuple, +) + +from web3 import Web3 +from web3._utils.caching import ( + generate_cache_key, +) +from web3.middleware import ( # noqa: F401 + construct_error_generator_middleware, + construct_latest_block_based_cache_middleware, + construct_result_generator_middleware, +) +from web3.providers.base import ( + BaseProvider, +) + + +@pytest.fixture +def w3_base(): + return Web3(provider=BaseProvider(), middlewares=[]) + + +def _mk_block(n, timestamp): + return { + 'hash': codecs.decode(str(n).zfill(32), 'hex'), + 'number': n, + 'timestamp': timestamp, + } + + +@to_tuple +def generate_block_history(num_mined_blocks=5, block_time=1): + genesis = _mk_block(0, time.time()) + yield genesis + for block_number in range(1, num_mined_blocks + 1): + yield _mk_block( + block_number, + genesis['timestamp'] + 2 * block_number, + ) + + +@pytest.fixture +def construct_block_data_middleware(): + def _construct_block_data_middleware(num_blocks): + blocks = generate_block_history(num_blocks) + _block_info = { + 'blocks': blocks, + 'head_block_number': blocks[0]['number'] + } + + def _evm_mine(method, params, block_info=_block_info): + num_blocks = params[0] + head_block_number = block_info['head_block_number'] + if head_block_number + num_blocks >= len(block_info['blocks']): + raise ValueError("no more blocks to mine") + + block_info['head_block_number'] += num_blocks + + def _get_block_by_number(method, params, block_info=_block_info): + block_id = params[0] + blocks = block_info['blocks'] + head_block_number = block_info['head_block_number'] + + if block_id == 'latest': + return blocks[head_block_number] + elif block_id == 'pending': + if head_block_number + 1 >= len(blocks): + raise ValueError("no pending block") + return blocks[head_block_number + 1] + elif block_id == 'earliest': + return blocks[0] + elif is_integer(block_id): + if block_id <= head_block_number: + return blocks[block_id] + else: + return None + else: + raise TypeError('Invalid type for block_id') + + def _get_block_by_hash(method, params, block_info=_block_info): + block_hash = params[0] + blocks = block_info['blocks'] + head_block_number = block_info['head_block_number'] + + blocks_by_hash = { + block['hash']: block + for block + in blocks + } + try: + block = blocks_by_hash[block_hash] + if block['number'] <= head_block_number: + return block + else: + return None + except KeyError: + return None + + return construct_result_generator_middleware({ + 'vns_getBlockByNumber': _get_block_by_number, + 'vns_getBlockByHash': _get_block_by_hash, + 'evm_mine': _evm_mine, + }) + return _construct_block_data_middleware + + +@pytest.fixture +def block_data_middleware(construct_block_data_middleware): + return construct_block_data_middleware(5) + + +@pytest.fixture +def result_generator_middleware(): + return construct_result_generator_middleware({ + 'fake_endpoint': lambda *_: str(uuid.uuid4()), + 'not_whitelisted': lambda *_: str(uuid.uuid4()), + }) + + +@pytest.fixture +def latest_block_based_cache_middleware(): + return construct_latest_block_based_cache_middleware( + cache_class=dict, + average_block_time_sample_size=1, + default_average_block_time=0.1, + rpc_whitelist={'fake_endpoint'}, + ) + + +@pytest.fixture +def w3(w3_base, + result_generator_middleware, + block_data_middleware, + latest_block_based_cache_middleware): + w3_base.middleware_onion.add(block_data_middleware) + w3_base.middleware_onion.add(result_generator_middleware) + w3_base.middleware_onion.add(latest_block_based_cache_middleware) + return w3_base + + +def test_latest_block_based_cache_middleware_pulls_from_cache( + w3_base, + block_data_middleware, + result_generator_middleware): + w3 = w3_base + w3.middleware_onion.add(block_data_middleware) + w3.middleware_onion.add(result_generator_middleware) + + current_block_hash = w3.vns.getBlock('latest')['hash'] + + def cache_class(): + return { + generate_cache_key( + (current_block_hash, 'fake_endpoint', [1]) + ): {'result': 'value-a'}, + } + + w3.middleware_onion.add(construct_latest_block_based_cache_middleware( + cache_class=cache_class, + rpc_whitelist={'fake_endpoint'}, + )) + + assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' + + +def test_latest_block_based_cache_middleware_populates_cache(w3): + result = w3.manager.request_blocking('fake_endpoint', []) + + assert w3.manager.request_blocking('fake_endpoint', []) == result + assert w3.manager.request_blocking('fake_endpoint', [1]) != result + + +def test_latest_block_based_cache_middleware_busts_cache(w3, mocker): + result = w3.manager.request_blocking('fake_endpoint', []) + + assert w3.manager.request_blocking('fake_endpoint', []) == result + w3.testing.mine() + + # should still be cached for at least 1 second. This also verifies that + # the middleware caches the latest block based on the block time. + assert w3.manager.request_blocking('fake_endpoint', []) == result + + mocker.patch('time.time', return_value=time.time() + 5) + + assert w3.manager.request_blocking('fake_endpoint', []) != result + + +def test_latest_block_cache_middleware_does_not_cache_bad_responses( + w3_base, + block_data_middleware, + latest_block_based_cache_middleware): + counter = itertools.count() + w3 = w3_base + + def result_cb(method, params): + next(counter) + return None + + w3 = w3_base + w3.middleware_onion.add(block_data_middleware) + w3.middleware_onion.add(construct_result_generator_middleware({ + 'fake_endpoint': result_cb, + })) + w3.middleware_onion.add(latest_block_based_cache_middleware) + + w3.manager.request_blocking('fake_endpoint', []) + w3.manager.request_blocking('fake_endpoint', []) + + assert next(counter) == 2 + + +def test_latest_block_cache_middleware_does_not_cache_error_response( + w3_base, + block_data_middleware, + latest_block_based_cache_middleware): + counter = itertools.count() + w3 = w3_base + + def error_cb(method, params): + next(counter) + return "the error message" + + w3.middleware_onion.add(block_data_middleware) + w3.middleware_onion.add(construct_error_generator_middleware({ + 'fake_endpoint': error_cb, + })) + w3.middleware_onion.add(latest_block_based_cache_middleware) + + with pytest.raises(ValueError): + w3.manager.request_blocking('fake_endpoint', []) + with pytest.raises(ValueError): + w3.manager.request_blocking('fake_endpoint', []) + + assert next(counter) == 2 diff --git a/tests/core/middleware/test_name_to_address_middleware.py b/tests/core/middleware/test_name_to_address_middleware.py index 567f38ea41..140d675c34 100644 --- a/tests/core/middleware/test_name_to_address_middleware.py +++ b/tests/core/middleware/test_name_to_address_middleware.py @@ -1,54 +1,54 @@ -import pytest - -from web3 import Web3 -from web3.exceptions import ( - InvalidAddress, -) -from web3.middleware import ( # noqa: F401 - construct_fixture_middleware, - name_to_address_middleware, -) -from web3.providers.base import ( - BaseProvider, -) - -NAME = "dump.eth" -ADDRESS = "0x0000000000000000000000000000000000000000" -BALANCE = 0 - - -class TempENS(): - def __init__(self, name_addr_pairs): - self.registry = dict(name_addr_pairs) - - def address(self, name): - return self.registry.get(name, None) - - -@pytest.fixture -def w3(): - w3 = Web3(provider=BaseProvider(), middlewares=[]) - w3.ens = TempENS({NAME: ADDRESS}) - w3.middleware_onion.add(name_to_address_middleware(w3)) - return w3 - - -def test_pass_name_resolver(w3): - return_chain_on_mainnet = construct_fixture_middleware({ - 'net_version': '1', - }) - return_balance = construct_fixture_middleware({ - 'eth_getBalance': BALANCE - }) - w3.middleware_onion.inject(return_chain_on_mainnet, layer=0) - w3.middleware_onion.inject(return_balance, layer=0) - assert w3.eth.getBalance(NAME) == BALANCE - - -def test_fail_name_resolver(w3): - return_chain_on_mainnet = construct_fixture_middleware({ - 'net_version': '2', - }) - w3.middleware_onion.inject(return_chain_on_mainnet, layer=0) - with pytest.raises(InvalidAddress, match=r'.*ethereum\.eth.*'): - w3.eth.getBalance("ethereum.eth") +import pytest + +from web3 import Web3 +from web3.exceptions import ( + InvalidAddress, +) +from web3.middleware import ( # noqa: F401 + construct_fixture_middleware, + name_to_address_middleware, +) +from web3.providers.base import ( + BaseProvider, +) + +NAME = "dump.vns" +ADDRESS = "0x0000000000000000000000000000000000000000" +BALANCE = 0 + + +class TempENS(): + def __init__(self, name_addr_pairs): + self.registry = dict(name_addr_pairs) + + def address(self, name): + return self.registry.get(name, None) + + +@pytest.fixture +def w3(): + w3 = Web3(provider=BaseProvider(), middlewares=[]) + w3.ens = TempENS({NAME: ADDRESS}) + w3.middleware_onion.add(name_to_address_middleware(w3)) + return w3 + + +def test_pass_name_resolver(w3): + return_chain_on_mainnet = construct_fixture_middleware({ + 'net_version': '1', + }) + return_balance = construct_fixture_middleware({ + 'vns_getBalance': BALANCE + }) + w3.middleware_onion.inject(return_chain_on_mainnet, layer=0) + w3.middleware_onion.inject(return_balance, layer=0) + assert w3.vns.getBalance(NAME) == BALANCE + + +def test_fail_name_resolver(w3): + return_chain_on_mainnet = construct_fixture_middleware({ + 'net_version': '2', + }) + w3.middleware_onion.inject(return_chain_on_mainnet, layer=0) + with pytest.raises(InvalidAddress, match=r'.*ethereum\.vns.*'): + w3.vns.getBalance("ethereum.vns") diff --git a/tests/core/middleware/test_request_param_normalizer.py b/tests/core/middleware/test_request_param_normalizer.py index 2b3f18e169..c9ba97daf9 100644 --- a/tests/core/middleware/test_request_param_normalizer.py +++ b/tests/core/middleware/test_request_param_normalizer.py @@ -1,35 +1,35 @@ -import pytest - -from web3 import Web3 -from web3.middleware import ( # noqa: F401 - construct_result_generator_middleware, - request_parameter_normalizer, -) -from web3.providers.base import ( - BaseProvider, -) - - -@pytest.fixture -def w3_base(): - return Web3(provider=BaseProvider(), middlewares=[]) - - -@pytest.fixture -def result_generator_middleware(): - return construct_result_generator_middleware({ - 'eth_getLogs': lambda _, params: params, - }) - - -@pytest.fixture -def w3(w3_base, result_generator_middleware): - w3_base.middleware_onion.add(result_generator_middleware) - w3_base.middleware_onion.add(request_parameter_normalizer) - return w3_base - - -def test_eth_getLogs_param_normalization(w3): - result = w3.eth.getLogs({ - 'from': 'latest', 'address': '0x1111111111111111111111111111111111111111'}) - assert isinstance(result[0]['address'], list) +import pytest + +from web3 import Web3 +from web3.middleware import ( # noqa: F401 + construct_result_generator_middleware, + request_parameter_normalizer, +) +from web3.providers.base import ( + BaseProvider, +) + + +@pytest.fixture +def w3_base(): + return Web3(provider=BaseProvider(), middlewares=[]) + + +@pytest.fixture +def result_generator_middleware(): + return construct_result_generator_middleware({ + 'vns_getLogs': lambda _, params: params, + }) + + +@pytest.fixture +def w3(w3_base, result_generator_middleware): + w3_base.middleware_onion.add(result_generator_middleware) + w3_base.middleware_onion.add(request_parameter_normalizer) + return w3_base + + +def test_vns_getLogs_param_normalization(w3): + result = w3.vns.getLogs({ + 'from': 'latest', 'address': '0x1111111111111111111111111111111111111111'}) + assert isinstance(result[0]['address'], list) diff --git a/tests/core/middleware/test_simple_cache_middleware.py b/tests/core/middleware/test_simple_cache_middleware.py index 2f09a10ff9..32f6b5d41c 100644 --- a/tests/core/middleware/test_simple_cache_middleware.py +++ b/tests/core/middleware/test_simple_cache_middleware.py @@ -1,115 +1,115 @@ -import itertools -import pytest -import uuid - -from web3 import Web3 -from web3._utils.caching import ( - generate_cache_key, -) -from web3.middleware import ( - construct_error_generator_middleware, - construct_result_generator_middleware, - construct_simple_cache_middleware, -) -from web3.providers.base import ( - BaseProvider, -) - - -@pytest.fixture -def w3_base(): - return Web3(provider=BaseProvider(), middlewares=[]) - - -@pytest.fixture -def result_generator_middleware(): - return construct_result_generator_middleware({ - 'fake_endpoint': lambda *_: str(uuid.uuid4()), - 'not_whitelisted': lambda *_: str(uuid.uuid4()), - }) - - -@pytest.fixture -def w3(w3_base, result_generator_middleware): - w3_base.middleware_onion.add(result_generator_middleware) - return w3_base - - -def test_simple_cache_middleware_pulls_from_cache(w3): - def cache_class(): - return { - generate_cache_key(('fake_endpoint', [1])): {'result': 'value-a'}, - } - - w3.middleware_onion.add(construct_simple_cache_middleware( - cache_class=cache_class, - rpc_whitelist={'fake_endpoint'}, - )) - - assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' - - -def test_simple_cache_middleware_populates_cache(w3): - w3.middleware_onion.add(construct_simple_cache_middleware( - cache_class=dict, - rpc_whitelist={'fake_endpoint'}, - )) - - result = w3.manager.request_blocking('fake_endpoint', []) - - assert w3.manager.request_blocking('fake_endpoint', []) == result - assert w3.manager.request_blocking('fake_endpoint', [1]) != result - - -def test_simple_cache_middleware_does_not_cache_none_responses(w3_base): - counter = itertools.count() - w3 = w3_base - - def result_cb(method, params): - next(counter) - return None - - w3.middleware_onion.add(construct_result_generator_middleware({ - 'fake_endpoint': result_cb, - })) - - w3.middleware_onion.add(construct_simple_cache_middleware( - cache_class=dict, - rpc_whitelist={'fake_endpoint'}, - )) - - w3.manager.request_blocking('fake_endpoint', []) - w3.manager.request_blocking('fake_endpoint', []) - - assert next(counter) == 2 - - -def test_simple_cache_middleware_does_not_cache_error_responses(w3_base): - w3 = w3_base - w3.middleware_onion.add(construct_error_generator_middleware({ - 'fake_endpoint': lambda *_: 'msg-{0}'.format(str(uuid.uuid4())), - })) - - w3.middleware_onion.add(construct_simple_cache_middleware( - cache_class=dict, - rpc_whitelist={'fake_endpoint'}, - )) - - with pytest.raises(ValueError) as err_a: - w3.manager.request_blocking('fake_endpoint', []) - with pytest.raises(ValueError) as err_b: - w3.manager.request_blocking('fake_endpoint', []) - - assert str(err_a) != str(err_b) - - -def test_simple_cache_middleware_does_not_cache_endpoints_not_in_whitelist(w3): - w3.middleware_onion.add(construct_simple_cache_middleware( - cache_class=dict, - rpc_whitelist={'fake_endpoint'}, - )) - - result_a = w3.manager.request_blocking('not_whitelisted', []) - result_b = w3.manager.request_blocking('not_whitelisted', []) - - assert result_a != result_b +import itertools +import pytest +import uuid + +from web3 import Web3 +from web3._utils.caching import ( + generate_cache_key, +) +from web3.middleware import ( + construct_error_generator_middleware, + construct_result_generator_middleware, + construct_simple_cache_middleware, +) +from web3.providers.base import ( + BaseProvider, +) + + +@pytest.fixture +def w3_base(): + return Web3(provider=BaseProvider(), middlewares=[]) + + +@pytest.fixture +def result_generator_middleware(): + return construct_result_generator_middleware({ + 'fake_endpoint': lambda *_: str(uuid.uuid4()), + 'not_whitelisted': lambda *_: str(uuid.uuid4()), + }) + + +@pytest.fixture +def w3(w3_base, result_generator_middleware): + w3_base.middleware_onion.add(result_generator_middleware) + return w3_base + + +def test_simple_cache_middleware_pulls_from_cache(w3): + def cache_class(): + return { + generate_cache_key(('fake_endpoint', [1])): {'result': 'value-a'}, + } + + w3.middleware_onion.add(construct_simple_cache_middleware( + cache_class=cache_class, + rpc_whitelist={'fake_endpoint'}, + )) + + assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' + + +def test_simple_cache_middleware_populates_cache(w3): + w3.middleware_onion.add(construct_simple_cache_middleware( + cache_class=dict, + rpc_whitelist={'fake_endpoint'}, + )) + + result = w3.manager.request_blocking('fake_endpoint', []) + + assert w3.manager.request_blocking('fake_endpoint', []) == result + assert w3.manager.request_blocking('fake_endpoint', [1]) != result + + +def test_simple_cache_middleware_does_not_cache_none_responses(w3_base): + counter = itertools.count() + w3 = w3_base + + def result_cb(method, params): + next(counter) + return None + + w3.middleware_onion.add(construct_result_generator_middleware({ + 'fake_endpoint': result_cb, + })) + + w3.middleware_onion.add(construct_simple_cache_middleware( + cache_class=dict, + rpc_whitelist={'fake_endpoint'}, + )) + + w3.manager.request_blocking('fake_endpoint', []) + w3.manager.request_blocking('fake_endpoint', []) + + assert next(counter) == 2 + + +def test_simple_cache_middleware_does_not_cache_error_responses(w3_base): + w3 = w3_base + w3.middleware_onion.add(construct_error_generator_middleware({ + 'fake_endpoint': lambda *_: 'msg-{0}'.format(str(uuid.uuid4())), + })) + + w3.middleware_onion.add(construct_simple_cache_middleware( + cache_class=dict, + rpc_whitelist={'fake_endpoint'}, + )) + + with pytest.raises(ValueError) as err_a: + w3.manager.request_blocking('fake_endpoint', []) + with pytest.raises(ValueError) as err_b: + w3.manager.request_blocking('fake_endpoint', []) + + assert str(err_a) != str(err_b) + + +def test_simple_cache_middleware_does_not_cache_endpoints_not_in_whitelist(w3): + w3.middleware_onion.add(construct_simple_cache_middleware( + cache_class=dict, + rpc_whitelist={'fake_endpoint'}, + )) + + result_a = w3.manager.request_blocking('not_whitelisted', []) + result_b = w3.manager.request_blocking('not_whitelisted', []) + + assert result_a != result_b diff --git a/tests/core/middleware/test_stalecheck.py b/tests/core/middleware/test_stalecheck.py index 3cc9a53752..497621a248 100644 --- a/tests/core/middleware/test_stalecheck.py +++ b/tests/core/middleware/test_stalecheck.py @@ -1,118 +1,118 @@ - -import pytest -from unittest.mock import ( - Mock, - patch, -) - -from web3.datastructures import ( - AttributeDict, -) -from web3.middleware import ( - make_stalecheck_middleware, -) -from web3.middleware.stalecheck import ( - StaleBlockchain, - _isfresh, -) - - -@pytest.fixture -def now(): - return 3141592653 - - -@pytest.fixture -def allowable_delay(): - return 3 * 24 * 60 * 60 - - -@pytest.fixture -def request_middleware(allowable_delay): - middleware = make_stalecheck_middleware(allowable_delay) - make_request, web3 = Mock(), Mock() - initialized = middleware(make_request, web3) - # for easier mocking, later: - initialized.web3 = web3 - initialized.make_request = make_request - return initialized - - -def stub_block(timestamp): - return AttributeDict({ - 'timestamp': timestamp, - 'number': 123, - }) - - -def test_is_not_fresh_with_no_block(): - assert not _isfresh(None, 1) - - -def test_is_not_fresh(now): - with patch('time.time', return_value=now): - SECONDS_ALLOWED = 2 * 86400 - stale = stub_block(now - SECONDS_ALLOWED - 1) - assert not _isfresh(stale, SECONDS_ALLOWED) - - -def test_is_fresh(now): - with patch('time.time', return_value=now): - SECONDS_ALLOWED = 2 * 86400 - stale = stub_block(now - SECONDS_ALLOWED) - assert _isfresh(stale, SECONDS_ALLOWED) - - -def test_stalecheck_pass(request_middleware): - with patch('web3.middleware.stalecheck._isfresh', return_value=True): - method, params = object(), object() - request_middleware(method, params) - request_middleware.make_request.assert_called_once_with(method, params) - - -def test_stalecheck_fail(request_middleware, now): - with patch('web3.middleware.stalecheck._isfresh', return_value=False): - request_middleware.web3.eth.getBlock.return_value = stub_block(now) - with pytest.raises(StaleBlockchain): - request_middleware('', []) - - -@pytest.mark.parametrize( - 'rpc_method', - [ - 'eth_getBlockByNumber', - ] -) -def test_stalecheck_ignores_get_by_block_methods(request_middleware, rpc_method): - # This is especially critical for getBlock('latest') which would cause infinite recursion - with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True]): - request_middleware(rpc_method, []) - assert not request_middleware.web3.eth.getBlock.called - - -def test_stalecheck_calls_isfresh_with_empty_cache(request_middleware, allowable_delay): - with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True]) as freshspy: - block = object() - request_middleware.web3.eth.getBlock.return_value = block - request_middleware('', []) - cache_call, live_call = freshspy.call_args_list - assert cache_call[0] == (None, allowable_delay) - assert live_call[0] == (block, allowable_delay) - - -def test_stalecheck_adds_block_to_cache(request_middleware, allowable_delay): - with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True, True]) as freshspy: - block = object() - request_middleware.web3.eth.getBlock.return_value = block - - # cache miss - request_middleware('', []) - cache_call, live_call = freshspy.call_args_list - assert freshspy.call_count == 2 - assert cache_call == ((None, allowable_delay), ) - assert live_call == ((block, allowable_delay), ) - - # cache hit - request_middleware('', []) - assert freshspy.call_count == 3 - assert freshspy.call_args == ((block, allowable_delay), ) + +import pytest +from unittest.mock import ( + Mock, + patch, +) + +from web3.datastructures import ( + AttributeDict, +) +from web3.middleware import ( + make_stalecheck_middleware, +) +from web3.middleware.stalecheck import ( + StaleBlockchain, + _isfresh, +) + + +@pytest.fixture +def now(): + return 3141592653 + + +@pytest.fixture +def allowable_delay(): + return 3 * 24 * 60 * 60 + + +@pytest.fixture +def request_middleware(allowable_delay): + middleware = make_stalecheck_middleware(allowable_delay) + make_request, web3 = Mock(), Mock() + initialized = middleware(make_request, web3) + # for easier mocking, later: + initialized.web3 = web3 + initialized.make_request = make_request + return initialized + + +def stub_block(timestamp): + return AttributeDict({ + 'timestamp': timestamp, + 'number': 123, + }) + + +def test_is_not_fresh_with_no_block(): + assert not _isfresh(None, 1) + + +def test_is_not_fresh(now): + with patch('time.time', return_value=now): + SECONDS_ALLOWED = 2 * 86400 + stale = stub_block(now - SECONDS_ALLOWED - 1) + assert not _isfresh(stale, SECONDS_ALLOWED) + + +def test_is_fresh(now): + with patch('time.time', return_value=now): + SECONDS_ALLOWED = 2 * 86400 + stale = stub_block(now - SECONDS_ALLOWED) + assert _isfresh(stale, SECONDS_ALLOWED) + + +def test_stalecheck_pass(request_middleware): + with patch('web3.middleware.stalecheck._isfresh', return_value=True): + method, params = object(), object() + request_middleware(method, params) + request_middleware.make_request.assert_called_once_with(method, params) + + +def test_stalecheck_fail(request_middleware, now): + with patch('web3.middleware.stalecheck._isfresh', return_value=False): + request_middleware.web3.vns.getBlock.return_value = stub_block(now) + with pytest.raises(StaleBlockchain): + request_middleware('', []) + + +@pytest.mark.parametrize( + 'rpc_method', + [ + 'vns_getBlockByNumber', + ] +) +def test_stalecheck_ignores_get_by_block_methods(request_middleware, rpc_method): + # This is especially critical for getBlock('latest') which would cause infinite recursion + with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True]): + request_middleware(rpc_method, []) + assert not request_middleware.web3.vns.getBlock.called + + +def test_stalecheck_calls_isfresh_with_empty_cache(request_middleware, allowable_delay): + with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True]) as freshspy: + block = object() + request_middleware.web3.vns.getBlock.return_value = block + request_middleware('', []) + cache_call, live_call = freshspy.call_args_list + assert cache_call[0] == (None, allowable_delay) + assert live_call[0] == (block, allowable_delay) + + +def test_stalecheck_adds_block_to_cache(request_middleware, allowable_delay): + with patch('web3.middleware.stalecheck._isfresh', side_effect=[False, True, True]) as freshspy: + block = object() + request_middleware.web3.vns.getBlock.return_value = block + + # cache miss + request_middleware('', []) + cache_call, live_call = freshspy.call_args_list + assert freshspy.call_count == 2 + assert cache_call == ((None, allowable_delay), ) + assert live_call == ((block, allowable_delay), ) + + # cache hit + request_middleware('', []) + assert freshspy.call_count == 3 + assert freshspy.call_args == ((block, allowable_delay), ) diff --git a/tests/core/middleware/test_time_based_cache_middleware.py b/tests/core/middleware/test_time_based_cache_middleware.py index 968160171f..eb39ec683c 100644 --- a/tests/core/middleware/test_time_based_cache_middleware.py +++ b/tests/core/middleware/test_time_based_cache_middleware.py @@ -1,153 +1,153 @@ -import itertools -import pytest -import time -import uuid - -from web3 import Web3 -from web3._utils.caching import ( - generate_cache_key, -) -from web3.middleware import ( # noqa: F401 - construct_error_generator_middleware, - construct_result_generator_middleware, - construct_time_based_cache_middleware, -) -from web3.providers.base import ( - BaseProvider, -) - - -@pytest.fixture -def w3_base(): - return Web3(provider=BaseProvider(), middlewares=[]) - - -@pytest.fixture -def result_generator_middleware(): - return construct_result_generator_middleware({ - 'fake_endpoint': lambda *_: str(uuid.uuid4()), - 'not_whitelisted': lambda *_: str(uuid.uuid4()), - }) - - -@pytest.fixture -def time_cache_middleware(): - return construct_time_based_cache_middleware( - cache_class=dict, - cache_expire_seconds=10, - rpc_whitelist={'fake_endpoint'}, - ) - - -@pytest.fixture -def w3(w3_base, result_generator_middleware, time_cache_middleware): - w3_base.middleware_onion.add(result_generator_middleware) - w3_base.middleware_onion.add(time_cache_middleware) - return w3_base - - -def test_time_based_cache_middleware_pulls_from_cache(w3_base): - w3 = w3_base - - def cache_class(): - return { - generate_cache_key(('fake_endpoint', [1])): ( - time.time(), - {'result': 'value-a'}, - ), - } - - w3.middleware_onion.add(construct_time_based_cache_middleware( - cache_class=cache_class, - cache_expire_seconds=10, - rpc_whitelist={'fake_endpoint'}, - )) - - assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' - - -def test_time_based_cache_middleware_populates_cache(w3): - result = w3.manager.request_blocking('fake_endpoint', []) - - assert w3.manager.request_blocking('fake_endpoint', []) == result - assert w3.manager.request_blocking('fake_endpoint', [1]) != result - - -def test_time_based_cache_middleware_expires_old_values(w3_base, result_generator_middleware): - w3 = w3_base - w3.middleware_onion.add(result_generator_middleware) - - def cache_class(): - return { - generate_cache_key(('fake_endpoint', [1])): ( - time.time() - 10, - {'result': 'value-a'}, - ), - } - - w3.middleware_onion.add(construct_time_based_cache_middleware( - cache_class=cache_class, - cache_expire_seconds=10, - rpc_whitelist={'fake_endpoint'}, - )) - - result = w3.manager.request_blocking('fake_endpoint', [1]) - assert result != 'value-a' - assert w3.manager.request_blocking('fake_endpoint', [1]) == result - - -@pytest.mark.parametrize( - 'response', - ( - {}, - {'result': None}, - ) -) -def test_time_based_cache_middleware_does_not_cache_bad_responses( - w3_base, - response, - time_cache_middleware): - w3 = w3_base - counter = itertools.count() - - def mk_result(method, params): - next(counter) - return None - - w3.middleware_onion.add(construct_result_generator_middleware({'fake_endpoint': mk_result})) - w3.middleware_onion.add(time_cache_middleware) - - w3.manager.request_blocking('fake_endpoint', []) - w3.manager.request_blocking('fake_endpoint', []) - - assert next(counter) == 2 - - -def test_time_based_cache_middleware_does_not_cache_error_response( - w3_base, - time_cache_middleware): - w3 = w3_base - counter = itertools.count() - - def mk_error(method, params): - return "error-number-{0}".format(next(counter)) - - w3.middleware_onion.add(construct_error_generator_middleware({ - 'fake_endpoint': mk_error, - })) - w3.middleware_onion.add(time_cache_middleware) - - with pytest.raises(ValueError) as err: - w3.manager.request_blocking('fake_endpoint', []) - assert 'error-number-0' in str(err) - - with pytest.raises(ValueError) as err: - w3.manager.request_blocking('fake_endpoint', []) - assert 'error-number-1' in str(err) - - -def test_time_based_cache_middleware_does_not_cache_endpoints_not_in_whitelist(w3): - result_a = w3.manager.request_blocking('not_whitelisted', []) - result_b = w3.manager.request_blocking('not_whitelisted', []) - - assert result_a != result_b +import itertools +import pytest +import time +import uuid + +from web3 import Web3 +from web3._utils.caching import ( + generate_cache_key, +) +from web3.middleware import ( # noqa: F401 + construct_error_generator_middleware, + construct_result_generator_middleware, + construct_time_based_cache_middleware, +) +from web3.providers.base import ( + BaseProvider, +) + + +@pytest.fixture +def w3_base(): + return Web3(provider=BaseProvider(), middlewares=[]) + + +@pytest.fixture +def result_generator_middleware(): + return construct_result_generator_middleware({ + 'fake_endpoint': lambda *_: str(uuid.uuid4()), + 'not_whitelisted': lambda *_: str(uuid.uuid4()), + }) + + +@pytest.fixture +def time_cache_middleware(): + return construct_time_based_cache_middleware( + cache_class=dict, + cache_expire_seconds=10, + rpc_whitelist={'fake_endpoint'}, + ) + + +@pytest.fixture +def w3(w3_base, result_generator_middleware, time_cache_middleware): + w3_base.middleware_onion.add(result_generator_middleware) + w3_base.middleware_onion.add(time_cache_middleware) + return w3_base + + +def test_time_based_cache_middleware_pulls_from_cache(w3_base): + w3 = w3_base + + def cache_class(): + return { + generate_cache_key(('fake_endpoint', [1])): ( + time.time(), + {'result': 'value-a'}, + ), + } + + w3.middleware_onion.add(construct_time_based_cache_middleware( + cache_class=cache_class, + cache_expire_seconds=10, + rpc_whitelist={'fake_endpoint'}, + )) + + assert w3.manager.request_blocking('fake_endpoint', [1]) == 'value-a' + + +def test_time_based_cache_middleware_populates_cache(w3): + result = w3.manager.request_blocking('fake_endpoint', []) + + assert w3.manager.request_blocking('fake_endpoint', []) == result + assert w3.manager.request_blocking('fake_endpoint', [1]) != result + + +def test_time_based_cache_middleware_expires_old_values(w3_base, result_generator_middleware): + w3 = w3_base + w3.middleware_onion.add(result_generator_middleware) + + def cache_class(): + return { + generate_cache_key(('fake_endpoint', [1])): ( + time.time() - 10, + {'result': 'value-a'}, + ), + } + + w3.middleware_onion.add(construct_time_based_cache_middleware( + cache_class=cache_class, + cache_expire_seconds=10, + rpc_whitelist={'fake_endpoint'}, + )) + + result = w3.manager.request_blocking('fake_endpoint', [1]) + assert result != 'value-a' + assert w3.manager.request_blocking('fake_endpoint', [1]) == result + + +@pytest.mark.parametrize( + 'response', + ( + {}, + {'result': None}, + ) +) +def test_time_based_cache_middleware_does_not_cache_bad_responses( + w3_base, + response, + time_cache_middleware): + w3 = w3_base + counter = itertools.count() + + def mk_result(method, params): + next(counter) + return None + + w3.middleware_onion.add(construct_result_generator_middleware({'fake_endpoint': mk_result})) + w3.middleware_onion.add(time_cache_middleware) + + w3.manager.request_blocking('fake_endpoint', []) + w3.manager.request_blocking('fake_endpoint', []) + + assert next(counter) == 2 + + +def test_time_based_cache_middleware_does_not_cache_error_response( + w3_base, + time_cache_middleware): + w3 = w3_base + counter = itertools.count() + + def mk_error(method, params): + return "error-number-{0}".format(next(counter)) + + w3.middleware_onion.add(construct_error_generator_middleware({ + 'fake_endpoint': mk_error, + })) + w3.middleware_onion.add(time_cache_middleware) + + with pytest.raises(ValueError) as err: + w3.manager.request_blocking('fake_endpoint', []) + assert 'error-number-0' in str(err) + + with pytest.raises(ValueError) as err: + w3.manager.request_blocking('fake_endpoint', []) + assert 'error-number-1' in str(err) + + +def test_time_based_cache_middleware_does_not_cache_endpoints_not_in_whitelist(w3): + result_a = w3.manager.request_blocking('not_whitelisted', []) + result_b = w3.manager.request_blocking('not_whitelisted', []) + + assert result_a != result_b diff --git a/tests/core/middleware/test_transaction_signing.py b/tests/core/middleware/test_transaction_signing.py index 7f11d28040..f30e08bd66 100644 --- a/tests/core/middleware/test_transaction_signing.py +++ b/tests/core/middleware/test_transaction_signing.py @@ -1,321 +1,315 @@ -import pytest - -from eth_account import ( - Account, -) -from eth_account.signers.local import ( - LocalAccount, -) -import eth_keys -from eth_tester.exceptions import ( - ValidationError, -) -from eth_utils import ( - to_bytes, - to_hex, -) -from eth_utils.toolz import ( - identity, - merge, - valfilter, -) -from hexbytes import ( - HexBytes, -) - -from web3 import Web3 -from web3.exceptions import ( - InvalidAddress, -) -from web3.middleware import ( - construct_result_generator_middleware, - construct_sign_and_send_raw_middleware, -) -from web3.middleware.signing import ( - gen_normalized_accounts, -) -from web3.providers import ( - BaseProvider, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - -PRIVATE_KEY_1 = to_bytes( - hexstr='0x6a8b4de52b288e111c14e1c4b868bc125d325d40331d86d875a3467dd44bf829') - -ADDRESS_1 = '0x634743b15C948820069a43f6B361D03EfbBBE5a8' - -PRIVATE_KEY_2 = to_bytes( - hexstr='0xbf963e13b164c2100795f53e5590010f76b7a91b5a78de8e2b97239c8cfca8e8') - -ADDRESS_2 = '0x91eD14b5956DBcc1310E65DC4d7E82f02B95BA46' - -KEY_FUNCS = ( - eth_keys.keys.PrivateKey, - Account.from_key, - HexBytes, - to_hex, - identity, -) - - -SAME_KEY_MIXED_TYPE = tuple(key_func(PRIVATE_KEY_1) for key_func in KEY_FUNCS) - -MIXED_KEY_MIXED_TYPE = tuple( - key_func(key) for key in [PRIVATE_KEY_1, PRIVATE_KEY_2] for key_func in KEY_FUNCS -) - -SAME_KEY_SAME_TYPE = ( - eth_keys.keys.PrivateKey(PRIVATE_KEY_1), - eth_keys.keys.PrivateKey(PRIVATE_KEY_1) -) - -MIXED_KEY_SAME_TYPE = ( - eth_keys.keys.PrivateKey(PRIVATE_KEY_1), eth_keys.keys.PrivateKey(PRIVATE_KEY_2) -) - - -class DummyProvider(BaseProvider): - def make_request(self, method, params): - raise NotImplementedError("Cannot make request for {0}:{1}".format( - method, - params, - )) - - -@pytest.fixture() -def result_generator_middleware(): - return construct_result_generator_middleware({ - 'eth_sendRawTransaction': lambda *args: args, - 'net_version': lambda *_: 1, - 'eth_chainId': lambda *_: "0x02", - }) - - -@pytest.fixture() -def w3_base(): - return Web3(provider=DummyProvider(), middlewares=[]) - - -@pytest.fixture() -def w3_dummy(w3_base, result_generator_middleware): - w3_base.middleware_onion.add(result_generator_middleware) - return w3_base - - -def hex_to_bytes(s): - return to_bytes(hexstr=s) - - -@pytest.mark.parametrize( - 'method,key_object,from_,expected', - ( - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE, ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE, ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', MIXED_KEY_MIXED_TYPE, ADDRESS_2, 'eth_sendRawTransaction'), - ('eth_sendTransaction', MIXED_KEY_MIXED_TYPE, ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_SAME_TYPE, ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_SAME_TYPE, ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', MIXED_KEY_SAME_TYPE, ADDRESS_2, 'eth_sendRawTransaction'), - ('eth_sendTransaction', MIXED_KEY_SAME_TYPE, ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[0], ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[1], ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[2], ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[3], ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[4], ADDRESS_1, 'eth_sendRawTransaction'), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[0], ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[1], ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[2], ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[3], ADDRESS_2, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_MIXED_TYPE[4], ADDRESS_2, NotImplementedError), - ('eth_call', MIXED_KEY_MIXED_TYPE, ADDRESS_1, NotImplementedError), - ('eth_sendTransaction', SAME_KEY_SAME_TYPE, hex_to_bytes(ADDRESS_1), - 'eth_sendRawTransaction'), - ) -) -def test_sign_and_send_raw_middleware( - w3_dummy, - w3, - method, - from_, - expected, - key_object): - w3_dummy.middleware_onion.add( - construct_sign_and_send_raw_middleware(key_object)) - - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - w3_dummy.manager.request_blocking( - method, - [{ - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'from': from_, - 'gas': 21000, - 'gasPrice': 0, - 'value': 1, - 'nonce': 0 - }]) - else: - actual = w3_dummy.manager.request_blocking( - method, - [{ - 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', - 'from': from_, - 'gas': 21000, - 'gasPrice': 0, - 'value': 1, - 'nonce': 0 - }]) - raw_txn = actual[1][0] - actual_method = actual[0] - assert actual_method == expected - assert isinstance(raw_txn, bytes) - - -@pytest.fixture() -def w3(): - return Web3(EthereumTesterProvider()) - - -@pytest.mark.parametrize( - 'key_object', - ( - (SAME_KEY_MIXED_TYPE), - (MIXED_KEY_MIXED_TYPE), - (SAME_KEY_SAME_TYPE), - (MIXED_KEY_SAME_TYPE), - (SAME_KEY_MIXED_TYPE[0]), - (SAME_KEY_MIXED_TYPE[1]), - (SAME_KEY_MIXED_TYPE[2]), - (SAME_KEY_MIXED_TYPE[3]), - (SAME_KEY_MIXED_TYPE[4]), - ) -) -def test_gen_normalized_accounts(key_object): - accounts = gen_normalized_accounts(key_object) - assert all(isinstance(account, LocalAccount) for account in accounts.values()) - - -def test_gen_normalized_accounts_type_error(w3): - with pytest.raises(TypeError): - gen_normalized_accounts(1234567890) - - -@pytest.fixture() -def fund_account(w3): - # fund local account - tx_value = w3.toWei(10, 'ether') - for address in (ADDRESS_1, ADDRESS_2): - w3.eth.sendTransaction({ - 'to': address, - 'from': w3.eth.accounts[0], - 'gas': 21000, - 'value': tx_value}) - assert w3.eth.getBalance(address) == tx_value - - -@pytest.mark.parametrize( - 'transaction,expected,key_object,from_', - ( - ( - # Transaction with set gas - { - 'gas': 21000, - 'gasPrice': 0, - 'value': 1 - }, - -1, - MIXED_KEY_MIXED_TYPE, - ADDRESS_1, - ), - ( - # Transaction with no set gas - { - 'value': 1 - }, - -1, - MIXED_KEY_MIXED_TYPE, - ADDRESS_1, - ), - ( - # Transaction with mismatched sender - # expect a validation error with sendTransaction + unmanaged account - { - 'gas': 21000, - 'value': 10 - }, - ValidationError, - SAME_KEY_MIXED_TYPE, - ADDRESS_2, - ), - ( - # Transaction with invalid sender - { - 'gas': 21000, - 'value': 10 - }, - InvalidAddress, - SAME_KEY_MIXED_TYPE, - '0x0000', - ) - ) -) -def test_signed_transaction( - w3, - fund_account, - transaction, - expected, - key_object, - from_): - w3.middleware_onion.add(construct_sign_and_send_raw_middleware(key_object)) - - # Drop any falsy addresses - to_from = valfilter(bool, {'to': w3.eth.accounts[0], 'from': from_}) - - _transaction = merge(transaction, to_from) - - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - start_balance = w3.eth.getBalance(_transaction.get('from', w3.eth.accounts[0])) - w3.eth.sendTransaction(_transaction) - else: - start_balance = w3.eth.getBalance(_transaction.get('from', w3.eth.accounts[0])) - w3.eth.sendTransaction(_transaction) - assert w3.eth.getBalance(_transaction.get('from')) <= start_balance + expected - - -@pytest.mark.parametrize( - 'from_converter,to_converter', - ( - (identity, identity), - (hex_to_bytes, identity), - (identity, hex_to_bytes), - (hex_to_bytes, hex_to_bytes), - ) -) -def test_sign_and_send_raw_middleware_with_byte_addresses( - w3_dummy, - from_converter, - to_converter): - private_key = PRIVATE_KEY_1 - from_ = from_converter(ADDRESS_1) - to_ = to_converter(ADDRESS_2) - - w3_dummy.middleware_onion.add( - construct_sign_and_send_raw_middleware(private_key)) - - actual = w3_dummy.manager.request_blocking( - 'eth_sendTransaction', - [{ - 'to': to_, - 'from': from_, - 'gas': 21000, - 'gasPrice': 0, - 'value': 1, - 'nonce': 0 - }]) - raw_txn = actual[1][0] - actual_method = actual[0] - assert actual_method == 'eth_sendRawTransaction' - assert isinstance(raw_txn, bytes) +import pytest + +import vns_account +import vns_keys +from vns_tester.exceptions import ( + ValidationError, +) +from vns_utils import ( + to_bytes, + to_hex, +) +from hexbytes import ( + HexBytes, +) + +from web3 import Web3 +from web3._utils.toolz import ( + identity, + merge, + valfilter, +) +from web3.exceptions import ( + InvalidAddress, +) +from web3.middleware import ( + construct_result_generator_middleware, + construct_sign_and_send_raw_middleware, +) +from web3.middleware.signing import ( + gen_normalized_accounts, +) +from web3.providers import ( + BaseProvider, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) + +PRIVATE_KEY_1 = to_bytes( + hexstr='0x6a8b4de52b288e111c14e1c4b868bc125d325d40331d86d875a3467dd44bf829') + +ADDRESS_1 = '0x634743b15C948820069a43f6B361D03EfbBBE5a8' + +PRIVATE_KEY_2 = to_bytes( + hexstr='0xbf963e13b164c2100795f53e5590010f76b7a91b5a78de8e2b97239c8cfca8e8') + +ADDRESS_2 = '0x91eD14b5956DBcc1310E65DC4d7E82f02B95BA46' + +KEY_FUNCS = ( + vns_keys.keys.PrivateKey, + vns_account.Account.privateKeyToAccount, + HexBytes, + to_hex, + identity, +) + + +SAME_KEY_MIXED_TYPE = tuple(key_func(PRIVATE_KEY_1) for key_func in KEY_FUNCS) + +MIXED_KEY_MIXED_TYPE = tuple( + key_func(key) for key in [PRIVATE_KEY_1, PRIVATE_KEY_2] for key_func in KEY_FUNCS +) + +SAME_KEY_SAME_TYPE = ( + vns_keys.keys.PrivateKey(PRIVATE_KEY_1), + vns_keys.keys.PrivateKey(PRIVATE_KEY_1) +) + +MIXED_KEY_SAME_TYPE = ( + vns_keys.keys.PrivateKey(PRIVATE_KEY_1), vns_keys.keys.PrivateKey(PRIVATE_KEY_2) +) + + +class DummyProvider(BaseProvider): + def make_request(self, method, params): + raise NotImplementedError("Cannot make request for {0}:{1}".format( + method, + params, + )) + + +@pytest.fixture() +def result_generator_middleware(): + return construct_result_generator_middleware({ + 'vns_sendRawTransaction': lambda *args: args, + 'net_version': lambda *_: 1, + }) + + +@pytest.fixture() +def w3_base(): + return Web3(provider=DummyProvider(), middlewares=[]) + + +@pytest.fixture() +def w3_dummy(w3_base, result_generator_middleware): + w3_base.middleware_onion.add(result_generator_middleware) + return w3_base + + +def hex_to_bytes(s): + return to_bytes(hexstr=s) + + +@pytest.mark.parametrize( + 'method,key_object,from_,expected', + ( + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE, ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE, ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', MIXED_KEY_MIXED_TYPE, ADDRESS_2, 'vns_sendRawTransaction'), + ('vns_sendTransaction', MIXED_KEY_MIXED_TYPE, ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_SAME_TYPE, ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_SAME_TYPE, ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', MIXED_KEY_SAME_TYPE, ADDRESS_2, 'vns_sendRawTransaction'), + ('vns_sendTransaction', MIXED_KEY_SAME_TYPE, ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[0], ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[1], ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[2], ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[3], ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[4], ADDRESS_1, 'vns_sendRawTransaction'), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[0], ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[1], ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[2], ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[3], ADDRESS_2, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_MIXED_TYPE[4], ADDRESS_2, NotImplementedError), + ('vns_call', MIXED_KEY_MIXED_TYPE, ADDRESS_1, NotImplementedError), + ('vns_sendTransaction', SAME_KEY_SAME_TYPE, hex_to_bytes(ADDRESS_1), + 'vns_sendRawTransaction'), + ) +) +def test_sign_and_send_raw_middleware( + w3_dummy, + w3, + method, + from_, + expected, + key_object): + w3_dummy.middleware_onion.add( + construct_sign_and_send_raw_middleware(key_object)) + + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + w3_dummy.manager.request_blocking( + method, + [{ + 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'from': from_, + 'gas': 21000, + 'gasPrice': 0, + 'value': 1, + 'nonce': 0 + }]) + else: + actual = w3_dummy.manager.request_blocking( + method, + [{ + 'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'from': from_, + 'gas': 21000, + 'gasPrice': 0, + 'value': 1, + 'nonce': 0 + }]) + raw_txn = actual[1][0] + actual_method = actual[0] + assert actual_method == expected + assert isinstance(raw_txn, bytes) + + +@pytest.fixture() +def w3(): + return Web3(EthereumTesterProvider()) + + +@pytest.mark.parametrize( + 'key_object', + ( + (SAME_KEY_MIXED_TYPE), + (MIXED_KEY_MIXED_TYPE), + (SAME_KEY_SAME_TYPE), + (MIXED_KEY_SAME_TYPE), + (SAME_KEY_MIXED_TYPE[0]), + (SAME_KEY_MIXED_TYPE[1]), + (SAME_KEY_MIXED_TYPE[2]), + (SAME_KEY_MIXED_TYPE[3]), + (SAME_KEY_MIXED_TYPE[4]), + ) +) +def test_gen_normalized_accounts(key_object): + accounts = gen_normalized_accounts(key_object) + assert all(isinstance(account, vns_account.local.LocalAccount) for account in accounts.values()) + + +def test_gen_normalized_accounts_type_error(w3): + with pytest.raises(TypeError): + gen_normalized_accounts(1234567890) + + +@pytest.fixture() +def fund_account(w3): + # fund local account + tx_value = w3.toWei(10, 'ether') + for address in (ADDRESS_1, ADDRESS_2): + w3.vns.sendTransaction({ + 'to': address, + 'from': w3.vns.accounts[0], + 'gas': 21000, + 'value': tx_value}) + assert w3.vns.getBalance(address) == tx_value + + +@pytest.mark.parametrize( + 'transaction,expected,key_object,from_', + ( + ( + # Transaction with set gas + { + 'gas': 21000, + 'gasPrice': 0, + 'value': 1 + }, + -1, + MIXED_KEY_MIXED_TYPE, + ADDRESS_1, + ), + ( + # Transaction with no set gas + { + 'value': 1 + }, + -1, + MIXED_KEY_MIXED_TYPE, + ADDRESS_1, + ), + ( + # Transaction with mismatched sender + # expect a validation error with sendTransaction + unmanaged account + { + 'gas': 21000, + 'value': 10 + }, + ValidationError, + SAME_KEY_MIXED_TYPE, + ADDRESS_2, + ), + ( + # Transaction with invalid sender + { + 'gas': 21000, + 'value': 10 + }, + InvalidAddress, + SAME_KEY_MIXED_TYPE, + '0x0000', + ) + ) +) +def test_signed_transaction( + w3, + fund_account, + transaction, + expected, + key_object, + from_): + w3.middleware_onion.add(construct_sign_and_send_raw_middleware(key_object)) + + # Drop any falsy addresses + to_from = valfilter(bool, {'to': w3.vns.accounts[0], 'from': from_}) + + _transaction = merge(transaction, to_from) + + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + start_balance = w3.vns.getBalance(_transaction.get('from', w3.vns.accounts[0])) + w3.vns.sendTransaction(_transaction) + else: + start_balance = w3.vns.getBalance(_transaction.get('from', w3.vns.accounts[0])) + w3.vns.sendTransaction(_transaction) + assert w3.vns.getBalance(_transaction.get('from')) <= start_balance + expected + + +@pytest.mark.parametrize( + 'from_converter,to_converter', + ( + (identity, identity), + (hex_to_bytes, identity), + (identity, hex_to_bytes), + (hex_to_bytes, hex_to_bytes), + ) +) +def test_sign_and_send_raw_middleware_with_byte_addresses( + w3_dummy, + from_converter, + to_converter): + private_key = PRIVATE_KEY_1 + from_ = from_converter(ADDRESS_1) + to_ = to_converter(ADDRESS_2) + + w3_dummy.middleware_onion.add( + construct_sign_and_send_raw_middleware(private_key)) + + actual = w3_dummy.manager.request_blocking( + 'vns_sendTransaction', + [{ + 'to': to_, + 'from': from_, + 'gas': 21000, + 'gasPrice': 0, + 'value': 1, + 'nonce': 0 + }]) + raw_txn = actual[1][0] + actual_method = actual[0] + assert actual_method == 'vns_sendRawTransaction' + assert isinstance(raw_txn, bytes) diff --git a/tests/core/mining-module/conftest.py b/tests/core/mining-module/conftest.py index 4e6ab94eb8..0d4765e196 100644 --- a/tests/core/mining-module/conftest.py +++ b/tests/core/mining-module/conftest.py @@ -1,13 +1,13 @@ -import pytest - - -@pytest.fixture(autouse=True) -def always_wait_for_mining_start(web3, - wait_for_miner_start, - skip_if_testrpc): - skip_if_testrpc(web3) - - wait_for_miner_start(web3) - - assert web3.eth.mining - assert web3.eth.hashrate +import pytest + + +@pytest.fixture(autouse=True) +def always_wait_for_mining_start(web3, + wait_for_miner_start, + skip_if_testrpc): + skip_if_testrpc(web3) + + wait_for_miner_start(web3) + + assert web3.vns.mining + assert web3.vns.hashrate diff --git a/tests/core/mining-module/test_miner_hashrate.py b/tests/core/mining-module/test_miner_hashrate.py index d28acfcc72..35fb05155c 100644 --- a/tests/core/mining-module/test_miner_hashrate.py +++ b/tests/core/mining-module/test_miner_hashrate.py @@ -1,11 +1,11 @@ -from flaky import ( - flaky, -) - - -@flaky(max_runs=3) -def test_miner_hashrate(web3_empty, wait_for_miner_start): - web3 = web3_empty - - hashrate = web3.eth.hashrate - assert hashrate > 0 +from flaky import ( + flaky, +) + + +@flaky(max_runs=3) +def test_miner_hashrate(web3_empty, wait_for_miner_start): + web3 = web3_empty + + hashrate = web3.vns.hashrate + assert hashrate > 0 diff --git a/tests/core/mining-module/test_miner_setExtra.py b/tests/core/mining-module/test_miner_setExtra.py index 64f5f25a6b..637debfbf6 100644 --- a/tests/core/mining-module/test_miner_setExtra.py +++ b/tests/core/mining-module/test_miner_setExtra.py @@ -1,37 +1,37 @@ -import random - -from eth_utils import ( - decode_hex, -) -from flaky import ( - flaky, -) - -from web3._utils.threads import ( - Timeout, -) - - -@flaky(max_runs=3) -def test_miner_setExtra(web3_empty, wait_for_block): - web3 = web3_empty - - initial_extra = decode_hex(web3.eth.getBlock(web3.eth.blockNumber)['extraData']) - - new_extra_data = b'-this-is-32-bytes-of-extra-data-' - - # sanity - assert initial_extra != new_extra_data - - web3.geth.miner.setExtra(new_extra_data) - - with Timeout(60) as timeout: - while True: - extra_data = decode_hex(web3.eth.getBlock(web3.eth.blockNumber)['extraData']) - if extra_data == new_extra_data: - break - timeout.sleep(random.random()) - - after_extra = decode_hex(web3.eth.getBlock(web3.eth.blockNumber)['extraData']) - - assert after_extra == new_extra_data +import random + +from vns_utils import ( + decode_hex, +) +from flaky import ( + flaky, +) + +from web3._utils.threads import ( + Timeout, +) + + +@flaky(max_runs=3) +def test_miner_setExtra(web3_empty, wait_for_block): + web3 = web3_empty + + initial_extra = decode_hex(web3.vns.getBlock(web3.vns.blockNumber)['extraData']) + + new_extra_data = b'-this-is-32-bytes-of-extra-data-' + + # sanity + assert initial_extra != new_extra_data + + web3.geth.miner.setExtra(new_extra_data) + + with Timeout(60) as timeout: + while True: + extra_data = decode_hex(web3.vns.getBlock(web3.vns.blockNumber)['extraData']) + if extra_data == new_extra_data: + break + timeout.sleep(random.random()) + + after_extra = decode_hex(web3.vns.getBlock(web3.vns.blockNumber)['extraData']) + + assert after_extra == new_extra_data diff --git a/tests/core/mining-module/test_miner_setGasPrice.py b/tests/core/mining-module/test_miner_setGasPrice.py index 9b9ab949f2..df6a92dc9b 100644 --- a/tests/core/mining-module/test_miner_setGasPrice.py +++ b/tests/core/mining-module/test_miner_setGasPrice.py @@ -1,28 +1,28 @@ -import random - -from flaky import ( - flaky, -) - -from web3._utils.threads import ( - Timeout, -) - - -@flaky(max_runs=3) -def test_miner_setGasPrice(web3_empty, wait_for_block): - web3 = web3_empty - - initial_gas_price = web3.eth.gasPrice - - # sanity check - assert web3.eth.gasPrice > 1000 - - web3.geth.miner.setGasPrice(initial_gas_price // 2) - - with Timeout(60) as timeout: - while web3.eth.gasPrice == initial_gas_price: - timeout.sleep(random.random()) - - after_gas_price = web3.eth.gasPrice - assert after_gas_price < initial_gas_price +import random + +from flaky import ( + flaky, +) + +from web3._utils.threads import ( + Timeout, +) + + +@flaky(max_runs=3) +def test_miner_setGasPrice(web3_empty, wait_for_block): + web3 = web3_empty + + initial_gas_price = web3.vns.gasPrice + + # sanity check + assert web3.vns.gasPrice > 1000 + + web3.geth.miner.setGasPrice(initial_gas_price // 2) + + with Timeout(60) as timeout: + while web3.vns.gasPrice == initial_gas_price: + timeout.sleep(random.random()) + + after_gas_price = web3.vns.gasPrice + assert after_gas_price < initial_gas_price diff --git a/tests/core/mining-module/test_miner_start.py b/tests/core/mining-module/test_miner_start.py index 80de7839c4..dc6e8b878e 100644 --- a/tests/core/mining-module/test_miner_start.py +++ b/tests/core/mining-module/test_miner_start.py @@ -1,34 +1,34 @@ -import random - -from flaky import ( - flaky, -) - -from web3._utils.threads import ( - Timeout, -) - - -@flaky(max_runs=3) -def test_miner_start(web3_empty, wait_for_miner_start): - web3 = web3_empty - - # sanity - assert web3.eth.mining - assert web3.eth.hashrate - - web3.geth.miner.stop() - - with Timeout(60) as timeout: - while web3.eth.mining or web3.eth.hashrate: - timeout.sleep(random.random()) - - assert not web3.eth.mining - assert not web3.eth.hashrate - - web3.miner.start(1) - - wait_for_miner_start(web3) - - assert web3.eth.mining - assert web3.eth.hashrate +import random + +from flaky import ( + flaky, +) + +from web3._utils.threads import ( + Timeout, +) + + +@flaky(max_runs=3) +def test_miner_start(web3_empty, wait_for_miner_start): + web3 = web3_empty + + # sanity + assert web3.vns.mining + assert web3.vns.hashrate + + web3.geth.miner.stop() + + with Timeout(60) as timeout: + while web3.vns.mining or web3.vns.hashrate: + timeout.sleep(random.random()) + + assert not web3.vns.mining + assert not web3.vns.hashrate + + web3.miner.start(1) + + wait_for_miner_start(web3) + + assert web3.vns.mining + assert web3.vns.hashrate diff --git a/tests/core/mining-module/test_miner_stop.py b/tests/core/mining-module/test_miner_stop.py index 60b6633b3f..1525d4d32d 100644 --- a/tests/core/mining-module/test_miner_stop.py +++ b/tests/core/mining-module/test_miner_stop.py @@ -1,27 +1,27 @@ -import random - -from flaky import ( - flaky, -) - -from web3._utils.threads import ( - Timeout, -) - - -@flaky(max_runs=3) -def test_miner_stop(web3_empty): - web3 = web3_empty - - assert web3.eth.mining - assert web3.eth.hashrate - - web3.geth.miner.stop() - - with Timeout(60) as timeout: - while web3.eth.mining or web3.eth.hashrate: - timeout.sleep(random.random()) - timeout.check() - - assert not web3.eth.mining - assert not web3.eth.hashrate +import random + +from flaky import ( + flaky, +) + +from web3._utils.threads import ( + Timeout, +) + + +@flaky(max_runs=3) +def test_miner_stop(web3_empty): + web3 = web3_empty + + assert web3.vns.mining + assert web3.vns.hashrate + + web3.geth.miner.stop() + + with Timeout(60) as timeout: + while web3.vns.mining or web3.vns.hashrate: + timeout.sleep(random.random()) + timeout.check() + + assert not web3.vns.mining + assert not web3.vns.hashrate diff --git a/tests/core/mining-module/test_setEtherBase.py b/tests/core/mining-module/test_setEtherBase.py index 1daffd4039..8e24b7ba1b 100644 --- a/tests/core/mining-module/test_setEtherBase.py +++ b/tests/core/mining-module/test_setEtherBase.py @@ -1,7 +1,7 @@ - -def test_miner_setEtherbase(web3_empty): - web3 = web3_empty - assert web3.eth.coinbase == web3.eth.accounts[0] - new_account = web3.personal.newAccount('this-is-a-password') - web3.geth.miner.setEtherBase(new_account) - assert web3.eth.coinbase == new_account + +def test_miner_setEtherbase(web3_empty): + web3 = web3_empty + assert web3.vns.coinbase == web3.vns.accounts[0] + new_account = web3.personal.newAccount('this-is-a-password') + web3.geth.miner.setEtherBase(new_account) + assert web3.vns.coinbase == new_account diff --git a/tests/core/pm-module/conftest.py b/tests/core/pm-module/conftest.py index 4fa13fa90a..385f4408d1 100644 --- a/tests/core/pm-module/conftest.py +++ b/tests/core/pm-module/conftest.py @@ -1,119 +1,255 @@ -import json -import pytest - -from eth_tester import ( - EthereumTester, - PyEVMBackend, -) -from eth_utils import ( - to_bytes, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.contract import ( - LinkableContract, -) -from web3 import Web3 -from web3.pm import ( - SimpleRegistry, -) -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) - -SOL_PACKAGE_ID_1 = to_bytes(hexstr='0x60c5112b61159e6b42d54d945078394e9d5fc9c6ff0f3df78977006f8bbc06d4') # noqa: E501 -SOL_PACKAGE_ID_2 = to_bytes(hexstr='0xdbcfb0bd7115bf659350d77bb22bb889ca8294f61b0ca480f8a47bb8fc904cc9') # noqa: E501 -SOL_PACKAGE_ID_3 = to_bytes(hexstr='0xf3e4002c48a7f8f3485d62988317849c175340b66517c3b2993f725643eba84b') # noqa: E501 -SOL_RELEASE_ID_1 = to_bytes(hexstr='0x13414014c4f3c0ee41f1ede8e612e0377ae741f3abaa8d22e84e6b3759334fe9') # noqa: E501 -SOL_RELEASE_ID_2 = to_bytes(hexstr='0x30cb63a88e721b461e294fa212af64f12e9500b3892e0e65fa70090ab63afb4d') # noqa: E501 -SOL_RELEASE_ID_3 = to_bytes(hexstr='0x73f5fafa3d9bd5080d9b27c092cd65fdbf7c8f982df4d5d0de22eb2cd56f4fcb') # noqa: E501 -SOL_RELEASE_ID_4 = to_bytes(hexstr='0x7fc4e4c04e1a4e5cba315f8fce216f8a77e1a1dd7c6539635555f95d1042667f') # noqa: E501 - - -def setup_w3(): - genesis_overrides = {"gas_limit": 5500000} - custom_genesis_params = PyEVMBackend._generate_genesis_params( - overrides=genesis_overrides - ) - pyevm_backend = PyEVMBackend(genesis_parameters=custom_genesis_params) - t = EthereumTester(backend=pyevm_backend) - w3 = Web3(Web3.EthereumTesterProvider(ethereum_tester=t)) - w3.eth.defaultAccount = w3.eth.accounts[0] - w3.eth.defaultContractFactory = LinkableContract - w3.enable_unstable_package_management_api() - return w3 - - -def sol_registry(w3): - manifest = json.loads((ASSETS_DIR / "registry" / "2.0.0a1.json").read_text()) - registry_package = Package(manifest, w3) - registry_deployer = Deployer(registry_package) - deployed_registry_package = registry_deployer.deploy("PackageRegistry") - assert isinstance(registry_package, Package) - registry = deployed_registry_package.deployments.get_instance("PackageRegistry") - return SimpleRegistry(registry.address, w3) - - -def release_packages(registry): - registry._release( - "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - ) - registry._release( - "package", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW" - ) - registry._release( - "package", "1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX" - ) - registry._release( - "package", "1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ" - ) - registry._release( - "package", "1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK" - ) - registry._release( - "package", "1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH" - ) - registry._release( - "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - ) - registry._release( - "package2", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT" - ) - registry._release( - "package3", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGA" - ) - registry._release( - "package4", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGB" - ) - registry._release( - "package5", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGC" - ) - return registry - - -# Module-level variables used here for efficiency -# Tests are written against the sample packages released in `release_packages()` above, if more -# tests are needed, they should take into account the releases that exist on a "loaded registry". -W3 = setup_w3() -FRESH_SOL_REGISTRY = sol_registry(W3) -LOADED_SOL_REGISTRY = release_packages(sol_registry(W3)) -SOL_PKG_IDS = (SOL_PACKAGE_ID_1, SOL_PACKAGE_ID_2, SOL_PACKAGE_ID_3) -SOL_RLS_IDS = (SOL_RELEASE_ID_1, SOL_RELEASE_ID_2, SOL_RELEASE_ID_3, SOL_RELEASE_ID_4) - - -@pytest.fixture -def w3(): - return W3 - - -@pytest.fixture -def empty_sol_registry(): - return FRESH_SOL_REGISTRY - - -@pytest.fixture -def loaded_sol_registry(): - return LOADED_SOL_REGISTRY, SOL_PKG_IDS, SOL_RLS_IDS +import json +import pytest + +from vns_tester import ( + EthereumTester, + PyEVMBackend, +) +from vns_utils import ( + function_abi_to_4byte_selector, + to_bytes, + to_canonical_address, +) +from ethpm import ( + ASSETS_DIR, + Package, +) +from ethpm.contract import ( + LinkableContract, +) +from pytest_ethereum import ( + linker, +) +from pytest_ethereum.deployer import ( + Deployer, +) + +from web3 import Web3 +from web3.pm import ( + SolidityReferenceRegistry, + VyperReferenceRegistry, +) + +VY_PACKAGE_ID_1 = to_bytes(hexstr='0xd059e8a6ea5a8bbf8dd097a7b8922316dcb7f024e8220b56d3c9e7188a6a7640') # noqa: E501 +VY_PACKAGE_ID_2 = to_bytes(hexstr='0x6df709a85698ad921462b8979547e3a873e22e1c73b1cb691f9376847fb2d402') # noqa: E501 +VY_PACKAGE_ID_3 = to_bytes(hexstr='0x80e41a42e3b8c3af0ea51c3fd50d481eef1367dd9d3797ba93d35dcf60660882') # noqa: E501 +VY_RELEASE_ID_1 = to_bytes(hexstr='0x595e26f1b2247bacc57e32807f8059e0b0837dd9c47e6946994196bd29c9ca97') # noqa: E501 +VY_RELEASE_ID_2 = to_bytes(hexstr='0x04592cb9ced5413e1b09e87b089bf696a05efb76ee87c8c41259635fac6d938a') # noqa: E501 +VY_RELEASE_ID_3 = to_bytes(hexstr='0xa06dbc51f8891894778ce03dc23d706d228c99789595758bd3dcac3a93f47f0a') # noqa: E501 +VY_RELEASE_ID_4 = to_bytes(hexstr='0x9c6f87dda6435b2506e81206b39ed782a44b92d826c24ab02bbded321b86e2ad') # noqa: E501 +SOL_PACKAGE_ID_1 = to_bytes(hexstr='0x60c5112b61159e6b42d54d945078394e9d5fc9c6ff0f3df78977006f8bbc06d4') # noqa: E501 +SOL_PACKAGE_ID_2 = to_bytes(hexstr='0xdbcfb0bd7115bf659350d77bb22bb889ca8294f61b0ca480f8a47bb8fc904cc9') # noqa: E501 +SOL_PACKAGE_ID_3 = to_bytes(hexstr='0xf3e4002c48a7f8f3485d62988317849c175340b66517c3b2993f725643eba84b') # noqa: E501 +SOL_RELEASE_ID_1 = to_bytes(hexstr='0x73835668f71c7ae85cbdcdbb5a9905fa420ffe85a847d283fa9beefcd56cacc4') # noqa: E501 +SOL_RELEASE_ID_2 = to_bytes(hexstr='0xe5ef0292a3b36b6ac2be07ee92df61be15e0b9f102df32cbe3f0c012ef69d462') # noqa: E501 +SOL_RELEASE_ID_3 = to_bytes(hexstr='0x1280148e0af5c47e95b41df15734d0726c7320fc4cf21efe3923fb047b53899d') # noqa: E501 +SOL_RELEASE_ID_4 = to_bytes(hexstr='0x7082e954e4fd6adf8a25c6cefe218f32ec66d8a197197f1d05aa67a65caf5111') # noqa: E501 + + +def setup_w3(): + genesis_overrides = {"gas_limit": 5500000} + custom_genesis_params = PyEVMBackend._generate_genesis_params( + overrides=genesis_overrides + ) + pyevm_backend = PyEVMBackend(genesis_parameters=custom_genesis_params) + t = EthereumTester(backend=pyevm_backend) + w3 = Web3 (Web3.EthereumTesterProvider(ethereum_tester=t)) + w3.vns.defaultAccount = w3.vns.accounts[0] + w3.vns.defaultContractFactory = LinkableContract + w3.enable_unstable_package_management_api() + return w3 + + +def solidity_registry_strategy(): + def set_authority(package): + w3 = package.w3 + authority = package.deployments.get_instance("WhitelistAuthority").address + package_registry = package.deployments.get_instance("PackageRegistry") + package_db = package.deployments.get_instance("PackageDB") + release_db = package.deployments.get_instance("ReleaseDB") + txh_1 = package_registry.functions.setAuthority(authority).transact() + w3.vns.waitForTransactionReceipt(txh_1) + txh_2 = package_db.functions.setAuthority(authority).transact() + w3.vns.waitForTransactionReceipt(txh_2) + txh_3 = release_db.functions.setAuthority(authority).transact() + w3.vns.waitForTransactionReceipt(txh_3) + + def set_dependencies(package): + w3 = package.w3 + package_db = package.deployments.get_instance("PackageDB").address + release_db = package.deployments.get_instance("ReleaseDB").address + release_validator = package.deployments.get_instance("ReleaseValidator").address + package_registry = package.deployments.get_instance("PackageRegistry") + txh_1 = package_registry.functions.setPackageDb(package_db).transact() + w3.vns.waitForTransactionReceipt(txh_1) + txh_2 = package_registry.functions.setReleaseDb(release_db).transact() + w3.vns.waitForTransactionReceipt(txh_2) + txh_3 = package_registry.functions.setReleaseValidator( + release_validator + ).transact() + w3.vns.waitForTransactionReceipt(txh_3) + + def get_selector(deployments, contract, fn): + function_abi = [ + x for x in deployments.get_instance(contract).abi if x["name"] == fn + ][0] + return function_abi_to_4byte_selector(function_abi) + + def set_permissions(package): + w3 = package.w3 + deployments = package.deployments + set_version = get_selector(deployments, "ReleaseDB", "setVersion") + set_release = get_selector(deployments, "ReleaseDB", "setRelease") + set_package = get_selector(deployments, "PackageDB", "setPackage") + set_package_owner = get_selector(deployments, "PackageDB", "setPackageOwner") + release = get_selector(deployments, "PackageRegistry", "release") + transfer_package_owner = get_selector( + deployments, "PackageRegistry", "transferPackageOwner" + ) + package_db = package.deployments.get_instance("PackageDB").address + release_db = package.deployments.get_instance("ReleaseDB").address + package_registry = package.deployments.get_instance("PackageRegistry").address + authority = package.deployments.get_instance("WhitelistAuthority") + txh_1 = authority.functions.setCanCall( + package_registry, release_db, set_release, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_1) + txh_2 = authority.functions.setCanCall( + package_registry, package_db, set_package, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_2) + txh_3 = authority.functions.setCanCall( + package_registry, package_db, set_package_owner, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_3) + txh_4 = authority.functions.setAnyoneCanCall( + release_db, set_version, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_4) + txh_5 = authority.functions.setAnyoneCanCall( + package_registry, release, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_5) + txh_6 = authority.functions.setAnyoneCanCall( + package_registry, transfer_package_owner, True + ).transact() + w3.vns.waitForTransactionReceipt(txh_6) + + strategy = linker.linker( + linker.deploy("IndexedOrderedSetLib"), + linker.link("PackageDB", "IndexedOrderedSetLib"), + linker.link("ReleaseDB", "IndexedOrderedSetLib"), + linker.deploy("PackageRegistry"), + linker.deploy("WhitelistAuthority"), + linker.deploy("PackageDB"), + linker.deploy("ReleaseDB"), + linker.deploy("ReleaseValidator"), + linker.run_python(set_authority), + linker.run_python(set_dependencies), + linker.run_python(set_permissions), + ) + return strategy + + +def sol_registry(w3): + manifest = json.loads((ASSETS_DIR / "registry" / "1.0.0.json").read_text()) + strategy = solidity_registry_strategy() + registry_package = Package(manifest, w3) + registry_deployer = Deployer(registry_package) + registry_deployer.register_strategy("PackageRegistry", strategy) + deployed_registry_package = registry_deployer.deploy("PackageRegistry") + assert isinstance(registry_package, Package) + registry = deployed_registry_package.deployments.get_instance("PackageRegistry") + return SolidityReferenceRegistry(to_canonical_address(registry.address), w3) + + +def vy_registry(w3): + registry_path = ASSETS_DIR / "vyper_registry" + manifest = json.loads((registry_path / "0.1.0.json").read_text().rstrip('\n')) + registry_package = Package(manifest, w3) + registry_deployer = Deployer(registry_package) + deployed_registry_package = registry_deployer.deploy("registry") + registry_instance = deployed_registry_package.deployments.get_instance("registry") + assert registry_instance.functions.owner().call() == w3.vns.defaultAccount + return VyperReferenceRegistry(to_canonical_address(registry_instance.address), w3) + + +def release_packages(registry): + registry._release( + "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" + ) + registry._release( + "package", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW" + ) + registry._release( + "package", "1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX" + ) + registry._release( + "package", "1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ" + ) + registry._release( + "package", "1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK" + ) + registry._release( + "package", "1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH" + ) + registry._release( + "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" + ) + registry._release( + "package2", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT" + ) + registry._release( + "package3", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGA" + ) + registry._release( + "package4", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGB" + ) + registry._release( + "package5", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGC" + ) + return registry + + +# Module-level variables used here for efficiency +# Tests are written against the sample packages released in `release_packages()` above, if more +# tests are needed, they should take into account the releases that exist on a "loaded registry". +W3 = setup_w3() +FRESH_VY_REGISTRY = vy_registry(W3) +FRESH_SOL_REGISTRY = sol_registry(W3) +LOADED_VY_REGISTRY = release_packages(vy_registry(W3)) +LOADED_SOL_REGISTRY = release_packages(sol_registry(W3)) +VY_PKG_IDS = (VY_PACKAGE_ID_1, VY_PACKAGE_ID_2, VY_PACKAGE_ID_3) +SOL_PKG_IDS = (SOL_PACKAGE_ID_1, SOL_PACKAGE_ID_2, SOL_PACKAGE_ID_3) +VY_RLS_IDS = (VY_RELEASE_ID_1, VY_RELEASE_ID_2, VY_RELEASE_ID_3, VY_RELEASE_ID_4) +SOL_RLS_IDS = (SOL_RELEASE_ID_1, SOL_RELEASE_ID_2, SOL_RELEASE_ID_3, SOL_RELEASE_ID_4) + + +@pytest.fixture +def w3(): + return W3 + + +@pytest.fixture +def empty_vy_registry(): + return FRESH_VY_REGISTRY + + +@pytest.fixture +def empty_sol_registry(): + return FRESH_SOL_REGISTRY + + +@pytest.fixture +def loaded_vy_registry(): + return LOADED_VY_REGISTRY, VY_PKG_IDS, VY_RLS_IDS + + +@pytest.fixture +def loaded_sol_registry(): + return LOADED_SOL_REGISTRY, SOL_PKG_IDS, SOL_RLS_IDS + + +@pytest.fixture +def registry_getter(request): + return request.getfixturevalue(request.param) diff --git a/tests/core/pm-module/test_ens_integration.py b/tests/core/pm-module/test_ens_integration.py index 8413f32cd0..5740decaa4 100644 --- a/tests/core/pm-module/test_ens_integration.py +++ b/tests/core/pm-module/test_ens_integration.py @@ -1,143 +1,143 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ens import ENS -from ethpm import ( - ASSETS_DIR, -) -from web3.exceptions import ( - InvalidAddress, -) -from web3.pm import ( - SimpleRegistry, -) - - -def bytes32(val): - if isinstance(val, int): - result = to_bytes(val) - else: - raise TypeError('val %r could not be converted to bytes') - return result.rjust(32, b'\0') - - -@pytest.fixture -def ens_setup(deployer): - # todo: move to module level once ethpm alpha stable - ENS_MANIFEST = ASSETS_DIR / 'ens' / '1.0.1.json' - ens_deployer = deployer(ENS_MANIFEST) - w3 = ens_deployer.package.w3 - - # ** Set up ENS contracts ** - - # remove account that creates ENS, so test transactions don't have write access - accounts = w3.eth.accounts - ens_key = accounts.pop() - - # create ENS contract - # values borrowed from: - # https://github.com/ethereum/web3.py/blob/master/tests/ens/conftest.py#L109 - eth_labelhash = w3.keccak(text='eth') - eth_namehash = bytes32(0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae) - resolver_namehash = bytes32(0xfdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5) - ens_package = ens_deployer.deploy("ENSRegistry", transaction={"from": ens_key}) - ens_contract = ens_package.deployments.get_instance("ENSRegistry") - - # create public resolver - public_resolver_package = ens_deployer.deploy( - "PublicResolver", - ens_contract.address, - transaction={"from": ens_key} - ) - public_resolver = public_resolver_package.deployments.get_instance("PublicResolver") - - # set 'resolver.eth' to resolve to public resolver - ens_contract.functions.setSubnodeOwner( - b'\0' * 32, - eth_labelhash, - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setSubnodeOwner( - eth_namehash, - w3.keccak(text='resolver'), - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setResolver( - resolver_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - public_resolver.functions.setAddr( - resolver_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - # create .eth auction registrar - eth_registrar_package = ens_deployer.deploy( - "FIFSRegistrar", - ens_contract.address, - eth_namehash, - transaction={"from": ens_key} - ) - eth_registrar = eth_registrar_package.deployments.get_instance("FIFSRegistrar") - - # set '.eth' to resolve to the registrar - ens_contract.functions.setResolver( - eth_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - public_resolver.functions.setAddr( - eth_namehash, - eth_registrar.address - ).transact({'from': ens_key}) - - # set owner of tester.eth to an account controlled by tests - ens_contract.functions.setSubnodeOwner( - eth_namehash, - w3.keccak(text='tester'), - w3.eth.accounts[2] # note that this does not have to be the default, only in the list - ).transact({'from': ens_key}) - - # make the registrar the owner of the 'eth' name - ens_contract.functions.setSubnodeOwner( - b'\0' * 32, - eth_labelhash, - eth_registrar.address - ).transact({'from': ens_key}) - return ENS.fromWeb3(w3, ens_contract.address) - - -@pytest.fixture -def ens(ens_setup, mocker): - mocker.patch('web3.middleware.stalecheck._isfresh', return_value=True) - ens_setup.web3.eth.defaultAccount = ens_setup.web3.eth.coinbase - ens_setup.web3.enable_unstable_package_management_api() - return ens_setup - - -def test_ens_must_be_set_before_ens_methods_can_be_used(ens): - w3 = ens.web3 - with pytest.raises(InvalidAddress): - w3.pm.set_registry("tester.eth") - - -def test_web3_ens(ens): - w3 = ens.web3 - ns = ENS.fromWeb3(w3, ens.ens.address) - w3.ens = ns - registry = SimpleRegistry.deploy_new_instance(w3) - w3.ens.setup_address('tester.eth', registry.address) - actual_addr = ens.address('tester.eth') - w3.pm.set_registry('tester.eth') - assert w3.pm.registry.address == actual_addr - w3.pm.release_package('owned', '1.0.0', 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW') - pkg_name, version, manifest_uri = w3.pm.get_release_data('owned', '1.0.0') - assert pkg_name == 'owned' - assert version == '1.0.0' - assert manifest_uri == 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW' +import pytest + +from vns_utils import ( + to_bytes, +) +from ethpm import ( + ASSETS_DIR, +) + +from ens import ENS +from web3.exceptions import ( + InvalidAddress, +) +from web3.pm import ( + VyperReferenceRegistry, +) + + +def bytes32(val): + if isinstance(val, int): + result = to_bytes(val) + else: + raise TypeError('val %r could not be converted to bytes') + return result.rjust(32, b'\0') + + +@pytest.fixture +def ens_setup(deployer): + # todo: move to module level once ethpm alpha stable + ENS_MANIFEST = ASSETS_DIR / 'ens' / '1.0.1.json' + ens_deployer = deployer(ENS_MANIFEST) + w3 = ens_deployer.package.w3 + + # ** Set up ENS contracts ** + + # remove account that creates ENS, so test transactions don't have write access + accounts = w3.vns.accounts + ens_key = accounts.pop() + + # create ENS contract + # values borrowed from: + # https://github.com/ethereum/web3.py/blob/master/tests/ens/conftest.py#L109 + vns_labelhash = w3.keccak(text='vns') + vns_namehash = bytes32(0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae) + resolver_namehash = bytes32(0xfdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5) + ens_package = ens_deployer.deploy("ENSRegistry", transaction={"from": ens_key}) + ens_contract = ens_package.deployments.get_instance("ENSRegistry") + + # create public resolver + public_resolver_package = ens_deployer.deploy( + "PublicResolver", + ens_contract.address, + transaction={"from": ens_key} + ) + public_resolver = public_resolver_package.deployments.get_instance("PublicResolver") + + # set 'resolver.vns' to resolve to public resolver + ens_contract.functions.setSubnodeOwner( + b'\0' * 32, + vns_labelhash, + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setSubnodeOwner( + vns_namehash, + w3.keccak(text='resolver'), + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setResolver( + resolver_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + public_resolver.functions.setAddr( + resolver_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + # create .vns auction registrar + vns_registrar_package = ens_deployer.deploy( + "FIFSRegistrar", + ens_contract.address, + vns_namehash, + transaction={"from": ens_key} + ) + vns_registrar = vns_registrar_package.deployments.get_instance("FIFSRegistrar") + + # set '.vns' to resolve to the registrar + ens_contract.functions.setResolver( + vns_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + public_resolver.functions.setAddr( + vns_namehash, + vns_registrar.address + ).transact({'from': ens_key}) + + # set owner of tester.vns to an account controlled by tests + ens_contract.functions.setSubnodeOwner( + vns_namehash, + w3.keccak(text='tester'), + w3.vns.accounts[2] # note that this does not have to be the default, only in the list + ).transact({'from': ens_key}) + + # make the registrar the owner of the 'vns' name + ens_contract.functions.setSubnodeOwner( + b'\0' * 32, + vns_labelhash, + vns_registrar.address + ).transact({'from': ens_key}) + return ENS.fromWeb3(w3, ens_contract.address) + + +@pytest.fixture +def ens(ens_setup, mocker): + mocker.patch('web3.middleware.stalecheck._isfresh', return_value=True) + ens_setup.web3.vns.defaultAccount = ens_setup.web3.vns.coinbase + ens_setup.web3.enable_unstable_package_management_api() + return ens_setup + + +def test_ens_must_be_set_before_ens_methods_can_be_used(ens): + w3 = ens.web3 + with pytest.raises(InvalidAddress): + w3.pm.set_registry("tester.vns") + + +def test_web3_ens(ens): + w3 = ens.web3 + ns = ENS.fromWeb3(w3, ens.ens.address) + w3.ens = ns + registry = VyperReferenceRegistry.deploy_new_instance(w3) + w3.ens.setup_address('tester.vns', registry.address) + actual_addr = ens.address('tester.vns') + w3.pm.set_registry('tester.vns') + assert w3.pm.registry.address == actual_addr + w3.pm.release_package('owned', '1.0.0', 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW') + pkg_name, version, manifest_uri = w3.pm.get_release_data('owned', '1.0.0') + assert pkg_name == 'owned' + assert version == '1.0.0' + assert manifest_uri == 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW' diff --git a/tests/core/pm-module/test_pm_init.py b/tests/core/pm-module/test_pm_init.py index 93b3c9da8c..68427c869e 100644 --- a/tests/core/pm-module/test_pm_init.py +++ b/tests/core/pm-module/test_pm_init.py @@ -1,123 +1,91 @@ -import json -import pytest - -from ethpm import ( - Package, -) -from ethpm.exceptions import ( - InsufficientAssetsError, -) -from ethpm.tools import ( - get_manifest as get_ethpm_manifest, -) -from web3.exceptions import ( - PMError, -) - - -def test_pm_init_with_minimal_manifest(w3): - owned_manifest = get_ethpm_manifest('owned', '1.0.1.json') - pm = w3.pm.get_package_from_manifest(owned_manifest) - assert pm.name == 'owned' - - -def test_get_contract_factory_raises_insufficient_assets_error(w3): - insufficient_owned_manifest = get_ethpm_manifest('owned', '1.0.0.json') - owned_package = w3.pm.get_package_from_manifest(insufficient_owned_manifest) - with pytest.raises(InsufficientAssetsError): - owned_package.get_contract_factory('Owned') - - -def test_get_contract_factory_with_valid_owned_manifest(w3): - owned_manifest = get_ethpm_manifest('owned', '1.0.1.json') - owned_package = w3.pm.get_package_from_manifest(owned_manifest) - owned_factory = owned_package.get_contract_factory('Owned') - tx_hash = owned_factory.constructor().transact() - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - owned_address = tx_receipt.contractAddress - owned_instance = owned_package.get_contract_instance("Owned", owned_address) - assert owned_instance.abi == owned_factory.abi - - -def test_get_contract_factory_with_valid_safe_math_lib_manifest(w3): - safe_math_lib_manifest = get_ethpm_manifest('safe-math-lib', '1.0.1.json') - safe_math_package = w3.pm.get_package_from_manifest(safe_math_lib_manifest) - safe_math_factory = safe_math_package.get_contract_factory("SafeMathLib") - tx_hash = safe_math_factory.constructor().transact() - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - safe_math_address = tx_receipt.contractAddress - safe_math_instance = safe_math_package.get_contract_instance("SafeMathLib", safe_math_address) - assert safe_math_instance.functions.safeAdd(1, 2).call() == 3 - - -def test_get_contract_factory_with_valid_escrow_manifest(w3): - escrow_manifest = get_ethpm_manifest("escrow", "1.0.2.json") - escrow_package = w3.pm.get_package_from_manifest(escrow_manifest) - escrow_factory = escrow_package.get_contract_factory('Escrow') - assert escrow_factory.needs_bytecode_linking - safe_send_factory = escrow_package.get_contract_factory('SafeSendLib') - safe_send_tx_hash = safe_send_factory.constructor().transact() - safe_send_tx_receipt = w3.eth.waitForTransactionReceipt(safe_send_tx_hash) - safe_send_address = safe_send_tx_receipt.contractAddress - linked_escrow_factory = escrow_factory.link_bytecode({"SafeSendLib": safe_send_address}) - assert linked_escrow_factory.needs_bytecode_linking is False - escrow_tx_hash = linked_escrow_factory.constructor(w3.eth.accounts[0]).transact() - escrow_tx_receipt = w3.eth.waitForTransactionReceipt(escrow_tx_hash) - escrow_address = escrow_tx_receipt.contractAddress - escrow_instance = linked_escrow_factory(address=escrow_address) - assert escrow_instance.functions.sender().call() == w3.eth.accounts[0] - - -def test_deploy_a_standalone_package_integration(w3): - standard_token_manifest = get_ethpm_manifest("standard-token", "1.0.1.json") - token_package = w3.pm.get_package_from_manifest(standard_token_manifest) - # Added deployment bytecode to manifest to be able to generate factory - ERC20 = token_package.get_contract_factory('StandardToken') - # totalSupply = 100 - tx_hash = ERC20.constructor(100).transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - address = tx_receipt["contractAddress"] - erc20 = w3.eth.contract(address=address, abi=ERC20.abi) - total_supply = erc20.functions.totalSupply().call() - assert total_supply == 100 - - -def test_pm_init_with_manifest_uri(w3, monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - dummy_standard_token_uri = "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg" - pkg = w3.pm.get_package_from_uri(dummy_standard_token_uri) - assert isinstance(pkg, Package) - assert pkg.name == "standard-token" - - -@pytest.fixture -def tmp_ethpmdir(tmp_path): - owned_manifest = get_ethpm_manifest("owned", "1.0.0.json") - ethpmdir = tmp_path / '_ethpm_packages' - ethpmdir.mkdir() - owned_dir = ethpmdir / 'owned' - owned_dir.mkdir() - manifest = owned_dir / 'manifest.json' - manifest.touch() - manifest.write_text(json.dumps(owned_manifest, sort_keys=True, separators=(",", ":"))) - return ethpmdir - - -def test_get_local_package(w3, tmp_ethpmdir): - pkg = w3.pm.get_local_package("owned", tmp_ethpmdir) - assert isinstance(pkg, Package) - assert pkg.name == "owned" - - -def test_get_local_package_with_invalid_ethpmdir(w3, tmp_path): - invalid_ethpmdir = tmp_path / 'invalid' - invalid_ethpmdir.mkdir() - with pytest.raises(PMError, match="not a valid ethPM packages directory."): - w3.pm.get_local_package("owned", invalid_ethpmdir) - - -def test_get_local_package_with_uninstalled_package(w3, tmp_ethpmdir): - with pytest.raises(PMError, match="Package: safe-math not found in "): - w3.pm.get_local_package("safe-math", tmp_ethpmdir) +import pytest + +from vns_utils import ( + to_canonical_address, +) +from ethpm import ( + Package, +) +from ethpm.exceptions import ( + InsufficientAssetsError, +) +from ethpm.tools import ( + get_manifest as get_ethpm_manifest, +) + + +def test_pm_init_with_minimal_manifest(w3): + owned_manifest = get_ethpm_manifest('owned', '1.0.1.json') + pm = w3.pm.get_package_from_manifest(owned_manifest) + assert pm.name == 'owned' + + +def test_get_contract_factory_raises_insufficient_assets_error(w3): + insufficient_owned_manifest = get_ethpm_manifest('owned', '1.0.0.json') + owned_package = w3.pm.get_package_from_manifest(insufficient_owned_manifest) + with pytest.raises(InsufficientAssetsError): + owned_package.get_contract_factory('Owned') + + +def test_get_contract_factory_with_valid_owned_manifest(w3): + owned_manifest = get_ethpm_manifest('owned', '1.0.1.json') + owned_package = w3.pm.get_package_from_manifest(owned_manifest) + owned_factory = owned_package.get_contract_factory('Owned') + tx_hash = owned_factory.constructor().transact() + tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + owned_address = to_canonical_address(tx_receipt.contractAddress) + owned_instance = owned_package.get_contract_instance("Owned", owned_address) + assert owned_instance.abi == owned_factory.abi + + +def test_get_contract_factory_with_valid_safe_math_lib_manifest(w3): + safe_math_lib_manifest = get_ethpm_manifest('safe-math-lib', '1.0.1.json') + safe_math_package = w3.pm.get_package_from_manifest(safe_math_lib_manifest) + safe_math_factory = safe_math_package.get_contract_factory("SafeMathLib") + tx_hash = safe_math_factory.constructor().transact() + tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + safe_math_address = to_canonical_address(tx_receipt.contractAddress) + safe_math_instance = safe_math_package.get_contract_instance("SafeMathLib", safe_math_address) + assert safe_math_instance.functions.safeAdd(1, 2).call() == 3 + + +def test_get_contract_factory_with_valid_escrow_manifest(w3): + escrow_manifest = get_ethpm_manifest("escrow", "1.0.2.json") + escrow_package = w3.pm.get_package_from_manifest(escrow_manifest) + escrow_factory = escrow_package.get_contract_factory('Escrow') + assert escrow_factory.needs_bytecode_linking + safe_send_factory = escrow_package.get_contract_factory('SafeSendLib') + safe_send_tx_hash = safe_send_factory.constructor().transact() + safe_send_tx_receipt = w3.vns.waitForTransactionReceipt(safe_send_tx_hash) + safe_send_address = to_canonical_address(safe_send_tx_receipt.contractAddress) + linked_escrow_factory = escrow_factory.link_bytecode({"SafeSendLib": safe_send_address}) + assert linked_escrow_factory.needs_bytecode_linking is False + escrow_tx_hash = linked_escrow_factory.constructor(w3.vns.accounts[0]).transact() + escrow_tx_receipt = w3.vns.waitForTransactionReceipt(escrow_tx_hash) + escrow_address = to_canonical_address(escrow_tx_receipt.contractAddress) + escrow_instance = linked_escrow_factory(address=escrow_address) + assert escrow_instance.functions.sender().call() == w3.vns.accounts[0] + + +def test_deploy_a_standalone_package_integration(w3): + standard_token_manifest = get_ethpm_manifest("standard-token", "1.0.1.json") + token_package = w3.pm.get_package_from_manifest(standard_token_manifest) + # Added deployment bytecode to manifest to be able to generate factory + ERC20 = token_package.get_contract_factory('StandardToken') + # totalSupply = 100 + tx_hash = ERC20.constructor(100).transact() + tx_receipt = w3.vns.getTransactionReceipt(tx_hash) + address = to_canonical_address(tx_receipt["contractAddress"]) + erc20 = w3.vns.contract(address=address, abi=ERC20.abi) + total_supply = erc20.functions.totalSupply().call() + assert total_supply == 100 + + +def test_pm_init_with_manifest_uri(w3, monkeypatch): + monkeypatch.setenv( + "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" + ) + dummy_standard_token_uri = "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg" + pkg = w3.pm.get_package_from_uri(dummy_standard_token_uri) + assert isinstance(pkg, Package) + assert pkg.name == "standard-token" diff --git a/tests/core/pm-module/test_registry.py b/tests/core/pm-module/test_registry.py index a4292dbbd3..0cf62c6f87 100644 --- a/tests/core/pm-module/test_registry.py +++ b/tests/core/pm-module/test_registry.py @@ -1,96 +1,127 @@ -from eth_utils import ( - is_address, -) - -from web3.pm import ( - ERC1319Registry, - SimpleRegistry, -) - - -def test_simple_registry_deploy_new_instance(w3): - registry = SimpleRegistry.deploy_new_instance(w3) - assert isinstance(registry, SimpleRegistry) - assert isinstance(registry, ERC1319Registry) - assert is_address(registry.address) - - -def test_registry_releases_properly(empty_sol_registry): - release_id_1 = empty_sol_registry._release( - "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - ) - release_id_2 = empty_sol_registry._release( - "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - ) - release_data_1 = empty_sol_registry._get_release_data(release_id_1) - release_data_2 = empty_sol_registry._get_release_data(release_id_2) - assert release_data_1[0] == "package" - assert release_data_1[1] == "1.0.0" - assert release_data_1[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - assert release_data_2[0] == "package1" - assert release_data_2[1] == "1.0.1" - assert release_data_2[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" - - -def test_registry_get_all_package_ids_and_get_package_name(loaded_sol_registry): - registry, expected_ids, _ = loaded_sol_registry - package_ids = registry._get_all_package_ids() - assert len(package_ids) == 6 - assert package_ids[0] == expected_ids[0] - assert package_ids[1] == expected_ids[1] - assert package_ids[2] == expected_ids[2] - assert registry._get_package_name(package_ids[0]) == "package" - assert registry._get_package_name(package_ids[1]) == "package1" - assert registry._get_package_name(package_ids[2]) == "package2" - - -def test_registry_get_release_id_and_get_all_release_ids(loaded_sol_registry): - registry, _, expected_ids = loaded_sol_registry - release_ids = registry._get_all_release_ids("package") - assert len(release_ids) == 6 - assert release_ids[:3] == expected_ids[:3] - assert registry._get_release_id("package", "1.0.0") == expected_ids[0] - assert registry._get_release_id("package", "1.0.1") == expected_ids[1] - assert registry._get_release_id("package", "1.0.2") == expected_ids[2] - - -def test_registry_num_package_ids(loaded_sol_registry): - registry, _, _ = loaded_sol_registry - assert registry._num_package_ids() == 6 - - -def test_registry_num_release_ids(loaded_sol_registry): - registry, _, _ = loaded_sol_registry - assert registry._num_release_ids("package") == 6 - assert registry._num_release_ids("package1") == 1 - assert registry._num_release_ids("package2") == 1 - - -def test_registry_generate_release_id(loaded_sol_registry): - registry, _, expected_ids = loaded_sol_registry - assert registry._generate_release_id("package", "1.0.0") == expected_ids[0] - assert registry._generate_release_id("package", "1.0.1") == expected_ids[1] - assert registry._generate_release_id("package", "1.0.2") == expected_ids[2] - assert registry._generate_release_id("does-not-exist", "1.0.0") == expected_ids[3] - - -def test_registry_get_release_data(loaded_sol_registry): - registry, _, release_ids = loaded_sol_registry - release_data_1 = registry._get_release_data(release_ids[0]) - release_data_2 = registry._get_release_data(release_ids[1]) - release_data_3 = registry._get_release_data(release_ids[2]) - assert release_data_1 == ( - "package", - "1.0.0", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV", - ) - assert release_data_2 == ( - "package", - "1.0.1", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW", - ) - assert release_data_3 == ( - "package", - "1.0.2", - "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX", - ) +import pytest + +from vns_utils import ( + is_address, +) + +from web3.pm import ( + ERCRegistry, + VyperReferenceRegistry, +) + + +def test_vyper_registry_deploy_new_instance(w3): + registry = VyperReferenceRegistry.deploy_new_instance(w3) + assert isinstance(registry, ERCRegistry) + assert isinstance(registry, VyperReferenceRegistry) + assert is_address(registry.address) + + +def test_vyper_registry_auth(w3): + registry = VyperReferenceRegistry.deploy_new_instance(w3) + assert registry.owner() == w3.vns.accounts[0] + registry.transfer_owner(w3.vns.accounts[1]) + assert registry.owner() == w3.vns.accounts[1] + + +@pytest.mark.parametrize( + "registry_getter", ["empty_vy_registry", "empty_sol_registry"], indirect=True +) +def test_registry_releases_properly(registry_getter): + registry = registry_getter + release_id_1 = registry._release( + "package", "1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" + ) + release_id_2 = registry._release( + "package1", "1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" + ) + release_data_1 = registry._get_release_data(release_id_1) + release_data_2 = registry._get_release_data(release_id_2) + assert release_data_1[0] == "package" + assert release_data_1[1] == "1.0.0" + assert release_data_1[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" + assert release_data_2[0] == "package1" + assert release_data_2[1] == "1.0.1" + assert release_data_2[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ" + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_get_all_package_ids_and_get_package_name(registry_getter): + registry, expected_ids, _ = registry_getter + package_ids = registry._get_all_package_ids() + assert len(package_ids) == 6 + assert package_ids[0] == expected_ids[0] + assert package_ids[1] == expected_ids[1] + assert package_ids[2] == expected_ids[2] + assert registry._get_package_name(package_ids[0]) == "package" + assert registry._get_package_name(package_ids[1]) == "package1" + assert registry._get_package_name(package_ids[2]) == "package2" + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_get_release_id_and_get_all_release_ids(registry_getter): + registry, _, expected_ids = registry_getter + release_ids = registry._get_all_release_ids("package") + assert len(release_ids) == 6 + assert release_ids[:3] == expected_ids[:3] + assert registry._get_release_id("package", "1.0.0") == expected_ids[0] + assert registry._get_release_id("package", "1.0.1") == expected_ids[1] + assert registry._get_release_id("package", "1.0.2") == expected_ids[2] + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_num_package_ids(registry_getter): + registry, _, _ = registry_getter + assert registry._num_package_ids() == 6 + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_num_release_ids(registry_getter): + registry, _, _ = registry_getter + assert registry._num_release_ids("package") == 6 + assert registry._num_release_ids("package1") == 1 + assert registry._num_release_ids("package2") == 1 + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_generate_release_id(registry_getter): + registry, _, expected_ids = registry_getter + assert registry._generate_release_id("package", "1.0.0") == expected_ids[0] + assert registry._generate_release_id("package", "1.0.1") == expected_ids[1] + assert registry._generate_release_id("package", "1.0.2") == expected_ids[2] + assert registry._generate_release_id("does-not-exist", "1.0.0") == expected_ids[3] + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_registry_get_release_data(registry_getter): + registry, _, release_ids = registry_getter + release_data_1 = registry._get_release_data(release_ids[0]) + release_data_2 = registry._get_release_data(release_ids[1]) + release_data_3 = registry._get_release_data(release_ids[2]) + assert release_data_1 == ( + "package", + "1.0.0", + "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV", + ) + assert release_data_2 == ( + "package", + "1.0.1", + "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW", + ) + assert release_data_3 == ( + "package", + "1.0.2", + "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX", + ) diff --git a/tests/core/pm-module/test_registry_integration.py b/tests/core/pm-module/test_registry_integration.py index 4535a2be92..670fe1874e 100644 --- a/tests/core/pm-module/test_registry_integration.py +++ b/tests/core/pm-module/test_registry_integration.py @@ -1,178 +1,200 @@ -import pytest - -from eth_utils import ( - is_address, - to_checksum_address, -) - -from ethpm import ( - Package, -) -from ethpm.contract import ( - LinkableContract, -) -from web3 import Web3 -from web3.exceptions import ( - PMError, -) -from web3.pm import ( - SimpleRegistry, - get_simple_registry_manifest, -) - - -@pytest.fixture -def fresh_w3(): - w3 = Web3(Web3.EthereumTesterProvider()) - w3.eth.defaultAccount = w3.eth.accounts[0] - w3.eth.defaultContractFactory = LinkableContract - w3.enable_unstable_package_management_api() - return w3 - - -def test_pm_get_package_from_manifest(w3): - manifest = get_simple_registry_manifest() - package = w3.pm.get_package_from_manifest(manifest) - assert isinstance(package, Package) - assert package.name == "ethpm-registry" - - -def test_pm_deploy_and_set_registry(fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - registry_address = fresh_w3.pm.deploy_and_set_registry() - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(registry_address) - - -def test_pm_set_registry(empty_sol_registry, fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - fresh_w3.pm.set_registry(address=to_checksum_address(empty_sol_registry.address)) - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(fresh_w3.pm.registry.address) - - -def test_pm_set_custom_registry(empty_sol_registry, fresh_w3): - assert not hasattr(fresh_w3.pm, "registry") - fresh_w3.pm.registry = empty_sol_registry - assert isinstance(fresh_w3.pm.registry, SimpleRegistry) - assert is_address(fresh_w3.pm.registry.address) - - -def test_pm_must_set_registry_before_all_registry_interaction_functions(fresh_w3): - with pytest.raises(PMError): - fresh_w3.pm.release_package( - "package", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - ) - with pytest.raises(PMError): - fresh_w3.pm.get_release_id_data(b"invalid_release_id") - with pytest.raises(PMError): - fresh_w3.pm.get_release_id("package", "1.0.0") - with pytest.raises(PMError): - fresh_w3.pm.get_release_data("package", "1.0.0") - with pytest.raises(PMError): - fresh_w3.pm.get_package("package", "1.0.0") - with pytest.raises(PMError): - fresh_w3.pm.get_all_package_names() - with pytest.raises(PMError): - fresh_w3.pm.get_all_package_releases("package") - with pytest.raises(PMError): - fresh_w3.pm.get_release_count("package") - with pytest.raises(PMError): - fresh_w3.pm.get_package_count() - - -def test_pm_release_package(empty_sol_registry, w3): - w3.pm.registry = empty_sol_registry - w3.pm.release_package( - "escrow", "1.0.0", "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" - ) - w3.pm.release_package( - "owned", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - ) - release_id_1 = w3.pm.get_release_id("escrow", "1.0.0") - release_id_2 = w3.pm.get_release_id("owned", "1.0.0") - package_data_1 = w3.pm.get_release_id_data(release_id_1) - package_data_2 = w3.pm.get_release_id_data(release_id_2) - assert package_data_1[0] == "escrow" - assert package_data_1[1] == "1.0.0" - assert package_data_1[2] == "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" - assert package_data_2[0] == "owned" - assert package_data_2[1] == "1.0.0" - assert package_data_2[2] == "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - - -def test_pm_get_release_data(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - package_data = w3.pm.get_release_data("package", "1.0.0") - assert package_data[0] == "package" - assert package_data[1] == "1.0.0" - assert package_data[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - - -def test_pm_get_all_package_names(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - all_pkgs = w3.pm.get_all_package_names() - assert all_pkgs == ( - "package", - "package1", - "package2", - "package3", - "package4", - "package5", - ) - - -def test_pm_package_count(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - assert w3.pm.get_package_count() == 6 - - -def test_pm_get_release_count(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - pkg_0_release_count = w3.pm.get_release_count("package") - pkg_1_release_count = w3.pm.get_release_count("package1") - pkg_2_release_count = w3.pm.get_release_count("package2") - assert pkg_0_release_count == 6 - assert pkg_1_release_count == 1 - assert pkg_2_release_count == 1 - - -def test_pm_get_all_package_versions(loaded_sol_registry, w3): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - all_rls_pkg_0 = w3.pm.get_all_package_releases("package") - all_rls_pkg_1 = w3.pm.get_all_package_releases("package1") - all_rls_pkg_2 = w3.pm.get_all_package_releases("package2") - assert all_rls_pkg_0 == ( - ("1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV"), - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW"), - ("1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX"), - ("1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ"), - ("1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK"), - ("1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH"), - ) - assert all_rls_pkg_1 == ( - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ"), - ) - assert all_rls_pkg_2 == ( - ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT"), - ) - - -def test_pm_get_package(loaded_sol_registry, w3, monkeypatch): - registry, _, _ = loaded_sol_registry - w3.pm.registry = registry - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - w3.pm.deploy_and_set_registry() - w3.pm.release_package( - "owned", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - ) - pkg = w3.pm.get_package("owned", "1.0.0") - assert isinstance(pkg, Package) - assert pkg.name == "owned" +import pytest + +from vns_utils import ( + is_address, + to_checksum_address, +) +from ethpm import ( + Package, +) +from ethpm.contract import ( + LinkableContract, +) + +from web3 import Web3 +from web3.exceptions import ( + PMError, +) +from web3.pm import ( + ERCRegistry, + VyperReferenceRegistry, + get_vyper_registry_manifest, +) + + +@pytest.fixture +def fresh_w3(): + w3 = Web3 (Web3.EthereumTesterProvider()) + w3.vns.defaultAccount = w3.vns.accounts[0] + w3.vns.defaultContractFactory = LinkableContract + w3.enable_unstable_package_management_api() + return w3 + + +def test_pm_get_package_from_manifest(w3): + manifest = get_vyper_registry_manifest() + package = w3.pm.get_package_from_manifest(manifest) + assert isinstance(package, Package) + assert package.name == "vyper-registry" + + +def test_pm_deploy_and_set_registry(fresh_w3): + assert not hasattr(fresh_w3.pm, "registry") + registry_address = fresh_w3.pm.deploy_and_set_registry() + assert isinstance(fresh_w3.pm.registry, VyperReferenceRegistry) + assert is_address(registry_address) + + +def test_pm_set_registry_with_vyper_default(empty_vy_registry, fresh_w3): + assert not hasattr(fresh_w3.pm, "registry") + fresh_w3.pm.set_registry(address=to_checksum_address(empty_vy_registry.address)) + assert isinstance(fresh_w3.pm.registry, ERCRegistry) + assert is_address(fresh_w3.pm.registry.address) + + +def test_pm_set_solidity_registry(empty_sol_registry, fresh_w3): + assert not hasattr(fresh_w3.pm, "registry") + fresh_w3.pm.registry = empty_sol_registry + assert isinstance(fresh_w3.pm.registry, ERCRegistry) + assert is_address(fresh_w3.pm.registry.address) + + +def test_pm_must_set_registry_before_all_registry_interaction_functions(fresh_w3): + with pytest.raises(PMError): + fresh_w3.pm.release_package( + "package", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" + ) + with pytest.raises(PMError): + fresh_w3.pm.get_release_id_data(b"invalid_release_id") + with pytest.raises(PMError): + fresh_w3.pm.get_release_id("package", "1.0.0") + with pytest.raises(PMError): + fresh_w3.pm.get_release_data("package", "1.0.0") + with pytest.raises(PMError): + fresh_w3.pm.get_package("package", "1.0.0") + with pytest.raises(PMError): + fresh_w3.pm.get_all_package_names() + with pytest.raises(PMError): + fresh_w3.pm.get_all_package_releases("package") + with pytest.raises(PMError): + fresh_w3.pm.get_release_count("package") + with pytest.raises(PMError): + fresh_w3.pm.get_package_count() + + +@pytest.mark.parametrize( + "registry_getter", ["empty_vy_registry", "empty_sol_registry"], indirect=True +) +def test_pm_release_package(registry_getter, w3): + w3.pm.registry = registry_getter + w3.pm.release_package( + "escrow", "1.0.0", "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" + ) + w3.pm.release_package( + "owned", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" + ) + release_id_1 = w3.pm.get_release_id("escrow", "1.0.0") + release_id_2 = w3.pm.get_release_id("owned", "1.0.0") + package_data_1 = w3.pm.get_release_id_data(release_id_1) + package_data_2 = w3.pm.get_release_id_data(release_id_2) + assert package_data_1[0] == "escrow" + assert package_data_1[1] == "1.0.0" + assert package_data_1[2] == "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" + assert package_data_2[0] == "owned" + assert package_data_2[1] == "1.0.0" + assert package_data_2[2] == "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_get_release_data(registry_getter, w3): + registry, _, _ = registry_getter + w3.pm.registry = registry + package_data = w3.pm.get_release_data("package", "1.0.0") + assert package_data[0] == "package" + assert package_data[1] == "1.0.0" + assert package_data[2] == "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_get_all_package_names(registry_getter, w3): + registry, _, _ = registry_getter + w3.pm.registry = registry + all_pkgs = w3.pm.get_all_package_names() + assert all_pkgs == ( + "package", + "package1", + "package2", + "package3", + "package4", + "package5", + ) + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_package_count(registry_getter, w3): + registry, _, _ = registry_getter + w3.pm.registry = registry + assert w3.pm.get_package_count() == 6 + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_get_release_count(registry_getter, w3): + registry, _, _ = registry_getter + w3.pm.registry = registry + pkg_0_release_count = w3.pm.get_release_count("package") + pkg_1_release_count = w3.pm.get_release_count("package1") + pkg_2_release_count = w3.pm.get_release_count("package2") + assert pkg_0_release_count == 6 + assert pkg_1_release_count == 1 + assert pkg_2_release_count == 1 + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_get_all_package_versions(registry_getter, w3): + registry, _, _ = registry_getter + w3.pm.registry = registry + all_rls_pkg_0 = w3.pm.get_all_package_releases("package") + all_rls_pkg_1 = w3.pm.get_all_package_releases("package1") + all_rls_pkg_2 = w3.pm.get_all_package_releases("package2") + assert all_rls_pkg_0 == ( + ("1.0.0", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV"), + ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGW"), + ("1.0.2", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGX"), + ("1.0.3", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGJ"), + ("1.0.4", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGK"), + ("1.0.5", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGH"), + ) + assert all_rls_pkg_1 == ( + ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGZ"), + ) + assert all_rls_pkg_2 == ( + ("1.0.1", "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGT"), + ) + + +@pytest.mark.parametrize( + "registry_getter", ["loaded_vy_registry", "loaded_sol_registry"], indirect=True +) +def test_pm_get_package(registry_getter, w3, monkeypatch): + registry, _, _ = registry_getter + w3.pm.registry = registry + monkeypatch.setenv( + "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" + ) + w3.pm.deploy_and_set_registry() + w3.pm.release_package( + "owned", "1.0.0", "ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" + ) + pkg = w3.pm.get_package("owned", "1.0.0") + assert isinstance(pkg, Package) + assert pkg.name == "owned" diff --git a/tests/core/providers/test_auto_provider.py b/tests/core/providers/test_auto_provider.py index 6fd773a90a..651e0a075b 100644 --- a/tests/core/providers/test_auto_provider.py +++ b/tests/core/providers/test_auto_provider.py @@ -1,155 +1,155 @@ -import importlib -import os -import pytest - -from eth_utils import ( - ValidationError, -) - -from web3.exceptions import ( - InfuraKeyNotFound, -) -from web3.providers import ( - HTTPProvider, - IPCProvider, - WebsocketProvider, -) -from web3.providers.auto import ( - load_provider_from_environment, -) - -# Ugly hack to import infura now that API KEY is required -os.environ['WEB3_INFURA_API_KEY'] = 'test' -from web3.auto import ( # noqa E402 isort:skip - infura, -) - - -@pytest.fixture(autouse=True) -def delete_environment_variables(monkeypatch): - monkeypatch.delenv('WEB3_INFURA_PROJECT_ID', raising=False) - monkeypatch.delenv('WEB3_INFURA_API_KEY', raising=False) - monkeypatch.delenv('WEB3_INFURA_API_SECRET', raising=False) - monkeypatch.delenv('WEB3_INFURA_SCHEME', raising=False) - - -@pytest.mark.parametrize( - 'uri, expected_type, expected_attrs', - ( - ('', type(None), {}), - ('http://1.2.3.4:5678', HTTPProvider, {'endpoint_uri': 'http://1.2.3.4:5678'}), - ('https://node.ontheweb.com', HTTPProvider, {'endpoint_uri': 'https://node.ontheweb.com'}), - ('file:///root/path/to/file.ipc', IPCProvider, {'ipc_path': '/root/path/to/file.ipc'}), - ('ws://1.2.3.4:5679', WebsocketProvider, {'endpoint_uri': 'ws://1.2.3.4:5679'}) - ), -) -def test_load_provider_from_env(monkeypatch, uri, expected_type, expected_attrs): - monkeypatch.setenv('WEB3_PROVIDER_URI', uri) - provider = load_provider_from_environment() - assert isinstance(provider, expected_type) - for attr, val in expected_attrs.items(): - assert getattr(provider, attr) == val - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_empty_key(monkeypatch, caplog, environ_name): - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') - monkeypatch.setenv(environ_name, '') - - with pytest.raises(InfuraKeyNotFound): - importlib.reload(infura) - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_deleted_key(monkeypatch, caplog, environ_name): - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') - - monkeypatch.delenv(environ_name, raising=False) - - with pytest.raises(InfuraKeyNotFound): - importlib.reload(infura) - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_websocket_empty_key(monkeypatch, caplog, environ_name): - monkeypatch.setenv(environ_name, '') - - with pytest.raises(InfuraKeyNotFound): - importlib.reload(infura) - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_websocket_deleted_key(monkeypatch, caplog, environ_name): - monkeypatch.delenv(environ_name, raising=False) - - with pytest.raises(InfuraKeyNotFound): - importlib.reload(infura) - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura(monkeypatch, caplog, environ_name): - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') - API_KEY = 'aoeuhtns' - - monkeypatch.setenv(environ_name, API_KEY) - - importlib.reload(infura) - assert len(caplog.record_tuples) == 0 - - w3 = infura.w3 - assert isinstance(w3.provider, HTTPProvider) - expected_url = 'https://%s/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) - assert getattr(w3.provider, 'endpoint_uri') == expected_url - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_websocket_default(monkeypatch, caplog, environ_name): - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'wss') - API_KEY = 'aoeuhtns' - monkeypatch.setenv(environ_name, API_KEY) - expected_url = 'wss://%s/ws/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) - - importlib.reload(infura) - assert len(caplog.record_tuples) == 0 - - w3 = infura.w3 - assert isinstance(w3.provider, WebsocketProvider) - assert getattr(w3.provider, 'endpoint_uri') == expected_url - - -def test_web3_auto_infura_raises_error_with_nonexistent_scheme(monkeypatch): - monkeypatch.setenv('WEB3_INFURA_API_KEY', 'test') - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'not-a-scheme') - - error_msg = "Cannot connect to Infura with scheme 'not-a-scheme'" - with pytest.raises(ValidationError, match=error_msg): - importlib.reload(infura) - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_websocket_with_secret(monkeypatch, caplog, environ_name): - monkeypatch.setenv(environ_name, 'test') - monkeypatch.setenv('WEB3_INFURA_API_SECRET', 'secret') - - importlib.reload(infura) - - w3 = infura.w3 - assert isinstance(w3.provider, WebsocketProvider) - expected_url = 'wss://:secret@%s/ws/v3/test' % (infura.INFURA_MAINNET_DOMAIN) - assert getattr(w3.provider, 'endpoint_uri') == expected_url - - -@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) -def test_web3_auto_infura_with_secret(monkeypatch, caplog, environ_name): - monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') - monkeypatch.setenv(environ_name, 'test') - monkeypatch.setenv('WEB3_INFURA_API_SECRET', 'secret') - - importlib.reload(infura) - - w3 = infura.w3 - assert isinstance(w3.provider, HTTPProvider) - expected_url = 'https://%s/v3/test' % (infura.INFURA_MAINNET_DOMAIN) - expected_auth_value = ('', 'secret') - assert getattr(w3.provider, 'endpoint_uri') == expected_url - assert w3.provider.get_request_kwargs()['auth'] == expected_auth_value +import importlib +import os +import pytest + +from vns_utils import ( + ValidationError, +) + +from web3.exceptions import ( + InfuraKeyNotFound, +) +from web3.providers import ( + HTTPProvider, + IPCProvider, + WebsocketProvider, +) +from web3.providers.auto import ( + load_provider_from_environment, +) + +# Ugly hack to import infura now that API KEY is required +os.environ['WEB3_INFURA_API_KEY'] = 'test' +from web3.auto import ( # noqa E402 isort:skip + infura, +) + + +@pytest.fixture(autouse=True) +def delete_environment_variables(monkeypatch): + monkeypatch.delenv('WEB3_INFURA_PROJECT_ID', raising=False) + monkeypatch.delenv('WEB3_INFURA_API_KEY', raising=False) + monkeypatch.delenv('WEB3_INFURA_API_SECRET', raising=False) + monkeypatch.delenv('WEB3_INFURA_SCHEME', raising=False) + + +@pytest.mark.parametrize( + 'uri, expected_type, expected_attrs', + ( + ('', type(None), {}), + ('http://1.2.3.4:5678', HTTPProvider, {'endpoint_uri': 'http://1.2.3.4:5678'}), + ('https://node.ontheweb.com', HTTPProvider, {'endpoint_uri': 'https://node.ontheweb.com'}), + ('file:///root/path/to/file.ipc', IPCProvider, {'ipc_path': '/root/path/to/file.ipc'}), + ('ws://1.2.3.4:5679', WebsocketProvider, {'endpoint_uri': 'ws://1.2.3.4:5679'}) + ), +) +def test_load_provider_from_env(monkeypatch, uri, expected_type, expected_attrs): + monkeypatch.setenv('WEB3_PROVIDER_URI', uri) + provider = load_provider_from_environment() + assert isinstance(provider, expected_type) + for attr, val in expected_attrs.items(): + assert getattr(provider, attr) == val + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_empty_key(monkeypatch, caplog, environ_name): + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') + monkeypatch.setenv(environ_name, '') + + with pytest.raises(InfuraKeyNotFound): + importlib.reload(infura) + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_deleted_key(monkeypatch, caplog, environ_name): + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') + + monkeypatch.delenv(environ_name, raising=False) + + with pytest.raises(InfuraKeyNotFound): + importlib.reload(infura) + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_websocket_empty_key(monkeypatch, caplog, environ_name): + monkeypatch.setenv(environ_name, '') + + with pytest.raises(InfuraKeyNotFound): + importlib.reload(infura) + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_websocket_deleted_key(monkeypatch, caplog, environ_name): + monkeypatch.delenv(environ_name, raising=False) + + with pytest.raises(InfuraKeyNotFound): + importlib.reload(infura) + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura(monkeypatch, caplog, environ_name): + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') + API_KEY = 'aoeuhtns' + + monkeypatch.setenv(environ_name, API_KEY) + + importlib.reload(infura) + assert len(caplog.record_tuples) == 0 + + w3 = infura.w3 + assert isinstance(w3.provider, HTTPProvider) + expected_url = 'https://%s/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) + assert getattr(w3.provider, 'endpoint_uri') == expected_url + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_websocket_default(monkeypatch, caplog, environ_name): + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'wss') + API_KEY = 'aoeuhtns' + monkeypatch.setenv(environ_name, API_KEY) + expected_url = 'wss://:@%s/ws/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) + + importlib.reload(infura) + assert len(caplog.record_tuples) == 0 + + w3 = infura.w3 + assert isinstance(w3.provider, WebsocketProvider) + assert getattr(w3.provider, 'endpoint_uri') == expected_url + + +def test_web3_auto_infura_raises_error_with_nonexistent_scheme(monkeypatch): + monkeypatch.setenv('WEB3_INFURA_API_KEY', 'test') + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'not-a-scheme') + + error_msg = "Cannot connect to Infura with scheme 'not-a-scheme'" + with pytest.raises(ValidationError, match=error_msg): + importlib.reload(infura) + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_websocket_with_secret(monkeypatch, caplog, environ_name): + monkeypatch.setenv(environ_name, 'test') + monkeypatch.setenv('WEB3_INFURA_API_SECRET', 'secret') + + importlib.reload(infura) + + w3 = infura.w3 + assert isinstance(w3.provider, WebsocketProvider) + expected_url = 'wss://:secret@%s/ws/v3/test' % (infura.INFURA_MAINNET_DOMAIN) + assert getattr(w3.provider, 'endpoint_uri') == expected_url + + +@pytest.mark.parametrize('environ_name', ['WEB3_INFURA_API_KEY', 'WEB3_INFURA_PROJECT_ID']) +def test_web3_auto_infura_with_secret(monkeypatch, caplog, environ_name): + monkeypatch.setenv('WEB3_INFURA_SCHEME', 'https') + monkeypatch.setenv(environ_name, 'test') + monkeypatch.setenv('WEB3_INFURA_API_SECRET', 'secret') + + importlib.reload(infura) + + w3 = infura.w3 + assert isinstance(w3.provider, HTTPProvider) + expected_url = 'https://%s/v3/test' % (infura.INFURA_MAINNET_DOMAIN) + expected_auth_value = ('', 'secret') + assert getattr(w3.provider, 'endpoint_uri') == expected_url + assert w3.provider.get_request_kwargs()['auth'] == expected_auth_value diff --git a/tests/core/providers/test_ipc_provider.py b/tests/core/providers/test_ipc_provider.py index 4ae9b70b6a..9d18891d98 100644 --- a/tests/core/providers/test_ipc_provider.py +++ b/tests/core/providers/test_ipc_provider.py @@ -1,97 +1,97 @@ -import os -import pathlib -import pytest -import socket -import tempfile -from threading import ( - Thread, -) -import time -import uuid - -from web3.auto.gethdev import ( - w3, -) -from web3.middleware import ( - construct_fixture_middleware, -) -from web3.providers.ipc import ( - IPCProvider, -) - - -@pytest.fixture -def jsonrpc_ipc_pipe_path(): - with tempfile.TemporaryDirectory() as temp_dir: - ipc_path = os.path.join(temp_dir, '{0}.ipc'.format(uuid.uuid4())) - try: - yield ipc_path - finally: - if os.path.exists(ipc_path): - os.remove(ipc_path) - - -def test_ipc_no_path(): - """ - IPCProvider.isConnected() returns False when no path is supplied - """ - ipc = IPCProvider(None) - assert ipc.isConnected() is False - - -def test_ipc_tilda_in_path(): - expectedPath = str(pathlib.Path.home()) + '/foo' - assert IPCProvider('~/foo').ipc_path == expectedPath - assert IPCProvider(pathlib.Path('~/foo')).ipc_path == expectedPath - - -@pytest.fixture -def simple_ipc_server(jsonrpc_ipc_pipe_path): - serv = socket.socket(socket.AF_UNIX) - serv.bind(jsonrpc_ipc_pipe_path) - serv.listen(1) - try: - yield serv - finally: - serv.close() - - -@pytest.fixture -def serve_empty_result(simple_ipc_server): - def reply(): - connection, client_address = simple_ipc_server.accept() - try: - connection.recv(1024) - connection.sendall(b'{"id":1, "result": {}') - time.sleep(0.1) - connection.sendall(b'}') - finally: - # Clean up the connection - connection.close() - simple_ipc_server.close() - - thd = Thread(target=reply, daemon=True) - thd.start() - - try: - yield - finally: - thd.join() - - -def test_sync_waits_for_full_result(jsonrpc_ipc_pipe_path, serve_empty_result): - provider = IPCProvider(pathlib.Path(jsonrpc_ipc_pipe_path), timeout=3) - result = provider.make_request("method", []) - assert result == {'id': 1, 'result': {}} - provider._socket.sock.close() - - -def test_web3_auto_gethdev(): - assert isinstance(w3.provider, IPCProvider) - return_block_with_long_extra_data = construct_fixture_middleware({ - 'eth_getBlockByNumber': {'extraData': '0x' + 'ff' * 33}, - }) - w3.middleware_onion.inject(return_block_with_long_extra_data, layer=0) - block = w3.eth.getBlock('latest') - assert 'extraData' not in block - assert block.proofOfAuthorityData == b'\xff' * 33 +import os +import pathlib +import pytest +import socket +import tempfile +from threading import ( + Thread, +) +import time +import uuid + +from web3.auto.gethdev import ( + w3, +) +from web3.middleware import ( + construct_fixture_middleware, +) +from web3.providers.ipc import ( + IPCProvider, +) + + +@pytest.fixture +def jsonrpc_ipc_pipe_path(): + with tempfile.TemporaryDirectory() as temp_dir: + ipc_path = os.path.join(temp_dir, '{0}.ipc'.format(uuid.uuid4())) + try: + yield ipc_path + finally: + if os.path.exists(ipc_path): + os.remove(ipc_path) + + +def test_ipc_no_path(): + """ + IPCProvider.isConnected() returns False when no path is supplied + """ + ipc = IPCProvider(None) + assert ipc.isConnected() is False + + +def test_ipc_tilda_in_path(): + expectedPath = str(pathlib.Path.home()) + '/foo' + assert IPCProvider('~/foo').ipc_path == expectedPath + assert IPCProvider(pathlib.Path('~/foo')).ipc_path == expectedPath + + +@pytest.fixture +def simple_ipc_server(jsonrpc_ipc_pipe_path): + serv = socket.socket(socket.AF_UNIX) + serv.bind(jsonrpc_ipc_pipe_path) + serv.listen(1) + try: + yield serv + finally: + serv.close() + + +@pytest.fixture +def serve_empty_result(simple_ipc_server): + def reply(): + connection, client_address = simple_ipc_server.accept() + try: + connection.recv(1024) + connection.sendall(b'{"id":1, "result": {}') + time.sleep(0.1) + connection.sendall(b'}') + finally: + # Clean up the connection + connection.close() + simple_ipc_server.close() + + thd = Thread(target=reply, daemon=True) + thd.start() + + try: + yield + finally: + thd.join() + + +def test_sync_waits_for_full_result(jsonrpc_ipc_pipe_path, serve_empty_result): + provider = IPCProvider(pathlib.Path(jsonrpc_ipc_pipe_path), timeout=3) + result = provider.make_request("method", []) + assert result == {'id': 1, 'result': {}} + provider._socket.sock.close() + + +def test_web3_auto_gethdev(): + assert isinstance(w3.provider, IPCProvider) + return_block_with_long_extra_data = construct_fixture_middleware({ + 'vns_getBlockByNumber': {'extraData': '0x' + 'ff' * 33}, + }) + w3.middleware_onion.inject(return_block_with_long_extra_data, layer=0) + block = w3.vns.getBlock('latest') + assert 'extraData' not in block + assert block.proofOfAuthorityData == b'\xff' * 33 diff --git a/tests/core/providers/test_provider.py b/tests/core/providers/test_provider.py index d71744fb3a..1ba66987dd 100644 --- a/tests/core/providers/test_provider.py +++ b/tests/core/providers/test_provider.py @@ -1,53 +1,53 @@ -from web3 import Web3 -from web3.providers import ( - AutoProvider, - BaseProvider, -) - - -class ConnectedProvider(BaseProvider): - def isConnected(self): - return True - - -class DisconnectedProvider(BaseProvider): - def isConnected(self): - return False - - -def test_isConnected_connected(): - """ - Web3.isConnected() returns True when connected to a node. - """ - web3 = Web3(ConnectedProvider()) - assert web3.isConnected() is True - - -def test_isConnected_disconnected(): - """ - Web3.isConnected() returns False when configured with a provider - that's not connected to a node. - """ - web3 = Web3(DisconnectedProvider()) - assert web3.isConnected() is False - - -def test_autoprovider_detection(): - def no_provider(): - return None - - def must_not_call(): - assert False - - auto = AutoProvider([ - no_provider, - DisconnectedProvider, - ConnectedProvider, - must_not_call, - ]) - - w3 = Web3(auto) - - assert w3.isConnected() - - assert isinstance(auto._active_provider, ConnectedProvider) +from web3 import Web3 +from web3.providers import ( + AutoProvider, + BaseProvider, +) + + +class ConnectedProvider(BaseProvider): + def isConnected(self): + return True + + +class DisconnectedProvider(BaseProvider): + def isConnected(self): + return False + + +def test_isConnected_connected(): + """ + Web3.isConnected() returns True when connected to a node. + """ + web3 = Web3(ConnectedProvider()) + assert web3.isConnected() is True + + +def test_isConnected_disconnected(): + """ + Web3.isConnected() returns False when configured with a provider + that's not connected to a node. + """ + web3 = Web3(DisconnectedProvider()) + assert web3.isConnected() is False + + +def test_autoprovider_detection(): + def no_provider(): + return None + + def must_not_call(): + assert False + + auto = AutoProvider([ + no_provider, + DisconnectedProvider, + ConnectedProvider, + must_not_call, + ]) + + w3 = Web3(auto) + + assert w3.isConnected() + + assert isinstance(auto._active_provider, ConnectedProvider) diff --git a/tests/core/providers/test_websocket_provider.py b/tests/core/providers/test_websocket_provider.py index 1036b38fe1..4d831f1335 100644 --- a/tests/core/providers/test_websocket_provider.py +++ b/tests/core/providers/test_websocket_provider.py @@ -1,71 +1,64 @@ -import asyncio -import pytest -import sys -from threading import ( - Thread, -) - -import websockets - -from tests.utils import ( - wait_for_ws, -) -from web3 import Web3 -from web3.exceptions import ( - ValidationError, -) -from web3.providers.websocket import ( - WebsocketProvider, -) - -if sys.version_info >= (3, 8): - from asyncio.exceptions import ( - TimeoutError, - ) -else: - from concurrent.futures import ( - TimeoutError, - ) - - -@pytest.yield_fixture -def start_websocket_server(open_port): - event_loop = asyncio.new_event_loop() - - def run_server(): - async def empty_server(websocket, path): - data = await websocket.recv() - await asyncio.sleep(0.02) - await websocket.send(data) - server = websockets.serve(empty_server, '127.0.0.1', open_port, loop=event_loop) - event_loop.run_until_complete(server) - event_loop.run_forever() - - thd = Thread(target=run_server) - thd.start() - try: - yield - finally: - event_loop.call_soon_threadsafe(event_loop.stop) - - -@pytest.fixture() -def w3(open_port, start_websocket_server): - # need new event loop as the one used by server is already running - event_loop = asyncio.new_event_loop() - endpoint_uri = 'ws://127.0.0.1:{}'.format(open_port) - event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) - provider = WebsocketProvider(endpoint_uri, websocket_timeout=0.01) - return Web3(provider) - - -def test_websocket_provider_timeout(w3): - with pytest.raises(TimeoutError): - w3.eth.accounts - - -def test_restricted_websocket_kwargs(): - invalid_kwargs = {'uri': 'ws://127.0.0.1:8546'} - re_exc_message = r'.*found: {0}*'.format(set(invalid_kwargs.keys())) - with pytest.raises(ValidationError, match=re_exc_message): - WebsocketProvider(websocket_kwargs=invalid_kwargs) +import asyncio +from concurrent.futures import ( + TimeoutError, +) +import pytest +from threading import ( + Thread, +) + +import websockets + +from tests.utils import ( + wait_for_ws, +) +from web3 import Web3 +from web3.exceptions import ( + ValidationError, +) +from web3.providers.websocket import ( + WebsocketProvider, +) + + +@pytest.yield_fixture +def start_websocket_server(open_port): + event_loop = asyncio.new_event_loop() + + def run_server(): + async def empty_server(websocket, path): + data = await websocket.recv() + await asyncio.sleep(0.02) + await websocket.send(data) + server = websockets.serve(empty_server, '127.0.0.1', open_port, loop=event_loop) + event_loop.run_until_complete(server) + event_loop.run_forever() + + thd = Thread(target=run_server) + thd.start() + try: + yield + finally: + event_loop.call_soon_threadsafe(event_loop.stop) + + +@pytest.fixture() +def w3(open_port, start_websocket_server): + # need new event loop as the one used by server is already running + event_loop = asyncio.new_event_loop() + endpoint_uri = 'ws://127.0.0.1:{}'.format(open_port) + event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) + provider = WebsocketProvider(endpoint_uri, websocket_timeout=0.01) + return Web3(provider) + + +def test_websocket_provider_timeout(w3): + with pytest.raises(TimeoutError): + w3.vns.accounts + + +def test_restricted_websocket_kwargs(): + invalid_kwargs = {'uri': 'ws://127.0.0.1:8546'} + re_exc_message = r'.*found: {0}*'.format(set(invalid_kwargs.keys())) + with pytest.raises(ValidationError, match=re_exc_message): + WebsocketProvider(websocket_kwargs=invalid_kwargs) diff --git a/tests/core/shh-module/test_shh_filter.py b/tests/core/shh-module/test_shh_filter.py index 0071e2ff1f..881ab40abc 100644 --- a/tests/core/shh-module/test_shh_filter.py +++ b/tests/core/shh-module/test_shh_filter.py @@ -1,261 +1,129 @@ -import pytest -import time - -from hexbytes import ( - HexBytes, -) - - -def test_shh_sync_filter_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning): - sender = web3.shh.newKeyPair() - sender_pub = web3.shh.getPublicKey(sender) - - receiver = web3.shh.newKeyPair() - receiver_pub = web3.shh.getPublicKey(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter = web3.shh.newMessageFilter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - received_messages = shh_filter.get_new_entries() - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[1]) - assert message["topic"] == HexBytes(topic) - - -def test_shh_sync_filter(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - sender = web3.shh.new_key_pair() - sender_pub = web3.shh.get_public_key(sender) - - receiver = web3.shh.new_key_pair() - receiver_pub = web3.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter = web3.shh.new_message_filter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - received_messages = shh_filter.get_new_entries() - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[1]) - assert message["topic"] == HexBytes(topic) - - -def test_shh_async_filter_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - received_messages = [] - - with pytest.warns(DeprecationWarning) as warnings: - sender = web3.shh.newKeyPair() - sender_pub = web3.shh.getPublicKey(sender) - - receiver = web3.shh.newKeyPair() - receiver_pub = web3.shh.getPublicKey(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter = web3.shh.newMessageFilter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }, poll_interval=0.5) - watcher = shh_filter.watch(received_messages.extend) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'pubKey': receiver_pub - }) - time.sleep(1) - - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[0]) - assert message["topic"] == HexBytes(topic) - - assert len(warnings) == 5 - - watcher.stop() - - -def test_shh_async_filter(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - received_messages = [] - - sender = web3.shh.new_key_pair() - sender_pub = web3.shh.get_public_key(sender) - - receiver = web3.shh.new_key_pair() - receiver_pub = web3.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter = web3.shh.new_message_filter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }, poll_interval=0.5) - watcher = shh_filter.watch(received_messages.extend) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'pubKey': receiver_pub - }) - time.sleep(1) - - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[0]) - assert message["topic"] == HexBytes(topic) - - watcher.stop() - - -def test_shh_remove_filter_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning): - - receiver = web3.shh.newKeyPair() - receiver_pub = web3.shh.getPublicKey(receiver) - - payload = web3.toHex(text="test message :)") - shh_filter = web3.shh.newMessageFilter({'privateKeyID': receiver}) - - web3.shh.post({ - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payload, - 'pubKey': receiver_pub - }) - time.sleep(1) - - message = shh_filter.get_new_entries()[0] - assert message["payload"] == HexBytes(payload) - - assert web3.shh.deleteMessageFilter(shh_filter.filter_id) - - try: - web3.shh.getMessages(shh_filter.filter_id) - assert False - except: - assert True - - -def test_shh_remove_filter(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - receiver = web3.shh.new_key_pair() - receiver_pub = web3.shh.get_public_key(receiver) - - payload = web3.toHex(text="test message :)") - shh_filter = web3.shh.new_message_filter({'privateKeyID': receiver}) - - web3.shh.post({ - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payload, - 'pubKey': receiver_pub - }) - time.sleep(1) - - message = shh_filter.get_new_entries()[0] - assert message["payload"] == HexBytes(payload) - - assert web3.shh.delete_message_filter(shh_filter.filter_id) - - try: - web3.shh.get_messages(shh_filter.filter_id) - assert False - except: - assert True +import time + +from hexbytes import ( + HexBytes, +) + + +def test_shh_sync_filter(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + sender = web3.shh.newKeyPair() + sender_pub = web3.shh.getPublicKey(sender) + + receiver = web3.shh.newKeyPair() + receiver_pub = web3.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter = web3.shh.newMessageFilter({ + 'privateKeyID': receiver, + 'sig': sender_pub, + 'topics': [topic] + }) + + web3.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[0], + 'pubKey': receiver_pub + }) + time.sleep(1) + + web3.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[1], + 'topic': topic, + 'pubKey': receiver_pub + }) + time.sleep(1) + + received_messages = shh_filter.get_new_entries() + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[1]) + assert message["topic"] == HexBytes(topic) + + +def test_shh_async_filter(web3, skip_if_testrpc): + skip_if_testrpc(web3) + received_messages = [] + + sender = web3.shh.newKeyPair() + sender_pub = web3.shh.getPublicKey(sender) + + receiver = web3.shh.newKeyPair() + receiver_pub = web3.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter = web3.shh.newMessageFilter({ + 'privateKeyID': receiver, + 'sig': sender_pub, + 'topics': [topic] + }, poll_interval=0.5) + watcher = shh_filter.watch(received_messages.extend) + + web3.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[0], + 'topic': topic, + 'pubKey': receiver_pub + }) + time.sleep(1) + + web3.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[1], + 'pubKey': receiver_pub + }) + time.sleep(1) + + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[0]) + assert message["topic"] == HexBytes(topic) + + watcher.stop() + + +def test_shh_remove_filter(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + receiver = web3.shh.newKeyPair() + receiver_pub = web3.shh.getPublicKey(receiver) + + payload = web3.toHex(text="test message :)") + shh_filter = web3.shh.newMessageFilter({'privateKeyID': receiver}) + + web3.shh.post({ + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payload, + 'pubKey': receiver_pub + }) + time.sleep(1) + + message = shh_filter.get_new_entries()[0] + assert message["payload"] == HexBytes(payload) + + assert web3.shh.deleteMessageFilter(shh_filter.filter_id) + + try: + web3.shh.getMessages(shh_filter.filter_id) + assert False + except: + assert True diff --git a/tests/core/shh-module/test_shh_key_pair.py b/tests/core/shh-module/test_shh_key_pair.py index d201e4d5fd..a2ac98e8b1 100644 --- a/tests/core/shh-module/test_shh_key_pair.py +++ b/tests/core/shh-module/test_shh_key_pair.py @@ -1,102 +1,45 @@ -import pytest - - -def test_shh_asymmetric_key_pair_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - # Test generating key - with pytest.warns(DeprecationWarning) as warnings: - key_id = web3.shh.newKeyPair() - assert web3.shh.haskeyPair(key_id) - assert len(web3.shh.getPublicKey(key_id)) == 132 - private_key = web3.shh.getPrivateKey(key_id) - assert len(private_key) == 66 - assert web3.shh.deleteKeyPair(key_id) - - # Test adding a key - assert not web3.shh.hasKeyPair(key_id) - - key_id = web3.shh.addPrivateKey(private_key) - assert web3.shh.hasKeyPair(key_id) - assert web3.shh.deleteKeyPair(key_id) - - assert len(warnings) == 9 - - -def test_shh_asymmetric_key_pair(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - # Test generating key - - key_id = web3.shh.new_key_pair() - assert web3.shh.has_key_pair(key_id) - assert len(web3.shh.get_public_key(key_id)) == 132 - private_key = web3.shh.get_private_key(key_id) - assert len(private_key) == 66 - assert web3.shh.delete_key_pair(key_id) - - # Test adding a key - assert not web3.shh.has_key_pair(key_id) - - key_id = web3.shh.add_private_key(private_key) - assert web3.shh.has_key_pair(key_id) - assert web3.shh.delete_key_pair(key_id) - - -def test_shh_symmetric_key_pair_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - # Test generating key - with pytest.warns(DeprecationWarning) as warnings: - key_id = web3.shh.newSymKey() - assert web3.shh.hasSymKey(key_id) - key = web3.shh.getSymKey(key_id) - assert len(key) == 66 - assert web3.shh.deleteSymKey(key_id) - - # Test adding a key - assert not web3.shh.hasSymKey(key_id) - key_id = web3.shh.addSymKey(key) - assert web3.shh.hasSymKey(key_id) - assert web3.shh.deleteSymKey(key_id) - - assert len(warnings) == 8 - - -def test_shh_symmetric_key_pair(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - # Test generating key - - key_id = web3.shh.new_sym_key() - assert web3.shh.has_sym_key(key_id) - key = web3.shh.get_sym_key(key_id) - assert len(key) == 66 - assert web3.shh.delete_sym_key(key_id) - - # Test adding a key - assert not web3.shh.has_sym_key(key_id) - key_id = web3.shh.add_sym_key(key) - assert web3.shh.has_sym_key(key_id) - assert web3.shh.delete_sym_key(key_id) - - -def test_shh_symmetric_key_pair_from_password_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning): - key_id = web3.shh.generate_sym_key_from_password('shh be quiet') - - assert web3.shh.hasSymKey(key_id) - assert len(web3.shh.getSymKey(key_id)) == 66 - assert web3.shh.deleteSymKey(key_id) - - -def test_shh_symmetric_key_pair_from_password(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - key_id = web3.shh.generate_sym_key_from_password('shh be quiet') - - assert web3.shh.has_sym_key(key_id) - assert len(web3.shh.get_sym_key(key_id)) == 66 - assert web3.shh.delete_sym_key(key_id) +def test_shh_asymmetric_key_pair(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + # Test generating key + key_id = web3.shh.newKeyPair() + assert web3.shh.hasKeyPair(key_id) + assert len(web3.shh.getPublicKey(key_id)) == 132 + + private_key = web3.shh.getPrivateKey(key_id) + assert len(private_key) == 66 + assert web3.shh.deleteKeyPair(key_id) + + # Test adding a key + assert not web3.shh.hasKeyPair(key_id) + key_id = web3.shh.addPrivateKey(private_key) + assert web3.shh.hasKeyPair(key_id) + assert web3.shh.deleteKeyPair(key_id) + + +def test_shh_symmetric_key_pair(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + # Test generating key + key_id = web3.shh.newSymKey() + assert web3.shh.hasSymKey(key_id) + + key = web3.shh.getSymKey(key_id) + assert len(key) == 66 + assert web3.shh.deleteSymKey(key_id) + + # Test adding a key + assert not web3.shh.hasSymKey(key_id) + key_id = web3.shh.addSymKey(key) + assert web3.shh.hasSymKey(key_id) + assert web3.shh.deleteSymKey(key_id) + + +def test_shh_symmetric_key_pair_from_password(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + key_id = web3.shh.generateSymKeyFromPassword('shh be quiet') + + assert web3.shh.hasSymKey(key_id) + assert len(web3.shh.getSymKey(key_id)) == 66 + assert web3.shh.deleteSymKey(key_id) diff --git a/tests/core/shh-module/test_shh_post.py b/tests/core/shh-module/test_shh_post.py index be334f7d6d..6cdd274700 100644 --- a/tests/core/shh-module/test_shh_post.py +++ b/tests/core/shh-module/test_shh_post.py @@ -1,29 +1,10 @@ -import pytest - - -def test_shh_post_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - with pytest.warns(DeprecationWarning) as warnings: - receiver_pub = web3.shh.getPublicKey(web3.shh.newKeyPair()) - assert web3.shh.post({ - "topic": "0x12345678", - "powTarget": 2.5, - "powTime": 2, - "payload": web3.toHex(text="testing shh on web3.py"), - "pubKey": receiver_pub, - }) - assert len(warnings) == 1 - - -def test_shh_post(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - receiver_pub = web3.shh.get_public_key(web3.shh.new_key_pair()) - assert web3.shh.post({ - "topic": "0x12345678", - "powTarget": 2.5, - "powTime": 2, - "payload": web3.toHex(text="testing shh on web3.py"), - "pubKey": receiver_pub, - }) +def test_shh_post(web3, skip_if_testrpc): + skip_if_testrpc(web3) + receiver_pub = web3.shh.getPublicKey(web3.shh.newKeyPair()) + assert web3.shh.post({ + "topic": "0x12345678", + "powTarget": 2.5, + "powTime": 2, + "payload": web3.toHex(text="testing shh on web3.py"), + "pubKey": receiver_pub, + }) diff --git a/tests/core/shh-module/test_shh_properties.py b/tests/core/shh-module/test_shh_properties.py index 0df26b79ae..d249716e58 100644 --- a/tests/core/shh-module/test_shh_properties.py +++ b/tests/core/shh-module/test_shh_properties.py @@ -1,33 +1,16 @@ -import pytest - - -def test_shh_version(web3, skip_if_testrpc): - skip_if_testrpc(web3) - assert web3.shh.version == '6.0' - - -def test_shh_info_deprecated(web3, skip_if_testrpc): - skip_if_testrpc(web3) - with pytest.warns(DeprecationWarning) as warnings: - web3.shh.setMaxMessageSize(1024) - web3.shh.setMinPoW(0.5) - - info = web3.shh.info - - assert len(warnings) == 2 - assert len(info) == 4 - assert info["maxMessageSize"] == 1024 - assert info["minPow"] == 0.5 - - -def test_shh_info(web3, skip_if_testrpc): - skip_if_testrpc(web3) - - web3.shh.set_max_message_size(1024) - web3.shh.set_min_pow(0.5) - - info = web3.shh.info - - assert len(info) == 4 - assert info["maxMessageSize"] == 1024 - assert info["minPow"] == 0.5 +def test_shh_version(web3, skip_if_testrpc): + skip_if_testrpc(web3) + assert web3.shh.version == '6.0' + + +def test_shh_info(web3, skip_if_testrpc): + skip_if_testrpc(web3) + + web3.shh.setMaxMessageSize(1024) + web3.shh.setMinPoW(0.5) + + info = web3.shh.info + + assert len(info) == 4 + assert info["maxMessageSize"] == 1024 + assert info["minPow"] == 0.5 diff --git a/tests/core/testing-module/test_testing_mine.py b/tests/core/testing-module/test_testing_mine.py index 4a22524207..1cbf8a4cbc 100644 --- a/tests/core/testing-module/test_testing_mine.py +++ b/tests/core/testing-module/test_testing_mine.py @@ -1,22 +1,22 @@ -def test_testing_mine_single_block(web3): - web3.testing.mine() - - before_mining_block = web3.eth.getBlock("latest") - - web3.testing.mine() - - after_mining_block = web3.eth.getBlock("latest") - - assert after_mining_block['number'] - before_mining_block['number'] == 1 - - -def test_testing_mine_multiple_blocks(web3): - web3.testing.mine() - - before_mining_block = web3.eth.getBlock("latest") - - web3.testing.mine(5) - - after_mining_block = web3.eth.getBlock("latest") - - assert after_mining_block['number'] - before_mining_block['number'] == 5 +def test_testing_mine_single_block(web3): + web3.testing.mine() + + before_mining_block = web3.vns.getBlock("latest") + + web3.testing.mine() + + after_mining_block = web3.vns.getBlock("latest") + + assert after_mining_block['number'] - before_mining_block['number'] == 1 + + +def test_testing_mine_multiple_blocks(web3): + web3.testing.mine() + + before_mining_block = web3.vns.getBlock("latest") + + web3.testing.mine(5) + + after_mining_block = web3.vns.getBlock("latest") + + assert after_mining_block['number'] - before_mining_block['number'] == 5 diff --git a/tests/core/testing-module/test_testing_snapshot_and_revert.py b/tests/core/testing-module/test_testing_snapshot_and_revert.py index 3d697d13a9..6eec2975f3 100644 --- a/tests/core/testing-module/test_testing_snapshot_and_revert.py +++ b/tests/core/testing-module/test_testing_snapshot_and_revert.py @@ -1,47 +1,47 @@ -def test_snapshot_revert_to_latest_snapshot(web3): - web3.testing.mine(5) - - block_before_snapshot = web3.eth.getBlock("latest") - - web3.testing.snapshot() - - block_after_snapshot = web3.eth.getBlock("latest") - - web3.testing.mine(3) - - block_after_mining = web3.eth.getBlock("latest") - - web3.testing.revert() - - block_after_revert = web3.eth.getBlock("latest") - - assert block_after_mining['number'] > block_before_snapshot['number'] - assert block_before_snapshot['hash'] == block_after_snapshot['hash'] - assert block_after_snapshot['hash'] == block_after_revert['hash'] - - -def test_snapshot_revert_to_specific(web3): - web3.testing.mine(5) - - block_before_snapshot = web3.eth.getBlock("latest") - - snapshot_idx = web3.testing.snapshot() - - block_after_snapshot = web3.eth.getBlock("latest") - - web3.testing.mine() - web3.testing.snapshot() - web3.testing.mine() - web3.testing.snapshot() - web3.testing.mine() - web3.testing.snapshot() - - block_after_mining = web3.eth.getBlock("latest") - - web3.testing.revert(snapshot_idx) - - block_after_revert = web3.eth.getBlock("latest") - - assert block_after_mining['number'] > block_before_snapshot['number'] - assert block_before_snapshot['hash'] == block_after_snapshot['hash'] - assert block_after_snapshot['hash'] == block_after_revert['hash'] +def test_snapshot_revert_to_latest_snapshot(web3): + web3.testing.mine(5) + + block_before_snapshot = web3.vns.getBlock("latest") + + web3.testing.snapshot() + + block_after_snapshot = web3.vns.getBlock("latest") + + web3.testing.mine(3) + + block_after_mining = web3.vns.getBlock("latest") + + web3.testing.revert() + + block_after_revert = web3.vns.getBlock("latest") + + assert block_after_mining['number'] > block_before_snapshot['number'] + assert block_before_snapshot['hash'] == block_after_snapshot['hash'] + assert block_after_snapshot['hash'] == block_after_revert['hash'] + + +def test_snapshot_revert_to_specific(web3): + web3.testing.mine(5) + + block_before_snapshot = web3.vns.getBlock("latest") + + snapshot_idx = web3.testing.snapshot() + + block_after_snapshot = web3.vns.getBlock("latest") + + web3.testing.mine() + web3.testing.snapshot() + web3.testing.mine() + web3.testing.snapshot() + web3.testing.mine() + web3.testing.snapshot() + + block_after_mining = web3.vns.getBlock("latest") + + web3.testing.revert(snapshot_idx) + + block_after_revert = web3.vns.getBlock("latest") + + assert block_after_mining['number'] > block_before_snapshot['number'] + assert block_before_snapshot['hash'] == block_after_snapshot['hash'] + assert block_after_snapshot['hash'] == block_after_revert['hash'] diff --git a/tests/core/testing-module/test_testing_timeTravel.py b/tests/core/testing-module/test_testing_timeTravel.py index 50628fee8e..9470465116 100644 --- a/tests/core/testing-module/test_testing_timeTravel.py +++ b/tests/core/testing-module/test_testing_timeTravel.py @@ -1,9 +1,9 @@ -def test_time_traveling(web3): - current_block_time = web3.eth.getBlock("pending")['timestamp'] - - time_travel_to = current_block_time + 12345 - - web3.testing.timeTravel(time_travel_to) - - latest_block_time = web3.eth.getBlock("pending")['timestamp'] - assert latest_block_time >= time_travel_to +def test_time_traveling(web3): + current_block_time = web3.vns.getBlock("pending")['timestamp'] + + time_travel_to = current_block_time + 12345 + + web3.testing.timeTravel(time_travel_to) + + latest_block_time = web3.vns.getBlock("pending")['timestamp'] + assert latest_block_time >= time_travel_to diff --git a/tests/core/tools/pytest_ethereum/assets/greeter.json b/tests/core/tools/pytest_ethereum/assets/greeter.json deleted file mode 100644 index e08abd2610..0000000000 --- a/tests/core/tools/pytest_ethereum/assets/greeter.json +++ /dev/null @@ -1 +0,0 @@ -{"contract_types":{"greeter":{"abi":[{"constant":false,"inputs":[],"name":"__init__","outputs":[],"payable":false,"type":"constructor"},{"constant":false,"gas":70954,"inputs":[{"name":"x","type":"bytes"}],"name":"setGreeting","outputs":[],"payable":false,"type":"function"},{"constant":false,"gas":4486,"inputs":[],"name":"greet","outputs":[{"name":"out","type":"bytes"}],"payable":false,"type":"function"}],"deployment_bytecode":{"bytecode":"0x600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a052341561009e57600080fd5b6005610140527f48656c6c6f0000000000000000000000000000000000000000000000000000006101605261014080600060c052602060c020602082510161012060006002818352015b826101205160200211156100fb5761011d565b61012051602002850151610120518501555b81516001018083528114156100e8575b50505050505061032f56600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a05263b8e46d3a600051141561013057602060046101403734156100b457600080fd5b60346004356004016101603760146004356004013511156100d457600080fd5b61016080600060c052602060c020602082510161012060006002818352015b8261012051602002111561010657610128565b61012051602002850151610120518501555b81516001018083528114156100f3575b505050505050005b63cfae3217600051141561020157341561014957600080fd5b60008060c052602060c020610180602082540161012060006002818352015b8261012051602002111561017b5761019d565b61012051850154610120516020028501525b8151600101808352811415610168575b5050505050506101e0610180516014818352015b60146101e05111156101c2576101de565b60006101e0516101a001535b81516001018083528114156101b1575b50506020610160526040610180510160206001820306601f8201039050610160f3005b60006000fd5b61012861032f0361012860003961012861032f036000f3"}}},"deployments":{"blockchain://0817570684a8b349c2e15aa1a5ba48d269335d487bcf0d7a2f3ef1f9f764d5e4/block/03d7c71f9f8d8c29a17c00a0d5ce262ad243a575ce5023c430e58dbb02342901":{"greeter":{"address":"0xf2e246bb76df876cef8b38ae84130f4f55de395b","block":"0x03d7c71f9f8d8c29a17c00a0d5ce262ad243a575ce5023c430e58dbb02342901","contract_type":"greeter","transaction":"0x56e0b0d02c8f11e62937101ec419899b4f4a8be8ceec825eb9afb797f79e7262"}}},"manifest_version":"2","package_name":"greeter","sources":{"./contracts/greeter.vy":"# Vyper Greeter Contract\n\ngreeting: bytes[20]\n\n\n@public\ndef __init__():\n self.greeting = \"Hello\"\n\n\n@public\ndef setGreeting(x: bytes[20]):\n self.greeting = x\n\n\n@public\ndef greet() -> bytes[40]:\n return self.greeting\n"},"version":"1.0.0"} diff --git a/tests/core/tools/pytest_ethereum/conftest.py b/tests/core/tools/pytest_ethereum/conftest.py deleted file mode 100644 index f57be2efe5..0000000000 --- a/tests/core/tools/pytest_ethereum/conftest.py +++ /dev/null @@ -1,27 +0,0 @@ -from pathlib import ( - Path, -) -import pytest - -from ethpm import ( - ASSETS_DIR, -) -from web3 import Web3 - -PYTEST_ETH_TESTS_DIR = Path(__file__).parent - - -@pytest.fixture -def pte_assets_dir(): - return PYTEST_ETH_TESTS_DIR / "assets" - - -@pytest.fixture -def w3(): - return Web3(Web3.EthereumTesterProvider()) - - -@pytest.fixture -def escrow_deployer(deployer): - escrow_manifest_path = ASSETS_DIR / "escrow" / "1.0.2.json" - return deployer(escrow_manifest_path) diff --git a/tests/core/tools/pytest_ethereum/test_deployer.py b/tests/core/tools/pytest_ethereum/test_deployer.py deleted file mode 100644 index aed86540e7..0000000000 --- a/tests/core/tools/pytest_ethereum/test_deployer.py +++ /dev/null @@ -1,83 +0,0 @@ -import logging -import pytest - -from eth_utils import ( - is_address, -) - -from ethpm import ( - ASSETS_DIR, -) -import web3 -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) - -logging.getLogger("evm").setLevel(logging.INFO) - - -# -# Vyper Contracts -# - - -# User Code -@pytest.fixture -def greeter(deployer, pte_assets_dir): - return deployer(pte_assets_dir / "greeter.json").deploy("greeter") - - -def test_user_code_with_fixture(greeter): - greeter_instance = greeter.deployments.get_instance("greeter") - assert isinstance(greeter_instance, web3.contract.Contract) - greeting = greeter_instance.functions.greet().call() - assert greeting == b"Hello" - - -# -# Solidity Compiler Output -# - - -# SIMPLE -@pytest.fixture -def owned(deployer): - owned_manifest_path = ASSETS_DIR / "owned" / "1.0.1.json" - owned_deployer = deployer(path=owned_manifest_path) - return owned_deployer.deploy("Owned") - - -def test_owned_deployer(owned): - owned_contract_instance = owned.deployments.get_instance("Owned") - assert is_address(owned_contract_instance.address) - - -# CONSTRUCTOR ARGS -@pytest.fixture -def standard_token(deployer): - standard_token_manifest_path = ASSETS_DIR / "standard-token" / "1.0.1.json" - standard_token_deployer = deployer(standard_token_manifest_path) - return standard_token_deployer.deploy("StandardToken", 100) - - -def test_standard_token_deployer(standard_token): - standard_token_instance = standard_token.deployments.get_instance("StandardToken") - assert standard_token_instance.functions.totalSupply().call() == 100 - - -# LIBRARY -@pytest.fixture -def safe_math(deployer): - safe_math_manifest_path = ASSETS_DIR / "safe-math-lib" / "1.0.1.json" - safe_math_deployer = deployer(safe_math_manifest_path) - return safe_math_deployer.deploy("SafeMathLib") - - -def test_safe_math_deployer(safe_math): - safe_math_instance = safe_math.deployments.get_instance("SafeMathLib") - assert is_address(safe_math_instance.address) - - -def test_escrow_deployer_unlinked(escrow_deployer): - with pytest.raises(DeployerError): - escrow_deployer.deploy("Escrow", escrow_deployer.package.w3.eth.accounts[0]) diff --git a/tests/core/tools/pytest_ethereum/test_linker.py b/tests/core/tools/pytest_ethereum/test_linker.py deleted file mode 100644 index c0ca1d94ff..0000000000 --- a/tests/core/tools/pytest_ethereum/test_linker.py +++ /dev/null @@ -1,82 +0,0 @@ -import pytest - -from ethpm import ( - ASSETS_DIR, - Package, -) -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - link, - linker, - run_python, -) - - -@pytest.fixture -def escrow_deployer(deployer): - escrow_manifest_path = ASSETS_DIR / "escrow" / "1.0.3.json" - return deployer(escrow_manifest_path) - - -def test_linker(escrow_deployer, w3): - # todo test multiple links in one type - assert isinstance(escrow_deployer, Deployer) - with pytest.raises(DeployerError): - escrow_deployer.deploy("Escrow") - - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0]), - ) - assert hasattr(escrow_strategy, "__call__") - escrow_deployer.register_strategy("Escrow", escrow_strategy) - linked_escrow_package = escrow_deployer.deploy("Escrow") - assert isinstance(linked_escrow_package, Package) - linked_escrow_factory = linked_escrow_package.get_contract_factory("Escrow") - assert linked_escrow_factory.needs_bytecode_linking is False - - -def test_linker_with_from(escrow_deployer, w3): - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0], transaction={"from": w3.eth.accounts[5]}), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - linked_escrow_package = escrow_deployer.deploy("Escrow") - escrow_instance = linked_escrow_package.deployments.get_instance("Escrow") - assert escrow_instance.functions.sender().call() == w3.eth.accounts[5] - - -def test_linker_with_callback(escrow_deployer, w3): - sender = w3.eth.accounts[0] - recipient = w3.eth.accounts[5] - - def callback_fn(package): - escrow_instance = package.deployments.get_instance("Escrow") - tx_hash = escrow_instance.functions.releaseFunds().transact({"from": sender}) - w3.eth.waitForTransactionReceipt(tx_hash) - - escrow_strategy = linker( - deploy("SafeSendLib", transaction={"from": sender}), - link("Escrow", "SafeSendLib"), - deploy( - "Escrow", - recipient, - transaction={"from": sender, "value": w3.toWei("1", "ether")}, - ), - run_python(callback_fn), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - assert w3.eth.getBalance(recipient) == w3.toWei("1000000", "ether") - linked_escrow_package = escrow_deployer.deploy("Escrow") - escrow_instance = linked_escrow_package.deployments.get_instance("Escrow") - assert escrow_instance.functions.sender().call() == sender - assert w3.eth.getBalance(recipient) == w3.toWei("1000001", "ether") diff --git a/tests/core/tools/pytest_ethereum/test_linker_utils.py b/tests/core/tools/pytest_ethereum/test_linker_utils.py deleted file mode 100644 index fd2b097c08..0000000000 --- a/tests/core/tools/pytest_ethereum/test_linker_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -import pytest - -from eth_utils import ( - remove_0x_prefix, - to_hex, -) -from eth_utils.toolz import ( - assoc, -) - -from ethpm.uri import ( - create_latest_block_uri, -) -from web3.tools.pytest_ethereum._utils import ( - contains_matching_uri, - insert_deployment, - pluck_matching_uri, -) -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) - - -@pytest.fixture -def chain_setup(w3): - old_chain_id = remove_0x_prefix(to_hex(w3.eth.getBlock(0)["hash"])) - block_hash = remove_0x_prefix(to_hex(w3.eth.getBlock("earliest").hash)) - old_chain_uri = f"blockchain://{old_chain_id}/block/{block_hash}" - match_data = { - old_chain_uri: {"x": "x"}, - f"blockchain://1234/block/{block_hash}": {"x": "x"}, - } - no_match_data = { - f"blockchain://56775ac59d0774e6b603a79c4218efeb5653b99ba0ff14db983bac2662251a8a/block/{block_hash}": { # noqa: E501 - "x": "x" - } - } - return w3, match_data, no_match_data, old_chain_uri - - -def test_pluck_matching_uri(chain_setup): - w3, match_data, no_match_data, old_chain_uri = chain_setup - - assert pluck_matching_uri(match_data, w3) == old_chain_uri - with pytest.raises(LinkerError): - assert pluck_matching_uri(no_match_data, w3) - - -def test_contains_matching_uri(chain_setup): - w3, match_data, no_match_data, _ = chain_setup - - assert contains_matching_uri(match_data, w3) is True - assert contains_matching_uri(no_match_data, w3) is False - - -def test_insert_deployment(escrow_deployer): - w3 = escrow_deployer.package.w3 - escrow_package = escrow_deployer.package - init_deployment_data = { - "contract_type": "Escrow", - "address": "0x", - "transaction": "0x", - "block": "0x", - } - new_deployment_data = { - "contract_type": "Escrow", - "address": "0x123", - "transaction": "0x123", - "block": "0x123", - } - w3.testing.mine(1) - init_block_uri = create_latest_block_uri(w3, 0) - alt_block_uri = init_block_uri[:15] + "yxz123" + init_block_uri[21:] - init_block_deployment_data = { - init_block_uri: {"Other": {"x": "x"}, "Escrow": init_deployment_data}, - alt_block_uri: {"alt": {"x": "x"}}, - } - w3.testing.mine(1) - new_block_uri = create_latest_block_uri(w3, 0) - escrow_package.manifest = assoc( - escrow_package.manifest, "deployments", init_block_deployment_data - ) - updated_manifest = insert_deployment( - escrow_package, "Escrow", new_deployment_data, new_block_uri - ) - expected_deployments_data = { - new_block_uri: {"Other": {"x": "x"}, "Escrow": new_deployment_data}, - alt_block_uri: {"alt": {"x": "x"}}, - } - assert updated_manifest["deployments"] == expected_deployments_data diff --git a/tests/core/txpool-module/conftest.py b/tests/core/txpool-module/conftest.py index 0d64d61fa8..7298715385 100644 --- a/tests/core/txpool-module/conftest.py +++ b/tests/core/txpool-module/conftest.py @@ -1,13 +1,13 @@ -import pytest - - -@pytest.fixture(autouse=True) -def skip_testrpc_and_wait_for_mining_start(web3, - wait_for_miner_start, - skip_if_testrpc): - skip_if_testrpc(web3) - - wait_for_miner_start(web3) - - assert web3.eth.mining - assert web3.eth.hashrate +import pytest + + +@pytest.fixture(autouse=True) +def skip_testrpc_and_wait_for_mining_start(web3, + wait_for_miner_start, + skip_if_testrpc): + skip_if_testrpc(web3) + + wait_for_miner_start(web3) + + assert web3.vns.mining + assert web3.vns.hashrate diff --git a/tests/core/txpool-module/test_txpool_content.py b/tests/core/txpool-module/test_txpool_content.py index accd0e1fef..52dbae86fd 100644 --- a/tests/core/txpool-module/test_txpool_content.py +++ b/tests/core/txpool-module/test_txpool_content.py @@ -1,42 +1,42 @@ -import random - -from web3._utils.threads import ( - Timeout, -) - - -def test_txpool_content(web3_empty): - web3 = web3_empty - - web3.geth.miner.stop() - - with Timeout(60) as timeout: - while web3.eth.hashrate or web3.eth.mining: - timeout.sleep(random.random()) - - txn_1_hash = web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'value': 12345, - }) - txn_1 = web3.eth.getTransaction(txn_1_hash) - txn_2_hash = web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'value': 54321, - }) - txn_2 = web3.eth.getTransaction(txn_2_hash) - - content = web3.geth.txpool.content - - assert web3.eth.coinbase in content['pending'] - - pending_txns = content['pending'][web3.eth.coinbase] - - assert txn_1['nonce'] in pending_txns - assert txn_2['nonce'] in pending_txns - - assert pending_txns[txn_1['nonce']][0]['hash'] == txn_1_hash - assert pending_txns[txn_1['nonce']][0]['value'] == 12345 - assert pending_txns[txn_2['nonce']][0]['hash'] == txn_2_hash - assert pending_txns[txn_2['nonce']][0]['value'] == 54321 +import random + +from web3._utils.threads import ( + Timeout, +) + + +def test_txpool_content(web3_empty): + web3 = web3_empty + + web3.geth.miner.stop() + + with Timeout(60) as timeout: + while web3.vns.hashrate or web3.vns.mining: + timeout.sleep(random.random()) + + txn_1_hash = web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'value': 12345, + }) + txn_1 = web3.vns.getTransaction(txn_1_hash) + txn_2_hash = web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'value': 54321, + }) + txn_2 = web3.vns.getTransaction(txn_2_hash) + + content = web3.geth.txpool.content + + assert web3.vns.coinbase in content['pending'] + + pending_txns = content['pending'][web3.vns.coinbase] + + assert txn_1['nonce'] in pending_txns + assert txn_2['nonce'] in pending_txns + + assert pending_txns[txn_1['nonce']][0]['hash'] == txn_1_hash + assert pending_txns[txn_1['nonce']][0]['value'] == 12345 + assert pending_txns[txn_2['nonce']][0]['hash'] == txn_2_hash + assert pending_txns[txn_2['nonce']][0]['value'] == 54321 diff --git a/tests/core/txpool-module/test_txpool_inspect.py b/tests/core/txpool-module/test_txpool_inspect.py index 7e95b1ac82..b18cd01706 100644 --- a/tests/core/txpool-module/test_txpool_inspect.py +++ b/tests/core/txpool-module/test_txpool_inspect.py @@ -1,46 +1,46 @@ -import random - -from web3._utils.threads import ( - Timeout, -) - - -def test_txpool_inspect(web3_empty): - web3 = web3_empty - - web3.geth.miner.stop() - - with Timeout(60) as timeout: - while web3.eth.hashrate or web3.eth.mining: - timeout.sleep(random.random()) - - txn_1_hash = web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'value': 12345, - }) - txn_1 = web3.eth.getTransaction(txn_1_hash) - txn_2_hash = web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'value': 54321, - }) - txn_2 = web3.eth.getTransaction(txn_2_hash) - - inspect_content = web3.geth.txpool.inspect - - assert web3.eth.coinbase in inspect_content['pending'] - - pending_txns = inspect_content['pending'][web3.eth.coinbase] - - assert txn_1['nonce'] in pending_txns - assert txn_2['nonce'] in pending_txns - - txn_1_summary = pending_txns[txn_1['nonce']][0] - txn_2_summary = pending_txns[txn_2['nonce']][0] - - assert '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' in txn_1_summary - assert '12345 wei' in txn_1_summary - - assert '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' in txn_2_summary - assert '54321 wei' in txn_2_summary +import random + +from web3._utils.threads import ( + Timeout, +) + + +def test_txpool_inspect(web3_empty): + web3 = web3_empty + + web3.geth.miner.stop() + + with Timeout(60) as timeout: + while web3.vns.hashrate or web3.vns.mining: + timeout.sleep(random.random()) + + txn_1_hash = web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'value': 12345, + }) + txn_1 = web3.vns.getTransaction(txn_1_hash) + txn_2_hash = web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'value': 54321, + }) + txn_2 = web3.vns.getTransaction(txn_2_hash) + + inspect_content = web3.geth.txpool.inspect + + assert web3.vns.coinbase in inspect_content['pending'] + + pending_txns = inspect_content['pending'][web3.vns.coinbase] + + assert txn_1['nonce'] in pending_txns + assert txn_2['nonce'] in pending_txns + + txn_1_summary = pending_txns[txn_1['nonce']][0] + txn_2_summary = pending_txns[txn_2['nonce']][0] + + assert '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' in txn_1_summary + assert '12345 wei' in txn_1_summary + + assert '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' in txn_2_summary + assert '54321 wei' in txn_2_summary diff --git a/tests/core/utilities/test_abi.py b/tests/core/utilities/test_abi.py index 571585006f..a17d16868f 100644 --- a/tests/core/utilities/test_abi.py +++ b/tests/core/utilities/test_abi.py @@ -1,277 +1,277 @@ -import json -import pytest - -from web3._utils.abi import ( - abi_data_tree, - get_aligned_abi_inputs, - get_tuple_type_str_parts, - map_abi_data, -) -from web3._utils.normalizers import ( - BASE_RETURN_NORMALIZERS, - abi_string_to_text, - addresses_checksummed, -) - - -@pytest.mark.parametrize( - 'input, expected', - ( - # Well-formed tuple type strings - ('tuple', ('tuple', None)), - ('tuple[]', ('tuple', '[]')), - ('tuple[1]', ('tuple', '[1]')), - ('tuple[10]', ('tuple', '[10]')), - ('tuple[19]', ('tuple', '[19]')), - ('tuple[195]', ('tuple', '[195]')), - - # Malformed tuple type strings - ('tuple[][]', None), - ('tuple[1][1]', None), - ('tuple[0]', None), - ('tuple[01]', None), - ('tupleasfasdf', None), - ('uint256', None), - ('bool', None), - ('', None), - ('tupletuple', None), - ), -) -def test_get_tuple_type_str_parts(input, expected): - assert get_tuple_type_str_parts(input) == expected - - -TEST_FUNCTION_ABI_JSON = """ -{ - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "a", - "type": "uint256" - }, - { - "name": "b", - "type": "uint256[]" - }, - { - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ], - "name": "c", - "type": "tuple[]" - } - ], - "name": "s", - "type": "tuple" - }, - { - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ], - "name": "t", - "type": "tuple" - }, - { - "name": "a", - "type": "uint256" - } - ], - "name": "f", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" -} -""" -TEST_FUNCTION_ABI = json.loads(TEST_FUNCTION_ABI_JSON) - - -GET_ABI_INPUTS_OUTPUT = ( - ( - '(uint256,uint256[],(uint256,uint256)[])', # Type of s - '(uint256,uint256)', # Type of t - 'uint256', # Type of a - ), - ( - (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), # Value for s - (11, 12), # Value for t - 13, # Value for a - ), -) - -GET_ABI_INPUTS_TESTS = ( - ( - TEST_FUNCTION_ABI, - { - 's': { - 'a': 1, - 'b': [2, 3, 4], - 'c': [{'x': 5, 'y': 6}, {'x': 7, 'y': 8}, {'x': 9, 'y': 10}], - }, - 't': {'x': 11, 'y': 12}, - 'a': 13, - }, - GET_ABI_INPUTS_OUTPUT, - ), - ( - TEST_FUNCTION_ABI, - { - 's': {'a': 1, 'b': [2, 3, 4], 'c': [(5, 6), (7, 8), {'x': 9, 'y': 10}]}, - 't': {'x': 11, 'y': 12}, - 'a': 13, - }, - GET_ABI_INPUTS_OUTPUT, - ), - ( - TEST_FUNCTION_ABI, - { - 's': {'a': 1, 'b': [2, 3, 4], 'c': [(5, 6), (7, 8), (9, 10)]}, - 't': (11, 12), - 'a': 13, - }, - GET_ABI_INPUTS_OUTPUT, - ), - ( - TEST_FUNCTION_ABI, - { - 's': (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), - 't': (11, 12), - 'a': 13, - }, - GET_ABI_INPUTS_OUTPUT, - ), - ( - TEST_FUNCTION_ABI, - ( - (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), - (11, 12), - 13, - ), - GET_ABI_INPUTS_OUTPUT, - ), - ( - {}, - (), - ((), ()), - ), -) - - -@pytest.mark.parametrize( - 'abi, args, expected', - GET_ABI_INPUTS_TESTS, -) -def test_get_aligned_abi_inputs(abi, args, expected): - assert get_aligned_abi_inputs(abi, args) == expected - - -GET_ABI_INPUTS_RAISING_TESTS = ( - ( - TEST_FUNCTION_ABI, - { - 's': {'a': 1, 'b': [2, 3, 4], 'c': ['56', (7, 8), (9, 10)]}, - 't': (11, 12), - 'a': 13, - }, - ), - ( - TEST_FUNCTION_ABI, - { - 's': {'a': 1, 'b': [2, 3, 4], 'c': {(5, 6), (7, 8), (9, 10)}}, - 't': (11, 12), - 'a': 13, - }, - ), -) - - -@pytest.mark.parametrize( - 'abi, args', - GET_ABI_INPUTS_RAISING_TESTS, -) -def test_get_aligned_abi_inputs_raises_type_error(abi, args): - with pytest.raises(TypeError): - get_aligned_abi_inputs(abi, args) - - -@pytest.mark.parametrize( - 'types, data, expected', - [ - ( - ["bool[2]", "bytes"], - [[True, False], b'\x00\xFF'], - [("bool[2]", [("bool", True), ("bool", False)]), ("bytes", b'\x00\xFF')], - ), - ( - ["uint256[]"], - [[0, 2**256 - 1]], - [("uint256[]", [("uint256", 0), ("uint256", 2**256 - 1)])], - ), - ], -) -def test_abi_data_tree(types, data, expected): - assert abi_data_tree(types, data) == expected - - -@pytest.mark.parametrize( - 'types, data, funcs, expected', - [ - ( - ["bool[2]", "int256"], - [[True, False], 9876543210], - [ - lambda typ, dat: (typ, 'Tru-dat') if typ == 'bool' and dat else (typ, dat), - lambda typ, dat: (typ, hex(dat)) if typ == 'int256' else (typ, dat), - ], - [['Tru-dat', False], '0x24cb016ea'], - ), - ( - ["address"], - ['0x5b2063246f2191f18f2675cedb8b28102e957458'], - BASE_RETURN_NORMALIZERS, - ['0x5B2063246F2191f18F2675ceDB8b28102e957458'], - ), - ( - ["address[]"], - [['0x5b2063246f2191f18f2675cedb8b28102e957458'] * 2], - BASE_RETURN_NORMALIZERS, - [['0x5B2063246F2191f18F2675ceDB8b28102e957458'] * 2], - ), - ( - ["(address,address)[]"], - [[( - '0x5b2063246f2191f18f2675cedb8b28102e957458', - '0xebe0da78ecb266c7ea605dc889c64849f860383f', - )] * 2], - BASE_RETURN_NORMALIZERS, - [[( - '0x5B2063246F2191f18F2675ceDB8b28102e957458', - '0xeBe0DA78ecb266C7EA605DC889c64849F860383F', - )] * 2], - ), - ( - ['(string,address[])'], - [(b'a string', [b'\xf2\xe2F\xbbv\xdf\x87l\xef\x8b8\xae\x84\x13\x0fOU\xde9['])], - [addresses_checksummed, abi_string_to_text], - [('a string', ['0xF2E246BB76DF876Cef8b38ae84130F4F55De395b'])], - ), - ], -) -def test_map_abi_data(types, data, funcs, expected): - assert map_abi_data(funcs, types, data) == expected +import json +import pytest + +from web3._utils.abi import ( + abi_data_tree, + get_aligned_abi_inputs, + get_tuple_type_str_parts, + map_abi_data, +) +from web3._utils.normalizers import ( + BASE_RETURN_NORMALIZERS, + abi_string_to_text, + addresses_checksummed, +) + + +@pytest.mark.parametrize( + 'input, expected', + ( + # Well-formed tuple type strings + ('tuple', ('tuple', None)), + ('tuple[]', ('tuple', '[]')), + ('tuple[1]', ('tuple', '[1]')), + ('tuple[10]', ('tuple', '[10]')), + ('tuple[19]', ('tuple', '[19]')), + ('tuple[195]', ('tuple', '[195]')), + + # Malformed tuple type strings + ('tuple[][]', None), + ('tuple[1][1]', None), + ('tuple[0]', None), + ('tuple[01]', None), + ('tupleasfasdf', None), + ('uint256', None), + ('bool', None), + ('', None), + ('tupletuple', None), + ), +) +def test_get_tuple_type_str_parts(input, expected): + assert get_tuple_type_str_parts(input) == expected + + +TEST_FUNCTION_ABI_JSON = """ +{ + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256[]" + }, + { + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ], + "name": "c", + "type": "tuple[]" + } + ], + "name": "s", + "type": "tuple" + }, + { + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ], + "name": "t", + "type": "tuple" + }, + { + "name": "a", + "type": "uint256" + } + ], + "name": "f", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" +} +""" +TEST_FUNCTION_ABI = json.loads(TEST_FUNCTION_ABI_JSON) + + +GET_ABI_INPUTS_OUTPUT = ( + ( + '(uint256,uint256[],(uint256,uint256)[])', # Type of s + '(uint256,uint256)', # Type of t + 'uint256', # Type of a + ), + ( + (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), # Value for s + (11, 12), # Value for t + 13, # Value for a + ), +) + +GET_ABI_INPUTS_TESTS = ( + ( + TEST_FUNCTION_ABI, + { + 's': { + 'a': 1, + 'b': [2, 3, 4], + 'c': [{'x': 5, 'y': 6}, {'x': 7, 'y': 8}, {'x': 9, 'y': 10}], + }, + 't': {'x': 11, 'y': 12}, + 'a': 13, + }, + GET_ABI_INPUTS_OUTPUT, + ), + ( + TEST_FUNCTION_ABI, + { + 's': {'a': 1, 'b': [2, 3, 4], 'c': [(5, 6), (7, 8), {'x': 9, 'y': 10}]}, + 't': {'x': 11, 'y': 12}, + 'a': 13, + }, + GET_ABI_INPUTS_OUTPUT, + ), + ( + TEST_FUNCTION_ABI, + { + 's': {'a': 1, 'b': [2, 3, 4], 'c': [(5, 6), (7, 8), (9, 10)]}, + 't': (11, 12), + 'a': 13, + }, + GET_ABI_INPUTS_OUTPUT, + ), + ( + TEST_FUNCTION_ABI, + { + 's': (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), + 't': (11, 12), + 'a': 13, + }, + GET_ABI_INPUTS_OUTPUT, + ), + ( + TEST_FUNCTION_ABI, + ( + (1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), + (11, 12), + 13, + ), + GET_ABI_INPUTS_OUTPUT, + ), + ( + {}, + (), + ((), ()), + ), +) + + +@pytest.mark.parametrize( + 'abi, args, expected', + GET_ABI_INPUTS_TESTS, +) +def test_get_aligned_abi_inputs(abi, args, expected): + assert get_aligned_abi_inputs(abi, args) == expected + + +GET_ABI_INPUTS_RAISING_TESTS = ( + ( + TEST_FUNCTION_ABI, + { + 's': {'a': 1, 'b': [2, 3, 4], 'c': ['56', (7, 8), (9, 10)]}, + 't': (11, 12), + 'a': 13, + }, + ), + ( + TEST_FUNCTION_ABI, + { + 's': {'a': 1, 'b': [2, 3, 4], 'c': {(5, 6), (7, 8), (9, 10)}}, + 't': (11, 12), + 'a': 13, + }, + ), +) + + +@pytest.mark.parametrize( + 'abi, args', + GET_ABI_INPUTS_RAISING_TESTS, +) +def test_get_aligned_abi_inputs_raises_type_error(abi, args): + with pytest.raises(TypeError): + get_aligned_abi_inputs(abi, args) + + +@pytest.mark.parametrize( + 'types, data, expected', + [ + ( + ["bool[2]", "bytes"], + [[True, False], b'\x00\xFF'], + [("bool[2]", [("bool", True), ("bool", False)]), ("bytes", b'\x00\xFF')], + ), + ( + ["uint256[]"], + [[0, 2**256 - 1]], + [("uint256[]", [("uint256", 0), ("uint256", 2**256 - 1)])], + ), + ], +) +def test_abi_data_tree(types, data, expected): + assert abi_data_tree(types, data) == expected + + +@pytest.mark.parametrize( + 'types, data, funcs, expected', + [ + ( + ["bool[2]", "int256"], + [[True, False], 9876543210], + [ + lambda typ, dat: (typ, 'Tru-dat') if typ == 'bool' and dat else (typ, dat), + lambda typ, dat: (typ, hex(dat)) if typ == 'int256' else (typ, dat), + ], + [['Tru-dat', False], '0x24cb016ea'], + ), + ( + ["address"], + ['0x5b2063246f2191f18f2675cedb8b28102e957458'], + BASE_RETURN_NORMALIZERS, + ['0x5B2063246F2191f18F2675ceDB8b28102e957458'], + ), + ( + ["address[]"], + [['0x5b2063246f2191f18f2675cedb8b28102e957458'] * 2], + BASE_RETURN_NORMALIZERS, + [['0x5B2063246F2191f18F2675ceDB8b28102e957458'] * 2], + ), + ( + ["(address,address)[]"], + [[( + '0x5b2063246f2191f18f2675cedb8b28102e957458', + '0xebe0da78ecb266c7ea605dc889c64849f860383f', + )] * 2], + BASE_RETURN_NORMALIZERS, + [[( + '0x5B2063246F2191f18F2675ceDB8b28102e957458', + '0xeBe0DA78ecb266C7EA605DC889c64849F860383F', + )] * 2], + ), + ( + ['(string,address[])'], + [(b'a string', [b'\xf2\xe2F\xbbv\xdf\x87l\xef\x8b8\xae\x84\x13\x0fOU\xde9['])], + [addresses_checksummed, abi_string_to_text], + [('a string', ['0xF2E246BB76DF876Cef8b38ae84130F4F55De395b'])], + ), + ], +) +def test_map_abi_data(types, data, funcs, expected): + assert map_abi_data(funcs, types, data) == expected diff --git a/tests/core/utilities/test_abi_filter_by_abi_name.py b/tests/core/utilities/test_abi_filter_by_abi_name.py index cf7c4d1963..62f3b18318 100644 --- a/tests/core/utilities/test_abi_filter_by_abi_name.py +++ b/tests/core/utilities/test_abi_filter_by_abi_name.py @@ -1,71 +1,71 @@ -import pytest - -from web3._utils.abi import ( - filter_by_name, -) - -ABI_FUNC_1 = { - "constant": False, - "inputs": [], - "name": "func_1", - "outputs": [], - "type": "function", -} -ABI_CONSTRUCTOR = { - "constant": False, - "inputs": [], - "type": "constructor", -} -ABI_FALLBACK = { - "constant": False, - "type": "fallback", -} -ABI_FUNC_2_SIG_A = { - "constant": False, - "inputs": [ - {"name": "a", "type": "uint256"}, - ], - "name": "func_2", - "outputs": [], - "type": "function", -} -ABI_FUNC_2_SIG_B = { - "constant": False, - "inputs": [ - {"name": "a", "type": "uint256"}, - {"name": "b", "type": "uint256"}, - ], - "name": "func_2", - "outputs": [], - "type": "function", -} -ABI_FUNC_3 = { - "constant": False, - "inputs": [], - "name": "func_3", - "outputs": [], - "type": "function", -} - -ABI = [ - ABI_CONSTRUCTOR, - ABI_FALLBACK, - ABI_FUNC_1, - ABI_FUNC_2_SIG_A, - ABI_FUNC_2_SIG_B, - ABI_FUNC_3, -] - - -@pytest.mark.parametrize( - 'name,expected', - ( - ('func_1', [ABI_FUNC_1]), - ('func_2', [ABI_FUNC_2_SIG_A, ABI_FUNC_2_SIG_B]), - ('func_3', [ABI_FUNC_3]), - ('does_not_exist', []), - ) -) -def test_filter_by_arguments(name, expected): - actual_matches = filter_by_name(name, ABI) - assert actual_matches == expected +import pytest + +from web3._utils.abi import ( + filter_by_name, +) + +ABI_FUNC_1 = { + "constant": False, + "inputs": [], + "name": "func_1", + "outputs": [], + "type": "function", +} +ABI_CONSTRUCTOR = { + "constant": False, + "inputs": [], + "type": "constructor", +} +ABI_FALLBACK = { + "constant": False, + "type": "fallback", +} +ABI_FUNC_2_SIG_A = { + "constant": False, + "inputs": [ + {"name": "a", "type": "uint256"}, + ], + "name": "func_2", + "outputs": [], + "type": "function", +} +ABI_FUNC_2_SIG_B = { + "constant": False, + "inputs": [ + {"name": "a", "type": "uint256"}, + {"name": "b", "type": "uint256"}, + ], + "name": "func_2", + "outputs": [], + "type": "function", +} +ABI_FUNC_3 = { + "constant": False, + "inputs": [], + "name": "func_3", + "outputs": [], + "type": "function", +} + +ABI = [ + ABI_CONSTRUCTOR, + ABI_FALLBACK, + ABI_FUNC_1, + ABI_FUNC_2_SIG_A, + ABI_FUNC_2_SIG_B, + ABI_FUNC_3, +] + + +@pytest.mark.parametrize( + 'name,expected', + ( + ('func_1', [ABI_FUNC_1]), + ('func_2', [ABI_FUNC_2_SIG_A, ABI_FUNC_2_SIG_B]), + ('func_3', [ABI_FUNC_3]), + ('does_not_exist', []), + ) +) +def test_filter_by_arguments(name, expected): + actual_matches = filter_by_name(name, ABI) + assert actual_matches == expected diff --git a/tests/core/utilities/test_abi_filtering_by_argument_name.py b/tests/core/utilities/test_abi_filtering_by_argument_name.py index 2baac9e355..539314aa0a 100644 --- a/tests/core/utilities/test_abi_filtering_by_argument_name.py +++ b/tests/core/utilities/test_abi_filtering_by_argument_name.py @@ -1,61 +1,61 @@ -import pytest - -from web3._utils.abi import ( - filter_by_argument_name, -) - -ABI = [ - { - "constant": False, - "inputs": [], - "name": "func_1", - "outputs": [], - "type": "function", - }, - { - "constant": False, - "inputs": [ - {"name": "a", "type": "uint256"}, - ], - "name": "func_2", - "outputs": [], - "type": "function", - }, - { - "constant": False, - "inputs": [ - {"name": "a", "type": "uint256"}, - {"name": "b", "type": "uint256"}, - ], - "name": "func_3", - "outputs": [], - "type": "function", - }, - { - "constant": False, - "inputs": [ - {"name": "a", "type": "uint256"}, - {"name": "b", "type": "uint256"}, - {"name": "c", "type": "uint256"}, - ], - "name": "func_4", - "outputs": [], - "type": "function", - }, -] - - -@pytest.mark.parametrize( - 'argument_names,expected', - ( - ([], ['func_1', 'func_2', 'func_3', 'func_4']), - (['a'], ['func_2', 'func_3', 'func_4']), - (['a', 'c'], ['func_4']), - (['c'], ['func_4']), - (['b'], ['func_3', 'func_4']), - ) -) -def test_filter_by_arguments_1(argument_names, expected): - actual_matches = filter_by_argument_name(argument_names, ABI) - function_names = [match['name'] for match in actual_matches] - assert set(function_names) == set(expected) +import pytest + +from web3._utils.abi import ( + filter_by_argument_name, +) + +ABI = [ + { + "constant": False, + "inputs": [], + "name": "func_1", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "a", "type": "uint256"}, + ], + "name": "func_2", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "a", "type": "uint256"}, + {"name": "b", "type": "uint256"}, + ], + "name": "func_3", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "a", "type": "uint256"}, + {"name": "b", "type": "uint256"}, + {"name": "c", "type": "uint256"}, + ], + "name": "func_4", + "outputs": [], + "type": "function", + }, +] + + +@pytest.mark.parametrize( + 'argument_names,expected', + ( + ([], ['func_1', 'func_2', 'func_3', 'func_4']), + (['a'], ['func_2', 'func_3', 'func_4']), + (['a', 'c'], ['func_4']), + (['c'], ['func_4']), + (['b'], ['func_3', 'func_4']), + ) +) +def test_filter_by_arguments_1(argument_names, expected): + actual_matches = filter_by_argument_name(argument_names, ABI) + function_names = [match['name'] for match in actual_matches] + assert set(function_names) == set(expected) diff --git a/tests/core/utilities/test_abi_is_encodable.py b/tests/core/utilities/test_abi_is_encodable.py index 958c8e631a..2c8d9e60c3 100644 --- a/tests/core/utilities/test_abi_is_encodable.py +++ b/tests/core/utilities/test_abi_is_encodable.py @@ -1,117 +1,77 @@ -import pytest - - -@pytest.mark.parametrize( - 'value,_type,expected', - ( - # Some sanity checks to make sure our custom registrations didn't bork - # anything - ('0x' + '00' * 20, 'address', True), - ('0x' + '00' * 32, 'address', False), # no oversized addresses - ('0xff', 'address', False), # no undersized addresses - (None, 'address', False), # no wrong types - - (b'\x01\x23', 'bytes2', True), # valid cases should be same - (b'\x01\x23', 'bytes1', False), # no oversize bytes - (True, 'bytes32', False), # no wrong types - (0, 'bytes32', False), # no wrong types - - (b'\x12', 'bytes', True), - - ('', 'string', True), - ('anything', 'string', True), - - (['0x' + '00' * 20, 0], '(address,uint256)', True), - (('0x' + '00' * 20, 0), '(address,uint256)', True), - ([0], '(address,uint256)', False), - (['0x' + '00' * 20], '(uint256)', False), - - # Special address behavior - ('dennisthepeasant.eth', 'address', True), # passes because eth_utils converts to bytes :/ - ('autonomouscollective.eth', 'address', True), - ('all-TLDs-valid-now.test', 'address', True), - ('ff', 'address', True), # this could theoretically be a top-level domain (TLD) - ('0xname.eth', 'address', True), # 0x in name is fine, if it is not a TLD - ('rejects_invalid_names.eth', 'address', False), # no underscore in domain names - - # Special bytes behavior - ('0x12', 'bytes2', True), # with or without 0x OK - (b'\x12', 'bytes2', True), # as bytes value undersize OK - ('1', 'bytes2', False), # no odd length - ('0x1', 'bytes2', False), # no odd length - - # Special bytes behavior - ('0x12', 'bytes', True), - ('1', 'bytes', False), - ('0x1', 'bytes', False), - - # Special string behavior - (b'', 'string', True), - (b'anything', 'string', True), - (b'\x80', 'string', False), # bytes that cannot be decoded with utf-8 are invalid - - # Special tuple behavior - (('0x1234', 0), '(bytes2,int128)', True), - (('0x123456', 0), '(bytes2,int128)', False), - - (('0x1234', 0), '(bytes,int128)', True), - (('1', 0), '(bytes,int128)', False), - - (('dennisthepeasant.eth', 0), '(address,int128)', True), - (('rejects_invalid_domains.eth', 0), '(address,int128)', False), - - ((b'anything', 0), '(string,int128)', True), - ((b'\x80', 0), '(string,int128)', False), - ), -) -def test_is_encodable(web3, value, _type, expected): - actual = web3.is_encodable(_type, value) - assert actual is expected - - -@pytest.mark.parametrize( - 'value,_type,expected', - ( - ('12', 'bytes2', True), # no 0x prefix, can be decoded as hex - ('0123', 'bytes2', True), # no 0x prefix, can be decoded as hex - ('0123', 'bytes1', False), # no oversize values - ('12', 'bytes', True), # no 0x prefix, can be decoded as hex - ) -) -def test_is_encodable_warnings(web3, value, _type, expected): - with pytest.warns( - DeprecationWarning, - match='in v6 it will be invalid to pass a hex string without the "0x" prefix' - ): - actual = web3.is_encodable(_type, value) - assert actual is expected - - -@pytest.mark.parametrize( - 'value,_type,expected', - ( - # Special bytes behavior - ('12', 'bytes2', False), # no hex strings without leading 0x - ('0x12', 'bytes1', True), # with 0x OK - ('0123', 'bytes2', False), # needs a 0x - (b'\x12', 'bytes2', False), # no undersize bytes value - ('0123', 'bytes1', False), # no oversize hex strings - ('1', 'bytes2', False), # no odd length - ('0x1', 'bytes2', False), # no odd length - - # Special bytes behavior - ('12', 'bytes', False), # no hex strings without leading 0x - ('0x12', 'bytes', True), - ('1', 'bytes', False), # no hex strings without leading 0x - ('0x1', 'bytes', False), # cannot be decoded as hex - ('0x0x0x0x', 'bytes', False), # cannot be decoded as hex - - # Special string behavior - (b'', 'string', True), - (b'anything', 'string', True), - (b'\x80', 'string', False), # bytes that cannot be decoded with utf-8 are invalid - ), -) -def test_is_encodable_strict(w3_strict_abi, value, _type, expected): - actual = w3_strict_abi.is_encodable(_type, value) - assert actual is expected +import pytest + +from web3._utils.abi import ( + is_encodable, +) + + +@pytest.mark.parametrize( + 'value,_type,expected', + ( + # Some sanity checks to make sure our custom registrations didn't bork + # anything + ('0x' + '00' * 20, 'address', True), + ('0x' + '00' * 32, 'address', False), # no oversized addresses + ('0xff', 'address', False), # no undersized addresses + (None, 'address', False), # no wrong types + + (b'\x01\x23', 'bytes2', True), # valid cases should be same + (b'\x01\x23', 'bytes1', False), # no oversize bytes + (True, 'bytes32', False), # no wrong types + (0, 'bytes32', False), # no wrong types + + (b'\x12', 'bytes', True), + + ('', 'string', True), + ('anything', 'string', True), + + (['0x' + '00' * 20, 0], '(address,uint256)', True), + (('0x' + '00' * 20, 0), '(address,uint256)', True), + ([0], '(address,uint256)', False), + (['0x' + '00' * 20], '(uint256)', False), + + # Special address behavior + ('dennisthepeasant.vns', 'address', True), # passes because vns_utils converts to bytes :/ + ('autonomouscollective.vns', 'address', True), + ('all-TLDs-valid-now.test', 'address', True), + ('-rejects-invalid-names.test', 'address', False), + ('ff', 'address', True), # this could theoretically be a top-level domain (TLD) + ('0xname.vns', 'address', True), # 0x in name is fine, if it is not a TLD + + # Special bytes behavior + ('12', 'bytes2', True), # undersize OK + ('0x12', 'bytes2', True), # with or without 0x OK + ('0123', 'bytes2', True), # exact size OK + (b'\x12', 'bytes2', True), # as bytes value undersize OK + ('0123', 'bytes1', False), # no oversize hex strings + ('1', 'bytes2', False), # no odd length + ('0x1', 'bytes2', False), # no odd length + + # Special bytes behavior + ('12', 'bytes', True), + ('0x12', 'bytes', True), + ('1', 'bytes', False), + ('0x1', 'bytes', False), + + # Special string behavior + (b'', 'string', True), + (b'anything', 'string', True), + (b'\x80', 'string', False), # bytes that cannot be decoded with utf-8 are invalid + + # Special tuple behavior + (('0x1234', 0), '(bytes2,int128)', True), + (('0x123456', 0), '(bytes2,int128)', False), + + (('0x1234', 0), '(bytes,int128)', True), + (('1', 0), '(bytes,int128)', False), + + (('dennisthepeasant.vns', 0), '(address,int128)', True), + (('-rejects-invalid-names.test', 0), '(address,int128)', False), + + ((b'anything', 0), '(string,int128)', True), + ((b'\x80', 0), '(string,int128)', False), + ), +) +def test_is_encodable(value, _type, expected): + actual = is_encodable(_type, value) + assert actual is expected diff --git a/tests/core/utilities/test_attributedict.py b/tests/core/utilities/test_attributedict.py index a8f3dc2616..9ee2264034 100644 --- a/tests/core/utilities/test_attributedict.py +++ b/tests/core/utilities/test_attributedict.py @@ -1,107 +1,107 @@ -import pytest - -from web3.datastructures import ( - AttributeDict, -) - - -@pytest.mark.parametrize( - "dict1,dict2,same_hash", - [ - ({'b': 2, 'a': 1}, {'a': 1, 'b': 2}, True), - ({'a': 1, 'b': 2}, {'a': 1, 'b': 0}, False), - ], -) -def test_attributedict_hashable(dict1, dict2, same_hash): - dicts = map(AttributeDict, (dict1, dict2)) - dicts = set(dicts) - assert len(dicts) == (1 if same_hash else 2) - - -def test_attributedict_access(): - container = AttributeDict({'a': 1}) - assert container.a == 1 - - -def test_attributedict_repr(): - dict1 = AttributeDict({'a': 1}) - dict2 = eval(repr(dict1)) - assert dict1 == dict2 - - -def test_attributedict_setitem_invalid(): - container = AttributeDict({'a': 1}) - with pytest.raises(TypeError): - container['a'] = 0 - assert container['a'] == 1 - - -def test_attributedict_setattr_invalid(): - container = AttributeDict({'a': 1}) - with pytest.raises(TypeError): - container.a = 0 - assert container.a == 1 - - -def test_attributedict_delitem_invalid(): - container = AttributeDict({'a': 1}) - with pytest.raises(TypeError): - del container['a'] - assert container['a'] == 1 - - -@pytest.mark.parametrize( - "dict1,dict2", - [ - ({'b': 2, 'a': 1}, {'a': 1, 'b': 2}), - ({}, {}), - ], -) -def test_attributedict_equality(dict1, dict2): - assert AttributeDict(dict1) == dict2 - assert AttributeDict(dict1) == AttributeDict(dict2) - assert dict1 == AttributeDict(dict2) - - -@pytest.mark.parametrize( - "dict1,dict2", - [ - ({'a': 1, 'b': 2}, {'a': 1, 'b': 0}), - ({'a': 1, 'b': 2}, {'a': 1}), - ], -) -def test_attributedict_inequality(dict1, dict2): - assert AttributeDict(dict1) != dict2 - assert AttributeDict(dict1) != AttributeDict(dict2) - assert dict1 != AttributeDict(dict2) - - -def test_attributedict_recursive_dict(): - w = AttributeDict.recursive({'x': {'y': {'z': 8}}}) - assert w.x.y.z == 8 - - -@pytest.mark.parametrize('sequence', [list, tuple]) -def test_attributedict_sequence_with_dict(sequence): - data = sequence(['a', {'found': True}, 'c']) - dict_in_sequence = AttributeDict.recursive(data) - assert dict_in_sequence[1].found is True - - -def test_attributedict_dict_in_list_in_dict(): - data = {'instructions': [ - 0, - 1, - 'neither shalt thou count, excepting that thou then proceedeth to three', - {'if_naughty': 'snuff it'}, - 'shalt thou not count', - 'right out', - ]} - attrdict = AttributeDict.recursive(data) - assert attrdict.instructions[3].if_naughty == 'snuff it' - - -def test_attributedict_set_in_recursive_dict(): - data = {'mydict': {'myset': {'found'}}} - attrdict = AttributeDict.recursive(data) - assert 'found' in attrdict.mydict.myset +import pytest + +from web3.datastructures import ( + AttributeDict, +) + + +@pytest.mark.parametrize( + "dict1,dict2,same_hash", + [ + ({'b': 2, 'a': 1}, {'a': 1, 'b': 2}, True), + ({'a': 1, 'b': 2}, {'a': 1, 'b': 0}, False), + ], +) +def test_attributedict_hashable(dict1, dict2, same_hash): + dicts = map(AttributeDict, (dict1, dict2)) + dicts = set(dicts) + assert len(dicts) == (1 if same_hash else 2) + + +def test_attributedict_access(): + container = AttributeDict({'a': 1}) + assert container.a == 1 + + +def test_attributedict_repr(): + dict1 = AttributeDict({'a': 1}) + dict2 = eval(repr(dict1)) + assert dict1 == dict2 + + +def test_attributedict_setitem_invalid(): + container = AttributeDict({'a': 1}) + with pytest.raises(TypeError): + container['a'] = 0 + assert container['a'] == 1 + + +def test_attributedict_setattr_invalid(): + container = AttributeDict({'a': 1}) + with pytest.raises(TypeError): + container.a = 0 + assert container.a == 1 + + +def test_attributedict_delitem_invalid(): + container = AttributeDict({'a': 1}) + with pytest.raises(TypeError): + del container['a'] + assert container['a'] == 1 + + +@pytest.mark.parametrize( + "dict1,dict2", + [ + ({'b': 2, 'a': 1}, {'a': 1, 'b': 2}), + ({}, {}), + ], +) +def test_attributedict_equality(dict1, dict2): + assert AttributeDict(dict1) == dict2 + assert AttributeDict(dict1) == AttributeDict(dict2) + assert dict1 == AttributeDict(dict2) + + +@pytest.mark.parametrize( + "dict1,dict2", + [ + ({'a': 1, 'b': 2}, {'a': 1, 'b': 0}), + ({'a': 1, 'b': 2}, {'a': 1}), + ], +) +def test_attributedict_inequality(dict1, dict2): + assert AttributeDict(dict1) != dict2 + assert AttributeDict(dict1) != AttributeDict(dict2) + assert dict1 != AttributeDict(dict2) + + +def test_attributedict_recursive_dict(): + w = AttributeDict.recursive({'x': {'y': {'z': 8}}}) + assert w.x.y.z == 8 + + +@pytest.mark.parametrize('sequence', [list, tuple]) +def test_attributedict_sequence_with_dict(sequence): + data = sequence(['a', {'found': True}, 'c']) + dict_in_sequence = AttributeDict.recursive(data) + assert dict_in_sequence[1].found is True + + +def test_attributedict_dict_in_list_in_dict(): + data = {'instructions': [ + 0, + 1, + 'neither shalt thou count, excepting that thou then proceedeth to three', + {'if_naughty': 'snuff it'}, + 'shalt thou not count', + 'right out', + ]} + attrdict = AttributeDict.recursive(data) + assert attrdict.instructions[3].if_naughty == 'snuff it' + + +def test_attributedict_set_in_recursive_dict(): + data = {'mydict': {'myset': {'found'}}} + attrdict = AttributeDict.recursive(data) + assert 'found' in attrdict.mydict.myset diff --git a/tests/core/utilities/test_construct_event_data_set.py b/tests/core/utilities/test_construct_event_data_set.py index 6cbc7d73fd..07c7e25e21 100644 --- a/tests/core/utilities/test_construct_event_data_set.py +++ b/tests/core/utilities/test_construct_event_data_set.py @@ -1,152 +1,73 @@ -import pytest - -from eth_abi.exceptions import ( - EncodingTypeError, - ValueOutOfBounds, -) - -from web3._utils.events import ( - construct_event_data_set, -) - -EVENT_1_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "arg0", "type": "uint256"}, - {"indexed": True, "name": "arg1", "type": "uint256"}, - {"indexed": True, "name": "arg2", "type": "uint256"}, - {"indexed": False, "name": "arg3", "type": "uint256"}, - {"indexed": True, "name": "arg4", "type": "uint256"}, - {"indexed": False, "name": "arg5", "type": "uint256"}, - ], - "name": "Event_1", - "type": "event", -} -EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32' - -EVENT_2_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "arg0", "type": "bytes3"}, - ], - "name": "Event_2", - "type": "event", -} -EVENT_2_TOPIC = '0x84fa8d791e38d043e0c66b2437051fd24d32b1022f91a754123d8e1746e98453' - - -def hex_and_pad(i): - unpadded_hex_value = hex(i).rstrip('L') - return '0x' + unpadded_hex_value[2:].zfill(64) - - -@pytest.mark.parametrize( - 'arguments,expected', - ( - ( - {}, - [[]], - ), - ( - {'arg1': 1}, - [[]], - ), - ( - {'arg0': 1}, - [[hex_and_pad(1), None, None]], - ), - ( - {'arg0': [1]}, - [[hex_and_pad(1), None, None]], - ), - ( - {'arg0': [1, 2]}, - [ - [hex_and_pad(1), None, None], - [hex_and_pad(2), None, None], - ], - ), - ( - {'arg0': [1, 3], 'arg3': [2, 4]}, - [ - [hex_and_pad(1), hex_and_pad(2), None], - [hex_and_pad(1), hex_and_pad(4), None], - [hex_and_pad(3), hex_and_pad(2), None], - [hex_and_pad(3), hex_and_pad(4), None], - ], - ), - ) -) -def test_construct_event_data_set(web3, arguments, expected): - actual = construct_event_data_set(EVENT_1_ABI, web3.codec, arguments) - assert actual == expected - - -@pytest.mark.parametrize( - 'arguments,expected', - ( - ( - {}, - [[]], - ), - ( - {'arg1': 1}, - [[]], - ), - ( - {'arg0': 1}, - [[hex_and_pad(1), None, None]], - ), - ( - {'arg0': [1]}, - [[hex_and_pad(1), None, None]], - ), - ( - {'arg0': [1, 2]}, - [ - [hex_and_pad(1), None, None], - [hex_and_pad(2), None, None], - ], - ), - ( - {'arg0': [1, 3], 'arg3': [2, 4]}, - [ - [hex_and_pad(1), hex_and_pad(2), None], - [hex_and_pad(1), hex_and_pad(4), None], - [hex_and_pad(3), hex_and_pad(2), None], - [hex_and_pad(3), hex_and_pad(4), None], - ], - ), - ) -) -def test_construct_event_data_set_strict(w3_strict_abi, arguments, expected): - actual = construct_event_data_set(EVENT_1_ABI, w3_strict_abi.codec, arguments) - assert actual == expected - - -@pytest.mark.parametrize( - 'arguments,expected_error', - ( - ( - {'arg0': '131414'}, - EncodingTypeError, - ), - ( - {'arg0': b'131414'}, - ValueOutOfBounds, - ), - ( - {'arg0': b'13'}, - ValueOutOfBounds, - ), - ( - {'arg0': b'12'}, - ValueOutOfBounds, - ), - ) -) -def test_construct_event_data_set_strict_with_errors(w3_strict_abi, - arguments, - expected_error): - with pytest.raises(expected_error): - construct_event_data_set(EVENT_2_ABI, w3_strict_abi.codec, arguments) +import pytest + +from web3._utils.events import ( + construct_event_data_set, +) + +EVENT_1_ABI = { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + {"indexed": True, "name": "arg2", "type": "uint256"}, + {"indexed": False, "name": "arg3", "type": "uint256"}, + {"indexed": True, "name": "arg4", "type": "uint256"}, + {"indexed": False, "name": "arg5", "type": "uint256"}, + ], + "name": "Event_1", + "type": "event", +} +EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32' + + +def hex_and_pad(i): + unpadded_hex_value = hex(i).rstrip('L') + return '0x' + unpadded_hex_value[2:].zfill(64) + + +@pytest.mark.parametrize( + 'event_abi,arguments,expected', + ( + ( + EVENT_1_ABI, + {}, + [[]], + ), + ( + EVENT_1_ABI, + {'arg1': 1}, + [[]], + ), + ( + EVENT_1_ABI, + {'arg0': 1}, + [[hex_and_pad(1), None, None]], + ), + ( + EVENT_1_ABI, + {'arg0': [1]}, + [[hex_and_pad(1), None, None]], + ), + ( + EVENT_1_ABI, + {'arg0': [1, 2]}, + [ + [hex_and_pad(1), None, None], + [hex_and_pad(2), None, None], + ], + ), + ( + EVENT_1_ABI, + {'arg0': [1, 3], 'arg3': [2, 4]}, + [ + [hex_and_pad(1), hex_and_pad(2), None], + [hex_and_pad(1), hex_and_pad(4), None], + [hex_and_pad(3), hex_and_pad(2), None], + [hex_and_pad(3), hex_and_pad(4), None], + ], + ), + ) +) +def test_construct_event_data_set(event_abi, arguments, expected): + actual = construct_event_data_set(event_abi, arguments) + assert actual == expected diff --git a/tests/core/utilities/test_construct_event_filter_params.py b/tests/core/utilities/test_construct_event_filter_params.py index 882d04ef5b..a3e0218ada 100644 --- a/tests/core/utilities/test_construct_event_filter_params.py +++ b/tests/core/utilities/test_construct_event_filter_params.py @@ -1,74 +1,74 @@ -import pytest - -from web3._utils.filters import ( - construct_event_filter_params, -) - -EVENT_1_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "arg0", "type": "uint256"}, - {"indexed": True, "name": "arg1", "type": "uint256"}, - ], - "name": "Event_1", - "type": "event", -} - - -@pytest.mark.parametrize( - "event_abi,fn_kwargs,expected", - ( - (EVENT_1_ABI, {}, { - "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], - }), - (EVENT_1_ABI, {'topics': ['should-overwrite-topics']}, { - "topics": [ - 'should-overwrite-topics' - ] - }), - (EVENT_1_ABI, {'contract_address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601'}, { - "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], - 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - }), - (EVENT_1_ABI, { - 'contract_address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - 'address': '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - }, { - "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], - 'address': [ - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - ], - }), - (EVENT_1_ABI, {'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601'}, { - "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], - 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', - }), - ), -) -def test_construct_event_filter_params(web3, event_abi, fn_kwargs, expected): - _, actual = construct_event_filter_params(event_abi, web3.codec, **fn_kwargs) - assert actual == expected - - -def hex_and_pad(i): - unpadded_hex_value = hex(i).rstrip('L') - return '0x' + unpadded_hex_value[2:].zfill(64) - - -@pytest.mark.parametrize( - "event_abi,fn_kwargs,expected", - ( - (EVENT_1_ABI, {}, [[]]), - (EVENT_1_ABI, {'argument_filters': {'arg0': 1}}, [[hex_and_pad(1)]]), - (EVENT_1_ABI, {'argument_filters': {'arg0': [1]}}, [[hex_and_pad(1)]]), - (EVENT_1_ABI, {'argument_filters': {'arg0': [1, 2]}}, [ - [hex_and_pad(1)], - [hex_and_pad(2)], - ]), - ), -) -def test_construct_event_filter_params_for_data_filters(event_abi, web3, fn_kwargs, - expected): - actual, _ = construct_event_filter_params(event_abi, web3.codec, **fn_kwargs) - assert actual == expected +import pytest + +from web3._utils.filters import ( + construct_event_filter_params, +) + +EVENT_1_ABI = { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + ], + "name": "Event_1", + "type": "event", +} + + +@pytest.mark.parametrize( + "event_abi,fn_kwargs,expected", + ( + (EVENT_1_ABI, {}, { + "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], + }), + (EVENT_1_ABI, {'topics': ['should-overwrite-topics']}, { + "topics": [ + 'should-overwrite-topics' + ] + }), + (EVENT_1_ABI, {'contract_address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601'}, { + "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], + 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + }), + (EVENT_1_ABI, { + 'contract_address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'address': '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + }, { + "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], + 'address': [ + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + ], + }), + (EVENT_1_ABI, {'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601'}, { + "topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'], + 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + }), + ), +) +def test_construct_event_filter_params(event_abi, fn_kwargs, expected): + _, actual = construct_event_filter_params(event_abi, **fn_kwargs) + assert actual == expected + + +def hex_and_pad(i): + unpadded_hex_value = hex(i).rstrip('L') + return '0x' + unpadded_hex_value[2:].zfill(64) + + +@pytest.mark.parametrize( + "event_abi,fn_kwargs,expected", + ( + (EVENT_1_ABI, {}, [[]]), + (EVENT_1_ABI, {'argument_filters': {'arg0': 1}}, [[hex_and_pad(1)]]), + (EVENT_1_ABI, {'argument_filters': {'arg0': [1]}}, [[hex_and_pad(1)]]), + (EVENT_1_ABI, {'argument_filters': {'arg0': [1, 2]}}, [ + [hex_and_pad(1)], + [hex_and_pad(2)], + ]), + ), +) +def test_construct_event_filter_params_for_data_filters(event_abi, fn_kwargs, + expected): + actual, _ = construct_event_filter_params(event_abi, **fn_kwargs) + assert actual == expected diff --git a/tests/core/utilities/test_construct_event_topics.py b/tests/core/utilities/test_construct_event_topics.py index 0931ba2cda..947f2a3565 100644 --- a/tests/core/utilities/test_construct_event_topics.py +++ b/tests/core/utilities/test_construct_event_topics.py @@ -1,164 +1,80 @@ -import pytest - -from eth_abi.exceptions import ( - EncodingTypeError, - ValueOutOfBounds, -) - -from web3._utils.events import ( - construct_event_topic_set, -) - -EVENT_1_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "arg0", "type": "uint256"}, - {"indexed": True, "name": "arg1", "type": "uint256"}, - {"indexed": True, "name": "arg2", "type": "uint256"}, - {"indexed": False, "name": "arg3", "type": "uint256"}, - {"indexed": True, "name": "arg4", "type": "uint256"}, - {"indexed": False, "name": "arg5", "type": "uint256"}, - ], - "name": "Event_1", - "type": "event", -} -EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32' -EVENT_2_TOPIC = '0xe74d4a9355b06f414793e46ef1aed5b520bf68289bbd21b6bbfdbf4154451d64' -EVENT_2_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": True, "name": "arg0", "type": "bytes2"}, - {"indexed": True, "name": "arg1", "type": "bytes2"}, - ], - "name": "Event_2", - "type": "event", -} - - -def hex_and_pad(i): - unpadded_hex_value = hex(i).rstrip('L') - return '0x' + unpadded_hex_value[2:].zfill(64) - - -@pytest.mark.parametrize( - 'arguments,expected', - ( - ( - {}, - [EVENT_1_TOPIC], - ), - ( - {'arg0': 1}, - [EVENT_1_TOPIC], - ), - ( - {'arg0': 1, 'arg3': [1, 2]}, - [EVENT_1_TOPIC], - ), - ( - {'arg1': 1}, - [ - EVENT_1_TOPIC, hex_and_pad(1) - ], - ), - ( - {'arg1': [1, 2]}, - [ - EVENT_1_TOPIC, [hex_and_pad(1), hex_and_pad(2)], - ], - ), - ( - {'arg1': [1], 'arg2': [2]}, - [ - EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(2), - ], - ), - ( - {'arg1': [1, 3], 'arg2': [2, 4]}, - [ - EVENT_1_TOPIC, - [hex_and_pad(1), hex_and_pad(3)], - [hex_and_pad(2), hex_and_pad(4)] - ], - ), - ) -) -def test_construct_event_topics(web3, arguments, expected): - actual = construct_event_topic_set(EVENT_1_ABI, web3.codec, arguments) - assert actual == expected - - -@pytest.mark.parametrize( - 'arguments,expected', - ( - ( - {}, - [EVENT_1_TOPIC], - ), - ( - {'arg0': 1}, - [EVENT_1_TOPIC], - ), - ( - {'arg0': 1, 'arg3': [1, 2]}, - [EVENT_1_TOPIC], - ), - ( - {'arg1': 1}, - [ - EVENT_1_TOPIC, hex_and_pad(1) - ], - ), - ( - {'arg1': [1, 2]}, - [ - EVENT_1_TOPIC, [hex_and_pad(1), hex_and_pad(2)], - ], - ), - ( - {'arg1': [1], 'arg2': [2]}, - [ - EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(2), - ], - ), - ( - {'arg1': [1, 3], 'arg2': [2, 4]}, - [ - EVENT_1_TOPIC, - [hex_and_pad(1), hex_and_pad(3)], - [hex_and_pad(2), hex_and_pad(4)] - ], - ), - ) -) -def test_construct_event_topics_strict(w3_strict_abi, arguments, expected): - actual = construct_event_topic_set(EVENT_1_ABI, w3_strict_abi.codec, arguments) - assert actual == expected - - -@pytest.mark.parametrize( - 'arguments,error', - ( - ( - {'arg0': [b'123412']}, - ValueOutOfBounds, - ), - ( - {'arg1': [b'']}, - ValueOutOfBounds, - ), - ( - {'arg0': [b''], 'arg1': [b'']}, - ValueOutOfBounds, - ), - ( - {'arg0': ['']}, - EncodingTypeError, - ), - ) -) -def test_construct_event_topics_strict_errors(w3_strict_abi, - arguments, - error): - with pytest.raises(error): - construct_event_topic_set(EVENT_2_ABI, w3_strict_abi.codec, arguments) +import pytest + +from web3._utils.events import ( + construct_event_topic_set, +) + +EVENT_1_ABI = { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + {"indexed": True, "name": "arg2", "type": "uint256"}, + {"indexed": False, "name": "arg3", "type": "uint256"}, + {"indexed": True, "name": "arg4", "type": "uint256"}, + {"indexed": False, "name": "arg5", "type": "uint256"}, + ], + "name": "Event_1", + "type": "event", +} +EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32' + + +def hex_and_pad(i): + unpadded_hex_value = hex(i).rstrip('L') + return '0x' + unpadded_hex_value[2:].zfill(64) + + +@pytest.mark.parametrize( + 'event_abi,arguments,expected', + ( + ( + EVENT_1_ABI, + {}, + [EVENT_1_TOPIC], + ), + ( + EVENT_1_ABI, + {'arg0': 1}, + [EVENT_1_TOPIC], + ), + ( + EVENT_1_ABI, + {'arg0': 1, 'arg3': [1, 2]}, + [EVENT_1_TOPIC], + ), + ( + EVENT_1_ABI, + {'arg1': 1}, + [ + EVENT_1_TOPIC, hex_and_pad(1) + ], + ), + ( + EVENT_1_ABI, + {'arg1': [1, 2]}, + [ + EVENT_1_TOPIC, [hex_and_pad(1), hex_and_pad(2)], + ], + ), + ( + EVENT_1_ABI, + {'arg1': [1], 'arg2': [2]}, + [ + EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(2), + ], + ), + ( + EVENT_1_ABI, + {'arg1': [1, 3], 'arg2': [2, 4]}, + [ + EVENT_1_TOPIC, + [hex_and_pad(1), hex_and_pad(3)], + [hex_and_pad(2), hex_and_pad(4)] + ], + ), + ) +) +def test_construct_event_topics(event_abi, arguments, expected): + actual = construct_event_topic_set(event_abi, arguments) + assert actual == expected diff --git a/tests/core/utilities/test_datatypes.py b/tests/core/utilities/test_datatypes.py index 754b4316d8..ac64120906 100644 --- a/tests/core/utilities/test_datatypes.py +++ b/tests/core/utilities/test_datatypes.py @@ -1,35 +1,35 @@ -import pytest - -from web3._utils.datatypes import ( - PropertyCheckingFactory, -) - - -class InheritedBaseClass: - arg0 = None - arg1 = None - - -class BaseClass(InheritedBaseClass): - arg2 = None - arg3 = None - - -def test_property_checking_metaclass_attribute_error(): - # Test proper attribute checking, arg from both bases. - namespace = {'arg2': True, 'arg0': True, 'arg4': True} - with pytest.raises(AttributeError): - PropertyCheckingFactory('class_name', (BaseClass,), namespace) - - # Test proper attribute checking, only absent arg. - namespace = {'arg4': True} - with pytest.raises(AttributeError): - PropertyCheckingFactory('class_name', (BaseClass,), namespace) - - -def test_property_checking_metaclass_inherited_attributes(): - from_inherited_namespace = {'arg0': True, 'arg1': True} - PropertyCheckingFactory('class_name', (BaseClass,), from_inherited_namespace) - - from_base_namespace = {'arg2': True, 'arg3': True} - PropertyCheckingFactory('class_name', (BaseClass,), from_base_namespace) +import pytest + +from web3._utils.datatypes import ( + PropertyCheckingFactory, +) + + +class InheritedBaseClass: + arg0 = None + arg1 = None + + +class BaseClass(InheritedBaseClass): + arg2 = None + arg3 = None + + +def test_property_checking_metaclass_attribute_error(): + # Test proper attribute checking, arg from both bases. + namespace = {'arg2': True, 'arg0': True, 'arg4': True} + with pytest.raises(AttributeError): + PropertyCheckingFactory('class_name', (BaseClass,), namespace) + + # Test proper attribute checking, only absent arg. + namespace = {'arg4': True} + with pytest.raises(AttributeError): + PropertyCheckingFactory('class_name', (BaseClass,), namespace) + + +def test_property_checking_metaclass_inherited_attributes(): + from_inherited_namespace = {'arg0': True, 'arg1': True} + PropertyCheckingFactory('class_name', (BaseClass,), from_inherited_namespace) + + from_base_namespace = {'arg2': True, 'arg3': True} + PropertyCheckingFactory('class_name', (BaseClass,), from_base_namespace) diff --git a/tests/core/utilities/test_decorators.py b/tests/core/utilities/test_decorators.py index b1a10d4b3d..b3137fdd88 100644 --- a/tests/core/utilities/test_decorators.py +++ b/tests/core/utilities/test_decorators.py @@ -1,22 +1,22 @@ -import time - -from web3._utils.decorators import ( - reject_recursive_repeats, -) -from web3._utils.threads import ( - spawn, -) - - -def test_reject_recursive_repeats_multithreaded(): - @reject_recursive_repeats - def recurse(sleep_now): - time.sleep(sleep_now) - try: - recurse(0.05) - return True - except ValueError: - return False - thd1 = spawn(recurse, 0) - thd2 = spawn(recurse, 0.02) - assert thd2.get() and thd1.get() +import time + +from web3._utils.decorators import ( + reject_recursive_repeats, +) +from web3._utils.threads import ( + spawn, +) + + +def test_reject_recursive_repeats_multithreaded(): + @reject_recursive_repeats + def recurse(sleep_now): + time.sleep(sleep_now) + try: + recurse(0.05) + return True + except ValueError: + return False + thd1 = spawn(recurse, 0) + thd2 = spawn(recurse, 0.02) + assert thd2.get() and thd1.get() diff --git a/tests/core/utilities/test_encoding.py b/tests/core/utilities/test_encoding.py index ebfeb9164b..01e7dc7c98 100644 --- a/tests/core/utilities/test_encoding.py +++ b/tests/core/utilities/test_encoding.py @@ -1,237 +1,237 @@ -from ast import ( - literal_eval, -) -import datetime -import pytest -from unittest.mock import ( - Mock, -) - -from eth_utils import ( - is_hex, -) -from hypothesis import ( - example, - given, - strategies as st, -) - -from web3._utils.encoding import ( - FriendlyJsonSerde as FriendlyJson, - hex_encode_abi_type, - hexstr_if_str, - text_if_str, - to_hex, - to_int, -) -from web3._utils.hypothesis import ( - hexstr_strategy, -) -from web3.providers import ( - JSONBaseProvider, -) - - -@pytest.mark.parametrize( - "value,expected", - [ - (1, '0x1'), - (15, '0xf'), - (-1, '-0x1'), - (-15, '-0xf'), - (0, '0x0'), - (-0, '0x0'), - ] -) -def test_to_hex(value, expected): - assert to_hex(value) == expected - - -@given(value=st.integers(min_value=-1 * 2**255 + 1, max_value=2**256 - 1)) -def test_conversion_round_trip(value): - intermediate_value = to_hex(value) - result_value = to_int(hexstr=intermediate_value) - error_msg = "Expected: {0!r}, Result: {1!r}, Intermediate: {2!r}".format( - value, - result_value, - intermediate_value, - ) - assert result_value == value, error_msg - - -def test_bytes_that_start_with_0x(): - sneaky_bytes = b'0x\xde\xad' - assert to_hex(sneaky_bytes) == '0x3078dead' - - -@pytest.mark.parametrize( - "abi_type,value,expected", - [ - ('bool', True, "0x01"), - ('bool', False, "0x00"), - ('uint16', 8, "0x0008"), - ('int16', 8, "0x0008"), - ('int16', -8, "0xfff8"), - ( - 'address', - "0x00360d2b7D240Ec0643B6D819ba81A09e40E5bCd", - "0x00360d2b7D240Ec0643B6D819ba81A09e40E5bCd" - ), - ("bytes2", b"T\x02", "0x5402"), - ("bytes3", b"T\x02", "0x5402"), - ("bytes", '0x5402', "0x5402"), - ("bytes", '5402', TypeError), - ("string", "testing a string!", "0x74657374696e67206120737472696e6721"), - ("strings", "bad abi!", ValueError), - ("bool[", True, ValueError), - ("bool", "string", TypeError), - ("uint24", -20, TypeError), - ] -) -def test_hex_encode_abi_type(abi_type, value, expected): - - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - hex_encode_abi_type(abi_type, value) - return - - actual = hex_encode_abi_type(abi_type, value) - assert actual == expected - - -@given(st.one_of(st.integers(), st.booleans(), st.binary())) -@example(b'') -def test_hexstr_if_str_passthrough(val): - to_type = Mock(return_value='zoot') - assert hexstr_if_str(to_type, val) == 'zoot' - assert to_type.call_args == ((val, ), {'hexstr': None}) - - -def test_hexstr_if_str_curried(): - converter = hexstr_if_str(to_hex) - assert converter(255) == '0xff' - - -@given(hexstr_strategy()) -@example('0x') -@example('0') -def test_hexstr_if_str_on_valid_hex(val): - to_type = Mock(return_value='zoot') - assert hexstr_if_str(to_type, val) == 'zoot' - assert to_type.call_args == ((None, ), {'hexstr': val}) - - -@given(st.text()) -def test_hexstr_if_str_on_invalid_hex(val): - try: - is_hexstr = (is_hex(val) or val == '') - except ValueError: - is_hexstr = False - - if not is_hexstr: - with pytest.raises(ValueError): - hexstr_if_str(Mock(), val) - - -@given(st.one_of(st.integers(), st.booleans(), st.binary())) -@example(b'') -def test_text_if_str_passthrough(val): - to_type = Mock(return_value='zoot') - assert text_if_str(to_type, val) == 'zoot' - assert to_type.call_args == ((val, ), {'text': None}) - - -@given(st.text()) -@example('0xa1') # valid hexadecimal is still interpreted as unicode characters -def test_text_if_str_on_text(val): - to_type = Mock(return_value='zoot') - assert text_if_str(to_type, val) == 'zoot' - assert to_type.call_args == ((None, ), {'text': val}) - - -@pytest.mark.parametrize( - "py_obj, exc_type, expected", - ( - ( - { - 'date': [ - datetime.datetime(2018, 5, 10, 1, 5, 10).isoformat(), - datetime.datetime(2018, 5, 10, 1, 5, 10).isoformat(), - ], - 'other_date': datetime.datetime(2018, 5, 10, 1, 5, 10).date().isoformat(), - }, - None, - '{"date": ["2018-05-10T01:05:10", "2018-05-10T01:05:10"], "other_date": "2018-05-10"}', - ), - ( - { - 'date': [datetime.datetime.utcnow(), datetime.datetime.now()], - 'other_date': datetime.datetime.utcnow().date(), - }, - TypeError, - "Could not encode to JSON: .*'other_date'.*is not JSON serializable", - ), - ), -) -def test_friendly_json_encode(py_obj, exc_type, expected): - if exc_type is None: - assert literal_eval(FriendlyJson().json_encode(py_obj)) == literal_eval(expected) - else: - with pytest.raises(exc_type, match=expected): - FriendlyJson().json_encode(py_obj) - - -@pytest.mark.parametrize( - "json_str, expected", - ( - ( - '{"date": ["2018-05-10T01:05:10", "2018-05-10T01:05:10"],"other_date": "2018-05-10"}', - dict, - ), - ), -) -def test_friendly_json_decode(json_str, expected): - assert isinstance(FriendlyJson().json_decode(json_str), expected) - - -@pytest.mark.parametrize( - "rpc_response, expected", - ( - ( - '{"jsonrpc": "2.0", "method": "test_method", "params": [], "id": 1}', - {"jsonrpc": "2.0", "method": "test_method", "params": [], "id": 1}, - ), - ), -) -def test_decode_rpc_response(rpc_response, expected): - assert JSONBaseProvider().decode_rpc_response(rpc_response.encode('utf8')) == expected - - -@pytest.mark.parametrize( - "rpc_kwargs, exc_type, expected", - ( - ( - {'id': 1, 'method': 'test', 'params': [], "jsonrpc": "2.0"}, - None, - '{"id": 0, "method": "test", "params": [], "jsonrpc": "2.0",}', - ), - ( - {'id': 0, 'method': 'test', 'params': [datetime.datetime(2018, 5, 10, 1, 5, 10)]}, - TypeError, - r"Could not encode to JSON: .*'params'.* is not JSON serializable", - ), - ), -) -def test_encode_rpc_request(rpc_kwargs, exc_type, expected): - if exc_type is None: - res = JSONBaseProvider().encode_rpc_request( - rpc_kwargs['method'], - rpc_kwargs['params'] - ) - assert literal_eval(res.decode('utf8')) == literal_eval(expected) - else: - with pytest.raises(exc_type, match=expected): - JSONBaseProvider().encode_rpc_request( - rpc_kwargs['method'], - rpc_kwargs['params'], - ) +from ast import ( + literal_eval, +) +import datetime +import pytest +from unittest.mock import ( + Mock, +) + +from vns_utils import ( + is_hex, +) +from hypothesis import ( + example, + given, + strategies as st, +) + +from web3._utils.encoding import ( + FriendlyJsonSerde as FriendlyJson, + hex_encode_abi_type, + hexstr_if_str, + text_if_str, + to_hex, + to_int, +) +from web3._utils.hypothesis import ( + hexstr_strategy, +) +from web3.providers import ( + JSONBaseProvider, +) + + +@pytest.mark.parametrize( + "value,expected", + [ + (1, '0x1'), + (15, '0xf'), + (-1, '-0x1'), + (-15, '-0xf'), + (0, '0x0'), + (-0, '0x0'), + ] +) +def test_to_hex(value, expected): + assert to_hex(value) == expected + + +@given(value=st.integers(min_value=-1 * 2**255 + 1, max_value=2**256 - 1)) +def test_conversion_round_trip(value): + intermediate_value = to_hex(value) + result_value = to_int(hexstr=intermediate_value) + error_msg = "Expected: {0!r}, Result: {1!r}, Intermediate: {2!r}".format( + value, + result_value, + intermediate_value, + ) + assert result_value == value, error_msg + + +def test_bytes_that_start_with_0x(): + sneaky_bytes = b'0x\xde\xad' + assert to_hex(sneaky_bytes) == '0x3078dead' + + +@pytest.mark.parametrize( + "abi_type,value,expected", + [ + ('bool', True, "0x01"), + ('bool', False, "0x00"), + ('uint16', 8, "0x0008"), + ('int16', 8, "0x0008"), + ('int16', -8, "0xfff8"), + ( + 'address', + "0x00360d2b7D240Ec0643B6D819ba81A09e40E5bCd", + "0x00360d2b7D240Ec0643B6D819ba81A09e40E5bCd" + ), + ("bytes2", b"T\x02", "0x5402"), + ("bytes3", b"T\x02", "0x5402"), + ("bytes", '0x5402', "0x5402"), + ("bytes", '5402', TypeError), + ("string", "testing a string!", "0x74657374696e67206120737472696e6721"), + ("strings", "bad abi!", ValueError), + ("bool[", True, ValueError), + ("bool", "string", TypeError), + ("uint24", -20, TypeError), + ] +) +def test_hex_encode_abi_type(abi_type, value, expected): + + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + hex_encode_abi_type(abi_type, value) + return + + actual = hex_encode_abi_type(abi_type, value) + assert actual == expected + + +@given(st.one_of(st.integers(), st.booleans(), st.binary())) +@example(b'') +def test_hexstr_if_str_passthrough(val): + to_type = Mock(return_value='zoot') + assert hexstr_if_str(to_type, val) == 'zoot' + assert to_type.call_args == ((val, ), {'hexstr': None}) + + +def test_hexstr_if_str_curried(): + converter = hexstr_if_str(to_hex) + assert converter(255) == '0xff' + + +@given(hexstr_strategy()) +@example('0x') +@example('0') +def test_hexstr_if_str_on_valid_hex(val): + to_type = Mock(return_value='zoot') + assert hexstr_if_str(to_type, val) == 'zoot' + assert to_type.call_args == ((None, ), {'hexstr': val}) + + +@given(st.text()) +def test_hexstr_if_str_on_invalid_hex(val): + try: + is_hexstr = (is_hex(val) or val == '') + except ValueError: + is_hexstr = False + + if not is_hexstr: + with pytest.raises(ValueError): + hexstr_if_str(Mock(), val) + + +@given(st.one_of(st.integers(), st.booleans(), st.binary())) +@example(b'') +def test_text_if_str_passthrough(val): + to_type = Mock(return_value='zoot') + assert text_if_str(to_type, val) == 'zoot' + assert to_type.call_args == ((val, ), {'text': None}) + + +@given(st.text()) +@example('0xa1') # valid hexadecimal is still interpreted as unicode characters +def test_text_if_str_on_text(val): + to_type = Mock(return_value='zoot') + assert text_if_str(to_type, val) == 'zoot' + assert to_type.call_args == ((None, ), {'text': val}) + + +@pytest.mark.parametrize( + "py_obj, exc_type, expected", + ( + ( + { + 'date': [ + datetime.datetime(2018, 5, 10, 1, 5, 10).isoformat(), + datetime.datetime(2018, 5, 10, 1, 5, 10).isoformat(), + ], + 'other_date': datetime.datetime(2018, 5, 10, 1, 5, 10).date().isoformat(), + }, + None, + '{"date": ["2018-05-10T01:05:10", "2018-05-10T01:05:10"], "other_date": "2018-05-10"}', + ), + ( + { + 'date': [datetime.datetime.utcnow(), datetime.datetime.now()], + 'other_date': datetime.datetime.utcnow().date(), + }, + TypeError, + "Could not encode to JSON: .*'other_date'.*is not JSON serializable", + ), + ), +) +def test_friendly_json_encode(py_obj, exc_type, expected): + if exc_type is None: + assert literal_eval(FriendlyJson().json_encode(py_obj)) == literal_eval(expected) + else: + with pytest.raises(exc_type, match=expected): + FriendlyJson().json_encode(py_obj) + + +@pytest.mark.parametrize( + "json_str, expected", + ( + ( + '{"date": ["2018-05-10T01:05:10", "2018-05-10T01:05:10"],"other_date": "2018-05-10"}', + dict, + ), + ), +) +def test_friendly_json_decode(json_str, expected): + assert isinstance(FriendlyJson().json_decode(json_str), expected) + + +@pytest.mark.parametrize( + "rpc_response, expected", + ( + ( + '{"jsonrpc": "2.0", "method": "test_method", "params": [], "id": 1}', + {"jsonrpc": "2.0", "method": "test_method", "params": [], "id": 1}, + ), + ), +) +def test_decode_rpc_response(rpc_response, expected): + assert JSONBaseProvider().decode_rpc_response(rpc_response.encode('utf8')) == expected + + +@pytest.mark.parametrize( + "rpc_kwargs, exc_type, expected", + ( + ( + {'id': 1, 'method': 'test', 'params': [], "jsonrpc": "2.0"}, + None, + '{"id": 0, "method": "test", "params": [], "jsonrpc": "2.0",}', + ), + ( + {'id': 0, 'method': 'test', 'params': [datetime.datetime(2018, 5, 10, 1, 5, 10)]}, + TypeError, + r"Could not encode to JSON: .*'params'.* is not JSON serializable", + ), + ), +) +def test_encode_rpc_request(rpc_kwargs, exc_type, expected): + if exc_type is None: + res = JSONBaseProvider().encode_rpc_request( + rpc_kwargs['method'], + rpc_kwargs['params'] + ) + assert literal_eval(res.decode('utf8')) == literal_eval(expected) + else: + with pytest.raises(exc_type, match=expected): + JSONBaseProvider().encode_rpc_request( + rpc_kwargs['method'], + rpc_kwargs['params'], + ) diff --git a/tests/core/utilities/test_event_filter_builder.py b/tests/core/utilities/test_event_filter_builder.py index 08ac40339e..c67f2a91c0 100644 --- a/tests/core/utilities/test_event_filter_builder.py +++ b/tests/core/utilities/test_event_filter_builder.py @@ -1,83 +1,52 @@ -import pytest - -from eth_abi.exceptions import ( - ValueOutOfBounds, -) -from hypothesis import ( - given, - strategies as st, -) - -from web3._utils.events import ( - DataArgumentFilter, - TopicArgumentFilter, - normalize_topic_list, -) - - -@pytest.mark.parametrize( - "topic_list,expected", - ( - ( - ("0x1", "0x2", ["0x3"], None, "0x4", None, None, None), - ("0x1", "0x2", "0x3", None, "0x4") - ), - ( - (None, ["0x2", "0x2a"], "0x3", None, "0x4", None, [None], None), - (None, ["0x2", "0x2a"], "0x3", None, "0x4") - ), - ( - (None, None, [None]), - tuple() - ) - ) -) -def test_normalize_topic_list(topic_list, expected): - assert normalize_topic_list(topic_list) == expected - - -@given(st.text()) -def test_match_single_string_type_properties_data_arg(value): - data_filter = DataArgumentFilter(arg_type="string") - data_filter.match_single(value) - - -@given(st.text()) -def test_match_single_string_type_properties_topic_arg(web3, value): - topic_filter = TopicArgumentFilter(arg_type="string", abi_codec=web3.codec) - topic_filter.match_single(value) - - -@given(st.lists(elements=st.text(), max_size=10, min_size=0)) -def test_match_any_string_type_properties(web3, values): - topic_filter = TopicArgumentFilter(arg_type="string", abi_codec=web3.codec) - topic_filter.match_any(*values) - assert len(topic_filter.match_values) == len(values) - - -@given(st.lists(elements=st.binary(), max_size=10, min_size=0)) -def test_match_any_bytes_type_properties(web3, values): - topic_filter = TopicArgumentFilter(arg_type="bytes", abi_codec=web3.codec) - topic_filter.match_any(*values) - assert len(topic_filter.match_values) == len(values) - - -@given(st.lists(elements=st.binary(), max_size=10, min_size=1)) -def test_match_any_bytes_type_properties_strict(w3_strict_abi, values): - topic_filter = TopicArgumentFilter(arg_type="bytes", abi_codec=w3_strict_abi.codec) - topic_filter.match_any(*values) - assert len(topic_filter.match_values) == len(values) - - -def test_match_hex_type_properties_strict(w3_strict_abi): - topic_filter = TopicArgumentFilter(arg_type="bytes2", abi_codec=w3_strict_abi.codec) - topic_filter.match_any("0x1233") - assert len(topic_filter.match_values) == 1 - - -@pytest.mark.parametrize("values", (b"123", b"1", "0x12", "0x", "0x121212")) -def test_match_any_bytes_type_properties_strict_errors(w3_strict_abi, values): - topic_filter = TopicArgumentFilter(arg_type="bytes2", abi_codec=w3_strict_abi.codec) - topic_filter.match_any(values) - with pytest.raises(ValueOutOfBounds): - topic_filter.match_values +import pytest + +from hypothesis import ( + given, + strategies as st, +) + +from web3._utils.events import ( + DataArgumentFilter, + TopicArgumentFilter, + normalize_topic_list, +) + + +@pytest.mark.parametrize( + "topic_list,expected", + ( + ( + ("0x1", "0x2", ["0x3"], None, "0x4", None, None, None), + ("0x1", "0x2", "0x3", None, "0x4") + ), + ( + (None, ["0x2", "0x2a"], "0x3", None, "0x4", None, [None], None), + (None, ["0x2", "0x2a"], "0x3", None, "0x4") + ), + ( + (None, None, [None]), + tuple() + ) + ) +) +def test_normalize_topic_list(topic_list, expected): + assert normalize_topic_list(topic_list) == expected + + +@given(st.text()) +def test_match_single_string_type_properties_data_arg(value): + data_filter = DataArgumentFilter(arg_type="string") + data_filter.match_single(value) + + +@given(st.text()) +def test_match_single_string_type_properties_topic_arg(value): + topic_filter = TopicArgumentFilter(arg_type="string") + topic_filter.match_single(value) + + +@given(st.lists(elements=st.text(), max_size=10, min_size=0)) +def test_match_any_string_type_properties(values): + topic_filter = TopicArgumentFilter(arg_type="string") + topic_filter.match_any(*values) + assert len(topic_filter.match_values) == len(values) diff --git a/tests/core/utilities/test_event_interface.py b/tests/core/utilities/test_event_interface.py index 4f72ccd5fb..aa5fb5a4e9 100644 --- a/tests/core/utilities/test_event_interface.py +++ b/tests/core/utilities/test_event_interface.py @@ -1,34 +1,34 @@ -import pytest - -from web3.exceptions import ( - MismatchedABI, - NoABIEventsFound, -) - -EVENT_1_ABI = { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "arg0", "type": "uint256"}, - {"indexed": True, "name": "arg1", "type": "uint256"}, - ], - "name": "Event_1", - "type": "event", -} - - -def test_access_event_with_no_abi(web3): - contract = web3.eth.contract() - with pytest.raises(NoABIEventsFound): - contract.events.thisEventDoesNotExist() - - -def test_access_event_abi_with_no_events(web3): - contract = web3.eth.contract(abi=[]) - with pytest.raises(NoABIEventsFound): - contract.events.thisEventDoesNotExist() - - -def test_access_nonexistent_event(web3): - contract = web3.eth.contract(abi=[EVENT_1_ABI]) - with pytest.raises(MismatchedABI): - contract.events.thisEventDoesNotExist() +import pytest + +from web3.exceptions import ( + MismatchedABI, + NoABIEventsFound, +) + +EVENT_1_ABI = { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + ], + "name": "Event_1", + "type": "event", +} + + +def test_access_event_with_no_abi(web3): + contract = web3.vns.contract() + with pytest.raises(NoABIEventsFound): + contract.events.thisEventDoesNotExist() + + +def test_access_event_abi_with_no_events(web3): + contract = web3.vns.contract(abi=[]) + with pytest.raises(NoABIEventsFound): + contract.events.thisEventDoesNotExist() + + +def test_access_nonexistent_event(web3): + contract = web3.vns.contract(abi=[EVENT_1_ABI]) + with pytest.raises(MismatchedABI): + contract.events.thisEventDoesNotExist() diff --git a/tests/core/utilities/test_formatters.py b/tests/core/utilities/test_formatters.py index 649578f7ce..50c3718543 100644 --- a/tests/core/utilities/test_formatters.py +++ b/tests/core/utilities/test_formatters.py @@ -1,54 +1,51 @@ - -import pytest - -from eth_utils.curried import ( - apply_formatters_to_dict, -) - -from web3._utils.formatters import ( - map_collection, - recursive_map, -) - - -def square_int(x): - if isinstance(x, int): - return x * x - else: - return x - - -@pytest.mark.parametrize('non_collection', [1, 'abc', u'def', True, None]) -def test_map_collection_on_non_collection(non_collection): - assert map_collection(lambda x: x + 2, non_collection) == non_collection - - -@pytest.mark.parametrize('coll', [set, list, tuple]) -def test_collection_apply(coll): - vals = coll([1, 2]) - assert map_collection(lambda x: x + 2, vals) == coll([3, 4]) - - -def test_collection_apply_to_mapping(): - vals = {'a': 1, 'b': 2} - assert map_collection(lambda x: x + 2, vals) == {'a': 3, 'b': 4} - - -def test_recursive_collection_apply(): - assert recursive_map(square_int, [[3]]) == [[9]] - - -def test_recursive_collection_cycle(): - data = [3] - data.append(data) - with pytest.raises(ValueError): - recursive_map(square_int, data) - - -def test_format_dict_error(): - with pytest.raises(ValueError) as exc_info: - apply_formatters_to_dict( - {'myfield': int}, - {'myfield': 'a'}, - ) - assert 'myfield' in str(exc_info.value) + +import pytest + +from web3._utils.formatters import ( + apply_formatters_to_dict, + map_collection, + recursive_map, +) + + +def square_int(x): + if isinstance(x, int): + return x * x + else: + return x + + +@pytest.mark.parametrize('non_collection', [1, 'abc', u'def', True, None]) +def test_map_collection_on_non_collection(non_collection): + assert map_collection(lambda x: x + 2, non_collection) == non_collection + + +@pytest.mark.parametrize('coll', [set, list, tuple]) +def test_collection_apply(coll): + vals = coll([1, 2]) + assert map_collection(lambda x: x + 2, vals) == coll([3, 4]) + + +def test_collection_apply_to_mapping(): + vals = {'a': 1, 'b': 2} + assert map_collection(lambda x: x + 2, vals) == {'a': 3, 'b': 4} + + +def test_recursive_collection_apply(): + assert recursive_map(square_int, [[3]]) == [[9]] + + +def test_recursive_collection_cycle(): + data = [3] + data.append(data) + with pytest.raises(ValueError): + recursive_map(square_int, data) + + +def test_format_dict_error(): + with pytest.raises(ValueError) as exc_info: + apply_formatters_to_dict( + {'myfield': int}, + {'myfield': 'a'}, + ) + assert 'myfield' in str(exc_info.value) diff --git a/tests/core/utilities/test_is_predefined_block_number.py b/tests/core/utilities/test_is_predefined_block_number.py index 95782ea4a6..98788e8b49 100644 --- a/tests/core/utilities/test_is_predefined_block_number.py +++ b/tests/core/utilities/test_is_predefined_block_number.py @@ -1,20 +1,20 @@ -import pytest - -from web3._utils.blocks import ( - is_predefined_block_number, -) - - -@pytest.mark.parametrize( - 'block_identifier,expected', - ( - ('earliest', True), - ('latest', True), - ('pending', True), - (1, False), - ('0x1', False), - ), -) -def test_is_predefined_block_number(block_identifier, expected): - actual = is_predefined_block_number(block_identifier) - assert actual is expected +import pytest + +from web3._utils.blocks import ( + is_predefined_block_number, +) + + +@pytest.mark.parametrize( + 'block_identifier,expected', + ( + ('earliest', True), + ('latest', True), + ('pending', True), + (1, False), + ('0x1', False), + ), +) +def test_is_predefined_block_number(block_identifier, expected): + actual = is_predefined_block_number(block_identifier) + assert actual is expected diff --git a/tests/core/utilities/test_is_probably_enum.py b/tests/core/utilities/test_is_probably_enum.py index 6b37b9b3fc..17f137346e 100644 --- a/tests/core/utilities/test_is_probably_enum.py +++ b/tests/core/utilities/test_is_probably_enum.py @@ -1,23 +1,23 @@ -import pytest - -from web3._utils.abi import ( - is_probably_enum, -) - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('SomeEnum.SomeValue', True), - ('Some_Enum.Some_Value', True), - ('SomeEnum.someValue', True), - ('SomeEnum.some_value', True), - ('__SomeEnum__.some_value', True), - ('__SomeEnum__.__some_value__', True), - ('SomeEnum.__some_value__', True), - ('uint256', False), - ), -) -def test_is_probably_enum(abi_type, should_match): - is_match = is_probably_enum(abi_type) - assert is_match is should_match +import pytest + +from web3._utils.abi import ( + is_probably_enum, +) + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('SomeEnum.SomeValue', True), + ('Some_Enum.Some_Value', True), + ('SomeEnum.someValue', True), + ('SomeEnum.some_value', True), + ('__SomeEnum__.some_value', True), + ('__SomeEnum__.__some_value__', True), + ('SomeEnum.__some_value__', True), + ('uint256', False), + ), +) +def test_is_probably_enum(abi_type, should_match): + is_match = is_probably_enum(abi_type) + assert is_match is should_match diff --git a/tests/core/utilities/test_is_recognized_type.py b/tests/core/utilities/test_is_recognized_type.py index 679655e983..fde81fea36 100644 --- a/tests/core/utilities/test_is_recognized_type.py +++ b/tests/core/utilities/test_is_recognized_type.py @@ -1,183 +1,183 @@ -import pytest - -from web3._utils.abi import ( - is_address_type, - is_array_type, - is_bool_type, - is_bytes_type, - is_int_type, - is_recognized_type, - is_string_type, - is_uint_type, -) - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('bool', True), - ('uint', False), - ('uint256', True), - ('uint8', True), - ('uint7', False), - ('int', False), - ('int256', True), - ('int8', True), - ('int7', False), - ('byte', False), - ('bytes1', True), - ('bytes7', True), - ('bytes32', True), - ('bytes32.byte', True), - ('bytes', True), - ('string', True), - ('address', True), - ('uint256[]', True), - ('uint256[][]', True), - ('uint256[][][]', True), - ('uint256[1][][3]', True), - ('uint256[1][20000][3]', True), - ('uint256[][20000][]', True), - ('bytes20[]', True), - ('bytes20[][]', True), - ('bytes20[][][]', True), - ('bytes20[1][][3]', True), - ('bytes20[1][20000][3]', True), - ('bytes20[][20000][]', True), - ('address[]', True), - ('address[][]', True), - ('address[][][]', True), - ('address[1][][3]', True), - ('address[1][20000][3]', True), - ('address[][20000][]', True), - ('SomeEnum.SomeValue', False), - ), -) -def test_is_recognized_type(abi_type, should_match): - is_match = is_recognized_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('bool', True), - ('uint', False), - ('sbool', False), - ('bools', False), - ), -) -def test_is_bool_type(abi_type, should_match): - is_match = is_bool_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('uint', False), - ('uint32', True), - ('uint255', False), - ('uint256', True), - ('uint0', False), - ('int', False), - ('int16', False), - ('suint', False), - ('uints', False), - ), -) -def test_is_uint_type(abi_type, should_match): - is_match = is_uint_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('int', False), - ('int32', True), - ('int255', False), - ('int256', True), - ('int0', False), - ('uint', False), - ('uint16', False), - ('sint', False), - ('ints', False), - ), -) -def test_is_int_type(abi_type, should_match): - is_match = is_int_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('address', True), - ('uint', False), - ('saddress', False), - ('addresss', False), - ), -) -def test_is_address_type(abi_type, should_match): - is_match = is_address_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('bytes', True), - ('bytes4', True), - ('bytes32.byte', True), - ('sbyte', False), - ('bytess', False), - ('byte', False), - ('byte0', False), - ('bytes33', False), - ('bytes32..byte', False), - ('bytes32mbyte', False), - ('bytes32byte', False), - - ), -) -def test_is_bytes_type(abi_type, should_match): - is_match = is_bytes_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('string', True), - ('uint', False), - ('sstring', False), - ('strings', False), - ), -) -def test_is_string_type(abi_type, should_match): - is_match = is_string_type(abi_type) - assert is_match is should_match - - -@pytest.mark.parametrize( - 'abi_type,should_match', - ( - ('bool[]', True), - ('uint[]', True), - ('uint[][]', True), - ('uint[5][]', True), - ('uint[][5]', True), - ('int[]', True), - ('string[]', True), - ('address[]', True), - ('bytes[]', True), - ('string', False), - ('bytes', False), - ('uint[', False), - ('uint]', False), - ), -) -def test_is_array_type(abi_type, should_match): - is_match = is_array_type(abi_type) - assert is_match is should_match +import pytest + +from web3._utils.abi import ( + is_address_type, + is_array_type, + is_bool_type, + is_bytes_type, + is_int_type, + is_recognized_type, + is_string_type, + is_uint_type, +) + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('bool', True), + ('uint', False), + ('uint256', True), + ('uint8', True), + ('uint7', False), + ('int', False), + ('int256', True), + ('int8', True), + ('int7', False), + ('byte', False), + ('bytes1', True), + ('bytes7', True), + ('bytes32', True), + ('bytes32.byte', True), + ('bytes', True), + ('string', True), + ('address', True), + ('uint256[]', True), + ('uint256[][]', True), + ('uint256[][][]', True), + ('uint256[1][][3]', True), + ('uint256[1][20000][3]', True), + ('uint256[][20000][]', True), + ('bytes20[]', True), + ('bytes20[][]', True), + ('bytes20[][][]', True), + ('bytes20[1][][3]', True), + ('bytes20[1][20000][3]', True), + ('bytes20[][20000][]', True), + ('address[]', True), + ('address[][]', True), + ('address[][][]', True), + ('address[1][][3]', True), + ('address[1][20000][3]', True), + ('address[][20000][]', True), + ('SomeEnum.SomeValue', False), + ), +) +def test_is_recognized_type(abi_type, should_match): + is_match = is_recognized_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('bool', True), + ('uint', False), + ('sbool', False), + ('bools', False), + ), +) +def test_is_bool_type(abi_type, should_match): + is_match = is_bool_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('uint', False), + ('uint32', True), + ('uint255', False), + ('uint256', True), + ('uint0', False), + ('int', False), + ('int16', False), + ('suint', False), + ('uints', False), + ), +) +def test_is_uint_type(abi_type, should_match): + is_match = is_uint_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('int', False), + ('int32', True), + ('int255', False), + ('int256', True), + ('int0', False), + ('uint', False), + ('uint16', False), + ('sint', False), + ('ints', False), + ), +) +def test_is_int_type(abi_type, should_match): + is_match = is_int_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('address', True), + ('uint', False), + ('saddress', False), + ('addresss', False), + ), +) +def test_is_address_type(abi_type, should_match): + is_match = is_address_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('bytes', True), + ('bytes4', True), + ('bytes32.byte', True), + ('sbyte', False), + ('bytess', False), + ('byte', False), + ('byte0', False), + ('bytes33', False), + ('bytes32..byte', False), + ('bytes32mbyte', False), + ('bytes32byte', False), + + ), +) +def test_is_bytes_type(abi_type, should_match): + is_match = is_bytes_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('string', True), + ('uint', False), + ('sstring', False), + ('strings', False), + ), +) +def test_is_string_type(abi_type, should_match): + is_match = is_string_type(abi_type) + assert is_match is should_match + + +@pytest.mark.parametrize( + 'abi_type,should_match', + ( + ('bool[]', True), + ('uint[]', True), + ('uint[][]', True), + ('uint[5][]', True), + ('uint[][5]', True), + ('int[]', True), + ('string[]', True), + ('address[]', True), + ('bytes[]', True), + ('string', False), + ('bytes', False), + ('uint[', False), + ('uint]', False), + ), +) +def test_is_array_type(abi_type, should_match): + is_match = is_array_type(abi_type) + assert is_match is should_match diff --git a/tests/core/utilities/test_math.py b/tests/core/utilities/test_math.py index 8495a7f06a..1eff1382b9 100644 --- a/tests/core/utilities/test_math.py +++ b/tests/core/utilities/test_math.py @@ -1,56 +1,56 @@ -import pytest - -from hypothesis import ( - given, - strategies as st, -) - -from web3._utils.math import ( - percentile, -) -from web3.exceptions import ( - InsufficientData, -) - -values = range(100) - - -@pytest.mark.parametrize( - "p,expected", - ( - (0, 0), - (1, 0), - (2, 1), - (3, 2), - (4, 3), - (50, 49), - (50.5, 49.5), - (100, 99) - ) -) -def test_percentiles_out_of_one_hundred(p, expected): - assert percentile(values, p) == expected - - -def test_percentiles_with_no_values(): - with pytest.raises(InsufficientData): - percentile([], 1) - - -def test_percentiles_with_out_of_bounds_fractions(): - assert 1 == percentile([1, 2, 3, 4], percentile=10) - assert 1 == percentile([1, 2, 3, 4], percentile=15) - assert 1 == percentile([1, 2, 3, 4], percentile=20) - assert 1 == percentile([1, 2, 3, 4], percentile=25) - assert 1 < percentile([1, 2, 3, 4], percentile=30) - - -@given( - values=st.lists(elements=st.integers(), min_size=1, max_size=200), - p=st.integers(max_value=100, min_value=0)) -def test_fuzz_test_percentiles(values, p): - if not values: - with pytest.raises(ValueError): - percentile(values, p) - else: - percentile(values, p) +import pytest + +from hypothesis import ( + given, + strategies as st, +) + +from web3._utils.math import ( + percentile, +) +from web3.exceptions import ( + InsufficientData, +) + +values = range(100) + + +@pytest.mark.parametrize( + "p,expected", + ( + (0, 0), + (1, 0), + (2, 1), + (3, 2), + (4, 3), + (50, 49), + (50.5, 49.5), + (100, 99) + ) +) +def test_percentiles_out_of_one_hundred(p, expected): + assert percentile(values, p) == expected + + +def test_percentiles_with_no_values(): + with pytest.raises(InsufficientData): + percentile([], 1) + + +def test_percentiles_with_out_of_bounds_fractions(): + assert 1 == percentile([1, 2, 3, 4], percentile=10) + assert 1 == percentile([1, 2, 3, 4], percentile=15) + assert 1 == percentile([1, 2, 3, 4], percentile=20) + assert 1 == percentile([1, 2, 3, 4], percentile=25) + assert 1 < percentile([1, 2, 3, 4], percentile=30) + + +@given( + values=st.lists(elements=st.integers(), min_size=1, max_size=200), + p=st.integers(max_value=100, min_value=0)) +def test_fuzz_test_percentiles(values, p): + if not values: + with pytest.raises(ValueError): + percentile(values, p) + else: + percentile(values, p) diff --git a/tests/core/utilities/test_prepare_transaction_replacement.py b/tests/core/utilities/test_prepare_transaction_replacement.py index e57b103aca..14745781f0 100644 --- a/tests/core/utilities/test_prepare_transaction_replacement.py +++ b/tests/core/utilities/test_prepare_transaction_replacement.py @@ -1,122 +1,122 @@ -import pytest - -from web3._utils.transactions import ( - prepare_replacement_transaction, -) - -SIMPLE_CURRENT_TRANSACTION = { - 'blockHash': None, - 'hash': '0x0', - 'nonce': 2, - 'gasPrice': 10, -} - - -def test_prepare_transaction_replacement(web3): - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 1, - 'nonce': 2, - } - replacement_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction) - - assert replacement_transaction == { - 'value': 1, - 'nonce': 2, - 'gasPrice': 11, - } - - -def test_prepare_transaction_replacement_without_nonce_sets_correct_nonce(web3): - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 1, - } - replacement_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction) - assert replacement_transaction == { - 'value': 1, - 'nonce': 2, - 'gasPrice': 11, - } - - -def test_prepare_transaction_replacement_already_mined_raises(web3): - with pytest.raises(ValueError): - prepare_replacement_transaction( - web3, {'blockHash': '0xa1a1a1', 'hash': '0x0'}, {'value': 2}) - - -def test_prepare_transaction_replacement_nonce_mismatch_raises(web3): - with pytest.raises(ValueError): - prepare_replacement_transaction(web3, { - 'blockHash': None, - 'hash': '0x0', - 'nonce': 1, - }, { - 'nonce': 2, - }) - - -def test_prepare_transaction_replacement_not_higher_gas_price_raises(web3): - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 1, - 'gasPrice': 5, - } - with pytest.raises(ValueError): - prepare_replacement_transaction( - web3, current_transaction, new_transaction) - - # Also raises when equal to the current transaction - new_transaction['gasPrice'] = 10 - with pytest.raises(ValueError): - prepare_replacement_transaction(web3, current_transaction, new_transaction) - - -def test_prepare_transaction_replacement_gas_price_defaulting(web3): - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 2, - } - replacement_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction) - - assert replacement_transaction['gasPrice'] == 11 - - -def test_prepare_transaction_replacement_gas_price_defaulting_when_strategy_higer(web3): - - def higher_gas_price_strategy(web3, txn): - return 20 - - web3.eth.setGasPriceStrategy(higher_gas_price_strategy) - - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 2, - } - - replacement_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction) - - assert replacement_transaction['gasPrice'] == 20 - - -def test_prepare_transaction_replacement_gas_price_defaulting_when_strategy_lower(web3): - - def lower_gas_price_strategy(web3, txn): - return 5 - - web3.eth.setGasPriceStrategy(lower_gas_price_strategy) - - current_transaction = SIMPLE_CURRENT_TRANSACTION - new_transaction = { - 'value': 2, - } - - replacement_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction) - - assert replacement_transaction['gasPrice'] == 11 +import pytest + +from web3._utils.transactions import ( + prepare_replacement_transaction, +) + +SIMPLE_CURRENT_TRANSACTION = { + 'blockHash': None, + 'hash': '0x0', + 'nonce': 2, + 'gasPrice': 10, +} + + +def test_prepare_transaction_replacement(web3): + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 1, + 'nonce': 2, + } + replacement_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction) + + assert replacement_transaction == { + 'value': 1, + 'nonce': 2, + 'gasPrice': 11, + } + + +def test_prepare_transaction_replacement_without_nonce_sets_correct_nonce(web3): + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 1, + } + replacement_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction) + assert replacement_transaction == { + 'value': 1, + 'nonce': 2, + 'gasPrice': 11, + } + + +def test_prepare_transaction_replacement_already_mined_raises(web3): + with pytest.raises(ValueError): + prepare_replacement_transaction( + web3, {'blockHash': '0xa1a1a1', 'hash': '0x0'}, {'value': 2}) + + +def test_prepare_transaction_replacement_nonce_mismatch_raises(web3): + with pytest.raises(ValueError): + prepare_replacement_transaction(web3, { + 'blockHash': None, + 'hash': '0x0', + 'nonce': 1, + }, { + 'nonce': 2, + }) + + +def test_prepare_transaction_replacement_not_higher_gas_price_raises(web3): + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 1, + 'gasPrice': 5, + } + with pytest.raises(ValueError): + prepare_replacement_transaction( + web3, current_transaction, new_transaction) + + # Also raises when equal to the current transaction + new_transaction['gasPrice'] = 10 + with pytest.raises(ValueError): + prepare_replacement_transaction(web3, current_transaction, new_transaction) + + +def test_prepare_transaction_replacement_gas_price_defaulting(web3): + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 2, + } + replacement_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction) + + assert replacement_transaction['gasPrice'] == 11 + + +def test_prepare_transaction_replacement_gas_price_defaulting_when_strategy_higer(web3): + + def higher_gas_price_strategy(web3, txn): + return 20 + + web3.vns.setGasPriceStrategy(higher_gas_price_strategy) + + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 2, + } + + replacement_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction) + + assert replacement_transaction['gasPrice'] == 20 + + +def test_prepare_transaction_replacement_gas_price_defaulting_when_strategy_lower(web3): + + def lower_gas_price_strategy(web3, txn): + return 5 + + web3.vns.setGasPriceStrategy(lower_gas_price_strategy) + + current_transaction = SIMPLE_CURRENT_TRANSACTION + new_transaction = { + 'value': 2, + } + + replacement_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction) + + assert replacement_transaction['gasPrice'] == 11 diff --git a/tests/core/utilities/test_threads.py b/tests/core/utilities/test_threads.py index 56f7254375..21a914717d 100644 --- a/tests/core/utilities/test_threads.py +++ b/tests/core/utilities/test_threads.py @@ -1,107 +1,107 @@ -import pytest -import time - -from web3._utils.threads import ( - ThreadWithReturn, - Timeout, - spawn, -) - - -class CustomThreadClass(ThreadWithReturn): - pass - - -def test_spawning_simple_thread(): - container = { - 'success': None, - } - - def target_fn(): - container['success'] = True - - thread = spawn(target_fn) - thread.join() - - assert container['success'] is True - - -def test_spawning_specific_thread_class(): - container = { - 'success': None, - } - - def target_fn(): - container['success'] = True - - thread = spawn(target_fn, thread_class=CustomThreadClass) - thread.join() - - assert isinstance(thread, CustomThreadClass) - - assert container['success'] is True - - -def test_thread_with_return_value(): - container = { - 'success': None, - } - - def target_fn(): - container['success'] = True - return 12345 - - thread = spawn(target_fn) - thread.join() - - assert container['success'] is True - - assert thread.get() == 12345 - - -def test_inline_completion_before_timeout(): - timeout = Timeout(0.01) - timeout.start() - timeout.check() - timeout.cancel() - time.sleep(0.02) - - -def test_inline_timeout(): - timeout = Timeout(0.01) - timeout.start() - time.sleep(0.02) - with pytest.raises(Timeout): - timeout.check() - - -def test_contextmanager_completion_before_timeout(): - with Timeout(0.01) as timeout: - timeout.check() - time.sleep(0.02) - - -def test_contextmanager_timeout(): - with pytest.raises(Timeout): - with Timeout(0.01) as timeout: - time.sleep(0.02) - timeout.check() - - -def test_with_custom_exception_type(): - timeout = Timeout(0.01, ValueError) - timeout.start() - time.sleep(0.02) - with pytest.raises(ValueError): - timeout.check() - - -def test_with_custom_exception_instance(): - exc = ValueError("an instance of an excepiton") - timeout = Timeout(0.01, exc) - timeout.start() - time.sleep(0.02) - with pytest.raises(ValueError) as err: - timeout.check() - - assert err.value is exc +import pytest +import time + +from web3._utils.threads import ( + ThreadWithReturn, + Timeout, + spawn, +) + + +class CustomThreadClass(ThreadWithReturn): + pass + + +def test_spawning_simple_thread(): + container = { + 'success': None, + } + + def target_fn(): + container['success'] = True + + thread = spawn(target_fn) + thread.join() + + assert container['success'] is True + + +def test_spawning_specific_thread_class(): + container = { + 'success': None, + } + + def target_fn(): + container['success'] = True + + thread = spawn(target_fn, thread_class=CustomThreadClass) + thread.join() + + assert isinstance(thread, CustomThreadClass) + + assert container['success'] is True + + +def test_thread_with_return_value(): + container = { + 'success': None, + } + + def target_fn(): + container['success'] = True + return 12345 + + thread = spawn(target_fn) + thread.join() + + assert container['success'] is True + + assert thread.get() == 12345 + + +def test_inline_completion_before_timeout(): + timeout = Timeout(0.01) + timeout.start() + timeout.check() + timeout.cancel() + time.sleep(0.02) + + +def test_inline_timeout(): + timeout = Timeout(0.01) + timeout.start() + time.sleep(0.02) + with pytest.raises(Timeout): + timeout.check() + + +def test_contextmanager_completion_before_timeout(): + with Timeout(0.01) as timeout: + timeout.check() + time.sleep(0.02) + + +def test_contextmanager_timeout(): + with pytest.raises(Timeout): + with Timeout(0.01) as timeout: + time.sleep(0.02) + timeout.check() + + +def test_with_custom_exception_type(): + timeout = Timeout(0.01, ValueError) + timeout.start() + time.sleep(0.02) + with pytest.raises(ValueError): + timeout.check() + + +def test_with_custom_exception_instance(): + exc = ValueError("an instance of an excepiton") + timeout = Timeout(0.01, exc) + timeout.start() + time.sleep(0.02) + with pytest.raises(ValueError) as err: + timeout.check() + + assert err.value is exc diff --git a/tests/core/utilities/test_valid_transaction_params.py b/tests/core/utilities/test_valid_transaction_params.py index 171699186b..15a85fe4fe 100644 --- a/tests/core/utilities/test_valid_transaction_params.py +++ b/tests/core/utilities/test_valid_transaction_params.py @@ -1,94 +1,94 @@ -import pytest - -from web3._utils.transactions import ( - assert_valid_transaction_params, - extract_valid_transaction_params, -) - - -def test_assert_valid_transaction_params_all_params(): - assert_valid_transaction_params({ - 'from': '0x0', - 'to': '0x0', - 'gas': 21000, - 'gasPrice': 5000000, - 'value': 1, - 'data': '0x0', - 'nonce': 2, - 'chainId': 1, - }) - - -def test_assert_valid_transaction_params_some_params(): - assert_valid_transaction_params({ - 'from': '0x0', - 'to': '0x0', - 'value': 1, - }) - - -def test_assert_valid_transaction_params_invalid_param(): - with pytest.raises(ValueError): - assert_valid_transaction_params({ - 'from': '0x0', - 'to': '0x0', - 'value': 1, - 'tokens': 9000, - }) - - -FULL_TXN_DICT = { - 'from': '0x0', - 'to': '0x1', - 'gas': 21000, - 'gasPrice': 5000000, - 'data': '0x2', - 'value': 3, - 'nonce': 2, - 'chainId': 1, -} - - -@pytest.mark.parametrize( - "transaction_params, expected", - ((FULL_TXN_DICT, FULL_TXN_DICT), - ({'data': '0x0', 'input': '0x0'}, {'data': '0x0'}), - ({'input': '0x0'}, {'data': '0x0'}), - ({}, {}), - ) -) -def test_extract_valid_transaction_params(transaction_params, expected): - valid_transaction_params = extract_valid_transaction_params(transaction_params) - assert valid_transaction_params == expected - - -INVALID_TXN_PARAMS = {'data': '0x0', 'input': '0x1'} -EXPECTED_EXC_MSG = r'.* "input:(.*)" and "data:(.*)" .*' - - -@pytest.mark.parametrize( - "transaction_params, expected_exc_msg", - ((INVALID_TXN_PARAMS, EXPECTED_EXC_MSG),) -) -def test_extract_valid_transaction_params_invalid(transaction_params, expected_exc_msg): - with pytest.raises(AttributeError, match=expected_exc_msg): - extract_valid_transaction_params(transaction_params) - - -def test_extract_valid_transaction_params_includes_invalid(): - input = { - 'from': '0x0', - 'to': '0x0', - 'gas': 21000, - 'gasPrice': 5000000, - 'value': 1, - 'tokens': 9000, - } - valid_transaction_params = extract_valid_transaction_params(input) - assert valid_transaction_params == { - 'from': '0x0', - 'to': '0x0', - 'gas': 21000, - 'gasPrice': 5000000, - 'value': 1, - } +import pytest + +from web3._utils.transactions import ( + assert_valid_transaction_params, + extract_valid_transaction_params, +) + + +def test_assert_valid_transaction_params_all_params(): + assert_valid_transaction_params({ + 'from': '0x0', + 'to': '0x0', + 'gas': 21000, + 'gasPrice': 5000000, + 'value': 1, + 'data': '0x0', + 'nonce': 2, + 'chainId': 1, + }) + + +def test_assert_valid_transaction_params_some_params(): + assert_valid_transaction_params({ + 'from': '0x0', + 'to': '0x0', + 'value': 1, + }) + + +def test_assert_valid_transaction_params_invalid_param(): + with pytest.raises(ValueError): + assert_valid_transaction_params({ + 'from': '0x0', + 'to': '0x0', + 'value': 1, + 'tokens': 9000, + }) + + +FULL_TXN_DICT = { + 'from': '0x0', + 'to': '0x1', + 'gas': 21000, + 'gasPrice': 5000000, + 'data': '0x2', + 'value': 3, + 'nonce': 2, + 'chainId': 1, +} + + +@pytest.mark.parametrize( + "transaction_params, expected", + ((FULL_TXN_DICT, FULL_TXN_DICT), + ({'data': '0x0', 'input': '0x0'}, {'data': '0x0'}), + ({'input': '0x0'}, {'data': '0x0'}), + ({}, {}), + ) +) +def test_extract_valid_transaction_params(transaction_params, expected): + valid_transaction_params = extract_valid_transaction_params(transaction_params) + assert valid_transaction_params == expected + + +INVALID_TXN_PARAMS = {'data': '0x0', 'input': '0x1'} +EXPECTED_EXC_MSG = r'.* "input:(.*)" and "data:(.*)" .*' + + +@pytest.mark.parametrize( + "transaction_params, expected_exc_msg", + ((INVALID_TXN_PARAMS, EXPECTED_EXC_MSG),) +) +def test_extract_valid_transaction_params_invalid(transaction_params, expected_exc_msg): + with pytest.raises(AttributeError, match=expected_exc_msg): + extract_valid_transaction_params(transaction_params) + + +def test_extract_valid_transaction_params_includes_invalid(): + input = { + 'from': '0x0', + 'to': '0x0', + 'gas': 21000, + 'gasPrice': 5000000, + 'value': 1, + 'tokens': 9000, + } + valid_transaction_params = extract_valid_transaction_params(input) + assert valid_transaction_params == { + 'from': '0x0', + 'to': '0x0', + 'gas': 21000, + 'gasPrice': 5000000, + 'value': 1, + } diff --git a/tests/core/utilities/test_validation.py b/tests/core/utilities/test_validation.py index 0dfe5359ee..28a2f76ff1 100644 --- a/tests/core/utilities/test_validation.py +++ b/tests/core/utilities/test_validation.py @@ -1,147 +1,147 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from web3._utils.validation import ( - validate_abi, - validate_abi_type, - validate_abi_value, - validate_address, -) -from web3.exceptions import ( - InvalidAddress, -) - -ABI = [ - { - "constant": False, - "inputs": [], - "name": "func_1", - "outputs": [], - "type": "function", - }, -] - -MALFORMED_ABI_1 = "NON-LIST ABI" -MALFORMED_ABI_2 = [5, {"test": "value"}, True] -MALFORMED_SELECTOR_COLLISION_ABI = [ - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}], - 'name': 'blockHashAmphithyronVersify', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}], - 'name': 'blockHashAskewLimitary', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - } -] -MALFORMED_SIGNATURE_COLLISION_ABI = [ - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}], - 'name': 'blockHashAmphithyronVersify', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': False, - 'inputs': [{'name': 'input', 'type': 'uint256'}], - 'name': 'blockHashAmphithyronVersify', - 'outputs': [{'name': '', 'type': 'uint256'}], - 'payable': False, - 'stateMutability': 'nonpayable', - 'type': 'function' - } -] - -ADDRESS = '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' -BYTES_ADDRESS = to_bytes(hexstr=ADDRESS) -PADDED_ADDRESS = '0x000000000000000000000000d3cda913deb6f67967b99d67acdfa1712c293601' -INVALID_CHECKSUM_ADDRESS = '0xd3CDA913deB6f67967B99D67aCDFa1712C293601' -NON_CHECKSUM_ADDRESS = '0xd3cda913deb6f67967b99d67acdfa1712c293601' -BYTES_ADDRESS_LEN_LT_20 = bytes(1) * 19 -BYTES_ADDRESS_LEN_GT_20 = bytes(1) * 21 - - -@pytest.mark.parametrize( - 'param,validation,expected', - ( - (ABI, validate_abi, None), - (MALFORMED_ABI_1, validate_abi, ValueError), - (MALFORMED_ABI_2, validate_abi, ValueError), - (MALFORMED_SELECTOR_COLLISION_ABI, validate_abi, ValueError), - (MALFORMED_SIGNATURE_COLLISION_ABI, validate_abi, ValueError), - (ADDRESS, validate_address, None), - (BYTES_ADDRESS, validate_address, None), - (PADDED_ADDRESS, validate_address, InvalidAddress), - (INVALID_CHECKSUM_ADDRESS, validate_address, InvalidAddress), - (NON_CHECKSUM_ADDRESS, validate_address, InvalidAddress), - (BYTES_ADDRESS_LEN_LT_20, validate_address, InvalidAddress), - (BYTES_ADDRESS_LEN_GT_20, validate_address, InvalidAddress), - ("NotAddress", validate_address, InvalidAddress), - (b'not string', validate_address, InvalidAddress), - ('bool', validate_abi_type, None), - ('bool[', validate_abi_type, ValueError), - ('sbool', validate_abi_type, ValueError), - ('stringx', validate_abi_type, ValueError), - ('address', validate_abi_type, None), - ) -) -def test_validation(param, validation, expected): - - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - validation(param) - return - - validation(param) - - -@pytest.mark.parametrize( - 'abi_type,value,expected', - ( - ('string', True, TypeError), - ('bool[]', [1, 3], TypeError), - ('bool[]', [True, False], None), - ('bool[][2]', [[True, False], [True, False, True]], None), - ('bool[][2]', [[True, False], [True], [False]], TypeError), - ('bool[3][]', [[True, False, False]], None), - ('bool[3][]', [[True, False]], TypeError), - ('bool[0]', [], TypeError), - ('bool[0][1]', [[]], TypeError), - ('uint8', -5, TypeError), - ('int8', -5, None), - ('address', "just a string", InvalidAddress), - ('address', b'not even a string', InvalidAddress), - ('address[][]', [[4, 5], [True]], TypeError), - ('address[][]', [[ADDRESS]], None), - ('address[2][]', [[ADDRESS], [ADDRESS, ADDRESS]], TypeError), - ('address[2][1]', [[ADDRESS, ADDRESS]], None), - ('address[2][1]', [[ADDRESS], [ADDRESS, ADDRESS]], TypeError), - ('bytes', True, TypeError), - ('bytes', "0x5402", None), - ('bytes', "5402", TypeError), - ('bytes', b'T\x02', None), - ) -) -def test_validate_abi_value(abi_type, value, expected): - - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - validate_abi_value(abi_type, value) - return - - validate_abi_value(abi_type, value) +import pytest + +from vns_utils import ( + to_bytes, +) + +from web3._utils.validation import ( + validate_abi, + validate_abi_type, + validate_abi_value, + validate_address, +) +from web3.exceptions import ( + InvalidAddress, +) + +ABI = [ + { + "constant": False, + "inputs": [], + "name": "func_1", + "outputs": [], + "type": "function", + }, +] + +MALFORMED_ABI_1 = "NON-LIST ABI" +MALFORMED_ABI_2 = [5, {"test": "value"}, True] +MALFORMED_SELECTOR_COLLISION_ABI = [ + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}], + 'name': 'blockHashAmphithyronVersify', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}], + 'name': 'blockHashAskewLimitary', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + } +] +MALFORMED_SIGNATURE_COLLISION_ABI = [ + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}], + 'name': 'blockHashAmphithyronVersify', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': False, + 'inputs': [{'name': 'input', 'type': 'uint256'}], + 'name': 'blockHashAmphithyronVersify', + 'outputs': [{'name': '', 'type': 'uint256'}], + 'payable': False, + 'stateMutability': 'nonpayable', + 'type': 'function' + } +] + +ADDRESS = '0xd3CdA913deB6f67967B99D67aCDFa1712C293601' +BYTES_ADDRESS = to_bytes(hexstr=ADDRESS) +PADDED_ADDRESS = '0x000000000000000000000000d3cda913deb6f67967b99d67acdfa1712c293601' +INVALID_CHECKSUM_ADDRESS = '0xd3CDA913deB6f67967B99D67aCDFa1712C293601' +NON_CHECKSUM_ADDRESS = '0xd3cda913deb6f67967b99d67acdfa1712c293601' +BYTES_ADDRESS_LEN_LT_20 = bytes(1) * 19 +BYTES_ADDRESS_LEN_GT_20 = bytes(1) * 21 + + +@pytest.mark.parametrize( + 'param,validation,expected', + ( + (ABI, validate_abi, None), + (MALFORMED_ABI_1, validate_abi, ValueError), + (MALFORMED_ABI_2, validate_abi, ValueError), + (MALFORMED_SELECTOR_COLLISION_ABI, validate_abi, ValueError), + (MALFORMED_SIGNATURE_COLLISION_ABI, validate_abi, ValueError), + (ADDRESS, validate_address, None), + (BYTES_ADDRESS, validate_address, None), + (PADDED_ADDRESS, validate_address, InvalidAddress), + (INVALID_CHECKSUM_ADDRESS, validate_address, InvalidAddress), + (NON_CHECKSUM_ADDRESS, validate_address, InvalidAddress), + (BYTES_ADDRESS_LEN_LT_20, validate_address, InvalidAddress), + (BYTES_ADDRESS_LEN_GT_20, validate_address, InvalidAddress), + ("NotAddress", validate_address, InvalidAddress), + (b'not string', validate_address, InvalidAddress), + ('bool', validate_abi_type, None), + ('bool[', validate_abi_type, ValueError), + ('sbool', validate_abi_type, ValueError), + ('stringx', validate_abi_type, ValueError), + ('address', validate_abi_type, None), + ) +) +def test_validation(param, validation, expected): + + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + validation(param) + return + + validation(param) + + +@pytest.mark.parametrize( + 'abi_type,value,expected', + ( + ('string', True, TypeError), + ('bool[]', [1, 3], TypeError), + ('bool[]', [True, False], None), + ('bool[][2]', [[True, False], [True, False, True]], None), + ('bool[][2]', [[True, False], [True], [False]], TypeError), + ('bool[3][]', [[True, False, False]], None), + ('bool[3][]', [[True, False]], TypeError), + ('bool[0]', [], TypeError), + ('bool[0][1]', [[]], TypeError), + ('uint8', -5, TypeError), + ('int8', -5, None), + ('address', "just a string", InvalidAddress), + ('address', b'not even a string', InvalidAddress), + ('address[][]', [[4, 5], [True]], TypeError), + ('address[][]', [[ADDRESS]], None), + ('address[2][]', [[ADDRESS], [ADDRESS, ADDRESS]], TypeError), + ('address[2][1]', [[ADDRESS, ADDRESS]], None), + ('address[2][1]', [[ADDRESS], [ADDRESS, ADDRESS]], TypeError), + ('bytes', True, TypeError), + ('bytes', "0x5402", None), + ('bytes', "5402", TypeError), + ('bytes', b'T\x02', None), + ) +) +def test_validate_abi_value(abi_type, value, expected): + + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + validate_abi_value(abi_type, value) + return + + validate_abi_value(abi_type, value) diff --git a/tests/core/version-module/test_version_module.py b/tests/core/version-module/test_version_module.py index dbffc5e6a3..123bb057c5 100644 --- a/tests/core/version-module/test_version_module.py +++ b/tests/core/version-module/test_version_module.py @@ -1,56 +1,56 @@ -import pytest - -from web3 import ( - EthereumTesterProvider, - Web3, -) -from web3.eth import ( - Eth, -) -from web3.providers.eth_tester.main import ( - AsyncEthereumTesterProvider, -) -from web3.version import ( - AsyncVersion, - BlockingVersion, - Version, -) - -# This file is being left in since the Version module is being experimented on for -# async behavior. But, this file along with web3/version.py should be removed eventually. - - -@pytest.fixture -def blocking_w3(): - return Web3( - EthereumTesterProvider(), - modules={ - "blocking_version": (BlockingVersion,), - "legacy_version": (Version,), - "eth": (Eth,), - }) - - -@pytest.fixture -def async_w3(): - return Web3( - AsyncEthereumTesterProvider(), - middlewares=[], - modules={ - 'async_version': (AsyncVersion,), - }) - - -def test_legacy_version_deprecation(blocking_w3): - with pytest.raises(DeprecationWarning): - blocking_w3.legacy_version.node - with pytest.raises(DeprecationWarning): - blocking_w3.legacy_version.ethereum - - -@pytest.mark.asyncio -async def test_async_blocking_version(async_w3, blocking_w3): - assert async_w3.async_version.api == blocking_w3.api - - assert await async_w3.async_version.node == blocking_w3.clientVersion - assert await async_w3.async_version.ethereum == blocking_w3.eth.protocolVersion +import pytest + +from web3 import ( + EthereumTesterProvider, + Web3, +) +from web3.providers.vns_tester.main import ( + AsyncEthereumTesterProvider, +) +from web3.version import ( + AsyncVersion, + BlockingVersion, + Version, +) + +# This file is being left in since the Version module is being experimented on for +# async behavior. But, this file along with web3/version.py should be removed eventually. + + +@pytest.fixture +def blocking_w3(): + return Web3( + EthereumTesterProvider(), + modules={ + "blocking_version": (BlockingVersion,), + "legacy_version": (Version,), + }) + + +@pytest.fixture +def async_w3(): + return Web3( + AsyncEthereumTesterProvider(), + middlewares=[], + modules={ + 'async_version': (AsyncVersion,), + }) + + +def test_legacy_version_deprecation(blocking_w3): + with pytest.raises(DeprecationWarning): + blocking_w3.legacy_version.node + with pytest.raises(DeprecationWarning): + blocking_w3.legacy_version.ethereum + + +@pytest.mark.asyncio +async def test_async_blocking_version(async_w3, blocking_w3): + assert async_w3.async_version.api == blocking_w3.legacy_version.api + + assert await async_w3.async_version.node == blocking_w3.legacy_version.node + with pytest.raises( + ValueError, + message="RPC Endpoint has not been implemented: vns_protocolVersion" + ): + assert await async_w3.async_version.ethereum == blocking_w3.legacy_version.ethereum diff --git a/tests/core/web3-module/test_api.py b/tests/core/web3-module/test_api.py index 1f404e695e..013d670a4f 100644 --- a/tests/core/web3-module/test_api.py +++ b/tests/core/web3-module/test_api.py @@ -1,2 +1,2 @@ -def test_web3_api(web3): - assert web3.api.startswith("5") +def test_web3_api(web3): + assert web3.api.startswith("5") diff --git a/tests/core/web3-module/test_clientVersion.py b/tests/core/web3-module/test_clientVersion.py index 4f2783adcf..d4f95ccb9f 100644 --- a/tests/core/web3-module/test_clientVersion.py +++ b/tests/core/web3-module/test_clientVersion.py @@ -1,2 +1,2 @@ -def test_web3_clientVersion(web3): - assert web3.clientVersion.startswith("EthereumTester/") +def test_web3_clientVersion(web3): + assert web3.clientVersion.startswith("EthereumTester/") diff --git a/tests/core/web3-module/test_conversions.py b/tests/core/web3-module/test_conversions.py index 6c347df642..fd69882d55 100644 --- a/tests/core/web3-module/test_conversions.py +++ b/tests/core/web3-module/test_conversions.py @@ -1,250 +1,250 @@ -# coding=utf-8 - -import pytest - -from hexbytes import ( - HexBytes, -) - -from web3 import Web3 -from web3.datastructures import ( - AttributeDict, -) - - -@pytest.mark.parametrize( - 'val, expected', - ( - (b'\x01', b'\x01'), - (b'\xff', b'\xff'), - (b'\x00', b'\x00'), - (0x1, b'\x01'), - (0x0001, b'\x01'), - (0xFF, b'\xff'), - (0, b'\x00'), - (256, b'\x01\x00'), - (True, b'\x01'), - (False, b'\x00'), - ), -) -def test_to_bytes_primitive(val, expected): - assert Web3.toBytes(val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('0x', b''), - ('0x0', b'\x00'), - ('0x1', b'\x01'), - ('0', b'\x00'), - ('1', b'\x01'), - ('0xFF', b'\xff'), - ('0x100', b'\x01\x00'), - ('0x0000', b'\x00\x00'), - ('0000', b'\x00\x00'), - ), -) -def test_to_bytes_hexstr(val, expected): - assert Web3.toBytes(hexstr=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('cowmö', b'cowm\xc3\xb6'), - ('', b''), - ), -) -def test_to_bytes_text(val, expected): - assert Web3.toBytes(text=val) == expected - - -def test_to_text_identity(): - assert Web3.toText(text='pass-through') == 'pass-through' - - -@pytest.mark.parametrize( - 'val, expected', - ( - (b'', ''), - ('0x', ''), - (b'cowm\xc3\xb6', 'cowmö'), - ('0x636f776dc3b6', 'cowmö'), - (0x636f776dc3b6, 'cowmö'), - ('0xa', '\n'), - ), -) -def test_to_text(val, expected): - assert Web3.toText(val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('0x', ''), - ('0xa', '\n'), - ('0x636f776dc3b6', 'cowmö'), - ('636f776dc3b6', 'cowmö'), - ), -) -def test_to_text_hexstr(val, expected): - assert Web3.toText(hexstr=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - (b'\x00', 0), - (b'\x01', 1), - (b'\x00\x01', 1), - (b'\x01\x00', 256), - (True, 1), - (False, 0), - ('255', TypeError), - ('-1', TypeError), - ('0x0', TypeError), - ('0x1', TypeError), - ), -) -def test_to_int(val, expected): - if isinstance(expected, type): - with pytest.raises(expected): - Web3.toInt(val) - else: - assert Web3.toInt(val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('0', 0), - ('-1', -1), - ('255', 255), - ('0x0', ValueError), - ('0x1', ValueError), - ('1.1', ValueError), - ('a', ValueError), - ), -) -def test_to_int_text(val, expected): - if isinstance(expected, type): - with pytest.raises(expected): - Web3.toInt(text=val) - else: - assert Web3.toInt(text=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('0x0', 0), - ('0x1', 1), - ('0x01', 1), - ('0x10', 16), - ('0', 0), - ('1', 1), - ('01', 1), - ('10', 16), - ), -) -def test_to_int_hexstr(val, expected): - assert Web3.toInt(hexstr=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - (b'\x00', '0x00'), - (b'\x01', '0x01'), - (b'\x10', '0x10'), - (b'\x01\x00', '0x0100'), - (b'\x00\x0F', '0x000f'), - (b'', '0x'), - (0, '0x0'), - (1, '0x1'), - (16, '0x10'), - (256, '0x100'), - (0x0, '0x0'), - (0x0F, '0xf'), - (False, '0x0'), - (True, '0x1'), - ), -) -def test_to_hex(val, expected): - assert Web3.toHex(val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('', '0x'), - ('cowmö', '0x636f776dc3b6'), - ), -) -def test_to_hex_text(val, expected): - assert Web3.toHex(text=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - ('0x0', '0x0'), - ('0x1', '0x1'), - ('0x0001', '0x0001'), - ('0x10', '0x10'), - ('0xF', '0xf'), - ('F', '0xf'), - ), -) -def test_to_hex_cleanup_only(val, expected): - assert Web3.toHex(hexstr=val) == expected - - -@pytest.mark.parametrize( - 'val, expected', - ( - (AttributeDict({'one': HexBytes('0x1')}), '{"one": "0x01"}'), - (AttributeDict({'two': HexBytes(2)}), '{"two": "0x02"}'), - (AttributeDict({ - 'three': AttributeDict({ - 'four': 4 - }) - }), '{"three": {"four": 4}}'), - ({'three': 3}, '{"three": 3}'), - ), -) -def test_to_json(val, expected): - assert Web3.toJSON(val) == expected - - -@pytest.mark.parametrize( - 'tx, expected', - ( - ( - AttributeDict({ - 'blockHash': HexBytes( - '0x849044202a39ae36888481f90d62c3826bca8269c2716d7a38696b4f45e61d83' - ), - 'blockNumber': 6928809, - 'from': '0xDEA141eF43A2fdF4e795adA55958DAf8ef5FA619', - 'gas': 21000, - 'gasPrice': 19110000000, - 'hash': HexBytes( - '0x1ccddd19830e998d7cf4d921b19fafd5021c9d4c4ba29680b66fb535624940fc' - ), - 'input': '0x', - 'nonce': 5522, - 'r': HexBytes('0x71ef3eed6242230a219d9dc7737cb5a3a16059708ee322e96b8c5774105b9b00'), - 's': HexBytes('0x48a076afe10b4e1ae82ef82b747e9be64e0bbb1cc90e173db8d53e7baba8ac46'), - 'to': '0x3a84E09D30476305Eda6b2DA2a4e199E2Dd1bf79', - 'transactionIndex': 8, - 'v': 27, - 'value': 2907000000000000 - }), - '{"blockHash": "0x849044202a39ae36888481f90d62c3826bca8269c2716d7a38696b4f45e61d83", "blockNumber": 6928809, "from": "0xDEA141eF43A2fdF4e795adA55958DAf8ef5FA619", "gas": 21000, "gasPrice": 19110000000, "hash": "0x1ccddd19830e998d7cf4d921b19fafd5021c9d4c4ba29680b66fb535624940fc", "input": "0x", "nonce": 5522, "r": "0x71ef3eed6242230a219d9dc7737cb5a3a16059708ee322e96b8c5774105b9b00", "s": "0x48a076afe10b4e1ae82ef82b747e9be64e0bbb1cc90e173db8d53e7baba8ac46", "to": "0x3a84E09D30476305Eda6b2DA2a4e199E2Dd1bf79", "transactionIndex": 8, "v": 27, "value": 2907000000000000}' # noqa: E501 - ), - ), -) -def test_to_json_with_transaction(tx, expected): - assert Web3.toJSON(tx) == expected +# coding=utf-8 + +import pytest + +from hexbytes import ( + HexBytes, +) + +from web3 import Web3 +from web3.datastructures import ( + AttributeDict, +) + + +@pytest.mark.parametrize( + 'val, expected', + ( + (b'\x01', b'\x01'), + (b'\xff', b'\xff'), + (b'\x00', b'\x00'), + (0x1, b'\x01'), + (0x0001, b'\x01'), + (0xFF, b'\xff'), + (0, b'\x00'), + (256, b'\x01\x00'), + (True, b'\x01'), + (False, b'\x00'), + ), +) +def test_to_bytes_primitive(val, expected): + assert Web3.toBytes(val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('0x', b''), + ('0x0', b'\x00'), + ('0x1', b'\x01'), + ('0', b'\x00'), + ('1', b'\x01'), + ('0xFF', b'\xff'), + ('0x100', b'\x01\x00'), + ('0x0000', b'\x00\x00'), + ('0000', b'\x00\x00'), + ), +) +def test_to_bytes_hexstr(val, expected): + assert Web3.toBytes(hexstr=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('cowmö', b'cowm\xc3\xb6'), + ('', b''), + ), +) +def test_to_bytes_text(val, expected): + assert Web3.toBytes(text=val) == expected + + +def test_to_text_identity(): + assert Web3.toText(text='pass-through') == 'pass-through' + + +@pytest.mark.parametrize( + 'val, expected', + ( + (b'', ''), + ('0x', ''), + (b'cowm\xc3\xb6', 'cowmö'), + ('0x636f776dc3b6', 'cowmö'), + (0x636f776dc3b6, 'cowmö'), + ('0xa', '\n'), + ), +) +def test_to_text(val, expected): + assert Web3.toText(val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('0x', ''), + ('0xa', '\n'), + ('0x636f776dc3b6', 'cowmö'), + ('636f776dc3b6', 'cowmö'), + ), +) +def test_to_text_hexstr(val, expected): + assert Web3.toText(hexstr=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + (b'\x00', 0), + (b'\x01', 1), + (b'\x00\x01', 1), + (b'\x01\x00', 256), + (True, 1), + (False, 0), + ('255', TypeError), + ('-1', TypeError), + ('0x0', TypeError), + ('0x1', TypeError), + ), +) +def test_to_int(val, expected): + if isinstance(expected, type): + with pytest.raises(expected): + Web3.toInt(val) + else: + assert Web3.toInt(val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('0', 0), + ('-1', -1), + ('255', 255), + ('0x0', ValueError), + ('0x1', ValueError), + ('1.1', ValueError), + ('a', ValueError), + ), +) +def test_to_int_text(val, expected): + if isinstance(expected, type): + with pytest.raises(expected): + Web3.toInt(text=val) + else: + assert Web3.toInt(text=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('0x0', 0), + ('0x1', 1), + ('0x01', 1), + ('0x10', 16), + ('0', 0), + ('1', 1), + ('01', 1), + ('10', 16), + ), +) +def test_to_int_hexstr(val, expected): + assert Web3.toInt(hexstr=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + (b'\x00', '0x00'), + (b'\x01', '0x01'), + (b'\x10', '0x10'), + (b'\x01\x00', '0x0100'), + (b'\x00\x0F', '0x000f'), + (b'', '0x'), + (0, '0x0'), + (1, '0x1'), + (16, '0x10'), + (256, '0x100'), + (0x0, '0x0'), + (0x0F, '0xf'), + (False, '0x0'), + (True, '0x1'), + ), +) +def test_to_hex(val, expected): + assert Web3.toHex(val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('', '0x'), + ('cowmö', '0x636f776dc3b6'), + ), +) +def test_to_hex_text(val, expected): + assert Web3.toHex(text=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + ('0x0', '0x0'), + ('0x1', '0x1'), + ('0x0001', '0x0001'), + ('0x10', '0x10'), + ('0xF', '0xf'), + ('F', '0xf'), + ), +) +def test_to_hex_cleanup_only(val, expected): + assert Web3.toHex(hexstr=val) == expected + + +@pytest.mark.parametrize( + 'val, expected', + ( + (AttributeDict({'one': HexBytes('0x1')}), '{"one": "0x01"}'), + (AttributeDict({'two': HexBytes(2)}), '{"two": "0x02"}'), + (AttributeDict({ + 'three': AttributeDict({ + 'four': 4 + }) + }), '{"three": {"four": 4}}'), + ({'three': 3}, '{"three": 3}'), + ), +) +def test_to_json(val, expected): + assert Web3.toJSON(val) == expected + + +@pytest.mark.parametrize( + 'tx, expected', + ( + ( + AttributeDict({ + 'blockHash': HexBytes( + '0x849044202a39ae36888481f90d62c3826bca8269c2716d7a38696b4f45e61d83' + ), + 'blockNumber': 6928809, + 'from': '0xDEA141eF43A2fdF4e795adA55958DAf8ef5FA619', + 'gas': 21000, + 'gasPrice': 19110000000, + 'hash': HexBytes( + '0x1ccddd19830e998d7cf4d921b19fafd5021c9d4c4ba29680b66fb535624940fc' + ), + 'input': '0x', + 'nonce': 5522, + 'r': HexBytes('0x71ef3eed6242230a219d9dc7737cb5a3a16059708ee322e96b8c5774105b9b00'), + 's': HexBytes('0x48a076afe10b4e1ae82ef82b747e9be64e0bbb1cc90e173db8d53e7baba8ac46'), + 'to': '0x3a84E09D30476305Eda6b2DA2a4e199E2Dd1bf79', + 'transactionIndex': 8, + 'v': 27, + 'value': 2907000000000000 + }), + '{"blockHash": "0x849044202a39ae36888481f90d62c3826bca8269c2716d7a38696b4f45e61d83", "blockNumber": 6928809, "from": "0xDEA141eF43A2fdF4e795adA55958DAf8ef5FA619", "gas": 21000, "gasPrice": 19110000000, "hash": "0x1ccddd19830e998d7cf4d921b19fafd5021c9d4c4ba29680b66fb535624940fc", "input": "0x", "nonce": 5522, "r": "0x71ef3eed6242230a219d9dc7737cb5a3a16059708ee322e96b8c5774105b9b00", "s": "0x48a076afe10b4e1ae82ef82b747e9be64e0bbb1cc90e173db8d53e7baba8ac46", "to": "0x3a84E09D30476305Eda6b2DA2a4e199E2Dd1bf79", "transactionIndex": 8, "v": 27, "value": 2907000000000000}' # noqa: E501 + ), + ), +) +def test_to_json_with_transaction(tx, expected): + assert Web3.toJSON(tx) == expected diff --git a/tests/core/web3-module/test_keccak.py b/tests/core/web3-module/test_keccak.py index 4e2fafdbb5..7dc5e64545 100644 --- a/tests/core/web3-module/test_keccak.py +++ b/tests/core/web3-module/test_keccak.py @@ -1,106 +1,99 @@ -# coding=utf-8 - -import pytest - -from hexbytes import ( - HexBytes, -) - -from web3 import Web3 - - -@pytest.mark.parametrize( - 'message, digest', - [ - ('cowmö', HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8')), - ('', HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470')), - ], -) -def test_keccak_text(message, digest): - assert Web3.keccak(text=message) == digest - - -@pytest.mark.parametrize( - 'hexstr, digest', - [ - ( - '0x636f776dc3b6', - HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') - ), - ( - '636f776dc3b6', - HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') - ), - ( - '0x', - HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') - ), - ( - '', - HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') - ), - ], -) -def test_keccak_hexstr(hexstr, digest): - assert Web3.keccak(hexstr=hexstr) == digest - - -@pytest.mark.parametrize( - 'primitive, exception', - [ - ('0x0', TypeError), - ('', TypeError), - (-1, ValueError), - ], -) -def test_keccak_primitive_invalid(primitive, exception): - with pytest.raises(exception): - Web3.keccak(primitive) - - -@pytest.mark.parametrize( - 'primitive, digest', - [ - ( - b'cowm\xc3\xb6', - HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') - ), - ( - b'', - HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') - ), - ], -) -def test_keccak_primitive(primitive, digest): - assert Web3.keccak(primitive) == digest - - -@pytest.mark.parametrize( - 'kwargs', - [ - {'text': ''}, - {'hexstr': '0x'}, - {'text': '', 'hexstr': '0x'}, - ], -) -def test_keccak_raise_if_primitive_and(kwargs): - # must not set more than one input - with pytest.raises(TypeError): - Web3.keccak('', **kwargs) - - -def test_keccak_raise_if_hexstr_and_text(): - with pytest.raises(TypeError): - Web3.keccak(hexstr='0x', text='') - - -def test_keccak_raise_if_no_args(): - with pytest.raises(TypeError): - Web3.keccak() - - -def test_deprecated_bound_method(): - w3 = Web3() - h = HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') - with pytest.warns(DeprecationWarning, match='sha3 is deprecated in favor of keccak'): - assert w3.sha3(text='cowmö') == h +# coding=utf-8 + +import pytest + +from hexbytes import ( + HexBytes, +) + +from web3 import Web3 + + +@pytest.mark.parametrize( + 'message, digest', + [ + ('cowmö', HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8')), + ('', HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470')), + ], +) +def test_keccak_text(message, digest): + assert Web3.keccak(text=message) == digest + + +@pytest.mark.parametrize( + 'hexstr, digest', + [ + ( + '0x636f776dc3b6', + HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') + ), + ( + '636f776dc3b6', + HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') + ), + ( + '0x', + HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') + ), + ( + '', + HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') + ), + ], +) +def test_keccak_hexstr(hexstr, digest): + assert Web3.keccak(hexstr=hexstr) == digest + + +@pytest.mark.parametrize( + 'primitive, exception', + [ + ('0x0', TypeError), + ('', TypeError), + (-1, ValueError), + ], +) +def test_keccak_primitive_invalid(primitive, exception): + with pytest.raises(exception): + Web3.keccak(primitive) + + +@pytest.mark.parametrize( + 'primitive, digest', + [ + ( + b'cowm\xc3\xb6', + HexBytes('0x0f355f04c0a06eebac1d219b34c598f85a1169badee164be8a30345944885fe8') + ), + ( + b'', + HexBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') + ), + ], +) +def test_keccak_primitive(primitive, digest): + assert Web3.keccak(primitive) == digest + + +@pytest.mark.parametrize( + 'kwargs', + [ + {'text': ''}, + {'hexstr': '0x'}, + {'text': '', 'hexstr': '0x'}, + ], +) +def test_keccak_raise_if_primitive_and(kwargs): + # must not set more than one input + with pytest.raises(TypeError): + Web3.keccak('', **kwargs) + + +def test_keccak_raise_if_hexstr_and_text(): + with pytest.raises(TypeError): + Web3.keccak(hexstr='0x', text='') + + +def test_keccak_raise_if_no_args(): + with pytest.raises(TypeError): + Web3.keccak() diff --git a/tests/core/web3-module/test_providers.py b/tests/core/web3-module/test_providers.py index 92069c71a9..fb839a3f16 100644 --- a/tests/core/web3-module/test_providers.py +++ b/tests/core/web3-module/test_providers.py @@ -1,25 +1,25 @@ -from web3 import Web3 -from web3.providers.auto import ( - AutoProvider, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - - -def test_set_provider(web3): - provider = EthereumTesterProvider() - - web3.provider = provider - - assert web3.provider == provider - - -def test_auto_provider_none(): - # init without provider succeeds, even when no provider available - w3 = Web3() - - # non-node requests succeed - w3.toHex(0) == '0x0' - - type(w3.provider) == AutoProvider +from web3 import Web3 +from web3.providers.auto import ( + AutoProvider, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) + + +def test_set_provider(web3): + provider = EthereumTesterProvider() + + web3.provider = provider + + assert web3.provider == provider + + +def test_auto_provider_none(): + # init without provider succeeds, even when no provider available + w3 = Web3() + + # non-node requests succeed + w3.toHex(0) == '0x0' + + type(w3.provider) == AutoProvider diff --git a/tests/ens/conftest.py b/tests/ens/conftest.py index b68aec3353..178bf380ed 100644 --- a/tests/ens/conftest.py +++ b/tests/ens/conftest.py @@ -1,229 +1,229 @@ - -import json -import pytest - -from eth_tester import ( - EthereumTester, -) - -from ens import ENS -from ens.contract_data import ( - registrar_abi, - registrar_bytecode, - registrar_bytecode_runtime, - resolver_abi, - resolver_bytecode, - resolver_bytecode_runtime, - reverse_registrar_abi, - reverse_registrar_bytecode, - reverse_registrar_bytecode_runtime, - reverse_resolver_abi, - reverse_resolver_bytecode, - reverse_resolver_bytecode_runtime, -) -from web3 import Web3 -from web3.contract import ( - Contract, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - - -def bytes32(val): - if isinstance(val, int): - result = Web3.toBytes(val) - else: - raise TypeError('val %r could not be converted to bytes') - if len(result) < 32: - return result.rjust(32, b'\0') - else: - return result - - -def deploy(w3, Factory, from_address, args=None): - args = args or [] - factory = Factory(w3) - deploy_txn = factory.constructor(*args).transact({'from': from_address}) - deploy_receipt = w3.eth.waitForTransactionReceipt(deploy_txn) - assert deploy_receipt is not None - return factory(address=deploy_receipt['contractAddress']) - - -def DefaultReverseResolver(w3): - return w3.eth.contract( - bytecode=reverse_resolver_bytecode, - bytecode_runtime=reverse_resolver_bytecode_runtime, - abi=reverse_resolver_abi, - ContractFactoryClass=Contract, - ) - - -def ReverseRegistrar(w3): - return w3.eth.contract( - bytecode=reverse_registrar_bytecode, - bytecode_runtime=reverse_registrar_bytecode_runtime, - abi=reverse_registrar_abi, - ContractFactoryClass=Contract, - ) - - -def PublicResolverFactory(w3): - return w3.eth.contract( - bytecode=resolver_bytecode, - bytecode_runtime=resolver_bytecode_runtime, - abi=resolver_abi, - ContractFactoryClass=Contract, - ) - - -def ENSFactory(w3): - return w3.eth.contract( - bytecode="6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610501806100626000396000f300606060405236156100805763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008557806302571be3146100b757806306ab5923146100cd57806314ab9038146100f457806316a25cbd146101175780631896f70a1461014a5780635b0fc9c31461016c575b600080fd5b341561009057600080fd5b61009b60043561018e565b604051600160a060020a03909116815260200160405180910390f35b34156100c257600080fd5b61009b6004356101ac565b34156100d857600080fd5b6100f2600435602435600160a060020a03604435166101c7565b005b34156100ff57600080fd5b6100f260043567ffffffffffffffff60243516610289565b341561012257600080fd5b61012d600435610355565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015557600080fd5b6100f2600435600160a060020a036024351661038c565b341561017757600080fd5b6100f2600435600160a060020a0360243516610432565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f057600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b257600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b557600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045b57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582050975b6c54a16d216b563f4c4960d6ebc5881eb1ec73c2ef1f87920a251159530029", # noqa: E501 - bytecode_runtime="606060405236156100805763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008557806302571be3146100b757806306ab5923146100cd57806314ab9038146100f457806316a25cbd146101175780631896f70a1461014a5780635b0fc9c31461016c575b600080fd5b341561009057600080fd5b61009b60043561018e565b604051600160a060020a03909116815260200160405180910390f35b34156100c257600080fd5b61009b6004356101ac565b34156100d857600080fd5b6100f2600435602435600160a060020a03604435166101c7565b005b34156100ff57600080fd5b6100f260043567ffffffffffffffff60243516610289565b341561012257600080fd5b61012d600435610355565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015557600080fd5b6100f2600435600160a060020a036024351661038c565b341561017757600080fd5b6100f2600435600160a060020a0360243516610432565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f057600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b257600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b557600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045b57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582050975b6c54a16d216b563f4c4960d6ebc5881eb1ec73c2ef1f87920a251159530029", # noqa: E501 - abi=json.loads('[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}]'), # noqa: E501 - ContractFactoryClass=Contract, - ) - - -def ETHRegistrarFactory(w3): - return w3.eth.contract( - bytecode=registrar_bytecode, - bytecode_runtime=registrar_bytecode_runtime, - abi=registrar_abi, - ContractFactoryClass=Contract, - ) - - -# session scope for performance -@pytest.fixture(scope="session") -def ens_setup(): - w3 = Web3(EthereumTesterProvider(EthereumTester())) - - # ** Set up ENS contracts ** - - # remove account that creates ENS, so test transactions don't have write access - accounts = w3.eth.accounts - ens_key = accounts.pop() - - # create ENS contract - eth_labelhash = w3.keccak(text='eth') - eth_namehash = bytes32(0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae) - resolver_namehash = bytes32(0xfdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5) - reverse_tld_namehash = bytes32(0xa097f6721ce401e757d1223a763fef49b8b5f90bb18567ddb86fd205dff71d34) # noqa: E501 - reverser_namehash = bytes32(0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2) - ens_contract = deploy(w3, ENSFactory, ens_key) - - # create public resolver - public_resolver = deploy(w3, PublicResolverFactory, ens_key, args=[ens_contract.address]) - - # set 'resolver.eth' to resolve to public resolver - ens_contract.functions.setSubnodeOwner( - b'\0' * 32, - eth_labelhash, - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setSubnodeOwner( - eth_namehash, - w3.keccak(text='resolver'), - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setResolver( - resolver_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - public_resolver.functions.setAddr( - resolver_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - # create .eth auction registrar - eth_registrar = deploy( - w3, - ETHRegistrarFactory, - ens_key, - args=[ens_contract.address, eth_namehash, 1], - ) - - # set '.eth' to resolve to the registrar - ens_contract.functions.setResolver( - eth_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - public_resolver.functions.setAddr( - eth_namehash, - eth_registrar.address - ).transact({'from': ens_key}) - - # create reverse resolver - reverse_resolver = deploy(w3, DefaultReverseResolver, ens_key, args=[ens_contract.address]) - - # create reverse registrar - reverse_registrar = deploy( - w3, - ReverseRegistrar, - ens_key, - args=[ens_contract.address, reverse_resolver.address] - ) - - # set 'addr.reverse' to resolve to reverse registrar - ens_contract.functions.setSubnodeOwner( - b'\0' * 32, - w3.keccak(text='reverse'), - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setSubnodeOwner( - reverse_tld_namehash, - w3.keccak(text='addr'), - ens_key - ).transact({'from': ens_key}) - - ens_contract.functions.setResolver( - reverser_namehash, - public_resolver.address - ).transact({'from': ens_key}) - - public_resolver.functions.setAddr( - reverser_namehash, - reverse_registrar.address - ).transact({'from': ens_key}) - - # set owner of tester.eth to an account controlled by tests - ens_contract.functions.setSubnodeOwner( - eth_namehash, - w3.keccak(text='tester'), - w3.eth.accounts[2] # note that this does not have to be the default, only in the list - ).transact({'from': ens_key}) - - # make the registrar the owner of the 'eth' name - ens_contract.functions.setSubnodeOwner( - b'\0' * 32, - eth_labelhash, - eth_registrar.address - ).transact({'from': ens_key}) - - # make the reverse registrar the owner of the 'addr.reverse' name - ens_contract.functions.setSubnodeOwner( - reverse_tld_namehash, - w3.keccak(text='addr'), - reverse_registrar.address - ).transact({'from': ens_key}) - - return ENS.fromWeb3(w3, ens_contract.address) - - -@pytest.fixture -def ens(ens_setup, mocker): - mocker.patch('web3.middleware.stalecheck._isfresh', return_value=True) - ens_setup.web3.eth.defaultAccount = ens_setup.web3.eth.coinbase - return ens_setup - - -@pytest.fixture() -def TEST_ADDRESS(address_conversion_func): - return address_conversion_func("0x000000000000000000000000000000000000dEaD") + +import json +import pytest + +from vns_tester import ( + EthereumTester, +) + +from ens import ENS +from ens.contract_data import ( + registrar_abi, + registrar_bytecode, + registrar_bytecode_runtime, + resolver_abi, + resolver_bytecode, + resolver_bytecode_runtime, + reverse_registrar_abi, + reverse_registrar_bytecode, + reverse_registrar_bytecode_runtime, + reverse_resolver_abi, + reverse_resolver_bytecode, + reverse_resolver_bytecode_runtime, +) +from web3 import Web3 +from web3.contract import ( + Contract, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) + + +def bytes32(val): + if isinstance(val, int): + result = Web3.toBytes(val) + else: + raise TypeError('val %r could not be converted to bytes') + if len(result) < 32: + return result.rjust(32, b'\0') + else: + return result + + +def deploy(w3, Factory, from_address, args=None): + args = args or [] + factory = Factory(w3) + deploy_txn = factory.constructor(*args).transact({'from': from_address}) + deploy_receipt = w3.vns.waitForTransactionReceipt(deploy_txn) + assert deploy_receipt is not None + return factory(address=deploy_receipt['contractAddress']) + + +def DefaultReverseResolver(w3): + return w3.vns.contract( + bytecode=reverse_resolver_bytecode, + bytecode_runtime=reverse_resolver_bytecode_runtime, + abi=reverse_resolver_abi, + ContractFactoryClass=Contract, + ) + + +def ReverseRegistrar(w3): + return w3.vns.contract( + bytecode=reverse_registrar_bytecode, + bytecode_runtime=reverse_registrar_bytecode_runtime, + abi=reverse_registrar_abi, + ContractFactoryClass=Contract, + ) + + +def PublicResolverFactory(w3): + return w3.vns.contract( + bytecode=resolver_bytecode, + bytecode_runtime=resolver_bytecode_runtime, + abi=resolver_abi, + ContractFactoryClass=Contract, + ) + + +def ENSFactory(w3): + return w3.vns.contract( + bytecode="6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610501806100626000396000f300606060405236156100805763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008557806302571be3146100b757806306ab5923146100cd57806314ab9038146100f457806316a25cbd146101175780631896f70a1461014a5780635b0fc9c31461016c575b600080fd5b341561009057600080fd5b61009b60043561018e565b604051600160a060020a03909116815260200160405180910390f35b34156100c257600080fd5b61009b6004356101ac565b34156100d857600080fd5b6100f2600435602435600160a060020a03604435166101c7565b005b34156100ff57600080fd5b6100f260043567ffffffffffffffff60243516610289565b341561012257600080fd5b61012d600435610355565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015557600080fd5b6100f2600435600160a060020a036024351661038c565b341561017757600080fd5b6100f2600435600160a060020a0360243516610432565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f057600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b257600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b557600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045b57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582050975b6c54a16d216b563f4c4960d6ebc5881eb1ec73c2ef1f87920a251159530029", # noqa: E501 + bytecode_runtime="606060405236156100805763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008557806302571be3146100b757806306ab5923146100cd57806314ab9038146100f457806316a25cbd146101175780631896f70a1461014a5780635b0fc9c31461016c575b600080fd5b341561009057600080fd5b61009b60043561018e565b604051600160a060020a03909116815260200160405180910390f35b34156100c257600080fd5b61009b6004356101ac565b34156100d857600080fd5b6100f2600435602435600160a060020a03604435166101c7565b005b34156100ff57600080fd5b6100f260043567ffffffffffffffff60243516610289565b341561012257600080fd5b61012d600435610355565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015557600080fd5b6100f2600435600160a060020a036024351661038c565b341561017757600080fd5b6100f2600435600160a060020a0360243516610432565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f057600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b257600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b557600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045b57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582050975b6c54a16d216b563f4c4960d6ebc5881eb1ec73c2ef1f87920a251159530029", # noqa: E501 + abi=json.loads('[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}]'), # noqa: E501 + ContractFactoryClass=Contract, + ) + + +def ETHRegistrarFactory(w3): + return w3.vns.contract( + bytecode=registrar_bytecode, + bytecode_runtime=registrar_bytecode_runtime, + abi=registrar_abi, + ContractFactoryClass=Contract, + ) + + +# session scope for performance +@pytest.fixture(scope="session") +def ens_setup(): + w3 = Web3(EthereumTesterProvider(EthereumTester())) + + # ** Set up ENS contracts ** + + # remove account that creates ENS, so test transactions don't have write access + accounts = w3.vns.accounts + ens_key = accounts.pop() + + # create ENS contract + vns_labelhash = w3.keccak(text='vns') + vns_namehash = bytes32(0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae) + resolver_namehash = bytes32(0xfdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5) + reverse_tld_namehash = bytes32(0xa097f6721ce401e757d1223a763fef49b8b5f90bb18567ddb86fd205dff71d34) # noqa: E501 + reverser_namehash = bytes32(0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2) + ens_contract = deploy(w3, ENSFactory, ens_key) + + # create public resolver + public_resolver = deploy(w3, PublicResolverFactory, ens_key, args=[ens_contract.address]) + + # set 'resolver.vns' to resolve to public resolver + ens_contract.functions.setSubnodeOwner( + b'\0' * 32, + vns_labelhash, + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setSubnodeOwner( + vns_namehash, + w3.keccak(text='resolver'), + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setResolver( + resolver_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + public_resolver.functions.setAddr( + resolver_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + # create .vns auction registrar + vns_registrar = deploy( + w3, + ETHRegistrarFactory, + ens_key, + args=[ens_contract.address, vns_namehash, 1], + ) + + # set '.vns' to resolve to the registrar + ens_contract.functions.setResolver( + vns_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + public_resolver.functions.setAddr( + vns_namehash, + vns_registrar.address + ).transact({'from': ens_key}) + + # create reverse resolver + reverse_resolver = deploy(w3, DefaultReverseResolver, ens_key, args=[ens_contract.address]) + + # create reverse registrar + reverse_registrar = deploy( + w3, + ReverseRegistrar, + ens_key, + args=[ens_contract.address, reverse_resolver.address] + ) + + # set 'addr.reverse' to resolve to reverse registrar + ens_contract.functions.setSubnodeOwner( + b'\0' * 32, + w3.keccak(text='reverse'), + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setSubnodeOwner( + reverse_tld_namehash, + w3.keccak(text='addr'), + ens_key + ).transact({'from': ens_key}) + + ens_contract.functions.setResolver( + reverser_namehash, + public_resolver.address + ).transact({'from': ens_key}) + + public_resolver.functions.setAddr( + reverser_namehash, + reverse_registrar.address + ).transact({'from': ens_key}) + + # set owner of tester.vns to an account controlled by tests + ens_contract.functions.setSubnodeOwner( + vns_namehash, + w3.keccak(text='tester'), + w3.vns.accounts[2] # note that this does not have to be the default, only in the list + ).transact({'from': ens_key}) + + # make the registrar the owner of the 'vns' name + ens_contract.functions.setSubnodeOwner( + b'\0' * 32, + vns_labelhash, + vns_registrar.address + ).transact({'from': ens_key}) + + # make the reverse registrar the owner of the 'addr.reverse' name + ens_contract.functions.setSubnodeOwner( + reverse_tld_namehash, + w3.keccak(text='addr'), + reverse_registrar.address + ).transact({'from': ens_key}) + + return ENS.fromWeb3(w3, ens_contract.address) + + +@pytest.fixture +def ens(ens_setup, mocker): + mocker.patch('web3.middleware.stalecheck._isfresh', return_value=True) + ens_setup.web3.vns.defaultAccount = ens_setup.web3.vns.coinbase + return ens_setup + + +@pytest.fixture() +def TEST_ADDRESS(address_conversion_func): + return address_conversion_func("0x000000000000000000000000000000000000dEaD") diff --git a/tests/ens/test_get_registry.py b/tests/ens/test_get_registry.py index 645fcff90d..cc8b043ac0 100644 --- a/tests/ens/test_get_registry.py +++ b/tests/ens/test_get_registry.py @@ -1,50 +1,50 @@ - -import pytest -from unittest.mock import ( - patch, -) - -from ens import ENS -from web3 import Web3 - - -def test_resolver_empty(ens): - with patch.object(ens.ens.caller, 'resolver', return_value=None): - assert ens.resolver('') is None - - -@pytest.mark.parametrize( - 'address, expected_reverse', - [ - ( - '0x1111111111111111111111111111111111111111', - '1111111111111111111111111111111111111111.addr.reverse', - ), - ( - '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', - 'bb9bc244d798123fde783fcc1c72d3bb8c189413.addr.reverse', - ), - ], -) -def test_reverse_domain(address, expected_reverse, address_conversion_func): - address = address_conversion_func(address) - assert ENS.reverse_domain(address) == expected_reverse - - -@pytest.mark.parametrize( - 'label, expected_hash', - [ - ('eth', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0'), - ('ETH', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0'), - ('a.b', ValueError), - ], -) -def test_labelhash(ens, label, expected_hash): - if isinstance(expected_hash, type): - with pytest.raises(expected_hash): - ens.labelhash(label) - else: - labelhash = ens.labelhash(label) - assert isinstance(labelhash, bytes) - hash_hex = Web3.toHex(labelhash) - assert hash_hex == expected_hash + +import pytest +from unittest.mock import ( + patch, +) + +from ens import ENS +from web3 import Web3 + + +def test_resolver_empty(ens): + with patch.object(ens.ens.caller, 'resolver', return_value=None): + assert ens.resolver('') is None + + +@pytest.mark.parametrize( + 'address, expected_reverse', + [ + ( + '0x1111111111111111111111111111111111111111', + '1111111111111111111111111111111111111111.addr.reverse', + ), + ( + '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413', + 'bb9bc244d798123fde783fcc1c72d3bb8c189413.addr.reverse', + ), + ], +) +def test_reverse_domain(address, expected_reverse, address_conversion_func): + address = address_conversion_func(address) + assert ENS.reverse_domain(address) == expected_reverse + + +@pytest.mark.parametrize( + 'label, expected_hash', + [ + ('vns', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0'), + ('ETH', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0'), + ('a.b', ValueError), + ], +) +def test_labelhash(ens, label, expected_hash): + if isinstance(expected_hash, type): + with pytest.raises(expected_hash): + ens.labelhash(label) + else: + labelhash = ens.labelhash(label) + assert isinstance(labelhash, bytes) + hash_hex = Web3.toHex(labelhash) + assert hash_hex == expected_hash diff --git a/tests/ens/test_nameprep.py b/tests/ens/test_nameprep.py index 4ef7ef2608..034e2f5b21 100644 --- a/tests/ens/test_nameprep.py +++ b/tests/ens/test_nameprep.py @@ -1,36 +1,23 @@ - -import pytest - -from ens import ( - InvalidName, -) - -# test content inspired by https://github.com/jcranmer/idna-uts46/blob/9e2ff191a8887c872ad172f2d72f8d4bf353aef8/test/test-uts46.js # noqa: E501 - - -def test_nameprep_basic_unicode(ens): - assert ens.nameprep("öbb.at") == "öbb.at" - assert ens.nameprep("Öbb.at") == "öbb.at" - assert ens.nameprep("O\u0308bb.at") == "öbb.at" - assert ens.nameprep("faß.de") == "faß.de" - assert ens.nameprep("fass.de") == "fass.de" - assert ens.nameprep("🌈rainbow.eth") == "🌈rainbow.eth" - assert ens.nameprep("🐔🐔.tk") == "🐔🐔.tk" - assert ens.nameprep("√.com") == "√.com" - assert ens.nameprep("ԛәлп.com") == "ԛәлп.com" - assert ens.nameprep("test\u200btest.com") == "testtest.com" - assert ens.nameprep("-test.com") == "-test.com" - assert ens.nameprep("1test.com") == "1test.com" - assert ens.nameprep("test.1com") == "test.1com" - - -@pytest.mark.parametrize( - 'url', [ - ('not=std3'), - ('not_std3.eth'), # underscores not allowed - ] -) -def test_nameprep_std3_rules(ens, url): - with pytest.raises(InvalidName, - match=f'{url} is an invalid name'): - ens.nameprep(url) + +import pytest + +from ens import ( + InvalidName, +) + +# test content inspired by https://github.com/jcranmer/idna-uts46/blob/9e2ff191a8887c872ad172f2d72f8d4bf353aef8/test/test-uts46.js # noqa: E501 + + +def test_nameprep_basic_unicode(ens): + assert ens.nameprep("öbb.at") == "öbb.at" + assert ens.nameprep("Öbb.at") == "öbb.at" + assert ens.nameprep("O\u0308bb.at") == "öbb.at" + assert ens.nameprep("xn--bb-eka.at") == "öbb.at" + assert ens.nameprep("faß.de") == "faß.de" + assert ens.nameprep("fass.de") == "fass.de" + assert ens.nameprep("xn--fa-hia.de") == "faß.de" + + +def test_nameprep_std3_rules(ens): + with pytest.raises(InvalidName): + ens.nameprep("not=std3") diff --git a/tests/ens/test_setup_address.py b/tests/ens/test_setup_address.py index 236c25cbf5..b688fe4259 100644 --- a/tests/ens/test_setup_address.py +++ b/tests/ens/test_setup_address.py @@ -1,165 +1,165 @@ -import pytest -from unittest.mock import ( - patch, -) - -from eth_utils import ( - is_same_address, - to_bytes, -) - -from ens.constants import ( - EMPTY_ADDR_HEX, -) -from ens.main import ( - UnauthorizedError, -) -from web3 import Web3 - - -""" -API at: https://github.com/carver/ens.py/issues/2 -""" - - -@pytest.mark.parametrize( - 'name, full_name, namehash_hex', - [ - ( - 'tester.eth', - 'tester.eth', - '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'TESTER.eth', - 'TESTER.eth', - '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - # handles alternative dot separators - ( - 'tester.eth', - 'tester.eth', - '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'tester。eth', - 'tester。eth', - '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'tester。eth', - 'tester。eth', - '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - # confirm that set-owner works - ( - 'lots.of.subdomains.tester.eth', - 'lots.of.subdomains.tester.eth', - '0x0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692', - ), - ], -) -def test_set_address(ens, name, full_name, namehash_hex, TEST_ADDRESS): - assert ens.address(name) is None - owner = ens.owner('tester.eth') - - ens.setup_address(name, TEST_ADDRESS) - assert is_same_address(ens.address(name), TEST_ADDRESS) - - namehash = Web3.toBytes(hexstr=namehash_hex) - normal_name = ens.nameprep(full_name) - assert is_same_address(ens.address(name), TEST_ADDRESS) - - # check that the correct namehash is set: - assert is_same_address(ens.resolver(normal_name).caller.addr(namehash), TEST_ADDRESS) - - # check that the correct owner is set: - assert ens.owner(name) == owner - - ens.setup_address(name, None) - assert ens.address(name) is None - - -@pytest.mark.parametrize( - 'name, equivalent', - [ - ('TESTER.eth', 'tester.eth'), - ('unicÖde.tester.eth', 'unicöde.tester.eth'), - ], -) -def test_set_address_equivalence(ens, name, equivalent, TEST_ADDRESS): - assert ens.address(name) is None - - ens.setup_address(name, TEST_ADDRESS) - assert is_same_address(ens.address(name), TEST_ADDRESS) - assert is_same_address(ens.address(equivalent), TEST_ADDRESS) - - ens.setup_address(name, None) - assert ens.address(name) is None - - -@pytest.mark.parametrize( - 'set_address', - [ - # since the test uses getTransactionCount, - # using a same address converted to bytes and hex will error with same count, - # use two different addresses of each type (hex, bytes) - "0x000000000000000000000000000000000000dEaD", - to_bytes(hexstr="0x5B2063246F2191f18F2675ceDB8b28102e957458"), - EMPTY_ADDR_HEX, - None, - '', - ], -) -def test_set_address_noop(ens, set_address): - eth = ens.web3.eth - owner = ens.owner('tester.eth') - ens.setup_address('noop.tester.eth', set_address) - starting_transactions = eth.getTransactionCount(owner) - - # do not issue transaction if address is already set - ens.setup_address('noop.tester.eth', set_address) - assert eth.getTransactionCount(owner) == starting_transactions - - -def test_set_address_unauthorized(ens, TEST_ADDRESS): - with pytest.raises(UnauthorizedError): - ens.setup_address('eth', TEST_ADDRESS) - - -def test_setup_address_default_address_to_owner(ens): - assert ens.address('default.tester.eth') is None - owner = ens.owner('tester.eth') - - ens.setup_address('default.tester.eth') - assert ens.address('default.tester.eth') == owner - - -def test_first_owner_upchain_identify(ens): - # _first_owner should auto-select the name owner to send the transaction from - addr = '0x5B2063246F2191f18F2675ceDB8b28102e957458' - - def getowner(name): - if name == "cdefghi.eth": - return addr - else: - return None - with patch.object(ens, 'owner', side_effect=getowner): - assert ens._first_owner('abcdefg.bcdefgh.cdefghi.eth') == \ - (addr, ['abcdefg', 'bcdefgh'], 'cdefghi.eth') - - -def test_set_resolver_leave_default(ens, TEST_ADDRESS): - owner = ens.owner('tester.eth') - ens.setup_address('leave-default-resolver.tester.eth', TEST_ADDRESS) - eth = ens.web3.eth - num_transactions = eth.getTransactionCount(owner) - - ens.setup_address( - 'leave-default-resolver.tester.eth', - '0x5B2063246F2191f18F2675ceDB8b28102e957458' - ) - - # should skip setting the owner and setting the default resolver, and only - # set the name in the default resolver to point to the new address - assert eth.getTransactionCount(owner) == num_transactions + 1 +import pytest +from unittest.mock import ( + patch, +) + +from vns_utils import ( + is_same_address, + to_bytes, +) + +from ens.constants import ( + EMPTY_ADDR_HEX, +) +from ens.main import ( + UnauthorizedError, +) +from web3 import Web3 + + +""" +API at: https://github.com/carver/ens.py/issues/2 +""" + + +@pytest.mark.parametrize( + 'name, full_name, namehash_hex', + [ + ( + 'tester.vns', + 'tester.vns', + '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'TESTER.vns', + 'TESTER.vns', + '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + # handles alternative dot separators + ( + 'tester. vns', + 'tester. vns', + '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'tester。 vns', + 'tester。 vns', + '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'tester。vns', + 'tester。vns', + '0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + # confirm that set-owner works + ( + 'lots.of.subdomains.tester.vns', + 'lots.of.subdomains.tester.vns', + '0x0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692', + ), + ], +) +def test_set_address(ens, name, full_name, namehash_hex, TEST_ADDRESS): + assert ens.address(name) is None + owner = ens.owner('tester.vns') + + ens.setup_address(name, TEST_ADDRESS) + assert is_same_address(ens.address(name), TEST_ADDRESS) + + namehash = Web3.toBytes(hexstr=namehash_hex) + normal_name = ens.nameprep(full_name) + assert is_same_address(ens.address(name), TEST_ADDRESS) + + # check that the correct namehash is set: + assert is_same_address(ens.resolver(normal_name).caller.addr(namehash), TEST_ADDRESS) + + # check that the correct owner is set: + assert ens.owner(name) == owner + + ens.setup_address(name, None) + assert ens.address(name) is None + + +@pytest.mark.parametrize( + 'name, equivalent', + [ + ('TESTER.vns', 'tester.vns'), + ('unicÖde.tester.vns', 'unicöde.tester.vns'), + ], +) +def test_set_address_equivalence(ens, name, equivalent, TEST_ADDRESS): + assert ens.address(name) is None + + ens.setup_address(name, TEST_ADDRESS) + assert is_same_address(ens.address(name), TEST_ADDRESS) + assert is_same_address(ens.address(equivalent), TEST_ADDRESS) + + ens.setup_address(name, None) + assert ens.address(name) is None + + +@pytest.mark.parametrize( + 'set_address', + [ + # since the test uses getTransactionCount, + # using a same address converted to bytes and hex will error with same count, + # use two different addresses of each type (hex, bytes) + "0x000000000000000000000000000000000000dEaD", + to_bytes(hexstr="0x5B2063246F2191f18F2675ceDB8b28102e957458"), + EMPTY_ADDR_HEX, + None, + '', + ], +) +def test_set_address_noop(ens, set_address): + vns = ens.web3.vns + owner = ens.owner('tester.vns') + ens.setup_address('noop.tester.vns', set_address) + starting_transactions = vns.getTransactionCount(owner) + + # do not issue transaction if address is already set + ens.setup_address('noop.tester.vns', set_address) + assert vns.getTransactionCount(owner) == starting_transactions + + +def test_set_address_unauthorized(ens, TEST_ADDRESS): + with pytest.raises(UnauthorizedError): + ens.setup_address('vns', TEST_ADDRESS) + + +def test_setup_address_default_address_to_owner(ens): + assert ens.address('default.tester.vns') is None + owner = ens.owner('tester.vns') + + ens.setup_address('default.tester.vns') + assert ens.address('default.tester.vns') == owner + + +def test_first_owner_upchain_identify(ens): + # _first_owner should auto-select the name owner to send the transaction from + addr = '0x5B2063246F2191f18F2675ceDB8b28102e957458' + + def getowner(name): + if name == "cdefghi.vns": + return addr + else: + return None + with patch.object(ens, 'owner', side_effect=getowner): + assert ens._first_owner('abcdefg.bcdefgh.cdefghi.vns') == \ + (addr, ['abcdefg', 'bcdefgh'], 'cdefghi.vns') + + +def test_set_resolver_leave_default(ens, TEST_ADDRESS): + owner = ens.owner('tester.vns') + ens.setup_address('leave-default-resolver.tester.vns', TEST_ADDRESS) + vns = ens.web3.vns + num_transactions = vns.getTransactionCount(owner) + + ens.setup_address( + 'leave-default-resolver.tester.vns', + '0x5B2063246F2191f18F2675ceDB8b28102e957458' + ) + + # should skip setting the owner and setting the default resolver, and only + # set the name in the default resolver to point to the new address + assert vns.getTransactionCount(owner) == num_transactions + 1 diff --git a/tests/ens/test_setup_name.py b/tests/ens/test_setup_name.py index 2d8fbc76fa..9b269c3c1a 100644 --- a/tests/ens/test_setup_name.py +++ b/tests/ens/test_setup_name.py @@ -1,133 +1,133 @@ -import pytest - -from ens.main import ( - AddressMismatch, - UnauthorizedError, - UnownedName, -) -from web3 import Web3 - - -""" -API at: https://github.com/carver/ens.py/issues/2 -""" - - -@pytest.fixture() -def TEST_ADDRESS(address_conversion_func): - return address_conversion_func("0x000000000000000000000000000000000000dEaD") - - -@pytest.mark.parametrize( - 'name, normalized_name, namehash_hex', - [ - ( - 'tester.eth', - 'tester.eth', - '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'TESTER.eth', - 'tester.eth', - '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'tester.eth', - 'tester.eth', - '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'tester。eth', - 'tester.eth', - '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - ( - 'tester。eth', - 'tester.eth', - '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', - ), - # confirm that set-owner works - ( - 'lots.of.subdomains.tester.eth', - 'lots.of.subdomains.tester.eth', - '0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692', - ), - ], -) -def test_setup_name(ens, name, normalized_name, namehash_hex): - address = ens.web3.eth.accounts[3] - assert not ens.name(address) - owner = ens.owner('tester.eth') - - ens.setup_name(name, address) - assert ens.name(address) == normalized_name - - # check that the correct namehash is set: - node = Web3.toBytes(hexstr=namehash_hex) - assert ens.resolver(normalized_name).caller.addr(node) == address - - # check that the correct owner is set: - assert ens.owner(name) == owner - - ens.setup_name(None, address) - ens.setup_address(name, None) - assert not ens.name(address) - assert not ens.address(name) - - -def test_cannot_set_name_on_mismatch_address(ens, TEST_ADDRESS): - ens.setup_address('mismatch-reverse.tester.eth', TEST_ADDRESS) - with pytest.raises(AddressMismatch): - ens.setup_name('mismatch-reverse.tester.eth', '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413') - - -def test_setup_name_default_address(ens): - name = 'reverse-defaults-to-forward.tester.eth' - owner = ens.owner('tester.eth') - new_resolution = ens.web3.eth.accounts[-1] - ens.setup_address(name, new_resolution) - assert not ens.name(new_resolution) - assert ens.owner(name) == owner - assert ens.address(name) == new_resolution - ens.setup_name(name) - assert ens.name(new_resolution) == name - ens.setup_name(None, new_resolution) - - -def test_setup_name_default_to_owner(ens): - name = 'reverse-defaults-to-owner.tester.eth' - new_owner = ens.web3.eth.accounts[-1] - ens.setup_owner(name, new_owner) - assert not ens.name(new_owner) - assert ens.owner(name) == new_owner - assert not ens.address(name) - ens.setup_name(name) - assert ens.name(new_owner) == name - - -def test_setup_name_unowned_exception(ens): - with pytest.raises(UnownedName): - ens.setup_name('unowned-name.tester.eth') - - -def test_setup_name_unauthorized(ens, TEST_ADDRESS): - with pytest.raises(UnauthorizedError): - ens.setup_name('root-owned-tld', TEST_ADDRESS) - - -def test_setup_reverse_dict_unmodified(ens): - # setup - owner = ens.owner('tester.eth') - eth = ens.web3.eth - start_count = eth.getTransactionCount(owner) - - address = ens.web3.eth.accounts[3] - transact = {} - ens.setup_name('tester.eth', address, transact=transact) - - # even though a transaction was issued, the dict argument was not modified - assert eth.getTransactionCount(owner) > start_count - assert transact == {} - - # teardown - ens.setup_name(None, address, transact=transact) +import pytest + +from ens.main import ( + AddressMismatch, + UnauthorizedError, + UnownedName, +) +from web3 import Web3 + + +""" +API at: https://github.com/carver/ens.py/issues/2 +""" + + +@pytest.fixture() +def TEST_ADDRESS(address_conversion_func): + return address_conversion_func("0x000000000000000000000000000000000000dEaD") + + +@pytest.mark.parametrize( + 'name, normalized_name, namehash_hex', + [ + ( + 'tester.vns', + 'tester.vns', + '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'TESTER.vns', + 'tester.vns', + '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'tester.vns', + 'tester.vns', + '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'tester。vns', + 'tester.vns', + '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + ( + 'tester。vns', + 'tester.vns', + '2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe', + ), + # confirm that set-owner works + ( + 'lots.of.subdomains.tester.vns', + 'lots.of.subdomains.tester.vns', + '0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692', + ), + ], +) +def test_setup_name(ens, name, normalized_name, namehash_hex): + address = ens.web3.vns.accounts[3] + assert not ens.name(address) + owner = ens.owner('tester.vns') + + ens.setup_name(name, address) + assert ens.name(address) == normalized_name + + # check that the correct namehash is set: + node = Web3.toBytes(hexstr=namehash_hex) + assert ens.resolver(normalized_name).caller.addr(node) == address + + # check that the correct owner is set: + assert ens.owner(name) == owner + + ens.setup_name(None, address) + ens.setup_address(name, None) + assert not ens.name(address) + assert not ens.address(name) + + +def test_cannot_set_name_on_mismatch_address(ens, TEST_ADDRESS): + ens.setup_address('mismatch-reverse.tester.vns', TEST_ADDRESS) + with pytest.raises(AddressMismatch): + ens.setup_name('mismatch-reverse.tester.vns', '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413') + + +def test_setup_name_default_address(ens): + name = 'reverse-defaults-to-forward.tester.vns' + owner = ens.owner('tester.vns') + new_resolution = ens.web3.vns.accounts[-1] + ens.setup_address(name, new_resolution) + assert not ens.name(new_resolution) + assert ens.owner(name) == owner + assert ens.address(name) == new_resolution + ens.setup_name(name) + assert ens.name(new_resolution) == name + ens.setup_name(None, new_resolution) + + +def test_setup_name_default_to_owner(ens): + name = 'reverse-defaults-to-owner.tester.vns' + new_owner = ens.web3.vns.accounts[-1] + ens.setup_owner(name, new_owner) + assert not ens.name(new_owner) + assert ens.owner(name) == new_owner + assert not ens.address(name) + ens.setup_name(name) + assert ens.name(new_owner) == name + + +def test_setup_name_unowned_exception(ens): + with pytest.raises(UnownedName): + ens.setup_name('unowned-name.tester.vns') + + +def test_setup_name_unauthorized(ens, TEST_ADDRESS): + with pytest.raises(UnauthorizedError): + ens.setup_name('root-owned-tld', TEST_ADDRESS) + + +def test_setup_reverse_dict_unmodified(ens): + # setup + owner = ens.owner('tester.vns') + vns = ens.web3.vns + start_count = vns.getTransactionCount(owner) + + address = ens.web3.vns.accounts[3] + transact = {} + ens.setup_name('tester.vns', address, transact=transact) + + # even though a transaction was issued, the dict argument was not modified + assert vns.getTransactionCount(owner) > start_count + assert transact == {} + + # teardown + ens.setup_name(None, address, transact=transact) diff --git a/tests/ens/test_utils.py b/tests/ens/test_utils.py index 052670a533..0093fc43c2 100644 --- a/tests/ens/test_utils.py +++ b/tests/ens/test_utils.py @@ -1,10 +1,10 @@ - -from ens.utils import ( - init_web3, -) - - -def test_init_adds_middlewares(): - w3 = init_web3() - middlewares = map(str, w3.manager.middleware_onion) - assert 'stalecheck_middleware' in next(middlewares) + +from ens.utils import ( + init_web3, +) + + +def test_init_adds_middlewares(): + w3 = init_web3() + middlewares = map(str, w3.manager.middleware_onion) + assert 'stalecheck_middleware' in next(middlewares) diff --git a/tests/ethpm/_utils/test_backend_utils.py b/tests/ethpm/_utils/test_backend_utils.py deleted file mode 100644 index c5909660b8..0000000000 --- a/tests/ethpm/_utils/test_backend_utils.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -import pytest - -from ethpm._utils.backend import ( - get_resolvable_backends_for_uri, - get_translatable_backends_for_uri, -) -from ethpm.backends.ipfs import ( - InfuraIPFSBackend, - LocalIPFSBackend, -) -from ethpm.backends.registry import ( - RegistryURIBackend, -) -from ethpm.exceptions import ( - CannotHandleURI, -) -from ethpm.uri import ( - resolve_uri_contents, -) - - -@pytest.mark.parametrize( - "uri,backends", - ( - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - (InfuraIPFSBackend, LocalIPFSBackend), - ), - ("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", ()), - ), -) -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_get_resolvable_backends_for_supported_uris(dummy_ipfs_backend, uri, backends): - good_backends = get_resolvable_backends_for_uri(uri) - assert good_backends == backends - - -@pytest.mark.parametrize( - "uri,backends", - ( - ("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0", (RegistryURIBackend,)), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", ()), - ), -) -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_get_translatable_backends_for_supported_uris( - dummy_ipfs_backend, uri, backends -): - good_backends = get_translatable_backends_for_uri(uri) - assert good_backends == backends - - -@pytest.mark.parametrize( - "uri", - ( - "xxx", - # filesystem - "file:///path_to_erc20.json", - # invalid registry URI scheme - "erc1128://packages.zeppelinos.eth:1/erc20/v1.0.0", - # swarm - "bzz://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - "bzz-immutable:://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - "bzz-raw://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - # internet - "http://github.com/ethpm/ethpm-spec/examples/owned/1.0.0.json#content_hash", - "https://github.com/ethpm/ethpm-spec/examples/owned/1.0.0.json#content_hash", - ), -) -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_resolve_uri_contents_raises_exception_for_unsupported_schemes(uri): - with pytest.raises(CannotHandleURI): - resolve_uri_contents(uri) diff --git a/tests/ethpm/_utils/test_chain_utils.py b/tests/ethpm/_utils/test_chain_utils.py deleted file mode 100644 index 97f8197ab7..0000000000 --- a/tests/ethpm/_utils/test_chain_utils.py +++ /dev/null @@ -1,62 +0,0 @@ -import pytest - -from ethpm._utils.chains import ( - is_BIP122_block_uri, - is_supported_chain_id, - parse_BIP122_uri, -) - -HASH_A = "0x1234567890123456789012345678901234567890123456789012345678901234" -HASH_A_NO_PREFIX = "1234567890123456789012345678901234567890123456789012345678901234" -HASH_B = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" -HASH_B_NO_PREFIX = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" -BLOCK_URI = f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B_NO_PREFIX}" -TRANSACTION_URI = f"blockchain://{HASH_A_NO_PREFIX}/transaction/{HASH_B_NO_PREFIX}" - - -@pytest.mark.parametrize( - "value,expected", - ( - (BLOCK_URI, True), - (TRANSACTION_URI, False), - (f"blockchain://{HASH_A}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B}", False), - (f"blockchain://{HASH_A}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX[:-1]}/block/{HASH_B_NO_PREFIX}", False), - (f"blockchain://{HASH_A_NO_PREFIX}/block/{HASH_B_NO_PREFIX[:-1]}", False), - ), -) -def test_is_BIP122_block_uri(value, expected): - actual = is_BIP122_block_uri(value) - assert actual is expected - - -@pytest.mark.parametrize( - "value, expected_resource_type", - ((TRANSACTION_URI, "transaction"), (BLOCK_URI, "block")), -) -def test_parse_BIP122_uri(value, expected_resource_type): - chain_id, resource_type, resource_identifier = parse_BIP122_uri(value) - assert chain_id == HASH_A - assert resource_type == expected_resource_type - assert resource_identifier == HASH_B - - -@pytest.mark.parametrize( - "chain_id,expected", - ( - (1, True), - (3, True), - (4, True), - (5, True), - (42, True), - (2, False), - ("1", False), - ({}, False), - (None, False), - (False, False), - ) -) -def test_is_supported_chain_id(chain_id, expected): - actual = is_supported_chain_id(chain_id) - assert actual is expected diff --git a/tests/ethpm/_utils/test_contract_utils.py b/tests/ethpm/_utils/test_contract_utils.py deleted file mode 100644 index 7c31c2a715..0000000000 --- a/tests/ethpm/_utils/test_contract_utils.py +++ /dev/null @@ -1,93 +0,0 @@ -import pytest - -from ethpm._utils.contract import ( - generate_contract_factory_kwargs, -) -from ethpm.exceptions import ( - EthPMValidationError, - InsufficientAssetsError, -) -from ethpm.validation.misc import ( - validate_w3_instance, -) -from ethpm.validation.package import ( - validate_contract_name, - validate_minimal_contract_factory_data, -) - - -@pytest.mark.parametrize( - "contract_data", - ( - {"abi": "", "deployment_bytecode": ""}, - { - "abi": "", - "deployment_bytecode": {"bytecode": ""}, - "runtime_bytecode": {"bytecode": ""}, - }, - ), -) -def test_validate_minimal_contract_factory_data_validates(contract_data): - assert validate_minimal_contract_factory_data(contract_data) is None - - -@pytest.mark.parametrize( - "contract_data", - ( - {"abi": ""}, - {"deployment_bytecode": {"bytecode": ""}}, - {"runtime_bytecode": {"bytecode": ""}, "other": ""}, - ), -) -def test_validate_minimal_contract_factory_data_invalidates(contract_data): - with pytest.raises(InsufficientAssetsError): - validate_minimal_contract_factory_data(contract_data) - - -@pytest.mark.parametrize("name", ("A1", "A-1", "A_1", "X" * 256)) -def test_validate_contract_name_validates(name): - assert validate_contract_name(name) is None - - -@pytest.mark.parametrize("name", ("", "-abc", "A=bc", "X" * 257)) -def test_validate_contract_name_invalidates(name): - with pytest.raises(EthPMValidationError): - assert validate_contract_name(name) - - -@pytest.mark.parametrize( - "contract_data,expected_kwargs", - ( - ({"abi": ""}, ["abi"]), - ({"deployment_bytecode": {"bytecode": ""}}, ["bytecode"]), - ( - {"abi": "", "runtime_bytecode": {"bytecode": ""}}, - ["abi", "bytecode_runtime"], - ), - ( - { - "abi": "", - "deployment_bytecode": { - "bytecode": "", - "link_references": [ - {"offsets": [402, 639], "length": 20, "name": "SafeSendLib"} - ], - }, - }, - ["abi", "bytecode", "unlinked_references"], - ), - ), -) -def test_generate_contract_factory_kwargs(contract_data, expected_kwargs): - contract_factory = generate_contract_factory_kwargs(contract_data) - assert set(contract_factory.keys()) == set(expected_kwargs) - - -def test_validate_w3_instance_validates(w3): - assert validate_w3_instance(w3) is None - - -@pytest.mark.parametrize("w3", ("NotWeb3", b"NotWeb3", 1234)) -def test_validate_w3_instance_invalidates(w3): - with pytest.raises(ValueError): - assert validate_w3_instance(w3) diff --git a/tests/ethpm/_utils/test_ipfs_utils.py b/tests/ethpm/_utils/test_ipfs_utils.py deleted file mode 100644 index 53bd0c90a7..0000000000 --- a/tests/ethpm/_utils/test_ipfs_utils.py +++ /dev/null @@ -1,122 +0,0 @@ -from pathlib import ( - Path, -) -import pytest - -from ethpm._utils.ipfs import ( - extract_ipfs_path_from_uri, - generate_file_hash, - is_ipfs_uri, -) - - -@pytest.mark.parametrize( - "value,expected", - ( - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ( - "ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - "QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - ), - ), -) -def test_extract_ipfs_path_from_uri(value, expected): - actual = extract_ipfs_path_from_uri(value) - assert actual == expected - - -@pytest.mark.parametrize( - "value,expected", - ( - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", True), - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", True), - # malformed - ("ipfs//QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - ("ipfs/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - ("ipfsQmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", False), - # HTTP - ("http://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", False), - ("https://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", False), - # No hash - ("ipfs://", False), - ), -) -def test_is_ipfs_uri(value, expected): - actual = is_ipfs_uri(value) - assert actual is expected - - -@pytest.mark.parametrize( - "file_name,file_contents,expected", - ( - ("test-1.txt", "piper\n", "QmUdxEGxvp71kqYLkA91mtNg9QRRSPBtA3UV6VuYhoP7DB"), - ( - "test-2.txt", - "pipermerriam\n", - "QmXqrQR7EMePe9LCRUVrfkxYg5EHRNpcA1PZnN4AnbM9DW", - ), - ( - "test-3.txt", - "this is a test file for ipfs hash generation\n", - "QmYknNUKXWSaxfCWVgHd8uVCYHhzPerVCLvCCBedWtqbnv", - ), - ), -) -def test_generate_file_hash(tmpdir, file_name, file_contents, expected): - p = tmpdir.mkdir("sub").join(file_name) - p.write(file_contents) - ipfs_multihash = generate_file_hash(Path(p).read_bytes()) - assert ipfs_multihash == expected diff --git a/tests/ethpm/_utils/test_registry_utils.py b/tests/ethpm/_utils/test_registry_utils.py deleted file mode 100644 index da4b002052..0000000000 --- a/tests/ethpm/_utils/test_registry_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -import pytest - -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.uri import ( - validate_registry_uri, -) - - -@pytest.mark.parametrize( - "uri", - ( - # no package id in uri - ("erc1319://zeppelinos.eth:1"), - ("erc1319://zeppelinos.eth:1/"), - ("erc1319://packages.zeppelinos.eth:1"), - ("erc1319://packages.zeppelinos.eth:1/"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/"), - # with package id in uri - ("erc1319://zeppelinos.eth:1/erc20/"), - ("erc1319://zeppelinos.eth:1/erc20//"), - ("erc1319://zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319://zeppelinos.eth:1/erc20?version=1.0.0/"), - ("erc1319://packages.zeppelinos.eth:1/erc20?version="), - ("erc1319://packages.zeppelinos.eth:1/erc20?version=/"), - ("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/erc20?version=1.0.0/"), - ("erc1319://packages.ethereum.eth:1/greeter?version=%3E%3D1.0.2%2C%3C2"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20?version=1.0.0"), - ("erc1319://0xd3CdA913deB6f67967B99D67aCDFa1712C293601:1/erc20?version=1.0.0/"), - ), -) -def test_is_registry_uri_validates(uri): - assert validate_registry_uri(uri) is None - - -@pytest.mark.parametrize( - "uri", - ( - # invalid authority - ("erc1319://zeppelinos.eth"), - ("erc1319://zeppelinos.eth/"), - ("erc1319://zeppelinos.eth/erc20?version=1.0.0"), - ("erc1319://zeppelinos.eth:333/erc20?version=1.0.0"), - ("erc1319://packages.zeppelinos.com:1/erc20?version=1.0.0"), - ("erc1319://package.manager.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319://packageszeppelinoseth:1/erc20?version=1.0.0"), - ("erc1319://0xd3cda913deb6f67967b99d67acdfa1712c293601:1/erc20?version=1.0.0"), - # invalid package name - ("erc1319://packages.zeppelinos.eth:1/?version=1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/?version=1.0.0/"), - ("erc1319://packages.zeppelinos.eth:1/!rc20?version=1.0.0"), - ("erc1319://packages.zeppelinos.eth:1/!rc20?version=1.0.0/"), - # invalid version param - ("erc1319://zeppelinos.eth:1/erc20?versions=1.0.0"), - ("erc1319://zeppelinos.eth:1/erc20?version1.0.0"), - # malformed - ("erc1319packageszeppelinosetherc20version1.0.0"), - ("erc1319:packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319:packages.zeppelinos.eth:1/erc20?version=1.0.0/"), - ("erc1319:/packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319:/packages.zeppelinos.eth:1/erc20?version=1.0.0/"), - ("erc1319/packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319//packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("erc1319packages.zeppelinos.eth:1/erc20?version=1.0.0"), - # wrong scheme - ("http://packages.zeppelinos.eth:1/erc20?version=1.0.0"), - ("ercXX://packages.zeppelinos.eth:1/erc20?version=1.0.0"), - # no path - ("erc1319://"), - # weird values - (b"erc1319://zeppelinos.eth:1/erc20?version=1.0.0"), - ("1234"), - ({}), - ), -) -def test_is_registry_uri_raises_exception_for_invalid_uris(uri): - with pytest.raises(EthPMValidationError): - validate_registry_uri(uri) diff --git a/tests/ethpm/backends/test_http_backends.py b/tests/ethpm/backends/test_http_backends.py deleted file mode 100644 index eb4df32a82..0000000000 --- a/tests/ethpm/backends/test_http_backends.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import pytest - -from requests.exceptions import ( - HTTPError, -) - -from ethpm import ( - Package, -) -from ethpm.backends.http import ( - GithubOverHTTPSBackend, -) -from ethpm.constants import ( - GITHUB_API_AUTHORITY, -) - - -@pytest.mark.parametrize( - "uri", - ( - "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480", # noqa: E501 - ), -) -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_github_over_https_backend_fetch_uri_contents(uri, owned_contract, w3): - # these tests may occassionally fail CI as a result of their network requests - backend = GithubOverHTTPSBackend() - assert backend.base_uri == GITHUB_API_AUTHORITY - # integration with Package.from_uri - owned_package = Package.from_uri(uri, w3) - assert owned_package.name == "owned" - - -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_github_over_https_backend_raises_error_with_invalid_content_hash(w3): - invalid_uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03123" # noqa: E501 - with pytest.raises(HTTPError): - Package.from_uri(invalid_uri, w3) diff --git a/tests/ethpm/backends/test_ipfs_backends.py b/tests/ethpm/backends/test_ipfs_backends.py deleted file mode 100644 index ba6589e07b..0000000000 --- a/tests/ethpm/backends/test_ipfs_backends.py +++ /dev/null @@ -1,126 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest - -from eth_utils import ( - to_text, -) - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.backends.ipfs import ( - DummyIPFSBackend, - InfuraIPFSBackend, - LocalIPFSBackend, - get_ipfs_backend, - get_ipfs_backend_class, -) -from ethpm.constants import ( - INFURA_GATEWAY_MULTIADDR, -) - -OWNED_MANIFEST_PATH = ASSETS_DIR / "owned" / "1.0.0.json" - - -@pytest.fixture -def fake_client(): - class FakeClient: - def cat(self, ipfs_hash): - return ipfs_hash - - def add(self, file_or_dir_path, recursive): - if Path(file_or_dir_path) == OWNED_MANIFEST_PATH: - return { - "Hash": "QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW", - "Name": "1.0.0.json", - "Size": "454", - } - - return FakeClient() - - -@pytest.mark.parametrize( - "base_uri,backend", ((INFURA_GATEWAY_MULTIADDR, InfuraIPFSBackend()),) -) -def test_ipfs_and_infura_gateway_backends_fetch_uri_contents(base_uri, backend): - uri = "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - assert backend.base_uri == base_uri - contents = backend.fetch_uri_contents(uri) - assert contents.startswith(b"pragma solidity") - - -def test_local_ipfs_backend(): - uri = "ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV" - backend = LocalIPFSBackend() - backend.pin_assets(OWNED_MANIFEST_PATH.parent / "contracts" / "Owned.sol") - contents = backend.fetch_uri_contents(uri) - assert contents.startswith(b"pragma solidity") - - -@pytest.mark.parametrize( - "uri,expected", - ( - ("ipfs:QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs:/QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("ipfs://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u", True), - ("http://raw.githubusercontent.com/ethpm/py-ethpm#0x123", False), - ("https://raw.githubusercontent.com/ethpm/py-ethpm#0x123", False), - ( - "bzz://679bde3ccb6fb911db96a0ea1586c04899c6c0cc6d3426e9ee361137b270a463", - False, - ), - ("ercxxx://packages.eth/owned?version=1.0.0", False), - ), -) -def test_base_ipfs_gateway_backend_correctly_handles_uri_schemes(uri, expected): - backend = InfuraIPFSBackend() - assert backend.can_resolve_uri(uri) is expected - - -def test_dummy_ipfs_backend(): - pkg = DummyIPFSBackend().fetch_uri_contents( - "ipfs://QmeD2s7KaBUoGYTP1eutHBmBkMMMoycdfiyGMx2DKrWXyV" - ) - mnfst = to_text(pkg) - manifest = json.loads(mnfst) - assert manifest["package_name"] == "safe-math-lib" - - -def test_get_ipfs_backend_class_with_default_backend(): - backend = get_ipfs_backend_class() - assert issubclass(backend, InfuraIPFSBackend) - - -def test_get_ipfs_backend_with_default_backend(): - backend = get_ipfs_backend() - assert isinstance(backend, InfuraIPFSBackend) - - -def test_get_uri_backend_with_env_variable(dummy_ipfs_backend, monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.LocalIPFSBackend" - ) - backend = get_ipfs_backend() - assert isinstance(backend, LocalIPFSBackend) - - -def test_pin_assets_to_dummy_backend(dummy_ipfs_backend): - # Test pinning a file - backend = get_ipfs_backend() - hashes = backend.pin_assets(OWNED_MANIFEST_PATH) - asset_data = hashes[0] - assert asset_data["Name"] == "1.0.0.json" - assert asset_data["Hash"] == "QmYogrz6sKuyMW8NHUdw1kEy9Tn15R3GaF9T14S4zwb4bR" - assert asset_data["Size"] == "433" - # Test pinning a directory - dir_data = backend.pin_assets(ASSETS_DIR / "standard-token" / "contracts") - dir_names = [result["Name"] for result in dir_data] - dir_hashes = [result["Hash"] for result in dir_data] - dir_sizes = [result["Size"] for result in dir_data] - assert len(dir_data) == 2 - assert "StandardToken.sol" in dir_names - assert "QmRJHLmPVct2rbBpdGjP3xkXbF7romQigtmcs8TRfV1yC7" in dir_hashes - assert "2865" in dir_sizes diff --git a/tests/ethpm/backends/test_registry_backend.py b/tests/ethpm/backends/test_registry_backend.py deleted file mode 100644 index f8f240f976..0000000000 --- a/tests/ethpm/backends/test_registry_backend.py +++ /dev/null @@ -1,46 +0,0 @@ -import os -import pytest - -from ethpm.backends.registry import ( - RegistryURIBackend, - parse_registry_uri, -) -from ethpm.exceptions import ( - CannotHandleURI, -) - - -@pytest.fixture -def backend(): - return RegistryURIBackend() - - -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_registry_uri_backend(backend): - valid_uri = "erc1319://0x1457890158DECD360e6d4d979edBcDD59c35feeB:1/owned?version=1.0.0" - expected_uri = 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW' - assert backend.can_translate_uri(valid_uri) is True - assert backend.can_resolve_uri(valid_uri) is False - assert backend.fetch_uri_contents(valid_uri) == expected_uri - - -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_registry_uri_supports_ens_domains(backend): - valid_uri = "erc1319://defi.snakecharmers.eth:1/compound?version=1.0.0" - parsed = parse_registry_uri(valid_uri) - expected_uri = 'ipfs://QmYvsyuxjj9mKmCvn3jrdfnaHYwFsyHXUu7kETrN4dBhE6' - assert backend.can_translate_uri(valid_uri) is True - assert backend.can_resolve_uri(valid_uri) is False - assert backend.fetch_uri_contents(valid_uri) == expected_uri - assert parsed.address == "0xA635F17288187daE5b424D343E21FF44a79ce922" - assert parsed.ens == "defi.snakecharmers.eth" - assert parsed.chain_id == '1' - assert parsed.name == "compound" - assert parsed.version == "1.0.0" - - -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_registry_uri_backend_raises_exception_for_non_mainnet_chains(backend): - ropsten_uri = "erc1319://snakecharmers.eth:3/owned?version=1.0.0" - with pytest.raises(CannotHandleURI, match="Currently only mainnet"): - backend.fetch_uri_contents(ropsten_uri) diff --git a/tests/ethpm/conftest.py b/tests/ethpm/conftest.py deleted file mode 100644 index cd5323f539..0000000000 --- a/tests/ethpm/conftest.py +++ /dev/null @@ -1,257 +0,0 @@ -import copy -import json -import pytest - -from eth_utils.toolz import ( - assoc_in, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm._utils.chains import ( - create_block_uri, -) -from ethpm.tools import ( - get_manifest as get_manifest_tool, -) -from ethpm.uri import ( - create_latest_block_uri, -) -from web3 import Web3 -from web3.tools import ( # noqa: E741 - linker as l, -) - -PACKAGE_NAMES = [ - ("escrow", "1.0.3.json"), - ("owned", "1.0.0.json"), - ("piper-coin", "1.0.0.json"), - ("safe-math-lib", "1.0.0.json"), - ("standard-token", "1.0.0.json"), - ("transferable", "1.0.0.json"), - ("wallet-with-send", "1.0.0.json"), - ("wallet", "1.0.0.json"), -] - - -def pytest_addoption(parser): - parser.addoption("--integration", action="store_true", default=False) - - -@pytest.fixture -def package_names(): - return PACKAGE_NAMES - - -@pytest.fixture(params=PACKAGE_NAMES) -def all_strict_manifests(request): - return (ASSETS_DIR / request.param[0] / "1.0.0.json").read_text().rstrip("\n") - - -@pytest.fixture(params=PACKAGE_NAMES) -def all_pretty_manifests(request): - return ( - (ASSETS_DIR / request.param[0] / "1.0.0-pretty.json") - .read_text() - .rstrip("\n") - ) - - -def fetch_manifest(name, version): - return get_manifest_tool(name, version) - - -MANIFESTS = {name: fetch_manifest(name, version) for name, version in PACKAGE_NAMES} - - -@pytest.fixture -def w3(): - w3 = Web3(Web3.EthereumTesterProvider()) - w3.eth.defaultAccount = w3.eth.accounts[0] - return w3 - - -@pytest.fixture -def dummy_ipfs_backend(monkeypatch): - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.DummyIPFSBackend" - ) - - -@pytest.fixture -def get_manifest(): - def _get_manifest(name): - return copy.deepcopy(MANIFESTS[name]) - - return _get_manifest - - -@pytest.fixture(params=PACKAGE_NAMES) -def all_manifests(request, get_manifest): - return get_manifest(request.param[0]) - - -# safe-math-lib currently used as default manifest for testing -# should be extended to all_manifest_types asap -@pytest.fixture -def safe_math_manifest(get_manifest): - return get_manifest("safe-math-lib") - - -@pytest.fixture -def piper_coin_manifest(): - return json.loads( - (ASSETS_DIR / "piper-coin" / "1.0.0-pretty.json").read_text() - ) - - -ESCROW_DEPLOYMENT_BYTECODE = { - "bytecode": "0x60806040526040516020806102a8833981016040525160008054600160a060020a0319908116331790915560018054600160a060020a0390931692909116919091179055610256806100526000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166366d003ac811461005b57806367e404ce1461008c57806369d89575146100a1575b600080fd5b34801561006757600080fd5b506100706100b8565b60408051600160a060020a039092168252519081900360200190f35b34801561009857600080fd5b506100706100c7565b3480156100ad57600080fd5b506100b66100d6565b005b600154600160a060020a031681565b600054600160a060020a031681565b600054600160a060020a031633141561019857600154604080517f9341231c000000000000000000000000000000000000000000000000000000008152600160a060020a039092166004830152303160248301525173000000000000000000000000000000000000000091639341231c916044808301926020929190829003018186803b15801561016657600080fd5b505af415801561017a573d6000803e3d6000fd5b505050506040513d602081101561019057600080fd5b506102289050565b600154600160a060020a031633141561005657600054604080517f9341231c000000000000000000000000000000000000000000000000000000008152600160a060020a039092166004830152303160248301525173000000000000000000000000000000000000000091639341231c916044808301926020929190829003018186803b15801561016657600080fd5b5600a165627a7a723058201766d3411ff91d047cf900369478c682a497a6e560cd1b2fe4d9f2d6fe13b4210029", # noqa: E501 - "link_references": [{"offsets": [383, 577], "length": 20, "name": "SafeSendLib"}], -} - - -@pytest.fixture -def escrow_manifest(get_manifest): - escrow_manifest = get_manifest("escrow") - escrow_manifest["contract_types"]["Escrow"][ - "deployment_bytecode" - ] = ESCROW_DEPLOYMENT_BYTECODE - return escrow_manifest - - -@pytest.fixture -def get_factory(get_manifest, escrow_manifest, w3): - def _get_factory(package, factory_name): - manifest = get_manifest(package) - # Special case to fetch escrow manifest with added deployment bytecode - if package == "escrow": - manifest = escrow_manifest - Pkg = Package(manifest, w3) - factory = Pkg.get_contract_factory(factory_name) - return factory - - return _get_factory - - -@pytest.fixture -def owned_contract(): - return (ASSETS_DIR / "owned" / "contracts" / "Owned.sol").read_text() - - -@pytest.fixture -def invalid_manifest(safe_math_manifest): - safe_math_manifest["manifest_version"] = 1 - return safe_math_manifest - - -@pytest.fixture -def manifest_with_no_deployments(safe_math_manifest): - manifest = copy.deepcopy(safe_math_manifest) - manifest.pop("deployments") - return manifest - - -@pytest.fixture -def manifest_with_empty_deployments(tmpdir, safe_math_manifest): - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"] = {} - return manifest - - -@pytest.fixture -def escrow_package(deployer, w3): - escrow_manifest = ASSETS_DIR / "escrow" / "1.0.3.json" - escrow_deployer = deployer(escrow_manifest) - escrow_strategy = l.linker( - l.deploy("SafeSendLib"), - l.link("Escrow", "SafeSendLib"), - l.deploy("Escrow", w3.eth.accounts[0]), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - return escrow_deployer.deploy("Escrow") - - -@pytest.fixture -def safe_math_lib_package(deployer, w3): - safe_math_lib_manifest = ASSETS_DIR / "safe-math-lib" / "1.0.1.json" - safe_math_deployer = deployer(safe_math_lib_manifest) - return safe_math_deployer.deploy("SafeMathLib") - - -@pytest.fixture -def safe_math_lib_package_with_alias(deployer, w3): - safe_math_lib_manifest = ASSETS_DIR / "safe-math-lib" / "1.0.1.json" - safe_math_deployer = deployer(safe_math_lib_manifest) - pkg = safe_math_deployer.deploy("SafeMathLib") - blockchain_uri = list(pkg.manifest["deployments"].keys())[0] - deployment_data = pkg.manifest["deployments"][blockchain_uri]["SafeMathLib"] - aliased_manifest = assoc_in( - pkg.manifest, - ["deployments", blockchain_uri], - {"safe-math-lib-alias": deployment_data}, - ) - return Package(aliased_manifest, w3) - - -@pytest.fixture -def manifest_with_no_matching_deployments(w3, tmpdir, safe_math_manifest): - w3.testing.mine(5) - incorrect_genesis_hash = b"\x00" * 31 + b"\x01" - block = w3.eth.getBlock("earliest") - block_uri = create_block_uri(w3.toHex(incorrect_genesis_hash), w3.toHex(block.hash)) - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][block_uri] = { - "SafeMathLib": { - "contract_type": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } - } - return manifest - - -@pytest.fixture -def manifest_with_multiple_matches(w3, tmpdir, safe_math_manifest): - w3.testing.mine(5) - block_uri = create_latest_block_uri(w3, from_blocks_ago=0) - w3.testing.mine(1) - second_block_uri = create_latest_block_uri(w3, from_blocks_ago=0) - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][block_uri] = { - "SafeMathLib": { - "contract_type": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } - } - manifest["deployments"][second_block_uri] = { - "SafeMathLib": { - "contract_type": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } - } - return manifest - - -@pytest.fixture -def manifest_with_conflicting_deployments(tmpdir, safe_math_manifest): - # two different blockchain uri's representing the same chain - manifest = copy.deepcopy(safe_math_manifest) - manifest["deployments"][ - "blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/1e96de11320c83cca02e8b9caf3e489497e8e432befe5379f2f08599f8aecede" # noqa: E501 - ] = { - "WrongNameLib": { - "contract_type": "WrongNameLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } - } - return manifest diff --git a/tests/ethpm/integration/test_escrow_manifest.py b/tests/ethpm/integration/test_escrow_manifest.py deleted file mode 100644 index de10fb18e3..0000000000 --- a/tests/ethpm/integration/test_escrow_manifest.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import pytest - -from eth_utils import ( - to_canonical_address, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.exceptions import ( - BytecodeLinkingError, -) -import web3 - - -def test_deployed_escrow_and_safe_send(escrow_manifest, w3): - # Deploy a SafeSendLib - safe_send_manifest = json.loads((ASSETS_DIR / "escrow" / "1.0.3.json").read_text()) - safe_send_contract_type = safe_send_manifest["contract_types"]["SafeSendLib"] - SafeSend = w3.eth.contract( - abi=safe_send_contract_type["abi"], - bytecode=safe_send_contract_type["deployment_bytecode"]["bytecode"], - ) - tx_hash = SafeSend.constructor().transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - safe_send_address = to_canonical_address(tx_receipt["contractAddress"]) - - EscrowPackage = Package(escrow_manifest, w3) - EscrowFactory = EscrowPackage.get_contract_factory("Escrow") - LinkedEscrowFactory = EscrowFactory.link_bytecode( - {"SafeSendLib": safe_send_address} - ) - - # Deploy an Escrow Contract - escrow_tx_hash = LinkedEscrowFactory.constructor( - "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" - ).transact() - escrow_tx_receipt = w3.eth.waitForTransactionReceipt(escrow_tx_hash) - escrow_address = escrow_tx_receipt.contractAddress - - # Cannot deploy with an unlinked factory - with pytest.raises(BytecodeLinkingError): - escrow_tx_hash = EscrowFactory.constructor( - "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" - ).transact() - - # Cannot instantiate a contract instance from an unlinked factory - with pytest.raises(BytecodeLinkingError): - EscrowFactory(escrow_address) - contract_instance = LinkedEscrowFactory(escrow_address) - assert EscrowFactory.needs_bytecode_linking is True - assert LinkedEscrowFactory.needs_bytecode_linking is False - assert isinstance(contract_instance, web3.contract.Contract) - assert safe_send_address in LinkedEscrowFactory.bytecode - assert safe_send_address in LinkedEscrowFactory.bytecode_runtime - assert safe_send_address not in EscrowFactory.bytecode - assert safe_send_address not in EscrowFactory.bytecode_runtime diff --git a/tests/ethpm/integration/test_ipfs_integration.py b/tests/ethpm/integration/test_ipfs_integration.py deleted file mode 100644 index 519aa4440d..0000000000 --- a/tests/ethpm/integration/test_ipfs_integration.py +++ /dev/null @@ -1,64 +0,0 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.backends.ipfs import ( - InfuraIPFSBackend, - LocalIPFSBackend, - get_ipfs_backend, -) -from ethpm.tools import ( - builder as b, -) - -OWNED_MANIFEST_PATH = ASSETS_DIR / "owned" / "1.0.0.json" - - -def test_local_ipfs_backend_integration_round_trip(monkeypatch, request): - """ - To run this integration test requires an running IPFS node. - If you want to run these tests, first start your IPFS node, and - then run pytest with the arg `--integration`. - """ - if not request.config.getoption("--integration"): - pytest.skip("Not asked to run integration tests") - - monkeypatch.setenv( - "ETHPM_IPFS_BACKEND_CLASS", "ethpm.backends.ipfs.LocalIPFSBackend" - ) - backend = get_ipfs_backend() - [asset_data] = backend.pin_assets(OWNED_MANIFEST_PATH) - assert asset_data["Name"] == "1.0.0.json" - assert asset_data["Hash"] == "QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW" - local_manifest = to_bytes(text=OWNED_MANIFEST_PATH.read_text()) - ipfs_manifest = backend.fetch_uri_contents(asset_data["Hash"]) - assert ipfs_manifest == local_manifest - - -@pytest.fixture(params=[LocalIPFSBackend, InfuraIPFSBackend]) -def backend(request): - return request.param() - - -def test_builder_pins_manifest_to_provided_ipfs_backend(backend, request): - if not request.config.getoption("--integration"): - pytest.skip("Not asked to run integration tests") - - minified_manifest_hash = "QmVwwpt2BAkmWQt4eNnswhWd6bYgLbnUQDMHdVMHotwiqz" - (manifest,) = b.build( - {}, - b.package_name("package"), - b.manifest_version("2"), - b.version("1.0.0"), - b.pin_to_ipfs(backend=backend), - ) - assert manifest["Hash"] == minified_manifest_hash - pinned_manifest = backend.fetch_uri_contents(manifest["Hash"]) - assert ( - pinned_manifest == b'{"manifest_version":"2","package_name":"package","version":"1.0.0"}' - ) diff --git a/tests/ethpm/test_contract.py b/tests/ethpm/test_contract.py deleted file mode 100644 index 23bc59fc39..0000000000 --- a/tests/ethpm/test_contract.py +++ /dev/null @@ -1,181 +0,0 @@ -import pytest - -from eth_utils import ( - to_canonical_address, -) - -from ethpm import ( - Package, -) -from ethpm.contract import ( - LinkableContract, - apply_all_link_refs, -) -from ethpm.exceptions import ( - BytecodeLinkingError, -) -from web3.contract import ( - Contract, -) - - -@pytest.mark.parametrize( - "package,factory,attr_dict", - ( - ( - "escrow", - "Escrow", - { - "SafeSendLib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" - }, - ), - ( - "wallet", - "Wallet", - { - "SafeMathLib": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B" - }, - ), - ), -) -def test_linkable_contract_class_handles_link_refs( - package, factory, attr_dict, get_factory, w3 -): - factory = get_factory(package, factory) - assert factory.needs_bytecode_linking is True - linked_factory = factory.link_bytecode(attr_dict) - assert issubclass(LinkableContract, Contract) - assert issubclass(factory, LinkableContract) - assert issubclass(linked_factory, LinkableContract) - assert factory.needs_bytecode_linking is True - assert linked_factory.needs_bytecode_linking is False - # Can't link a factory that's already linked - with pytest.raises(BytecodeLinkingError): - linked_factory.link_bytecode(attr_dict) - offset = factory.unlinked_references[0]["offsets"][0] - link_address = to_canonical_address(list(attr_dict.values())[0]) - # Ignore lint error b/c black conflict - assert factory.bytecode[offset : offset + 20] == b"\00" * 20 # noqa: E203 - assert linked_factory.bytecode[offset : offset + 20] == link_address # noqa: E203 - - -def test_linkable_contract_class_handles_missing_link_refs(get_manifest, w3): - safe_math_manifest = get_manifest("safe-math-lib") - SafeMathLib = Package(safe_math_manifest, w3) - safe_math_lib = SafeMathLib.get_contract_factory("SafeMathLib") - assert safe_math_lib.needs_bytecode_linking is False - with pytest.raises(BytecodeLinkingError): - safe_math_lib.link_bytecode( - {"SafeMathLib": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B"} - ) - - -SAFE_SEND_ADDRESS = "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0" -SAFE_MATH_ADDRESS = "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B" -SAFE_SEND_CANON = to_canonical_address(SAFE_SEND_ADDRESS) -SAFE_MATH_CANON = to_canonical_address(SAFE_MATH_ADDRESS) - - -@pytest.mark.parametrize( - "bytecode,link_refs,attr_dict,expected", - ( - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1]}], - {"SafeSendLib": SAFE_SEND_CANON}, - b"\00" + SAFE_SEND_CANON + bytearray(39), - ), - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1, 31]}], - {"SafeSendLib": SAFE_SEND_CANON}, - b"\00" + SAFE_SEND_CANON + bytearray(10) + SAFE_SEND_CANON + bytearray(9), - ), - ( - bytearray(80), - [ - {"length": 20, "name": "SafeSendLib", "offsets": [1, 50]}, - {"length": 20, "name": "SafeMathLib", "offsets": [25]}, - ], - {"SafeSendLib": SAFE_SEND_CANON, "SafeMathLib": SAFE_MATH_CANON}, - b"\00" + SAFE_SEND_CANON + bytearray(4) + SAFE_MATH_CANON + - bytearray(5) + SAFE_SEND_CANON + bytearray(10), - ), - ), -) -def test_apply_all_link_refs(bytecode, link_refs, attr_dict, expected): - actual = apply_all_link_refs(bytecode, link_refs, attr_dict) - assert actual == expected - - -@pytest.mark.parametrize( - "bytecode,link_refs,attr_dict", - ( - # Non-empty bytecode - ( - b"\01" * 60, - [{"length": 20, "name": "SafeSendLib", "offsets": [1]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal offset - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [61]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal offsets - ( - bytearray(60), - [{"length": 20, "name": "SafeSendLib", "offsets": [1, 3]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Illegal length - ( - bytearray(60), - [{"length": 61, "name": "SafeSendLib", "offsets": [0]}], - {"SafeSendLib": SAFE_SEND_CANON}, - ), - # Conflicting link refs - ( - bytearray(60), - [ - {"length": 20, "name": "SafeSendLib", "offsets": [1]}, - {"length": 20, "name": "SafeMathLib", "offsets": [15]}, - ], - {"SafeSendLib": SAFE_SEND_CANON, "SafeMathLib": SAFE_MATH_CANON}, - ), - ), -) -def test_apply_all_link_refs_with_incorrect_args(bytecode, link_refs, attr_dict): - with pytest.raises(BytecodeLinkingError): - apply_all_link_refs(bytecode, link_refs, attr_dict) - - -@pytest.mark.parametrize( - "attr_dict", - ( - {}, - # invalid address - {"SafeSendLib": "abc"}, - {"SafeSendLib": 123}, - {"SafeSendLib": b"abc"}, - # Non-matching refs - {"safe-send-lib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0"}, - { - "SafeSendLib": "0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0", - "Wallet": "0xa66A05D6AB5c1c955F4D2c3FCC166AE6300b452B", - }, - ), -) -def test_contract_factory_invalidates_incorrect_attr_dicts(get_factory, attr_dict): - safe_send = get_factory("escrow", "SafeSendLib") - assert safe_send.needs_bytecode_linking is False - with pytest.raises(BytecodeLinkingError): - safe_send.link_bytecode(attr_dict) - - -def test_unlinked_factory_cannot_be_deployed(get_factory): - escrow = get_factory("escrow", "Escrow") - assert escrow.needs_bytecode_linking - with pytest.raises(BytecodeLinkingError): - escrow.constructor("0x4F5B11c860b37b68DE6D14Fb7e7b5f18A9A1bdC0").transact() diff --git a/tests/ethpm/test_dependencies.py b/tests/ethpm/test_dependencies.py deleted file mode 100644 index b09d18bea1..0000000000 --- a/tests/ethpm/test_dependencies.py +++ /dev/null @@ -1,69 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.dependencies import ( - Dependencies, -) -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.package import ( - validate_build_dependency, -) - - -@pytest.fixture -def dependencies(dummy_ipfs_backend, piper_coin_manifest, w3): - deps = piper_coin_manifest["build_dependencies"] - dep_pkgs = {dep: Package.from_uri(uri, w3) for dep, uri in deps.items()} - return Dependencies(dep_pkgs) - - -@pytest.fixture -def safe_math_lib_package(safe_math_manifest, w3): - return Package(safe_math_manifest, w3) - - -def test_dependencies_implements_getitem(dependencies, safe_math_lib_package): - assert dependencies["standard-token"].name == "standard-token" - - -def test_dependencies_items(dependencies): - result = dependencies.items() - for key, value in result: - assert key == value.name - assert isinstance(value, Package) - - -def test_dependencies_values(dependencies): - result = dependencies.values() - for value in result: - assert isinstance(value, Package) - - -def test_get_dependency_package(dependencies): - result = dependencies.get_dependency_package("standard-token") - assert isinstance(result, Package) - assert result.name == "standard-token" - - -def test_validate_build_dependencies(dummy_ipfs_backend): - result = validate_build_dependency( - "standard-token", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg" - ) - assert result is None - - -@pytest.mark.parametrize( - "name,uri", - ( - ("standard-token", "invalid_ipfs_uri"), - ("_invalid_pkg_name", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg"), - ("_invalid_pkg_name", "ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg"), - ), -) -def test_get_build_dependencies_invalidates(name, uri): - with pytest.raises(EthPMValidationError): - validate_build_dependency(name, uri) diff --git a/tests/ethpm/test_deployments.py b/tests/ethpm/test_deployments.py deleted file mode 100644 index 8a173e3596..0000000000 --- a/tests/ethpm/test_deployments.py +++ /dev/null @@ -1,218 +0,0 @@ -import pytest - -from eth_utils import ( - to_bytes, -) - -from ethpm._utils.deployments import ( - get_linked_deployments, - normalize_linked_references, - validate_linked_references, -) -from ethpm.contract import ( - LinkableContract, -) -from ethpm.deployments import ( - Deployments, -) -from ethpm.exceptions import ( - BytecodeLinkingError, - EthPMValidationError, -) -from web3.eth import ( - Contract, -) - -DEPLOYMENT_DATA = { - "SafeMathLib": { - "contract_type": "SafeMathLib", - "address": "0x8d2c532d7d211816a2807a411f947b211569b68c", - "transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b", - "block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c", - } -} - - -@pytest.fixture -def contract_factory(safe_math_lib_package): - return safe_math_lib_package.get_contract_type("SafeMathLib") - - -VALID_CONTRACT_TYPES = {"SafeMathLib": contract_factory} -INVALID_CONTRACT_TYPES = {"INVALID": contract_factory} - - -@pytest.fixture -def deployment(w3): - return Deployments(DEPLOYMENT_DATA, VALID_CONTRACT_TYPES, w3) - - -@pytest.fixture -def invalid_deployment(w3): - return Deployments(DEPLOYMENT_DATA, INVALID_CONTRACT_TYPES, w3) - - -def test_deployment_implements_getitem(deployment): - assert deployment["SafeMathLib"] == DEPLOYMENT_DATA["SafeMathLib"] - - -@pytest.mark.parametrize("name", ("", "-abc", "A=bc", "X" * 257)) -def test_deployment_getitem_with_invalid_contract_name_raises_exception( - name, deployment -): - with pytest.raises(EthPMValidationError): - assert deployment[name] - - -def test_deployment_getitem_without_deployment_reference_raises_exception(deployment): - with pytest.raises(KeyError): - deployment["DoesNotExist"] - - -def test_deployment_implements_get_items(deployment): - expected_items = DEPLOYMENT_DATA.items() - assert deployment.items() == expected_items - - -def test_deployment_implements_get_values(deployment): - expected_values = list(DEPLOYMENT_DATA.values()) - assert deployment.values() == expected_values - - -def test_deployment_implements_key_lookup(deployment): - key = "SafeMathLib" in deployment - assert key is True - - -def test_deployment_implements_key_lookup_with_nonexistent_key_raises_exception( - deployment -): - key = "invalid" in deployment - assert key is False - - -@pytest.mark.parametrize("invalid_name", ("", "-abc", "A=bc", "X" * 257)) -def test_get_instance_with_invalid_name_raises_exception(deployment, invalid_name): - with pytest.raises(EthPMValidationError): - deployment.get_instance(invalid_name) - - -def test_get_instance_without_reference_in_deployments_raises_exception(deployment): - with pytest.raises(KeyError): - deployment.get_instance("InvalidContract") - - -def test_deployments_get_instance(safe_math_lib_package): - deps = safe_math_lib_package.deployments - safe_math_instance = deps.get_instance("SafeMathLib") - assert isinstance(safe_math_instance, Contract) - assert safe_math_instance.bytecode == to_bytes( - hexstr=safe_math_lib_package.manifest["contract_types"]["SafeMathLib"][ - "deployment_bytecode" - ]["bytecode"] - ) - - -def test_deployments_get_instance_with_contract_alias(safe_math_lib_package_with_alias): - deps = safe_math_lib_package_with_alias.deployments - safe_math_instance = deps.get_instance("safe-math-lib-alias") - assert isinstance(safe_math_instance, Contract) - assert safe_math_instance.bytecode == to_bytes( - hexstr=safe_math_lib_package_with_alias.manifest["contract_types"][ - "SafeMathLib" - ]["deployment_bytecode"]["bytecode"] - ) - - -def test_deployments_get_instance_with_link_dependency(escrow_package): - deployments = escrow_package.deployments - escrow_deployment = deployments.get_instance("Escrow") - assert isinstance(escrow_deployment, LinkableContract) - assert not escrow_deployment.needs_bytecode_linking - - -def test_get_linked_deployments(escrow_package): - escrow_manifest = escrow_package.manifest - all_deployments = list(escrow_manifest["deployments"].values())[0] - actual_linked_deployments = get_linked_deployments(all_deployments) - assert actual_linked_deployments == {"Escrow": all_deployments["Escrow"]} - # integration via package.deployments - deployments = escrow_package.deployments - assert len(deployments.contract_instances) == 2 - - -@pytest.mark.parametrize( - "deployments", - ( - ( - { - "Escrow": { - "contract_type": "Escrow", - "address": "0x8c1968deB27251A3f1F4508df32dA4dfD1b7b57f", - "transaction": "0xc60e32c63abf34579390ef65d83cc5eb52225de38c3eeca2e5afa961d71c16d0", # noqa: E501 - "block": "0x4d1a618802bb87752d95db453dddeea622820424a2f836bedf8769a67ee276b8", - "runtime_bytecode": { - "link_dependencies": [ - {"offsets": [200], "type": "reference", "value": "filler"}, - { - "offsets": [301, 495], - "type": "reference", - "value": "Escrow", - }, - ] - }, - } - }, - ) - ), -) -def test_get_linked_deployments_raises_exception_with_self_reference(deployments): - with pytest.raises(BytecodeLinkingError): - get_linked_deployments(deployments) - - -@pytest.mark.parametrize( - "link_data,expected", - ( - ( - [ - {"offsets": [1], "type": "reference", "value": "123"}, - {"offsets": [2, 3], "type": "literal", "value": "abc"}, - ], - ((1, "reference", "123"), (2, "literal", "abc"), (3, "literal", "abc")), - ), - ( - [{"offsets": [1, 2, 3], "type": "literal", "value": "123"}], - ((1, "literal", "123"), (2, "literal", "123"), (3, "literal", "123")), - ), - ), -) -def test_normalize_linked_references(link_data, expected): - link_deps = normalize_linked_references(link_data) - assert link_deps == expected - - -@pytest.mark.parametrize( - "link_deps,bytecode", - ( - (((1, b"abc"),), b"xabc"), - (((1, b"a"), (5, b"xx"), (15, b"1")), b"0a000xx000000001"), - ), -) -def test_validate_linked_references(link_deps, bytecode): - result = validate_linked_references(link_deps, bytecode) - assert result is None - - -@pytest.mark.parametrize( - "link_deps,bytecode", - ( - (((0, b"abc"),), b"xabc"), - (((2, b"abc"),), b"xabc"), - (((8, b"abc"),), b"xabc"), - (((1, b"a"), (5, b"xxx"), (15, b"1")), b"0a000xx000000001"), - ), -) -def test_validate_linked_references_invalidates(link_deps, bytecode): - with pytest.raises(EthPMValidationError): - validate_linked_references(link_deps, bytecode) diff --git a/tests/ethpm/test_get_build_dependencies.py b/tests/ethpm/test_get_build_dependencies.py deleted file mode 100644 index bb5902a7b1..0000000000 --- a/tests/ethpm/test_get_build_dependencies.py +++ /dev/null @@ -1,48 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.dependencies import ( - Dependencies, -) -from ethpm.exceptions import ( - EthPMValidationError, - FailureToFetchIPFSAssetsError, -) - - -@pytest.fixture -def piper_coin_pkg(piper_coin_manifest, w3): - return Package(piper_coin_manifest, w3) - - -def test_get_build_dependencies(dummy_ipfs_backend, piper_coin_pkg, w3): - build_deps = piper_coin_pkg.build_dependencies - assert isinstance(build_deps, Dependencies) - - -def test_get_build_dependencies_with_invalid_uris( - dummy_ipfs_backend, piper_coin_pkg, w3 -): - piper_coin_pkg.manifest["build_dependencies"]["standard-token"] = "invalid_ipfs_uri" - with pytest.raises(FailureToFetchIPFSAssetsError): - piper_coin_pkg.build_dependencies - - -def test_get_build_dependencies_without_dependencies_raises_exception( - piper_coin_manifest, w3 -): - piper_coin_manifest.pop("build_dependencies", None) - pkg = Package(piper_coin_manifest, w3) - with pytest.raises(EthPMValidationError): - pkg.build_dependencies - - -def test_get_build_dependencies_with_empty_dependencies_raises_exception( - dummy_ipfs_backend, piper_coin_manifest, w3 -): - piper_coin_manifest["build_dependencies"] = {} - pkg = Package(piper_coin_manifest, w3) - with pytest.raises(EthPMValidationError): - pkg.build_dependencies diff --git a/tests/ethpm/test_get_deployments.py b/tests/ethpm/test_get_deployments.py deleted file mode 100644 index ca0d9fbf83..0000000000 --- a/tests/ethpm/test_get_deployments.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest - -from ethpm import ( - Package, -) -from ethpm.deployments import ( - Deployments, -) -from ethpm.exceptions import ( - EthPMValidationError, -) - - -def test_get_deployments_with_no_deployments(w3, manifest_with_empty_deployments): - package = Package(manifest_with_empty_deployments, w3) - assert package.deployments == {} - - -def test_get_deployments_with_no_deployments_raises_exception( - w3, manifest_with_no_deployments -): - package = Package(manifest_with_no_deployments, w3) - assert package.deployments == {} - - -def test_get_deployments_with_no_match_raises_exception( - manifest_with_no_matching_deployments, w3 -): - package = Package(manifest_with_no_matching_deployments, w3) - with pytest.raises(EthPMValidationError): - package.deployments - - -def test_get_deployments_with_multiple_matches_raises_exception( - manifest_with_multiple_matches, w3 -): - package = Package(manifest_with_multiple_matches, w3) - with pytest.raises(EthPMValidationError): - package.deployments - - -def test_get_deployments_with_a_match_returns_deployments(w3, safe_math_lib_package): - deployment = safe_math_lib_package.deployments - assert isinstance(deployment, Deployments) - assert "SafeMathLib" in deployment diff --git a/tests/ethpm/test_package.py b/tests/ethpm/test_package.py deleted file mode 100644 index e119c7d0f3..0000000000 --- a/tests/ethpm/test_package.py +++ /dev/null @@ -1,89 +0,0 @@ -import pytest - -from eth_utils import ( - is_same_address, -) - -from ethpm.exceptions import ( - EthPMValidationError, - InsufficientAssetsError, -) -from ethpm.package import ( - Package, -) -from web3 import Web3 - - -@pytest.fixture() -def safe_math_package(get_manifest, w3): - safe_math_manifest = get_manifest("safe-math-lib") - return Package(safe_math_manifest, w3) - - -@pytest.fixture() -def deployed_safe_math(safe_math_package, w3): - SafeMath = safe_math_package.get_contract_factory("SafeMathLib") - tx_hash = SafeMath.constructor().transact() - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - return safe_math_package, tx_receipt.contractAddress - - -def test_package_object_instantiates_with_a_web3_object(all_manifests, w3): - package = Package(all_manifests, w3) - assert package.w3 is w3 - - -def test_update_web3(deployed_safe_math, w3): - new_w3 = Web3(Web3.EthereumTesterProvider()) - original_package, _ = deployed_safe_math - assert original_package.w3 is w3 - new_package = original_package.update_w3(new_w3) - assert new_package.w3 is new_w3 - assert original_package is not new_package - assert original_package.manifest == new_package.manifest - with pytest.raises(EthPMValidationError, match="Package has no matching URIs on chain."): - new_package.deployments - - -def test_get_contract_factory_with_default_web3(safe_math_package, w3): - contract_factory = safe_math_package.get_contract_factory("SafeMathLib") - assert hasattr(contract_factory, "address") - assert hasattr(contract_factory, "abi") - assert hasattr(contract_factory, "bytecode") - assert hasattr(contract_factory, "bytecode_runtime") - - -def test_get_contract_factory_with_missing_contract_types(safe_math_package, w3): - safe_math_package.manifest.pop("contract_types", None) - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_factory("SafeMathLib") - - -def test_get_contract_factory_throws_if_name_isnt_present(safe_math_package): - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_factory("DoesNotExist") - - -def test_get_contract_instance(deployed_safe_math): - safe_math_package, address = deployed_safe_math - contract_instance = safe_math_package.get_contract_instance("SafeMathLib", address) - assert contract_instance.abi is not False - assert is_same_address(contract_instance.address, address) - - -def test_get_contract_instance_throws_with_insufficient_assets(deployed_safe_math): - safe_math_package, address = deployed_safe_math - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_instance("IncorrectLib", address) - safe_math_package.manifest["contract_types"]["SafeMathLib"].pop("abi") - with pytest.raises(InsufficientAssetsError): - assert safe_math_package.get_contract_instance("SafeMathLib", address) - - -def test_package_object_properties(safe_math_package): - assert safe_math_package.name == "safe-math-lib" - assert safe_math_package.version == "1.0.0" - assert safe_math_package.manifest_version == "2" - assert safe_math_package.uri is None - assert safe_math_package.__repr__() == "" - assert safe_math_package.contract_types == ["SafeMathLib"] diff --git a/tests/ethpm/test_package_init.py b/tests/ethpm/test_package_init.py deleted file mode 100644 index 7fce42ed01..0000000000 --- a/tests/ethpm/test_package_init.py +++ /dev/null @@ -1,131 +0,0 @@ -import json -import os -from pathlib import ( - Path, -) -import pytest - -from ethpm import ( - Package, -) -from ethpm.exceptions import ( - CannotHandleURI, - EthPMValidationError, -) - - -@pytest.fixture -def valid_manifest_from_path(tmpdir): - valid_manifest = '{"manifest_version":"2","package_name":"foo","version":"1.0.0"}' - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write(valid_manifest) - yield Path(str(temp_manifest)) - - -@pytest.fixture -def invalid_manifest_from_path(tmpdir): - invalid_manifest = ( - '{"manifest_version":"xx","package_name":"foo","version":"1.0.0"}' - ) - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write(invalid_manifest) - yield Path(str(temp_manifest)) - - -@pytest.fixture -def non_json_manifest(tmpdir): - temp_manifest = tmpdir.mkdir("invalid").join("manifest.json") - temp_manifest.write("This is invalid json") - yield Path(str(temp_manifest)) - - -def test_init_from_minimal_valid_manifest(w3): - minimal_manifest = { - "package_name": "foo", - "manifest_version": "2", - "version": "1.0.0", - } - - Package(minimal_manifest, w3) - - -def test_package_init_for_all_manifest_use_cases(all_manifests, w3): - package = Package(all_manifests, w3) - assert isinstance(package, Package) - - -def test_package_init_for_manifest_with_build_dependency( - dummy_ipfs_backend, piper_coin_manifest, w3 -): - pkg = Package(piper_coin_manifest, w3) - assert isinstance(pkg, Package) - - -def test_init_from_invalid_manifest_data(w3): - with pytest.raises(EthPMValidationError): - Package({}, w3) - - -def test_init_from_invalid_argument_type(w3): - with pytest.raises(TypeError): - Package("not a manifest", w3) - - -def test_from_file_fails_with_missing_filepath(tmpdir, w3): - path = os.path.join(str(tmpdir.mkdir("invalid")), "manifest.json") - - assert not os.path.exists(path) - with pytest.raises(FileNotFoundError): - Package.from_file(Path(path), w3) - - -def test_from_file_fails_with_non_json(non_json_manifest, w3): - with pytest.raises(json.JSONDecodeError): - Package.from_file(non_json_manifest, w3) - - -def test_from_file_fails_with_invalid_manifest(invalid_manifest_from_path, w3): - with pytest.raises(EthPMValidationError): - Package.from_file(invalid_manifest_from_path, w3) - - -def test_from_file_succeeds_with_valid_manifest(valid_manifest_from_path, w3): - assert Package.from_file(valid_manifest_from_path, w3) - - -def test_from_file_raises_type_error_with_invalid_param_type(): - with pytest.raises(TypeError): - Package.from_file(1) - - -# -# From URI -# - -VALID_IPFS_PKG = "ipfs://QmeD2s7KaBUoGYTP1eutHBmBkMMMoycdfiyGMx2DKrWXyV" - - -def test_package_from_uri_with_valid_uri(dummy_ipfs_backend, w3): - package = Package.from_uri(VALID_IPFS_PKG, w3) - assert package.name == "safe-math-lib" - assert isinstance(package, Package) - - -@pytest.mark.parametrize( - "uri", - ( - # Invalid - "123", - b"123", - "ipfs://", - "http://QmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme", - "ipfsQmTKB75Y73zhNbD3Y73xeXGjYrZHmaXXNxoZqGCagu7r8u/readme/", - # Unsupported - "erc111://packages.zeppelin.os/owned", - "bzz://da6adeeb4589d8652bbe5679aae6b6409ec85a20e92a8823c7c99e25dba9493d", - ), -) -@pytest.mark.skipif('WEB3_INFURA_PROJECT_ID' not in os.environ, reason='Infura API key unavailable') -def test_package_from_uri_rejects_invalid_ipfs_uri(uri, w3): - with pytest.raises(CannotHandleURI): - Package.from_uri(uri, w3) diff --git a/tests/ethpm/test_uri.py b/tests/ethpm/test_uri.py deleted file mode 100644 index ed99324b2c..0000000000 --- a/tests/ethpm/test_uri.py +++ /dev/null @@ -1,96 +0,0 @@ -import pytest - -from ethpm.backends.http import ( - is_valid_api_github_uri, -) -from ethpm.backends.registry import ( - parse_registry_uri, -) -from ethpm.uri import ( - create_content_addressed_github_uri, - is_valid_content_addressed_github_uri, -) - - -@pytest.mark.parametrize( - "uri,expected", - ( - ({}, False), - (123, False), - ("xxx", False), - # invalid scheme - ("api.github.com/repos/contents/path", False), - ("http://api.github.com/repos/contents/path", False), - # invalid authority - ("http://raw.githubusercontent.com/repos/contents/path", False), - ("https://github.com/repos/contents/path", False), - # invalid path - ("https://api.github.com", False), - ("https://api.github.com/", False), - ("https://api.github.com/contents/", False), - ("https://api.github.com/repos/", False), - # valid github urls - ("https://api.github.com/repos/contents/path", True), - ( - "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/contracts/Owned.sol", # noqa: E501 - True, - ), - ), -) -def test_is_valid_github_uri(uri, expected): - actual = is_valid_api_github_uri(uri) - assert actual is expected - - -@pytest.mark.parametrize( - "uri,expected", - ( - ( - "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/contracts/Owned.sol", # noqa: E501 - False, - ), - ( - "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480", # noqa: E501 - True, - ), - ), -) -def test_is_valid_content_addressed_github_uri(uri, expected): - actual = is_valid_content_addressed_github_uri(uri) - assert actual is expected - - -def test_create_github_uri(): - api_uri = "https://api.github.com/repos/ethpm/py-ethpm/contents/ethpm/assets/owned/1.0.1.json" - expected_blob_uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480" # noqa: E501 - actual_blob_uri = create_content_addressed_github_uri(api_uri) - assert actual_blob_uri == expected_blob_uri - - -@pytest.mark.parametrize( - "uri,expected", - ( - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1", - ["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", None, None, None], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned", - ["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", "owned", None, None], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/owned?version=1.0.0", - ["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", "owned", "1.0.0", None], - ), - ( - "erc1319://0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729:1/wallet?version=2.8.0/", - ["0x6b5DA3cA4286Baa7fBaf64EEEE1834C7d430B729", "1", "wallet", "2.8.0", None], - ), - ), -) -def test_parse_registry_uri(uri, expected): - address, chain_id, pkg_name, pkg_version, ens = parse_registry_uri(uri) - assert address == expected[0] - assert chain_id == expected[1] - assert pkg_name == expected[2] - assert pkg_version == expected[3] diff --git a/tests/ethpm/tools/test_builder.py b/tests/ethpm/tools/test_builder.py deleted file mode 100644 index 778bc26806..0000000000 --- a/tests/ethpm/tools/test_builder.py +++ /dev/null @@ -1,735 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest - -from eth_utils import ( - to_canonical_address, -) -from eth_utils.toolz import ( - assoc, - assoc_in, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.backends.ipfs import ( - get_ipfs_backend, -) -from ethpm.exceptions import ( - EthPMValidationError, - ManifestBuildingError, -) -from ethpm.tools.builder import ( - as_package, - authors, - build, - build_dependency, - contract_type, - deployment, - deployment_type, - description, - init_manifest, - inline_source, - keywords, - license, - links, - manifest_version, - normalize_contract_type, - package_name, - pin_source, - source_inliner, - source_pinner, - validate, - version, - write_to_disk, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - link, - linker, -) - -BASE_MANIFEST = {"package_name": "package", "manifest_version": "2", "version": "1.0.0"} - - -@pytest.fixture -def owned_package(): - root = ASSETS_DIR / "owned" - manifest = json.loads((root / "1.0.0.json").read_text()) - compiler = json.loads((root / "owned_compiler_output.json").read_text())[ - "contracts" - ] - contracts_dir = root / "contracts" - return contracts_dir, manifest, compiler - - -@pytest.fixture -def owned_package_devdoc(): - root = ASSETS_DIR / "owned" - manifest = json.loads((root / "1.0.0.json").read_text()) - compiler = json.loads((root / "owned_compiler_output_devdoc.json").read_text())[ - "contracts" - ] - contracts_dir = root / "contracts" - return contracts_dir, manifest, compiler - - -# todo validate no duplicate contracts in package - - -@pytest.fixture -def standard_token_package(): - root = ASSETS_DIR / "standard-token" - manifest = json.loads((root / "1.0.0.json").read_text().rstrip("\n")) - compiler = json.loads((root / "standard_token_compiler_output.json").read_text())[ - "contracts" - ] - contracts_dir = root / "contracts" - return contracts_dir, manifest, compiler - - -@pytest.fixture -def registry_package(): - root = ASSETS_DIR / "registry" - compiler = json.loads(Path(root / "solc_output.json").read_text())["contracts"] - contracts_dir = root / "contracts" - manifest = json.loads((root / "2.0.0.json").read_text()) - return contracts_dir, manifest, compiler - - -@pytest.fixture -def manifest_dir(tmpdir): - return Path(tmpdir.mkdir("sub")) - - -def test_builder_simple_with_package(w3): - package = build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - as_package(w3), - ) - assert isinstance(package, Package) - assert package.version == "1.0.0" - - -PRETTY_MANIFEST = """{ - "manifest_version": "2", - "package_name": "package", - "version": "1.0.0" -}""" - -MINIFIED_MANIFEST = ( - '{"manifest_version":"2","package_name":"package","version":"1.0.0"}' -) - - -def test_builder_writes_manifest_to_disk(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - write_to_disk( - manifest_root_dir=manifest_dir, manifest_name="1.0.0.json", prettify=True - ), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == PRETTY_MANIFEST - - -def test_builder_to_disk_uses_default_cwd(manifest_dir, monkeypatch): - monkeypatch.chdir(manifest_dir) - build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - write_to_disk(), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -def test_to_disk_writes_minified_manifest_as_default(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - write_to_disk(manifest_root_dir=manifest_dir, manifest_name="1.0.0.json"), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -def test_to_disk_uses_default_manifest_name(manifest_dir): - build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - write_to_disk(manifest_root_dir=manifest_dir), - ) - actual_manifest = (manifest_dir / "1.0.0.json").read_text() - assert actual_manifest == MINIFIED_MANIFEST - - -@pytest.mark.parametrize( - "write_to_disk_fn", - ( - write_to_disk(manifest_root_dir=Path("not/a/directory")), - write_to_disk(manifest_name="invalid_name"), - ), -) -def test_to_disk_with_invalid_args_raises_exception(manifest_dir, write_to_disk_fn): - with pytest.raises(ManifestBuildingError): - build( - {}, - package_name("package"), - manifest_version("2"), - version("1.0.0"), - write_to_disk_fn, - ) - - -def test_builder_with_manifest_validation(): - with pytest.raises(EthPMValidationError, match="_invalid_package_name"): - build( - {}, - package_name("_invalid_package_name"), - manifest_version("2"), - version("1.0.0"), - validate(), - ) - - -@pytest.mark.parametrize( - "fn,value", - ( - (authors("some", "guy"), {"authors": ["some", "guy"]}), - (license("MIT"), {"license": "MIT"}), - (description("This is a package."), {"description": "This is a package."}), - (keywords("awesome", "package"), {"keywords": ["awesome", "package"]}), - ( - links(documentation="ipfs..", website="www"), - {"links": {"documentation": "ipfs..", "website": "www"}}, - ), - ), -) -def test_builder_with_simple_meta_fields(fn, value): - manifest = build(BASE_MANIFEST, fn, validate()) - expected = assoc(BASE_MANIFEST, "meta", value) - assert manifest == expected - - -def test_builder_simple_with_multi_meta_field(): - manifest = build( - BASE_MANIFEST, - authors("some", "guy"), - license("MIT"), - description("description"), - keywords("awesome", "package"), - links(website="www", repository="github"), - validate(), - ) - expected = assoc( - BASE_MANIFEST, - "meta", - { - "license": "MIT", - "authors": ["some", "guy"], - "description": "description", - "keywords": ["awesome", "package"], - "links": {"website": "www", "repository": "github"}, - }, - ) - assert manifest == expected - - -def test_builder_with_inline_source(owned_package, monkeypatch): - root, _, compiler_output = owned_package - - monkeypatch.chdir(root) - manifest = build(BASE_MANIFEST, inline_source("Owned", compiler_output), validate()) - - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": """pragma solidity ^0.4.24;\n\ncontract Owned {\n address""" - """ owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }\n\n """ - """constructor() public {\n owner = msg.sender;\n }\n}""" - }, - ) - assert manifest == expected - - -def test_builder_with_source_inliner(owned_package, monkeypatch): - root, _, compiler_output = owned_package - - monkeypatch.chdir(root) - inliner = source_inliner(compiler_output) - manifest = build(BASE_MANIFEST, inliner("Owned"), validate()) - - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": """pragma solidity ^0.4.24;\n\ncontract Owned {\n address""" - """ owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }\n\n """ - """constructor() public {\n owner = msg.sender;\n }\n}""" - }, - ) - assert manifest == expected - - -def test_builder_with_inline_source_with_package_root_dir_arg(owned_package): - root, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - inline_source("Owned", compiler_output, package_root_dir=root), - validate(), - ) - expected = assoc( - BASE_MANIFEST, - "sources", - { - "./Owned.sol": """pragma solidity ^0.4.24;\n\ncontract Owned {\n address""" - """ owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }\n\n """ - """constructor() public {\n owner = msg.sender;\n }\n}""" - }, - ) - assert manifest == expected - - -def test_builder_with_pin_source(owned_package, dummy_ipfs_backend): - root, expected_manifest, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - - manifest = build( - {}, - package_name("owned"), - manifest_version("2"), - version("1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pin_source("Owned", compiler_output, ipfs_backend, root), - validate(), - ) - - assert manifest == expected_manifest - - -def test_builder_with_pinner(owned_package, dummy_ipfs_backend): - root, expected_manifest, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - pinner = source_pinner(compiler_output, ipfs_backend, root) - manifest = build( - {}, - package_name("owned"), - manifest_version("2"), - version("1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pinner("Owned"), - validate(), - ) - - assert manifest == expected_manifest - - -def test_builder_with_init_manifest(owned_package, dummy_ipfs_backend): - root, expected_manifest, compiler_output = owned_package - ipfs_backend = get_ipfs_backend() - - manifest = build( - init_manifest(package_name="owned", version="1.0.0"), - authors("Piper Merriam "), - description( - "Reusable contracts which implement a privileged 'owner' model for authorization." - ), - keywords("authorization"), - license("MIT"), - links(documentation="ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"), - pin_source("Owned", compiler_output, ipfs_backend, root), - validate(), - ) - - assert manifest == expected_manifest - - -def test_builder_with_default_contract_types(owned_package): - _, _, compiler_output = owned_package - - manifest = build(BASE_MANIFEST, contract_type("Owned", compiler_output), validate()) - - contract_type_data = normalize_contract_type(compiler_output["Owned.sol"]["Owned"]) - expected = assoc(BASE_MANIFEST, "contract_types", {"Owned": contract_type_data}) - assert manifest == expected - - -def test_builder_with_single_alias_kwarg(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, - contract_type("Owned", compiler_output, alias="OwnedAlias"), - validate(), - ) - - contract_type_data = normalize_contract_type(compiler_output["Owned.sol"]["Owned"]) - expected = assoc( - BASE_MANIFEST, - "contract_types", - {"OwnedAlias": assoc(contract_type_data, "contract_type", "Owned")}, - ) - assert manifest == expected - - -def test_builder_without_alias_and_with_select_contract_types(owned_package): - _, _, compiler_output = owned_package - - manifest = build( - BASE_MANIFEST, contract_type("Owned", compiler_output, abi=True), validate() - ) - - contract_type_data = normalize_contract_type(compiler_output["Owned.sol"]["Owned"]) - selected_data = { - k: v for k, v in contract_type_data.items() if k != "deployment_bytecode" - } - expected = assoc(BASE_MANIFEST, "contract_types", {"Owned": selected_data}) - assert manifest == expected - - -def test_builder_with_alias_and_select_contract_types(owned_package_devdoc): - _, _, compiler_output = owned_package_devdoc - - manifest = build( - BASE_MANIFEST, - contract_type( - "Owned", - compiler_output, - alias="OwnedAlias", - abi=True, - natspec=True, - deployment_bytecode=True, - runtime_bytecode=True, - compiler=True, - ), - validate(), - ) - - contract_type_data = normalize_contract_type(compiler_output["Owned.sol"]["Owned"]) - expected = assoc( - BASE_MANIFEST, - "contract_types", - {"OwnedAlias": assoc(contract_type_data, "contract_type", "Owned")}, - ) - assert manifest == expected - - -def test_builder_raises_exception_if_selected_contract_type_missing_from_solc( - owned_package -): - _, _, compiler_output = owned_package - with pytest.raises(ManifestBuildingError): - build( - BASE_MANIFEST, - contract_type("Owned", compiler_output, abi=True, natspec=True), - ) - - -def test_builder_with_standard_token_manifest( - standard_token_package, dummy_ipfs_backend, monkeypatch -): - root, expected_manifest, compiler_output = standard_token_package - ipfs_backend = get_ipfs_backend() - - monkeypatch.chdir(root) - manifest = build( - {}, - package_name("standard-token"), - manifest_version("2"), - version("1.0.0"), - pin_source("StandardToken", compiler_output, ipfs_backend), - pin_source("Token", compiler_output, ipfs_backend), - contract_type("StandardToken", compiler_output, abi=True, natspec=True), - validate(), - ) - assert manifest == expected_manifest - - -def test_builder_with_link_references( - registry_package, dummy_ipfs_backend, monkeypatch -): - root, expected_manifest, compiler_output = registry_package - monkeypatch.chdir(root) - inliner = source_inliner(compiler_output) - manifest = build( - {}, - package_name("solidity-registry"), - manifest_version("2"), - version("2.0.0"), - inliner("Authorized"), - inliner("IndexedOrderedSetLib"), - inliner("PackageDB"), - inliner("PackageRegistry"), - inliner("PackageRegistryInterface"), - inliner("ReleaseDB"), - inliner("ReleaseValidator"), - contract_type( - "AuthorityInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - ), - contract_type( - "Authorized", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "AuthorizedInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - ), - contract_type( - "WhitelistAuthority", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "WhitelistAuthorityInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - ), - contract_type( - "IndexedOrderedSetLib", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "PackageDB", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "PackageRegistry", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "PackageRegistryInterface", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - ), - contract_type( - "ReleaseDB", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - contract_type( - "ReleaseValidator", - compiler_output, - abi=True, - deployment_bytecode=True, - runtime_bytecode=True, - natspec=True, - compiler=True, - ), - validate(), - ) - assert manifest == expected_manifest - - -@pytest.fixture -def escrow_package(w3, deployer): - manifest = ASSETS_DIR / "escrow" / "1.0.3.json" - escrow_deployer = deployer(manifest) - escrow_strategy = linker( - deploy("SafeSendLib"), - link("Escrow", "SafeSendLib"), - deploy("Escrow", w3.eth.accounts[0]), - ) - escrow_deployer.register_strategy("Escrow", escrow_strategy) - escrow_package = escrow_deployer.deploy("Escrow") - return escrow_package, w3 - - -def test_builder_deployment_simple(w3): - expected = json.dumps( - { - "package_name": "package", - "version": "1.0.0", - "manifest_version": "2", - "deployments": { - "blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef": { # noqa: E501 - "Owned": { - "contract_type": "Owned", - "address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601", - } - } - }, - } - ) - manifest = build( - BASE_MANIFEST, - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="Owned", - contract_type="Owned", - address=to_canonical_address("0xd3cda913deb6f67967b99d67acdfa1712c293601"), - ), - ) - assert manifest == json.loads(expected) - - -def test_builder_deployment_type_complex(escrow_package): - escrow, w3 = escrow_package - escrow_dep_type = deployment_type( - contract_instance="Escrow", - contract_type="Escrow", - deployment_bytecode={ - "bytecode": "0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001" # noqa: E501 - }, - runtime_bytecode={ - "bytecode": "0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029" # noqa: E501 - }, - compiler={ - "name": "solc", - "version": "0.4.24+commit.e67f0147.Emscripten.clang", - "settings": {"optimize": True}, - }, - ) - safesendlib_dep_type = deployment_type( - contract_instance="SafeSendLib", contract_type="SafeSendLib" - ) - manifest = build( - {}, - package_name("escrow"), - version("1.0.0"), - manifest_version("2"), - escrow_dep_type( - block_uri="blockchain://1111111111111111111111111111111111111111111111111111111111111111/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - address=escrow.deployments.get_instance("Escrow").address, - ), - # dep_type with block uri - safesendlib_dep_type( - block_uri="blockchain://1111111111111111111111111111111111111111111111111111111111111111/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - address=escrow.deployments.get_instance("SafeSendLib").address, - ), - # simple deployment - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="Escrow", - contract_type="Escrow", - address=escrow.deployments.get_instance("Escrow").address, - ), - # simple deployment - deployment( - block_uri="blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", # noqa: E501 - contract_instance="SafeSendLib", - contract_type="SafeSendLib", - address=escrow.deployments.get_instance("SafeSendLib").address, - ), - ) - assert len(manifest["deployments"].keys()) == 2 - assert len(list(manifest["deployments"].values())[0]) == 2 - assert len(list(manifest["deployments"].values())[1]) == 2 - - -def test_builder_with_single_build_dependency(): - expected_build_dep = { - "package": "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - } - expected = assoc_in(BASE_MANIFEST, ["build_dependencies"], expected_build_dep) - actual = build( - BASE_MANIFEST, - build_dependency( - "package", "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - ), - ) - assert actual == expected - - -def test_builder_with_multiple_build_dependencies(): - expected_build_deps = { - "escrow": "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv", - "package": "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW", - } - expected = assoc_in(BASE_MANIFEST, ["build_dependencies"], expected_build_deps) - actual = build( - BASE_MANIFEST, - build_dependency( - "package", "ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW" - ), - build_dependency( - "escrow", "ipfs://QmPDwMHk8e1aMEZg3iKsUiPSkhHkywpGB3KHKM52RtGrkv" - ), - ) - assert actual == expected - - -def test_builder_with_invalid_uri(): - with pytest.raises( - EthPMValidationError, match="is not a supported content-addressed URI" - ): - build( - {}, - package_name("package"), - version("1.0.0"), - manifest_version("2"), - build_dependency("package", "www.google.com"), - ) diff --git a/tests/ethpm/tools/test_checker.py b/tests/ethpm/tools/test_checker.py deleted file mode 100644 index ddefbcd7cb..0000000000 --- a/tests/ethpm/tools/test_checker.py +++ /dev/null @@ -1,150 +0,0 @@ -import pytest - -from ethpm.tools.checker import ( - WARNINGS, - check_contract_types, - check_manifest, - check_meta, - check_sources, -) - - -def test_checker_simple(): - warnings = check_manifest({}) - assert warnings == { - "manifest_version": "Manifest missing a required 'manifest_version' field.", - "package_name": "Manifest missing a required 'package_name' field", - "version": "Manifest missing a required 'version' field.", - "meta": "Manifest missing a suggested 'meta' field.", - "sources": """Manifest is missing a sources field, which defines a source tree that """ - """should comprise the full source tree necessary to recompile the contracts """ - """contained in this release.""", - "contract_types": """Manifest does not contain any 'contract_types'. Packages should """ - """only include contract types that can be found in the source files for this """ - """package. Packages should not include contract types from dependencies. """ - """Packages should not include abstract contracts in the contract types section """ - """of a release.""", - } - - -BASIC_MANIFEST = { - "package_name": "package", - "version": "1.0.0", - "manifest_version": "2", -} - - -@pytest.mark.parametrize( - "manifest,expected", - ( - ({}, {"meta": WARNINGS["meta_missing"]}), - ({"meta": {}}, {"meta": WARNINGS["meta_missing"]}), - ( - {"meta": {"x": "x"}}, - { - "meta.authors": WARNINGS["authors_missing"], - "meta.description": WARNINGS["description_missing"], - "meta.links": WARNINGS["links_missing"], - "meta.keywords": WARNINGS["keywords_missing"], - "meta.license": WARNINGS["license_missing"], - }, - ), - ), -) -def test_check_meta(manifest, expected): - warnings = check_meta(manifest, {}) - assert warnings == expected - - -@pytest.mark.parametrize( - "manifest,expected", - ( - # Sad paths - ({}, {"sources": WARNINGS["sources_missing"]}), - ({"sources": []}, {"sources": WARNINGS["sources_missing"]}), - # Happy path - ({"sources": {"links": "www.github.com"}}, {}), - ), -) -def test_check_sources(manifest, expected): - warnings = check_sources(manifest, {}) - assert warnings == expected - - -@pytest.mark.parametrize( - "manifest,expected", - ( - ({}, {"contract_types": WARNINGS["contract_type_missing"]}), - ({"contract_types": {}}, {"contract_types": WARNINGS["contract_type_missing"]}), - ( - {"contract_types": {"x": {"runtime_bytecode": {"invalid": "invalid"}}}}, - { - "contract_types": { - "x": { - "abi": WARNINGS["abi_missing"].format("x"), - "contract_type": WARNINGS[ - "contract_type_subfield_missing" - ].format("x"), - "deployment_bytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("x"), - "runtime_bytecode": WARNINGS[ - "bytecode_subfield_missing" - ].format("x", "runtime"), - "natspec": WARNINGS["natspec_missing"].format("x"), - "compiler": WARNINGS["compiler_missing"].format("x"), - } - } - }, - ), - ( - { - "contract_types": { - "x": { - "deployment_bytecode": [], - "runtime_bytecode": {"bytecode": []}, - }, - "y": { - "abi": [1], - "deployment_bytecode": [], - "runtime_bytecode": [], - }, - } - }, - { - "contract_types": { - "x": { - "abi": WARNINGS["abi_missing"].format("x"), - "contract_type": WARNINGS[ - "contract_type_subfield_missing" - ].format("x"), - "deployment_bytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("x"), - "runtime_bytecode": WARNINGS[ - "bytecode_subfield_missing" - ].format("x", "runtime"), - "natspec": WARNINGS["natspec_missing"].format("x"), - "compiler": WARNINGS["compiler_missing"].format("x"), - }, - "y": { - "contract_type": WARNINGS[ - "contract_type_subfield_missing" - ].format("y"), - "deployment_bytecode": WARNINGS[ - "deployment_bytecode_missing" - ].format("y"), - "runtime_bytecode": WARNINGS["runtime_bytecode_missing"].format( - "y" - ), - "natspec": WARNINGS["natspec_missing"].format("y"), - "compiler": WARNINGS["compiler_missing"].format("y"), - }, - } - }, - ), - ), -) -def test_check_contract_types(manifest, expected): - warnings = check_contract_types(manifest, {}) - assert warnings == expected diff --git a/tests/ethpm/validation/test_manifest.py b/tests/ethpm/validation/test_manifest.py deleted file mode 100644 index 0417f9acec..0000000000 --- a/tests/ethpm/validation/test_manifest.py +++ /dev/null @@ -1,253 +0,0 @@ -import pytest - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.manifest import ( - extract_contract_types_from_deployments, - validate_manifest_against_schema, - validate_manifest_deployments, - validate_manifest_exists, - validate_meta_object, - validate_raw_manifest_format, -) -from ethpm.validation.package import ( - validate_manifest_version, - validate_package_name, -) - - -def test_validate_raw_manifest_configuration_validates_strict_manifests( - all_strict_manifests -): - assert validate_raw_manifest_format(all_strict_manifests) is None - - -def test_validate_raw_manifest_format_invalidates_pretty_manifests( - all_pretty_manifests -): - with pytest.raises(EthPMValidationError): - validate_raw_manifest_format(all_pretty_manifests) - - -@pytest.mark.parametrize( - "manifest", - ( - # not alphabetical - '{"x":"y","a":"b"}', - # not UTF-8 - '{"\x80":"b","c":"d"}', - # newlines - '{"a":"b",\n"c":"d"}', - '{"a":"b","c":"d"}\n', - # whitespace - '{"a":"b","c": "d"}', - ), -) -def test_validate_raw_manifest_format_invalidates_invalid_manifests(tmpdir, manifest): - p = tmpdir.mkdir("invalid").join("manifest.json") - p.write(manifest) - invalid_manifest = p.read() - with pytest.raises(EthPMValidationError): - validate_raw_manifest_format(invalid_manifest) - - -def test_validate_manifest_exists_validates(): - assert ( - validate_manifest_exists(ASSETS_DIR / "safe-math-lib" / "1.0.0.json") - is None - ) - - -def test_validate_manifest_exists_invalidates(): - with pytest.raises(EthPMValidationError): - validate_manifest_exists("DNE") - - -def test_validate_manifest_against_all_manifest_types(all_manifests): - assert validate_manifest_against_schema(all_manifests) is None - - -def test_validate_manifest_invalidates(invalid_manifest): - with pytest.raises(EthPMValidationError): - validate_manifest_against_schema(invalid_manifest) - - -def test_validate_deployed_contracts_present_validates( - manifest_with_conflicting_deployments -): - with pytest.raises(EthPMValidationError): - validate_manifest_deployments(manifest_with_conflicting_deployments) - - -def test_validate_deployments(safe_math_lib_package): - validate = validate_manifest_deployments(safe_math_lib_package.manifest) - assert validate is None - - -def test_validate_deployed_contracts_pr(manifest_with_no_deployments): - validate = validate_manifest_deployments(manifest_with_no_deployments) - assert validate is None - - -@pytest.mark.parametrize( - "data,expected", - ( - ({}, set()), - ([{"some": {"contract_type": "one"}}], set(["one"])), - ( - [{"some": {"contract_type": "one"}, "other": {"contract_type": "two"}}], - set(["one", "two"]), - ), - ), -) -def test_extract_contract_types_from_deployments(data, expected): - actual = extract_contract_types_from_deployments(data) - assert actual == expected - - -@pytest.mark.parametrize("version", ("2")) -def test_validate_manifest_version_validates_version_two_string(version): - validate = validate_manifest_version(version) - assert validate is None - - -@pytest.mark.parametrize("version", (1, 2, "1" "3", b"3")) -def test_validate_manifest_version_invalidates_incorrect_versions(version): - with pytest.raises(EthPMValidationError): - validate_manifest_version(version) - - -@pytest.mark.parametrize( - "meta,extra_fields", - ( - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "A Package that does things.", - "keywords": ["ethpm", "package"], - "links": {"documentation": "ipfs://Qm..."}, - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "A Package that does things.", - "keywords": ["ethpm", "package"], - "links": {"documentation": "ipfs://Qm..."}, - "x-hash": "0x...", - }, - True, - ), - ), -) -def test_validate_meta_object_validates(meta, extra_fields): - result = validate_meta_object(meta, allow_extra_meta_fields=extra_fields) - assert result is None - - -@pytest.mark.parametrize( - "meta,extra_fields", - ( - # With allow_extra_meta_fields=False - ({"invalid": "field"}, False), - ({"license": 123}, False), - ({"license": "MIT", "authors": "auther@gmail.com"}, False), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": ["description", "of", "package"], - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": "singlekw", - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": ["ipfs://Qm"], - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": {"documentation": "ipfs://Qm"}, - "extra": "field", - }, - False, - ), - ( - { - "license": "MIT", - "authors": ["author@gmail.com"], - "description": "description", - "keywords": ["auth", "package"], - "links": {"documentation": "ipfs://Qm"}, - "x-hash": "0x", - }, - False, - ), - # With allow_extra_meta_fields=True - # Improperly formatted "x" field - ({"license": "MIT", "extra": "field"}, True), - ), -) -def test_validate_meta_object_invalidates(meta, extra_fields): - with pytest.raises(EthPMValidationError): - validate_meta_object(meta, allow_extra_meta_fields=extra_fields) - - -@pytest.mark.parametrize( - "package_name", - ( - "valid", - "Valid", - "pkg1", - "pkg_1", - "pkg-1", - "wallet0", - "wallet_", - "wallet-", - "x" * 256, - ) -) -def test_validate_package_name_with_valid_package_names(package_name): - assert validate_package_name(package_name) is None - - -@pytest.mark.parametrize( - "package_name", - ( - "", - "0", - "_invalid", - "-invalid", - ".invalid", - "wallet.bad", - "x" * 257, - ) -) -def test_validate_package_name_raises_exception_for_invalid_names(package_name): - with pytest.raises(EthPMValidationError): - validate_package_name(package_name) diff --git a/tests/ethpm/validation/test_manifest_assets_are_valid.py b/tests/ethpm/validation/test_manifest_assets_are_valid.py deleted file mode 100644 index 0a4805ca60..0000000000 --- a/tests/ethpm/validation/test_manifest_assets_are_valid.py +++ /dev/null @@ -1,42 +0,0 @@ -import json -import pytest - -from ethpm import ( - ASSETS_DIR, -) -from ethpm.exceptions import ( - InsufficientAssetsError, -) -from ethpm.validation.manifest import ( - validate_manifest_against_schema, -) - -SOURCES_GLOB = "**/*.json" - - -def get_all_manifest_paths(): - # Expects all json in ethpm/assets to be either compiler_output or a manifest - all_use_case_json = set(ASSETS_DIR.glob(SOURCES_GLOB)) - set( - (ASSETS_DIR / "spec").glob(SOURCES_GLOB) - ) - solc_outputs = ("solc", "output") - all_manifests = [ - json - for json in all_use_case_json - if all(known not in json.name for known in solc_outputs) - ] - if not all_manifests: - raise InsufficientAssetsError( - "Error importing manifests for validation, found no manifests in `ethpm/assets/`" - ) - return all_manifests - - -@pytest.fixture(params=get_all_manifest_paths()) -def manifest(request): - return json.loads(request.param.read_text()) - - -def test_manifest_assets_are_valid(manifest): - result = validate_manifest_against_schema(manifest) - assert result is None diff --git a/tests/ethpm/validation/test_misc.py b/tests/ethpm/validation/test_misc.py deleted file mode 100644 index 605193b206..0000000000 --- a/tests/ethpm/validation/test_misc.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest - -from ethpm.exceptions import ( - EthPMValidationError, -) -from ethpm.validation.misc import ( - validate_empty_bytes, -) - - -@pytest.mark.parametrize( - "offset,length,bytecode", - ( - (0, 3, b"\00\00\00"), - (1, 20, b"\01" + bytearray(20) + b"\01"), - (26, 20, b"\01" + bytearray(20) + b"\01" * 5 + bytearray(20) + b"\01"), - ), -) -def test_validate_empty_bytes(offset, length, bytecode): - result = validate_empty_bytes(offset, length, bytecode) - assert result is None - - -@pytest.mark.parametrize( - "offset,length,bytecode", - ( - (0, 2, b"\00"), - (0, 3, b"\01\01\01"), - (1, 1, b"\00\01\00\01"), - (1, 20, bytearray(20) + b"\01"), - ), -) -def test_validate_empty_bytes_invalidates(offset, length, bytecode): - with pytest.raises(EthPMValidationError): - validate_empty_bytes(offset, length, bytecode) diff --git a/tests/generate_go_ethereum_fixture.py b/tests/generate_go_ethereum_fixture.py index 8d307f8038..6894442714 100644 --- a/tests/generate_go_ethereum_fixture.py +++ b/tests/generate_go_ethereum_fixture.py @@ -1,408 +1,408 @@ -import contextlib -import json -import os -import pprint -import shutil -import signal -import socket -import subprocess -import sys -import tempfile -import time - -from eth_utils.curried import ( - apply_formatter_if, - is_bytes, - is_checksum_address, - is_dict, - is_same_address, - remove_0x_prefix, - to_hex, - to_text, - to_wei, -) -from eth_utils.toolz import ( - merge, - valmap, -) - -from tests.utils import ( - get_open_port, -) -from web3 import Web3 -from web3._utils.module_testing.emitter_contract import ( - EMITTER_ABI, - EMITTER_BYTECODE, - EMITTER_ENUM, -) -from web3._utils.module_testing.math_contract import ( - MATH_ABI, - MATH_BYTECODE, -) - -COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' -COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' - -KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501 - -KEYFILE_PW = 'web3py-test' -KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501 - -RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' - -UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' -UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13' -UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW - - -GENESIS_DATA = { - "nonce": "0xdeadbeefdeadbeef", - "timestamp": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", # noqa: E501 - "extraData": "0x7765623370792d746573742d636861696e", - "gasLimit": "0x47d5cc", - "difficulty": "0x01", - "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", # noqa: E501 - "coinbase": "0x3333333333333333333333333333333333333333", - "alloc": { - remove_0x_prefix(COINBASE): { - 'balance': str(to_wei(1000000000, 'ether')), - }, - remove_0x_prefix(RAW_TXN_ACCOUNT): { - 'balance': str(to_wei(10, 'ether')), - }, - remove_0x_prefix(UNLOCKABLE_ACCOUNT): { - 'balance': str(to_wei(10, 'ether')), - }, - }, - "config": { - "chainId": 131277322940537, # the string 'web3py' as an integer - "homesteadBlock": 0, - "eip155Block": 0, - "eip158Block": 0 - }, -} - - -def ensure_path_exists(dir_path): - """ - Make sure that a path exists - """ - if not os.path.exists(dir_path): - os.makedirs(dir_path) - return True - return False - - -@contextlib.contextmanager -def tempdir(): - dir_path = tempfile.mkdtemp() - try: - yield dir_path - finally: - shutil.rmtree(dir_path) - - -def get_geth_binary(): - from geth.install import ( - get_executable_path, - install_geth, - ) - - if 'GETH_BINARY' in os.environ: - return os.environ['GETH_BINARY'] - elif 'GETH_VERSION' in os.environ: - geth_version = os.environ['GETH_VERSION'] - _geth_binary = get_executable_path(geth_version) - if not os.path.exists(_geth_binary): - install_geth(geth_version) - assert os.path.exists(_geth_binary) - return _geth_binary - else: - return 'geth' - - -def wait_for_popen(proc, timeout): - start = time.time() - while time.time() < start + timeout: - if proc.poll() is None: - time.sleep(0.01) - else: - break - - -def kill_proc_gracefully(proc): - if proc.poll() is None: - proc.send_signal(signal.SIGINT) - wait_for_popen(proc, 13) - - if proc.poll() is None: - proc.terminate() - wait_for_popen(proc, 5) - - if proc.poll() is None: - proc.kill() - wait_for_popen(proc, 2) - - -def wait_for_socket(ipc_path, timeout=30): - start = time.time() - while time.time() < start + timeout: - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(ipc_path) - sock.settimeout(timeout) - except (FileNotFoundError, socket.error): - time.sleep(0.01) - else: - break - - -@contextlib.contextmanager -def graceful_kill_on_exit(proc): - try: - yield proc - finally: - kill_proc_gracefully(proc) - - -@contextlib.contextmanager -def get_geth_process(geth_binary, - datadir, - genesis_file_path, - geth_ipc_path, - geth_port): - init_datadir_command = ( - geth_binary, - '--datadir', datadir, - 'init', - genesis_file_path, - ) - subprocess.check_output( - init_datadir_command, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - - run_geth_command = ( - geth_binary, - '--datadir', datadir, - '--ipcpath', geth_ipc_path, - '--ethash.dagsondisk', '1', - '--gcmode', 'archive', - '--nodiscover', - '--port', geth_port, - '--etherbase', COINBASE[2:], - ) - - popen_proc = subprocess.Popen( - run_geth_command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - bufsize=1, - ) - with popen_proc as proc: - with graceful_kill_on_exit(proc) as graceful_proc: - yield graceful_proc - - output, errors = proc.communicate() - - print( - "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -def write_config_json(config, datadir): - bytes_to_hex = apply_formatter_if(is_bytes, to_hex) - config_json_dict = valmap(bytes_to_hex, config) - - config_path = os.path.join(datadir, 'config.json') - with open(config_path, 'w') as config_file: - config_file.write(json.dumps(config_json_dict)) - config_file.write('\n') - - -def generate_go_ethereum_fixture(destination_dir): - with contextlib.ExitStack() as stack: - datadir = stack.enter_context(tempdir()) - - keystore_dir = os.path.join(datadir, 'keystore') - ensure_path_exists(keystore_dir) - keyfile_path = os.path.join(keystore_dir, KEYFILE_FILENAME) - with open(keyfile_path, 'w') as keyfile: - keyfile.write(KEYFILE_DATA) - genesis_file_path = os.path.join(datadir, 'genesis.json') - with open(genesis_file_path, 'w') as genesis_file: - genesis_file.write(json.dumps(GENESIS_DATA)) - - geth_ipc_path_dir = stack.enter_context(tempdir()) - geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') - - geth_port = get_open_port() - geth_binary = get_geth_binary() - - with get_geth_process( - geth_binary=geth_binary, - datadir=datadir, - genesis_file_path=genesis_file_path, - geth_ipc_path=geth_ipc_path, - geth_port=geth_port): - - wait_for_socket(geth_ipc_path) - web3 = Web3(Web3.IPCProvider(geth_ipc_path)) - chain_data = setup_chain_state(web3) - # close geth by exiting context - # must be closed before copying data dir - verify_chain_state(web3, chain_data) - - # verify that chain state is still valid after closing - # and re-opening geth - with get_geth_process( - geth_binary=geth_binary, - datadir=datadir, - genesis_file_path=genesis_file_path, - geth_ipc_path=geth_ipc_path, - geth_port=geth_port): - - wait_for_socket(geth_ipc_path) - web3 = Web3(Web3.IPCProvider(geth_ipc_path)) - verify_chain_state(web3, chain_data) - - static_data = { - 'raw_txn_account': RAW_TXN_ACCOUNT, - 'keyfile_pw': KEYFILE_PW, - } - config = merge(chain_data, static_data) - pprint.pprint(config) - write_config_json(config, datadir) - - shutil.make_archive(destination_dir, 'zip', datadir) - - -def verify_chain_state(web3, chain_data): - receipt = web3.eth.waitForTransactionReceipt(chain_data['mined_txn_hash']) - latest = web3.eth.getBlock('latest') - assert receipt.blockNumber <= latest.number - - -def mine_transaction_hash(web3, txn_hash): - web3.geth.miner.start(1) - try: - return web3.eth.waitForTransactionReceipt(txn_hash, timeout=60) - finally: - web3.geth.miner.stop() - - -def mine_block(web3): - origin_block_number = web3.eth.blockNumber - - start_time = time.time() - web3.geth.miner.start(1) - while time.time() < start_time + 60: - block_number = web3.eth.blockNumber - if block_number > origin_block_number: - web3.geth.miner.stop() - return block_number - else: - time.sleep(0.1) - else: - raise ValueError("No block mined during wait period") - - -def deploy_contract(web3, name, factory): - web3.geth.personal.unlockAccount(web3.eth.coinbase, KEYFILE_PW) - deploy_txn_hash = factory.constructor().transact({'from': web3.eth.coinbase}) - print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) - deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash) - print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) - contract_address = deploy_receipt['contractAddress'] - assert is_checksum_address(contract_address) - print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) - return deploy_receipt - - -def setup_chain_state(web3): - coinbase = web3.eth.coinbase - - assert is_same_address(coinbase, COINBASE) - - # - # Math Contract - # - math_contract_factory = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_BYTECODE, - ) - math_deploy_receipt = deploy_contract(web3, 'math', math_contract_factory) - assert is_dict(math_deploy_receipt) - - # - # Emitter Contract - # - emitter_contract_factory = web3.eth.contract( - abi=EMITTER_ABI, - bytecode=EMITTER_BYTECODE, - ) - emitter_deploy_receipt = deploy_contract(web3, 'emitter', emitter_contract_factory) - emitter_contract = emitter_contract_factory(emitter_deploy_receipt['contractAddress']) - - txn_hash_with_log = emitter_contract.functions.logDouble( - which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, - ).transact({ - 'from': web3.eth.coinbase, - }) - print('TXN_HASH_WITH_LOG:', txn_hash_with_log) - txn_receipt_with_log = mine_transaction_hash(web3, txn_hash_with_log) - block_with_log = web3.eth.getBlock(txn_receipt_with_log['blockHash']) - print('BLOCK_HASH_WITH_LOG:', block_with_log['hash']) - - # - # Empty Block - # - empty_block_number = mine_block(web3) - print('MINED_EMPTY_BLOCK') - empty_block = web3.eth.getBlock(empty_block_number) - assert is_dict(empty_block) - assert not empty_block['transactions'] - print('EMPTY_BLOCK_HASH:', empty_block['hash']) - - # - # Block with Transaction - # - web3.geth.personal.unlockAccount(coinbase, KEYFILE_PW) - web3.geth.miner.start(1) - mined_txn_hash = web3.eth.sendTransaction({ - 'from': coinbase, - 'to': coinbase, - 'value': 1, - 'gas': 21000, - 'gas_price': web3.eth.gasPrice, - }) - mined_txn_receipt = mine_transaction_hash(web3, mined_txn_hash) - print('MINED_TXN_HASH:', mined_txn_hash) - block_with_txn = web3.eth.getBlock(mined_txn_receipt['blockHash']) - print('BLOCK_WITH_TXN_HASH:', block_with_txn['hash']) - - geth_fixture = { - 'math_deploy_txn_hash': math_deploy_receipt['transactionHash'], - 'math_address': math_deploy_receipt['contractAddress'], - 'emitter_deploy_txn_hash': emitter_deploy_receipt['transactionHash'], - 'emitter_address': emitter_deploy_receipt['contractAddress'], - 'txn_hash_with_log': txn_hash_with_log, - 'block_hash_with_log': block_with_log['hash'], - 'empty_block_hash': empty_block['hash'], - 'mined_txn_hash': mined_txn_hash, - 'block_with_txn_hash': block_with_txn['hash'], - } - return geth_fixture - - -if __name__ == '__main__': - fixture_dir = sys.argv[1] - generate_go_ethereum_fixture(fixture_dir) +import contextlib +import json +import os +import pprint +import shutil +import signal +import socket +import subprocess +import sys +import tempfile +import time + +from vnsutils.curried import ( + apply_formatter_if, + is_bytes, + is_checksum_address, + is_dict, + is_same_address, + remove_0x_prefix, + to_hex, + to_text, + to_wei, +) + +from tests.utils import ( + get_open_port, +) +from web3 import Web3 +from web3._utils.module_testing.emitter_contract import ( + EMITTER_ABI, + EMITTER_BYTECODE, + EMITTER_ENUM, +) +from web3._utils.module_testing.math_contract import ( + MATH_ABI, + MATH_BYTECODE, +) +from web3._utils.toolz import ( + merge, + valmap, +) + +COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' +COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' + +KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501 + +KEYFILE_PW = 'web3py-test' +KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501 + +RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' + +UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' +UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13' +UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW + + +GENESIS_DATA = { + "nonce": "0xdeadbeefdeadbeef", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", # noqa: E501 + "extraData": "0x7765623370792d746573742d636861696e", + "gasLimit": "0x47d5cc", + "difficulty": "0x01", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", # noqa: E501 + "coinbase": "0x3333333333333333333333333333333333333333", + "alloc": { + remove_0x_prefix(COINBASE): { + 'balance': str(to_wei(1000000000, 'ether')), + }, + remove_0x_prefix(RAW_TXN_ACCOUNT): { + 'balance': str(to_wei(10, 'ether')), + }, + remove_0x_prefix(UNLOCKABLE_ACCOUNT): { + 'balance': str(to_wei(10, 'ether')), + }, + }, + "config": { + "chainId": 131277322940537, # the string 'web3py' as an integer + "homesteadBlock": 0, + "eip155Block": 0, + "eip158Block": 0 + }, +} + + +def ensure_path_exists(dir_path): + """ + Make sure that a path exists + """ + if not os.path.exists(dir_path): + os.makedirs(dir_path) + return True + return False + + +@contextlib.contextmanager +def tempdir(): + dir_path = tempfile.mkdtemp() + try: + yield dir_path + finally: + shutil.rmtree(dir_path) + + +def get_geth_binary(): + from geth.install import ( + get_executable_path, + install_geth, + ) + + if 'GETH_BINARY' in os.environ: + return os.environ['GETH_BINARY'] + elif 'GETH_VERSION' in os.environ: + geth_version = os.environ['GETH_VERSION'] + _geth_binary = get_executable_path(geth_version) + if not os.path.exists(_geth_binary): + install_geth(geth_version) + assert os.path.exists(_geth_binary) + return _geth_binary + else: + return 'geth' + + +def wait_for_popen(proc, timeout): + start = time.time() + while time.time() < start + timeout: + if proc.poll() is None: + time.sleep(0.01) + else: + break + + +def kill_proc_gracefully(proc): + if proc.poll() is None: + proc.send_signal(signal.SIGINT) + wait_for_popen(proc, 13) + + if proc.poll() is None: + proc.terminate() + wait_for_popen(proc, 5) + + if proc.poll() is None: + proc.kill() + wait_for_popen(proc, 2) + + +def wait_for_socket(ipc_path, timeout=30): + start = time.time() + while time.time() < start + timeout: + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(ipc_path) + sock.settimeout(timeout) + except (FileNotFoundError, socket.error): + time.sleep(0.01) + else: + break + + +@contextlib.contextmanager +def graceful_kill_on_exit(proc): + try: + yield proc + finally: + kill_proc_gracefully(proc) + + +@contextlib.contextmanager +def get_geth_process(geth_binary, + datadir, + genesis_file_path, + geth_ipc_path, + geth_port): + init_datadir_command = ( + geth_binary, + '--datadir', datadir, + 'init', + genesis_file_path, + ) + subprocess.check_output( + init_datadir_command, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + run_geth_command = ( + geth_binary, + '--datadir', datadir, + '--ipcpath', geth_ipc_path, + '--ethash.dagsondisk', '1', + '--gcmode', 'archive', + '--nodiscover', + '--port', geth_port, + '--etherbase', COINBASE[2:], + ) + + popen_proc = subprocess.Popen( + run_geth_command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=1, + ) + with popen_proc as proc: + with graceful_kill_on_exit(proc) as graceful_proc: + yield graceful_proc + + output, errors = proc.communicate() + + print( + "Geth Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +def write_config_json(config, datadir): + bytes_to_hex = apply_formatter_if(is_bytes, to_hex) + config_json_dict = valmap(bytes_to_hex, config) + + config_path = os.path.join(datadir, 'config.json') + with open(config_path, 'w') as config_file: + config_file.write(json.dumps(config_json_dict)) + config_file.write('\n') + + +def generate_go_ethereum_fixture(destination_dir): + with contextlib.ExitStack() as stack: + datadir = stack.enter_context(tempdir()) + + keystore_dir = os.path.join(datadir, 'keystore') + ensure_path_exists(keystore_dir) + keyfile_path = os.path.join(keystore_dir, KEYFILE_FILENAME) + with open(keyfile_path, 'w') as keyfile: + keyfile.write(KEYFILE_DATA) + genesis_file_path = os.path.join(datadir, 'genesis.json') + with open(genesis_file_path, 'w') as genesis_file: + genesis_file.write(json.dumps(GENESIS_DATA)) + + geth_ipc_path_dir = stack.enter_context(tempdir()) + geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') + + geth_port = get_open_port() + geth_binary = get_geth_binary() + + with get_geth_process( + geth_binary=geth_binary, + datadir=datadir, + genesis_file_path=genesis_file_path, + geth_ipc_path=geth_ipc_path, + geth_port=geth_port): + + wait_for_socket(geth_ipc_path) + web3 = Web3 (Web3.IPCProvider(geth_ipc_path)) + chain_data = setup_chain_state(web3) + # close geth by exiting context + # must be closed before copying data dir + verify_chain_state(web3, chain_data) + + # verify that chain state is still valid after closing + # and re-opening geth + with get_geth_process( + geth_binary=geth_binary, + datadir=datadir, + genesis_file_path=genesis_file_path, + geth_ipc_path=geth_ipc_path, + geth_port=geth_port): + + wait_for_socket(geth_ipc_path) + web3 = Web3 (Web3.IPCProvider(geth_ipc_path)) + verify_chain_state(web3, chain_data) + + static_data = { + 'raw_txn_account': RAW_TXN_ACCOUNT, + 'keyfile_pw': KEYFILE_PW, + } + config = merge(chain_data, static_data) + pprint.pprint(config) + write_config_json(config, datadir) + + shutil.make_archive(destination_dir, 'zip', datadir) + + +def verify_chain_state(web3, chain_data): + receipt = web3.vns.waitForTransactionReceipt(chain_data['mined_txn_hash']) + latest = web3.vns.getBlock('latest') + assert receipt.blockNumber <= latest.number + + +def mine_transaction_hash(web3, txn_hash): + web3.geth.miner.start(1) + try: + return web3.vns.waitForTransactionReceipt(txn_hash, timeout=60) + finally: + web3.geth.miner.stop() + + +def mine_block(web3): + origin_block_number = web3.vns.blockNumber + + start_time = time.time() + web3.geth.miner.start(1) + while time.time() < start_time + 60: + block_number = web3.vns.blockNumber + if block_number > origin_block_number: + web3.geth.miner.stop() + return block_number + else: + time.sleep(0.1) + else: + raise ValueError("No block mined during wait period") + + +def deploy_contract(web3, name, factory): + web3.geth.personal.unlockAccount(web3.vns.coinbase, KEYFILE_PW) + deploy_txn_hash = factory.constructor().transact({'from': web3.vns.coinbase}) + print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) + deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash) + print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) + contract_address = deploy_receipt['contractAddress'] + assert is_checksum_address(contract_address) + print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) + return deploy_receipt + + +def setup_chain_state(web3): + coinbase = web3.vns.coinbase + + assert is_same_address(coinbase, COINBASE) + + # + # Math Contract + # + math_contract_factory = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_BYTECODE, + ) + math_deploy_receipt = deploy_contract(web3, 'math', math_contract_factory) + assert is_dict(math_deploy_receipt) + + # + # Emitter Contract + # + emitter_contract_factory = web3.vns.contract( + abi=EMITTER_ABI, + bytecode=EMITTER_BYTECODE, + ) + emitter_deploy_receipt = deploy_contract(web3, 'emitter', emitter_contract_factory) + emitter_contract = emitter_contract_factory(emitter_deploy_receipt['contractAddress']) + + txn_hash_with_log = emitter_contract.functions.logDouble( + which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, + ).transact({ + 'from': web3.vns.coinbase, + }) + print('TXN_HASH_WITH_LOG:', txn_hash_with_log) + txn_receipt_with_log = mine_transaction_hash(web3, txn_hash_with_log) + block_with_log = web3.vns.getBlock(txn_receipt_with_log['blockHash']) + print('BLOCK_HASH_WITH_LOG:', block_with_log['hash']) + + # + # Empty Block + # + empty_block_number = mine_block(web3) + print('MINED_EMPTY_BLOCK') + empty_block = web3.vns.getBlock(empty_block_number) + assert is_dict(empty_block) + assert not empty_block['transactions'] + print('EMPTY_BLOCK_HASH:', empty_block['hash']) + + # + # Block with Transaction + # + web3.geth.personal.unlockAccount(coinbase, KEYFILE_PW) + web3.geth.miner.start(1) + mined_txn_hash = web3.vns.sendTransaction({ + 'from': coinbase, + 'to': coinbase, + 'value': 1, + 'gas': 21000, + 'gas_price': web3.vns.gasPrice, + }) + mined_txn_receipt = mine_transaction_hash(web3, mined_txn_hash) + print('MINED_TXN_HASH:', mined_txn_hash) + block_with_txn = web3.vns.getBlock(mined_txn_receipt['blockHash']) + print('BLOCK_WITH_TXN_HASH:', block_with_txn['hash']) + + geth_fixture = { + 'math_deploy_txn_hash': math_deploy_receipt['transactionHash'], + 'math_address': math_deploy_receipt['contractAddress'], + 'emitter_deploy_txn_hash': emitter_deploy_receipt['transactionHash'], + 'emitter_address': emitter_deploy_receipt['contractAddress'], + 'txn_hash_with_log': txn_hash_with_log, + 'block_hash_with_log': block_with_log['hash'], + 'empty_block_hash': empty_block['hash'], + 'mined_txn_hash': mined_txn_hash, + 'block_with_txn_hash': block_with_txn['hash'], + } + return geth_fixture + + +if __name__ == '__main__': + fixture_dir = sys.argv[1] + generate_go_ethereum_fixture(fixture_dir) diff --git a/tests/integration/README.md b/tests/integration/README.md index fa32abba0a..d6adf22c1e 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -1,94 +1,94 @@ -For each integration test you need to define the following pytest fixtures. -They **must** all be *session* scoped. - -* `web3` -* `empty_block` -* `block_with_txn` -* `math_contract` -* `unlocked_account` -* `funded_account_for_raw_txn` -* `math_contract_deploy_txn_hash` -* `mined_txn_hash` -* `emitter_contract` -* `block_with_txn_with_log` -* `txn_hash_with_log` - -* `unlockable_account` -* `unlockable_account_pw` - -The details for these fixtures are as follows. - - -### Fixture Details - -#### `web3` - -A Web3 instance configured to connect to the backend for the integration test. - - -#### `empty_block` - -A block as returned by `web3.eth.getBlock` that has no transactions. - - -#### `block_with_txn` - -A block as returned by `web3.eth.getBlock` that has a single transaction. - - -#### `math_contract` - -An deployed Contract instance of the *Math* contract found in -`web3._utils.module_testing.math_contract`. - - -#### `unlocked_account` - -The address of an account which is *unlocked*. - - -#### `funded_account_for_raw_txn` - -An account which has not sent any transactions (nonce of 0) which has enough -ether for sending a single transaction. - - -#### `math_contract_deploy_txn_hash` - -The transaction hash used to deploy the `math_contract`. - - -#### `mined_txn_hash` - -The transaction hash of a transaction which has been mined. - - -#### `emitter_contract` - -An deployed Contract instance of the *Emitter* contract found in -`web3._utils.module_testing.emitter_contract`. - - -#### `block_with_txn_with_log` - -A block with a transaction with a single log entry. - - -#### `txn_hash_with_log` - -The hash of a transaction which fires a single log entry. - - -#### `unlockable_account` - -The address of an account that can be unlocked using the `unlockable_account_pw` - - -#### `unlockable_account_pw` - -The password that can be used to unlock the `unlockable_account` - - -# Updating Fixture ZIPs - -**ONLY** trusted parties should be allowed to update zipped fixtures, since they pose an attack surface through which a third party could inject malicious code into the codebase. +For each integration test you need to define the following pytest fixtures. +They **must** all be *session* scoped. + +* `web3` +* `empty_block` +* `block_with_txn` +* `math_contract` +* `unlocked_account` +* `funded_account_for_raw_txn` +* `math_contract_deploy_txn_hash` +* `mined_txn_hash` +* `emitter_contract` +* `block_with_txn_with_log` +* `txn_hash_with_log` + +* `unlockable_account` +* `unlockable_account_pw` + +The details for these fixtures are as follows. + + +### Fixture Details + +#### `web3` + +A Web3 instance configured to connect to the backend for the integration test. + + +#### `empty_block` + +A block as returned by `web3.vns.getBlock` that has no transactions. + + +#### `block_with_txn` + +A block as returned by `web3.vns.getBlock` that has a single transaction. + + +#### `math_contract` + +An deployed Contract instance of the *Math* contract found in +`web3._utils.module_testing.math_contract`. + + +#### `unlocked_account` + +The address of an account which is *unlocked*. + + +#### `funded_account_for_raw_txn` + +An account which has not sent any transactions (nonce of 0) which has enough +ether for sending a single transaction. + + +#### `math_contract_deploy_txn_hash` + +The transaction hash used to deploy the `math_contract`. + + +#### `mined_txn_hash` + +The transaction hash of a transaction which has been mined. + + +#### `emitter_contract` + +An deployed Contract instance of the *Emitter* contract found in +`web3._utils.module_testing.emitter_contract`. + + +#### `block_with_txn_with_log` + +A block with a transaction with a single log entry. + + +#### `txn_hash_with_log` + +The hash of a transaction which fires a single log entry. + + +#### `unlockable_account` + +The address of an account that can be unlocked using the `unlockable_account_pw` + + +#### `unlockable_account_pw` + +The password that can be used to unlock the `unlockable_account` + + +# Updating Fixture ZIPs + +**ONLY** trusted parties should be allowed to update zipped fixtures, since they pose an attack surface through which a third party could inject malicious code into the codebase. diff --git a/tests/integration/common.py b/tests/integration/common.py index 6ce2a8812b..b91cfa6087 100644 --- a/tests/integration/common.py +++ b/tests/integration/common.py @@ -1,17 +1,17 @@ -import pytest - -from websockets.exceptions import ( - ConnectionClosed, -) - -from web3 import Web3 - - -class MiscWebsocketTest: - - def test_websocket_max_size_error(self, web3, endpoint_uri): - w3 = Web3(Web3.WebsocketProvider( - endpoint_uri=endpoint_uri, websocket_kwargs={'max_size': 1}) - ) - with pytest.raises(ConnectionClosed): - w3.eth.getBlock(0) +import pytest + +from websockets.exceptions import ( + ConnectionClosed, +) + +from web3 import Web3 + + +class MiscWebsocketTest: + + def test_websocket_max_size_error(self, web3, endpoint_uri): + w3 = Web3 (Web3.WebsocketProvider( + endpoint_uri=endpoint_uri, websocket_kwargs={'max_size': 1}) + ) + with pytest.raises(ConnectionClosed): + w3.vns.getBlock(0) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 0b50516ceb..1e8b6a8741 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,30 +1,30 @@ -import asyncio -import pytest - -from web3._utils.module_testing.emitter_contract import ( - CONTRACT_EMITTER_ABI, - CONTRACT_EMITTER_CODE, -) -from web3._utils.module_testing.math_contract import ( - MATH_ABI, - MATH_BYTECODE, -) - - -@pytest.fixture(scope="module") -def math_contract_factory(web3): - contract_factory = web3.eth.contract(abi=MATH_ABI, bytecode=MATH_BYTECODE) - return contract_factory - - -@pytest.fixture(scope="module") -def emitter_contract_factory(web3): - contract_factory = web3.eth.contract(abi=CONTRACT_EMITTER_ABI, bytecode=CONTRACT_EMITTER_CODE) - return contract_factory - - -@pytest.yield_fixture(scope="module") -def event_loop(request): - loop = asyncio.get_event_loop_policy().new_event_loop() - yield loop - loop.close() +import asyncio +import pytest + +from web3._utils.module_testing.emitter_contract import ( + EMITTER_ABI, + EMITTER_BYTECODE, +) +from web3._utils.module_testing.math_contract import ( + MATH_ABI, + MATH_BYTECODE, +) + + +@pytest.fixture(scope="module") +def math_contract_factory(web3): + contract_factory = web3.vns.contract(abi=MATH_ABI, bytecode=MATH_BYTECODE) + return contract_factory + + +@pytest.fixture(scope="module") +def emitter_contract_factory(web3): + contract_factory = web3.vns.contract(abi=EMITTER_ABI, bytecode=EMITTER_BYTECODE) + return contract_factory + + +@pytest.yield_fixture(scope="module") +def event_loop(request): + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() diff --git a/tests/integration/generate_fixtures/common.py b/tests/integration/generate_fixtures/common.py index 4a1e490f80..9a228c3327 100644 --- a/tests/integration/generate_fixtures/common.py +++ b/tests/integration/generate_fixtures/common.py @@ -1,239 +1,239 @@ -import contextlib -import os -import shutil -import signal -import socket -import subprocess -import tempfile -import time - -from eth_utils import ( - is_checksum_address, - to_text, -) - -from web3.exceptions import ( - TransactionNotFound, -) - -COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' -COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' - -KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501 - -KEYFILE_PW = 'web3py-test' -KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501 - -RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' - -UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' -UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13' -UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW - -GENESIS_DATA = { - "config": { - "chainId": 1337, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 10, - "eip158Block": 10, - "eip160Block": 10, - }, - "nonce": "0x0000000000000042", - "alloc": { - COINBASE: { - "balance": "1000000000000000000000000000" - }, - UNLOCKABLE_ACCOUNT: { - "balance": "1000000000000000000000000000" - }, - RAW_TXN_ACCOUNT: { - "balance": "1000000000000000000000000000" - } - }, - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", - "gasLimit": "0x1000000", - "difficulty": "0x10000", - "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": COINBASE -} - - -def ensure_path_exists(dir_path): - """ - Make sure that a path exists - """ - if not os.path.exists(dir_path): - os.makedirs(dir_path) - return True - return False - - -@contextlib.contextmanager -def tempdir(): - dir_path = tempfile.mkdtemp() - try: - yield dir_path - finally: - shutil.rmtree(dir_path) - - -def get_geth_binary(): - from geth.install import ( - get_executable_path, - install_geth, - ) - - if 'GETH_BINARY' in os.environ: - return os.environ['GETH_BINARY'] - elif 'GETH_VERSION' in os.environ: - geth_version = os.environ['GETH_VERSION'] - _geth_binary = get_executable_path(geth_version) - if not os.path.exists(_geth_binary): - install_geth(geth_version) - assert os.path.exists(_geth_binary) - return _geth_binary - else: - return 'geth' - - -def wait_for_popen(proc, timeout): - start = time.time() - while time.time() < start + timeout: - if proc.poll() is None: - time.sleep(0.01) - else: - break - - -def kill_proc_gracefully(proc): - if proc.poll() is None: - proc.send_signal(signal.SIGINT) - wait_for_popen(proc, 13) - - if proc.poll() is None: - proc.terminate() - wait_for_popen(proc, 5) - - if proc.poll() is None: - proc.kill() - wait_for_popen(proc, 2) - - -def wait_for_socket(ipc_path, timeout=30): - start = time.time() - while time.time() < start + timeout: - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(ipc_path) - sock.settimeout(timeout) - except (FileNotFoundError, socket.error): - time.sleep(0.01) - else: - break - - -@contextlib.contextmanager -def get_geth_process(geth_binary, - datadir, - genesis_file_path, - ipc_path, - port, - networkid, - skip_init=False): - if not skip_init: - init_datadir_command = ( - geth_binary, - '--datadir', datadir, - 'init', - genesis_file_path, - ) - print(' '.join(init_datadir_command)) - subprocess.check_output( - init_datadir_command, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - - run_geth_command = ( - geth_binary, - '--datadir', datadir, - '--ipcpath', ipc_path, - '--nodiscover', - '--port', port, - '--networkid', networkid, - '--etherbase', COINBASE[2:], - ) - print(' '.join(run_geth_command)) - try: - proc = get_process(run_geth_command) - yield proc - finally: - kill_proc_gracefully(proc) - output, errors = proc.communicate() - print( - "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -def get_process(run_command): - proc = subprocess.Popen( - run_command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - bufsize=1, - ) - return proc - - -def mine_block(web3): - origin_block_number = web3.eth.blockNumber - - start_time = time.time() - web3.geth.miner.start(1) - while time.time() < start_time + 120: - block_number = web3.eth.blockNumber - if block_number > origin_block_number: - web3.geth.miner.stop() - return block_number - else: - time.sleep(0.1) - else: - raise ValueError("No block mined during wait period") - - -def mine_transaction_hash(web3, txn_hash): - start_time = time.time() - web3.geth.miner.start(1) - while time.time() < start_time + 120: - try: - receipt = web3.eth.getTransactionReceipt(txn_hash) - except TransactionNotFound: - continue - if receipt is not None: - web3.geth.miner.stop() - return receipt - else: - time.sleep(0.1) - else: - raise ValueError("Math contract deploy transaction not mined during wait period") - - -def deploy_contract(web3, name, factory): - web3.geth.personal.unlockAccount(web3.eth.coinbase, KEYFILE_PW) - deploy_txn_hash = factory.constructor().transact({'from': web3.eth.coinbase}) - print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) - deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash) - print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) - contract_address = deploy_receipt['contractAddress'] - assert is_checksum_address(contract_address) - print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) - return deploy_receipt +import contextlib +import os +import shutil +import signal +import socket +import subprocess +import tempfile +import time + +from vns_utils import ( + is_checksum_address, + to_text, +) + +from web3.exceptions import ( + TransactionNotFound, +) + +COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' +COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' + +KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501 + +KEYFILE_PW = 'web3py-test' +KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501 + +RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' + +UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' +UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13' +UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW + +GENESIS_DATA = { + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 10, + "eip158Block": 10, + "eip160Block": 10, + }, + "nonce": "0x0000000000000042", + "alloc": { + COINBASE: { + "balance": "1000000000000000000000000000" + }, + UNLOCKABLE_ACCOUNT: { + "balance": "1000000000000000000000000000" + }, + RAW_TXN_ACCOUNT: { + "balance": "1000000000000000000000000000" + } + }, + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", + "gasLimit": "0x1000000", + "difficulty": "0x10000", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": COINBASE +} + + +def ensure_path_exists(dir_path): + """ + Make sure that a path exists + """ + if not os.path.exists(dir_path): + os.makedirs(dir_path) + return True + return False + + +@contextlib.contextmanager +def tempdir(): + dir_path = tempfile.mkdtemp() + try: + yield dir_path + finally: + shutil.rmtree(dir_path) + + +def get_geth_binary(): + from geth.install import ( + get_executable_path, + install_geth, + ) + + if 'GETH_BINARY' in os.environ: + return os.environ['GETH_BINARY'] + elif 'GETH_VERSION' in os.environ: + geth_version = os.environ['GETH_VERSION'] + _geth_binary = get_executable_path(geth_version) + if not os.path.exists(_geth_binary): + install_geth(geth_version) + assert os.path.exists(_geth_binary) + return _geth_binary + else: + return 'geth' + + +def wait_for_popen(proc, timeout): + start = time.time() + while time.time() < start + timeout: + if proc.poll() is None: + time.sleep(0.01) + else: + break + + +def kill_proc_gracefully(proc): + if proc.poll() is None: + proc.send_signal(signal.SIGINT) + wait_for_popen(proc, 13) + + if proc.poll() is None: + proc.terminate() + wait_for_popen(proc, 5) + + if proc.poll() is None: + proc.kill() + wait_for_popen(proc, 2) + + +def wait_for_socket(ipc_path, timeout=30): + start = time.time() + while time.time() < start + timeout: + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(ipc_path) + sock.settimeout(timeout) + except (FileNotFoundError, socket.error): + time.sleep(0.01) + else: + break + + +@contextlib.contextmanager +def get_geth_process(geth_binary, + datadir, + genesis_file_path, + ipc_path, + port, + networkid, + skip_init=False): + if not skip_init: + init_datadir_command = ( + geth_binary, + '--datadir', datadir, + 'init', + genesis_file_path, + ) + print(' '.join(init_datadir_command)) + subprocess.check_output( + init_datadir_command, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + run_geth_command = ( + geth_binary, + '--datadir', datadir, + '--ipcpath', ipc_path, + '--nodiscover', + '--port', port, + '--networkid', networkid, + '--etherbase', COINBASE[2:], + ) + print(' '.join(run_geth_command)) + try: + proc = get_process(run_geth_command) + yield proc + finally: + kill_proc_gracefully(proc) + output, errors = proc.communicate() + print( + "Geth Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +def get_process(run_command): + proc = subprocess.Popen( + run_command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=1, + ) + return proc + + +def mine_block(web3): + origin_block_number = web3.vns.blockNumber + + start_time = time.time() + web3.geth.miner.start(1) + while time.time() < start_time + 120: + block_number = web3.vns.blockNumber + if block_number > origin_block_number: + web3.geth.miner.stop() + return block_number + else: + time.sleep(0.1) + else: + raise ValueError("No block mined during wait period") + + +def mine_transaction_hash(web3, txn_hash): + start_time = time.time() + web3.geth.miner.start(1) + while time.time() < start_time + 120: + try: + receipt = web3.vns.getTransactionReceipt(txn_hash) + except TransactionNotFound: + continue + if receipt is not None: + web3.geth.miner.stop() + return receipt + else: + time.sleep(0.1) + else: + raise ValueError("Math contract deploy transaction not mined during wait period") + + +def deploy_contract(web3, name, factory): + web3.geth.personal.unlockAccount(web3.vns.coinbase, KEYFILE_PW) + deploy_txn_hash = factory.constructor().transact({'from': web3.vns.coinbase}) + print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) + deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash) + print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) + contract_address = deploy_receipt['contractAddress'] + assert is_checksum_address(contract_address) + print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) + return deploy_receipt diff --git a/tests/integration/generate_fixtures/go_ethereum.py b/tests/integration/generate_fixtures/go_ethereum.py index 9f593ad446..666820f902 100644 --- a/tests/integration/generate_fixtures/go_ethereum.py +++ b/tests/integration/generate_fixtures/go_ethereum.py @@ -1,153 +1,153 @@ -import contextlib -import json -import os -import pprint -import shutil -import sys - -from eth_utils import ( - is_dict, - is_same_address, -) -from eth_utils.toolz import ( - merge, -) - -import common -from tests.utils import ( - get_open_port, -) -from web3 import Web3 -from web3._utils.module_testing.emitter_contract import ( - EMITTER_ABI, - EMITTER_BYTECODE, - EMITTER_ENUM, -) -from web3._utils.module_testing.math_contract import ( - MATH_ABI, - MATH_BYTECODE, -) - -# this script is used for generating the parity fixture -# to generate geth fixtures use tests/generate_go_ethereum_fixture.py - - -def generate_go_ethereum_fixture(destination_dir): - with contextlib.ExitStack() as stack: - datadir = stack.enter_context(common.tempdir()) - - keystore_dir = os.path.join(datadir, 'keystore') - common.ensure_path_exists(keystore_dir) - keyfile_path = os.path.join(keystore_dir, common.KEYFILE_FILENAME) - with open(keyfile_path, 'w') as keyfile: - keyfile.write(common.KEYFILE_DATA) - genesis_file_path = os.path.join(datadir, 'genesis.json') - with open(genesis_file_path, 'w') as genesis_file: - genesis_file.write(json.dumps(common.GENESIS_DATA)) - - geth_ipc_path_dir = stack.enter_context(common.tempdir()) - geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') - - geth_port = get_open_port() - geth_binary = common.get_geth_binary() - - geth_proc = stack.enter_context(common.get_geth_process( # noqa: F841 - geth_binary=geth_binary, - datadir=datadir, - genesis_file_path=genesis_file_path, - ipc_path=geth_ipc_path, - port=geth_port, - networkid=str(common.GENESIS_DATA['config']['chainId']) - )) - - common.wait_for_socket(geth_ipc_path) - web3 = Web3(Web3.IPCProvider(geth_ipc_path)) - chain_data = setup_chain_state(web3) - static_data = { - 'raw_txn_account': common.RAW_TXN_ACCOUNT, - 'keyfile_pw': common.KEYFILE_PW, - } - pprint.pprint(merge(chain_data, static_data)) - - shutil.make_archive(destination_dir, 'zip', datadir) - - -def setup_chain_state(web3): - coinbase = web3.eth.coinbase - - assert is_same_address(coinbase, common.COINBASE) - - # - # Math Contract - # - math_contract_factory = web3.eth.contract( - abi=MATH_ABI, - bytecode=MATH_BYTECODE, - ) - math_deploy_receipt = common.deploy_contract(web3, 'math', math_contract_factory) - assert is_dict(math_deploy_receipt) - - # - # Emitter Contract - # - emitter_contract_factory = web3.eth.contract( - abi=EMITTER_ABI, - bytecode=EMITTER_BYTECODE, - ) - emitter_deploy_receipt = common.deploy_contract(web3, 'emitter', emitter_contract_factory) - emitter_contract = emitter_contract_factory(emitter_deploy_receipt['contractAddress']) - - txn_hash_with_log = emitter_contract.functions.logDouble( - which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, - ).transact({ - 'from': web3.eth.coinbase, - }) - print('TXN_HASH_WITH_LOG:', txn_hash_with_log) - txn_receipt_with_log = common.mine_transaction_hash(web3, txn_hash_with_log) - block_with_log = web3.eth.getBlock(txn_receipt_with_log['blockHash']) - print('BLOCK_HASH_WITH_LOG:', block_with_log['hash']) - - # - # Empty Block - # - empty_block_number = common.mine_block(web3) - print('MINED_EMPTY_BLOCK') - empty_block = web3.eth.getBlock(empty_block_number) - assert is_dict(empty_block) - assert not empty_block['transactions'] - print('EMPTY_BLOCK_HASH:', empty_block['hash']) - - # - # Block with Transaction - # - web3.geth.personal.unlockAccount(coinbase, common.KEYFILE_PW) - web3.geth.miner.start(1) - mined_txn_hash = web3.eth.sendTransaction({ - 'from': coinbase, - 'to': coinbase, - 'value': 1, - 'gas': 21000, - 'gas_price': web3.eth.gasPrice, - }) - mined_txn_receipt = common.mine_transaction_hash(web3, mined_txn_hash) - print('MINED_TXN_HASH:', mined_txn_hash) - block_with_txn = web3.eth.getBlock(mined_txn_receipt['blockHash']) - print('BLOCK_WITH_TXN_HASH:', block_with_txn['hash']) - - geth_fixture = { - 'math_deploy_txn_hash': math_deploy_receipt['transactionHash'], - 'math_address': math_deploy_receipt['contractAddress'], - 'emitter_deploy_txn_hash': emitter_deploy_receipt['transactionHash'], - 'emitter_address': emitter_deploy_receipt['contractAddress'], - 'txn_hash_with_log': txn_hash_with_log, - 'block_hash_with_log': block_with_log['hash'], - 'empty_block_hash': empty_block['hash'], - 'mined_txn_hash': mined_txn_hash, - 'block_with_txn_hash': block_with_txn['hash'], - } - return geth_fixture - - -if __name__ == '__main__': - fixture_dir = sys.argv[1] - generate_go_ethereum_fixture(fixture_dir) +import contextlib +import json +import os +import pprint +import shutil +import sys + +from vns_utils import ( + is_dict, + is_same_address, +) + +import common +from tests.utils import ( + get_open_port, +) +from web3 import Web3 +from web3._utils.module_testing.emitter_contract import ( + EMITTER_ABI, + EMITTER_BYTECODE, + EMITTER_ENUM, +) +from web3._utils.module_testing.math_contract import ( + MATH_ABI, + MATH_BYTECODE, +) +from web3._utils.toolz import ( + merge, +) + +# this script is used for generating the parity fixture +# to generate geth fixtures use tests/generate_go_ethereum_fixture.py + + +def generate_go_ethereum_fixture(destination_dir): + with contextlib.ExitStack() as stack: + datadir = stack.enter_context(common.tempdir()) + + keystore_dir = os.path.join(datadir, 'keystore') + common.ensure_path_exists(keystore_dir) + keyfile_path = os.path.join(keystore_dir, common.KEYFILE_FILENAME) + with open(keyfile_path, 'w') as keyfile: + keyfile.write(common.KEYFILE_DATA) + genesis_file_path = os.path.join(datadir, 'genesis.json') + with open(genesis_file_path, 'w') as genesis_file: + genesis_file.write(json.dumps(common.GENESIS_DATA)) + + geth_ipc_path_dir = stack.enter_context(common.tempdir()) + geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') + + geth_port = get_open_port() + geth_binary = common.get_geth_binary() + + geth_proc = stack.enter_context(common.get_geth_process( # noqa: F841 + geth_binary=geth_binary, + datadir=datadir, + genesis_file_path=genesis_file_path, + ipc_path=geth_ipc_path, + port=geth_port, + networkid=str(common.GENESIS_DATA['config']['chainId']) + )) + + common.wait_for_socket(geth_ipc_path) + web3 = Web3 (Web3.IPCProvider(geth_ipc_path)) + chain_data = setup_chain_state(web3) + static_data = { + 'raw_txn_account': common.RAW_TXN_ACCOUNT, + 'keyfile_pw': common.KEYFILE_PW, + } + pprint.pprint(merge(chain_data, static_data)) + + shutil.make_archive(destination_dir, 'zip', datadir) + + +def setup_chain_state(web3): + coinbase = web3.vns.coinbase + + assert is_same_address(coinbase, common.COINBASE) + + # + # Math Contract + # + math_contract_factory = web3.vns.contract( + abi=MATH_ABI, + bytecode=MATH_BYTECODE, + ) + math_deploy_receipt = common.deploy_contract(web3, 'math', math_contract_factory) + assert is_dict(math_deploy_receipt) + + # + # Emitter Contract + # + emitter_contract_factory = web3.vns.contract( + abi=EMITTER_ABI, + bytecode=EMITTER_BYTECODE, + ) + emitter_deploy_receipt = common.deploy_contract(web3, 'emitter', emitter_contract_factory) + emitter_contract = emitter_contract_factory(emitter_deploy_receipt['contractAddress']) + + txn_hash_with_log = emitter_contract.functions.logDouble( + which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, + ).transact({ + 'from': web3.vns.coinbase, + }) + print('TXN_HASH_WITH_LOG:', txn_hash_with_log) + txn_receipt_with_log = common.mine_transaction_hash(web3, txn_hash_with_log) + block_with_log = web3.vns.getBlock(txn_receipt_with_log['blockHash']) + print('BLOCK_HASH_WITH_LOG:', block_with_log['hash']) + + # + # Empty Block + # + empty_block_number = common.mine_block(web3) + print('MINED_EMPTY_BLOCK') + empty_block = web3.vns.getBlock(empty_block_number) + assert is_dict(empty_block) + assert not empty_block['transactions'] + print('EMPTY_BLOCK_HASH:', empty_block['hash']) + + # + # Block with Transaction + # + web3.geth.personal.unlockAccount(coinbase, common.KEYFILE_PW) + web3.geth.miner.start(1) + mined_txn_hash = web3.vns.sendTransaction({ + 'from': coinbase, + 'to': coinbase, + 'value': 1, + 'gas': 21000, + 'gas_price': web3.vns.gasPrice, + }) + mined_txn_receipt = common.mine_transaction_hash(web3, mined_txn_hash) + print('MINED_TXN_HASH:', mined_txn_hash) + block_with_txn = web3.vns.getBlock(mined_txn_receipt['blockHash']) + print('BLOCK_WITH_TXN_HASH:', block_with_txn['hash']) + + geth_fixture = { + 'math_deploy_txn_hash': math_deploy_receipt['transactionHash'], + 'math_address': math_deploy_receipt['contractAddress'], + 'emitter_deploy_txn_hash': emitter_deploy_receipt['transactionHash'], + 'emitter_address': emitter_deploy_receipt['contractAddress'], + 'txn_hash_with_log': txn_hash_with_log, + 'block_hash_with_log': block_with_log['hash'], + 'empty_block_hash': empty_block['hash'], + 'mined_txn_hash': mined_txn_hash, + 'block_with_txn_hash': block_with_txn['hash'], + } + return geth_fixture + + +if __name__ == '__main__': + fixture_dir = sys.argv[1] + generate_go_ethereum_fixture(fixture_dir) diff --git a/tests/integration/generate_fixtures/parity.py b/tests/integration/generate_fixtures/parity.py index 903640530f..8b79f16bf9 100644 --- a/tests/integration/generate_fixtures/parity.py +++ b/tests/integration/generate_fixtures/parity.py @@ -1,288 +1,288 @@ -import contextlib -import json -import os -import pprint -import shutil -import sys -import time - -from eth_utils import ( - to_text, -) -from eth_utils.toolz import ( - merge, -) - -import common -import go_ethereum -from tests.utils import ( - get_open_port, -) -from web3 import Web3 - -CHAIN_CONFIG = { - "name": "CrossClient", - "dataDir": "CrossClient", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": 0, - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2", - "eip150Transition": 0, - "eip155Transition": 10, - "eip160Transition": 10, - "eip161abcTransition": 10, - "eip161dTransition": 10, - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "0x539", - "eip98Transition": "0x7fffffffffffffff", - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x10000", - "author": common.COINBASE, - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", - "gasLimit": "0x1000000" - }, - "accounts": { - common.COINBASE: { - "balance": "1000000000000000000000000000", - "nonce": "0", - "builtin": { - "name": "ecrecover", "pricing": {"linear": {"base": 3000, "word": 0}} - } - }, - common.UNLOCKABLE_ACCOUNT: { - "balance": "1000000000000000000000000000", - "nonce": "0", - "builtin": { - "name": "sha256", "pricing": {"linear": {"base": 60, "word": 12}} - } - }, - common.RAW_TXN_ACCOUNT: { - "balance": "1000000000000000000000000000", - "nonce": "0", - "builtin": { - "name": "ripemd160", - "pricing": {"linear": {"base": 600, "word": 120}} - } - } - } -} - - -def get_parity_binary(): - return 'parity' - - -@contextlib.contextmanager -def get_parity_process( - parity_binary, - datadir, - ipc_path, - keys_path, - chain_config_file_path, - parity_port): - - run_command = ( - parity_binary, - '--base-path', datadir, - '--ipc-path', ipc_path, - '--no-ws', - '--no-warp', - '--chain', chain_config_file_path, - '--keys-path', keys_path, - '--jsonrpc-apis', 'all', - '--jsonrpc-port', parity_port, - '--fat-db', 'on', - ) - print(' '.join(run_command)) - try: - proc = common.get_process(run_command) - yield proc - finally: - common.kill_proc_gracefully(proc) - output, errors = proc.communicate() - print( - "Parity Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -@contextlib.contextmanager -def parity_export_blocks_process( - parity_binary, - datadir, - chain_config_file_path, - parity_port): - - run_command = ( - parity_binary, - 'export', - 'blocks', os.path.join(datadir, 'blocks_export.rlp'), - '--base-path', datadir, - '--no-ws', - '--no-warp', - '--chain', chain_config_file_path, - '--jsonrpc-apis', 'all', - '--jsonrpc-port', parity_port, - '--fat-db', 'on', - ) - print(' '.join(run_command)) - try: - proc = common.get_process(run_command) - yield proc - finally: - time.sleep(10) - common.kill_proc_gracefully(proc) - output, errors = proc.communicate() - print( - "Parity Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -def generate_parity_fixture(destination_dir): - """ - The parity fixture generation strategy is to start a geth client with - existing fixtures copied into a temp datadir. Then a parity client - is started is peered with the geth client. - """ - with contextlib.ExitStack() as stack: - - geth_datadir = stack.enter_context(common.tempdir()) - - geth_port = get_open_port() - - geth_ipc_path_dir = stack.enter_context(common.tempdir()) - geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') - - geth_keystore_dir = os.path.join(geth_datadir, 'keystore') - common.ensure_path_exists(geth_keystore_dir) - geth_keyfile_path = os.path.join(geth_keystore_dir, common.KEYFILE_FILENAME) - with open(geth_keyfile_path, 'w') as keyfile: - keyfile.write(common.KEYFILE_DATA) - - genesis_file_path = os.path.join(geth_datadir, 'genesis.json') - with open(genesis_file_path, 'w') as genesis_file: - genesis_file.write(json.dumps(common.GENESIS_DATA)) - - stack.enter_context( - common.get_geth_process( - common.get_geth_binary(), - geth_datadir, - genesis_file_path, - geth_ipc_path, - geth_port, - str(CHAIN_CONFIG['params']['networkID'])) - ) - # set up fixtures - common.wait_for_socket(geth_ipc_path) - web3_geth = Web3(Web3.IPCProvider(geth_ipc_path)) - chain_data = go_ethereum.setup_chain_state(web3_geth) - fixture_block_count = web3_geth.eth.blockNumber - - datadir = stack.enter_context(common.tempdir()) - - keystore_dir = os.path.join(datadir, 'keys') - os.makedirs(keystore_dir, exist_ok=True) - parity_keyfile_path = os.path.join(keystore_dir, common.KEYFILE_FILENAME) - with open(parity_keyfile_path, 'w') as keyfile: - keyfile.write(common.KEYFILE_DATA) - - chain_config_file_path = os.path.join(datadir, 'chain_config.json') - with open(chain_config_file_path, 'w') as chain_file: - chain_file.write(json.dumps(CHAIN_CONFIG)) - - parity_ipc_path_dir = stack.enter_context(common.tempdir()) - parity_ipc_path = os.path.join(parity_ipc_path_dir, 'jsonrpc.ipc') - - parity_port = get_open_port() - parity_binary = get_parity_binary() - - parity_proc = stack.enter_context(get_parity_process( # noqa: F841 - parity_binary=parity_binary, - datadir=datadir, - ipc_path=parity_ipc_path, - keys_path=keystore_dir, - chain_config_file_path=chain_config_file_path, - parity_port=parity_port, - )) - - common.wait_for_socket(parity_ipc_path) - web3 = Web3(Web3.IPCProvider(parity_ipc_path)) - - time.sleep(10) - connect_nodes(web3, web3_geth) - time.sleep(10) - wait_for_chain_sync(web3, fixture_block_count) - - static_data = { - 'raw_txn_account': common.RAW_TXN_ACCOUNT, - 'keyfile_pw': common.KEYFILE_PW, - } - pprint.pprint(merge(chain_data, static_data)) - - shutil.copytree(datadir, destination_dir) - - parity_proc = stack.enter_context(parity_export_blocks_process( # noqa: F841 - parity_binary=parity_binary, - datadir=destination_dir, - chain_config_file_path=os.path.join(destination_dir, 'chain_config.json'), - parity_port=parity_port, - )) - - time.sleep(10) - shutil.make_archive(destination_dir, 'zip', destination_dir) - shutil.rmtree(destination_dir) - - -def connect_nodes(w3_parity, w3_secondary): - parity_peers = w3_parity.parity.netPeers() - parity_enode = w3_parity.parity.enode() - secondary_node_info = w3_secondary.geth.admin.node_info() - if secondary_node_info['id'] not in (node.get('id', tuple()) for node in parity_peers['peers']): - w3_secondary.geth.admin.add_peer(parity_enode) - - -def wait_for_chain_sync(web3, target): - start_time = time.time() - while time.time() < start_time + 120: - current_block_number = web3.eth.blockNumber - if current_block_number >= target: - break - else: - time.sleep(0.1) - else: - raise ValueError("Not synced after wait period") - - -if __name__ == '__main__': - fixture_dir = sys.argv[1] - generate_parity_fixture(fixture_dir) +import contextlib +import json +import os +import pprint +import shutil +import sys +import time + +from vns_utils import ( + to_text, +) + +import common +import go_ethereum +from tests.utils import ( + get_open_port, +) +from web3 import Web3 +from web3._utils.toolz import ( + merge, +) + +CHAIN_CONFIG = { + "name": "CrossClient", + "dataDir": "CrossClient", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": 0, + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2", + "eip150Transition": 0, + "eip155Transition": 10, + "eip160Transition": 10, + "eip161abcTransition": 10, + "eip161dTransition": 10, + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x539", + "eip98Transition": "0x7fffffffffffffff", + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x10000", + "author": common.COINBASE, + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", + "gasLimit": "0x1000000" + }, + "accounts": { + common.COINBASE: { + "balance": "1000000000000000000000000000", + "nonce": "0", + "builtin": { + "name": "ecrecover", "pricing": {"linear": {"base": 3000, "word": 0}} + } + }, + common.UNLOCKABLE_ACCOUNT: { + "balance": "1000000000000000000000000000", + "nonce": "0", + "builtin": { + "name": "sha256", "pricing": {"linear": {"base": 60, "word": 12}} + } + }, + common.RAW_TXN_ACCOUNT: { + "balance": "1000000000000000000000000000", + "nonce": "0", + "builtin": { + "name": "ripemd160", + "pricing": {"linear": {"base": 600, "word": 120}} + } + } + } +} + + +def get_parity_binary(): + return 'parity' + + +@contextlib.contextmanager +def get_parity_process( + parity_binary, + datadir, + ipc_path, + keys_path, + chain_config_file_path, + parity_port): + + run_command = ( + parity_binary, + '--base-path', datadir, + '--ipc-path', ipc_path, + '--no-ws', + '--no-warp', + '--chain', chain_config_file_path, + '--keys-path', keys_path, + '--jsonrpc-apis', 'all', + '--jsonrpc-port', parity_port, + '--fat-db', 'on', + ) + print(' '.join(run_command)) + try: + proc = common.get_process(run_command) + yield proc + finally: + common.kill_proc_gracefully(proc) + output, errors = proc.communicate() + print( + "Parity Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +@contextlib.contextmanager +def parity_export_blocks_process( + parity_binary, + datadir, + chain_config_file_path, + parity_port): + + run_command = ( + parity_binary, + 'export', + 'blocks', os.path.join(datadir, 'blocks_export.rlp'), + '--base-path', datadir, + '--no-ws', + '--no-warp', + '--chain', chain_config_file_path, + '--jsonrpc-apis', 'all', + '--jsonrpc-port', parity_port, + '--fat-db', 'on', + ) + print(' '.join(run_command)) + try: + proc = common.get_process(run_command) + yield proc + finally: + time.sleep(10) + common.kill_proc_gracefully(proc) + output, errors = proc.communicate() + print( + "Parity Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +def generate_parity_fixture(destination_dir): + """ + The parity fixture generation strategy is to start a geth client with + existing fixtures copied into a temp datadir. Then a parity client + is started is peered with the geth client. + """ + with contextlib.ExitStack() as stack: + + geth_datadir = stack.enter_context(common.tempdir()) + + geth_port = get_open_port() + + geth_ipc_path_dir = stack.enter_context(common.tempdir()) + geth_ipc_path = os.path.join(geth_ipc_path_dir, 'geth.ipc') + + geth_keystore_dir = os.path.join(geth_datadir, 'keystore') + common.ensure_path_exists(geth_keystore_dir) + geth_keyfile_path = os.path.join(geth_keystore_dir, common.KEYFILE_FILENAME) + with open(geth_keyfile_path, 'w') as keyfile: + keyfile.write(common.KEYFILE_DATA) + + genesis_file_path = os.path.join(geth_datadir, 'genesis.json') + with open(genesis_file_path, 'w') as genesis_file: + genesis_file.write(json.dumps(common.GENESIS_DATA)) + + stack.enter_context( + common.get_geth_process( + common.get_geth_binary(), + geth_datadir, + genesis_file_path, + geth_ipc_path, + geth_port, + str(CHAIN_CONFIG['params']['networkID'])) + ) + # set up fixtures + common.wait_for_socket(geth_ipc_path) + web3_geth = Web3 (Web3.IPCProvider(geth_ipc_path)) + chain_data = go_ethereum.setup_chain_state(web3_geth) + fixture_block_count = web3_geth.vns.blockNumber + + datadir = stack.enter_context(common.tempdir()) + + keystore_dir = os.path.join(datadir, 'keys') + os.makedirs(keystore_dir, exist_ok=True) + parity_keyfile_path = os.path.join(keystore_dir, common.KEYFILE_FILENAME) + with open(parity_keyfile_path, 'w') as keyfile: + keyfile.write(common.KEYFILE_DATA) + + chain_config_file_path = os.path.join(datadir, 'chain_config.json') + with open(chain_config_file_path, 'w') as chain_file: + chain_file.write(json.dumps(CHAIN_CONFIG)) + + parity_ipc_path_dir = stack.enter_context(common.tempdir()) + parity_ipc_path = os.path.join(parity_ipc_path_dir, 'jsonrpc.ipc') + + parity_port = get_open_port() + parity_binary = get_parity_binary() + + parity_proc = stack.enter_context(get_parity_process( # noqa: F841 + parity_binary=parity_binary, + datadir=datadir, + ipc_path=parity_ipc_path, + keys_path=keystore_dir, + chain_config_file_path=chain_config_file_path, + parity_port=parity_port, + )) + + common.wait_for_socket(parity_ipc_path) + web3 = Web3 (Web3.IPCProvider(parity_ipc_path)) + + time.sleep(10) + connect_nodes(web3, web3_geth) + time.sleep(10) + wait_for_chain_sync(web3, fixture_block_count) + + static_data = { + 'raw_txn_account': common.RAW_TXN_ACCOUNT, + 'keyfile_pw': common.KEYFILE_PW, + } + pprint.pprint(merge(chain_data, static_data)) + + shutil.copytree(datadir, destination_dir) + + parity_proc = stack.enter_context(parity_export_blocks_process( # noqa: F841 + parity_binary=parity_binary, + datadir=destination_dir, + chain_config_file_path=os.path.join(destination_dir, 'chain_config.json'), + parity_port=parity_port, + )) + + time.sleep(10) + shutil.make_archive(destination_dir, 'zip', destination_dir) + shutil.rmtree(destination_dir) + + +def connect_nodes(w3_parity, w3_secondary): + parity_peers = w3_parity.parity.netPeers() + parity_enode = w3_parity.parity.enode() + secondary_node_info = w3_secondary.geth.admin.nodeInfo() + if secondary_node_info['id'] not in (node.get('id', tuple()) for node in parity_peers['peers']): + w3_secondary.geth.admin.addPeer(parity_enode) + + +def wait_for_chain_sync(web3, target): + start_time = time.time() + while time.time() < start_time + 120: + current_block_number = web3.vns.blockNumber + if current_block_number >= target: + break + else: + time.sleep(0.1) + else: + raise ValueError("Not synced after wait period") + + +if __name__ == '__main__': + fixture_dir = sys.argv[1] + generate_parity_fixture(fixture_dir) diff --git a/tests/integration/go_ethereum/common.py b/tests/integration/go_ethereum/common.py index 08bdafb57c..7f4eeff0ea 100644 --- a/tests/integration/go_ethereum/common.py +++ b/tests/integration/go_ethereum/common.py @@ -1,126 +1,97 @@ -import pytest - -from web3._utils.module_testing import ( # noqa: F401 - EthModuleTest, - GoEthereumAdminModuleTest, - GoEthereumPersonalModuleTest, - GoEthereumShhModuleTest, - NetModuleTest, - VersionModuleTest, - Web3ModuleTest, -) - - -class GoEthereumTest(Web3ModuleTest): - def _check_web3_clientVersion(self, client_version): - assert client_version.startswith('Geth/') - - -class GoEthereumEthModuleTest(EthModuleTest): - @pytest.mark.xfail(reason='Needs ability to efficiently control mining') - def test_eth_replaceTransaction(self, web3, unlocked_account): - super().test_eth_replaceTransaction(web3, unlocked_account) - - def test_eth_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): - super().test_eth_replaceTransaction_incorrect_nonce(web3, unlocked_account) - - def test_eth_replaceTransaction_gas_price_too_low(self, web3, unlocked_account): - super().test_eth_replaceTransaction_gas_price_too_low(web3, unlocked_account) - - @pytest.mark.xfail(reason='Needs ability to efficiently control mining') - def test_eth_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) - - @pytest.mark.xfail(reason='Needs ability to efficiently control mining') - def test_eth_replaceTransaction_gas_price_defaulting_strategy_higher(self, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_higher( - web3, unlocked_account - ) - - @pytest.mark.xfail(reason='Needs ability to efficiently control mining') - def test_eth_replaceTransaction_gas_price_defaulting_strategy_lower(self, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_lower( - web3, unlocked_account - ) - - @pytest.mark.xfail(reason='Needs ability to efficiently control mining') - def test_eth_modifyTransaction(self, web3, unlocked_account): - super().test_eth_modifyTransaction(web3, unlocked_account) - - @pytest.mark.xfail(reason='Block identifier has not been implemented in geth') - def test_eth_estimateGas_with_block(self, - web3, - unlocked_account_dual_type): - super().test_eth_estimateGas_with_block( - web3, unlocked_account_dual_type - ) - - def test_eth_submitHashrate(self, web3): - if 'v1.8.22' in web3.clientVersion: - # https://github.com/ethereum/go-ethereum/commit/51db5975cc5fb88db6a0dba1826b534fd4df29d7 - pytest.xfail('eth_submitHashrate deprecated in 1.8.22 for ethash_submitHashRate') - super().test_eth_submitHashrate(web3) - - def test_eth_chainId(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('eth_chainId not implemented in geth 1.7.2') - super().test_eth_chainId(web3) - - @pytest.mark.xfail(reason='eth_signTypedData has not been released in geth') - def test_eth_signTypedData(self, - web3, - unlocked_account_dual_type): - super().test_eth_signTypedData( - web3, unlocked_account_dual_type - ) - - @pytest.mark.xfail(reason='eth_signTypedData has not been released in geth') - def test_invalid_eth_signTypedData(self, - web3, - unlocked_account_dual_type): - super().test_invalid_eth_signTypedData( - web3, unlocked_account_dual_type - ) - - -class GoEthereumVersionModuleTest(VersionModuleTest): - pass - - -class GoEthereumNetModuleTest(NetModuleTest): - pass - - -class CommonGoEthereumShhModuleTest(GoEthereumShhModuleTest): - def test_shh_sync_filter(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('Whisper version 6 not supported in geth 1.7.2') - super().test_shh_sync_filter(web3) - - def test_shh_sync_filter_deprecated(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('Whisper version 6 not supported in geth 1.7.2') - super().test_shh_sync_filter_deprecated(web3) - - def test_shh_async_filter(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('Whisper version 6 not supported in geth 1.7.2') - super().test_shh_async_filter(web3) - - def test_shh_async_filter_deprecated(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('Whisper version 6 not supported in geth 1.7.2') - super().test_shh_async_filter_deprecated(web3) - - def test_shh_post(self, web3): - if 'v1.7.2' in web3.clientVersion: - pytest.xfail('Whisper version 6 not supported in geth 1.7.2') - super().test_shh_post(web3) - - -class GoEthereumAdminModuleTest(GoEthereumAdminModuleTest): - pass +import pytest + +from web3._utils.module_testing import ( # noqa: F401 + EthModuleTest, + GoEthereumPersonalModuleTest, + GoEthereumShhModuleTest, + NetModuleTest, + VersionModuleTest, + Web3ModuleTest, +) + + +class GoEthereumTest (Web3ModuleTest): + def _check_web3_clientVersion(self, client_version): + assert client_version.startswith('Geth/') + + +class GoEthereumEthModuleTest(EthModuleTest): + def test_vns_replaceTransaction(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction(web3, unlocked_account) + + def test_vns_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_incorrect_nonce(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_too_low(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_too_low(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_higher(self, + web3, + unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_higher( + web3, unlocked_account + ) + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_lower(self, + web3, + unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_lower( + web3, unlocked_account + ) + + def test_vns_modifyTransaction(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_modifyTransaction(web3, unlocked_account) + + def test_vns_estimateGas_with_block(self, + web3, + unlocked_account_dual_type): + pytest.xfail('Block identifier has not been implemented in geth') + super().test_vns_estimateGas_with_block( + web3, unlocked_account_dual_type + ) + + def test_vns_submitHashrate(self, web3): + if 'v1.8.22' in web3.clientVersion: + # https://github.com/ethereum/go-ethereum/commit/51db5975cc5fb88db6a0dba1826b534fd4df29d7 + pytest.xfail('vns_submitHashrate deprecated in 1.8.22 for ethash_submitHashRate') + super().test_vns_submitHashrate(web3) + + def test_vns_chainId(self, web3): + if 'v1.7.2' in web3.clientVersion: + pytest.xfail('vns_chainId not implemented in geth 1.7.2') + super().test_vns_chainId(web3) + + +class GoEthereumVersionModuleTest(VersionModuleTest): + pass + + +class GoEthereumNetModuleTest(NetModuleTest): + pass + + +class CommonGoEthereumShhModuleTest(GoEthereumShhModuleTest): + def test_shh_sync_filter(self, web3): + if 'v1.7.2' in web3.clientVersion: + pytest.xfail('Whisper version 6 not supported in geth 1.7.2') + super().test_shh_sync_filter(web3) + + def test_shh_async_filter(self, web3): + if 'v1.7.2' in web3.clientVersion: + pytest.xfail('Whisper version 6 not supported in geth 1.7.2') + super().test_shh_async_filter(web3) + + def test_shh_post(self, web3): + if 'v1.7.2' in web3.clientVersion: + pytest.xfail('Whisper version 6 not supported in geth 1.7.2') + super().test_shh_post(web3) diff --git a/tests/integration/go_ethereum/conftest.py b/tests/integration/go_ethereum/conftest.py index 48d9cc9de1..04175094e6 100644 --- a/tests/integration/go_ethereum/conftest.py +++ b/tests/integration/go_ethereum/conftest.py @@ -1,217 +1,217 @@ -import json -import os -from pathlib import ( - Path, -) -import pytest -import subprocess -import zipfile - -from eth_utils import ( - is_checksum_address, - is_dict, - to_text, -) - -from .utils import ( - kill_proc_gracefully, -) - -KEYFILE_PW = 'web3py-test' - -GETH_17_ZIP = 'geth-17-fixture.zip' -GETH_1822_ZIP = 'geth-1.8.22-fixture.zip' - - -@pytest.fixture(scope='module') -def geth_binary(): - from geth.install import ( - get_executable_path, - install_geth, - ) - - if 'GETH_BINARY' in os.environ: - return os.environ['GETH_BINARY'] - elif 'GETH_VERSION' in os.environ: - geth_version = os.environ['GETH_VERSION'] - _geth_binary = get_executable_path(geth_version) - if not os.path.exists(_geth_binary): - install_geth(geth_version) - assert os.path.exists(_geth_binary) - return _geth_binary - else: - return 'geth' - - -def absolute_datadir(directory_name): - return os.path.abspath(os.path.join( - os.path.dirname(__file__), - '..', - directory_name, - )) - - -@pytest.fixture(scope="module") -def geth_zipfile_version(geth_binary): - from geth import get_geth_version - version = get_geth_version(geth_executable=os.path.expanduser(geth_binary)) - if version.major == 1: - if version.minor == 7: - return GETH_17_ZIP - elif version.minor == 8: - return GETH_1822_ZIP - assert False, "Unsupported geth version" - - -@pytest.fixture(scope='module') -def datadir(tmpdir_factory, geth_zipfile_version): - zipfile_path = absolute_datadir(geth_zipfile_version) - base_dir = tmpdir_factory.mktemp('goethereum') - tmp_datadir = os.path.join(str(base_dir), 'datadir') - with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: - zip_ref.extractall(tmp_datadir) - return tmp_datadir - - -@pytest.fixture(scope="module") -def geth_fixture_data(datadir): - config_file_path = Path(datadir) / 'config.json' - return json.loads(config_file_path.read_text()) - - -@pytest.fixture(scope='module') -def genesis_file(datadir): - genesis_file_path = os.path.join(datadir, 'genesis.json') - return genesis_file_path - - -@pytest.fixture(scope='module') -def geth_process(geth_binary, datadir, genesis_file, geth_command_arguments): - init_datadir_command = ( - geth_binary, - '--datadir', str(datadir), - 'init', - str(genesis_file), - ) - subprocess.check_output( - init_datadir_command, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - proc = subprocess.Popen( - geth_command_arguments, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - bufsize=1, - ) - try: - yield proc - finally: - kill_proc_gracefully(proc) - output, errors = proc.communicate() - print( - "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -@pytest.fixture(scope='module') -def coinbase(web3): - return web3.eth.coinbase - - -@pytest.fixture(scope="module") -def math_contract_deploy_txn_hash(geth_fixture_data): - return geth_fixture_data['math_deploy_txn_hash'] - - -@pytest.fixture(scope="module") -def math_contract(web3, math_contract_factory, geth_fixture_data): - return math_contract_factory(address=geth_fixture_data['math_address']) - - -@pytest.fixture(scope="module") -def math_contract_address(math_contract, address_conversion_func): - return address_conversion_func(math_contract.address) - - -@pytest.fixture(scope="module") -def emitter_contract(web3, emitter_contract_factory, geth_fixture_data): - return emitter_contract_factory(address=geth_fixture_data['emitter_address']) - - -@pytest.fixture(scope="module") -def emitter_contract_address(emitter_contract, address_conversion_func): - return address_conversion_func(emitter_contract.address) - - -@pytest.fixture -def unlocked_account(web3, unlockable_account, unlockable_account_pw): - web3.geth.personal.unlockAccount(unlockable_account, unlockable_account_pw) - yield unlockable_account - web3.geth.personal.lockAccount(unlockable_account) - - -@pytest.fixture(scope='module') -def unlockable_account_pw(geth_fixture_data): - return geth_fixture_data['keyfile_pw'] - - -@pytest.fixture(scope="module") -def unlockable_account(web3, coinbase): - yield coinbase - - -@pytest.fixture() -def unlockable_account_dual_type(unlockable_account, address_conversion_func): - return address_conversion_func(unlockable_account) - - -@pytest.yield_fixture -def unlocked_account_dual_type(web3, unlockable_account_dual_type, unlockable_account_pw): - web3.geth.personal.unlockAccount(unlockable_account_dual_type, unlockable_account_pw) - yield unlockable_account_dual_type - web3.geth.personal.lockAccount(unlockable_account_dual_type) - - -@pytest.fixture(scope="module") -def funded_account_for_raw_txn(geth_fixture_data): - account = geth_fixture_data['raw_txn_account'] - assert is_checksum_address(account) - return account - - -@pytest.fixture(scope="module") -def empty_block(web3, geth_fixture_data): - block = web3.eth.getBlock(geth_fixture_data['empty_block_hash']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def block_with_txn(web3, geth_fixture_data): - block = web3.eth.getBlock(geth_fixture_data['block_with_txn_hash']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def mined_txn_hash(geth_fixture_data): - return geth_fixture_data['mined_txn_hash'] - - -@pytest.fixture(scope="module") -def block_with_txn_with_log(web3, geth_fixture_data): - block = web3.eth.getBlock(geth_fixture_data['block_hash_with_log']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def txn_hash_with_log(geth_fixture_data): - return geth_fixture_data['txn_hash_with_log'] +import json +import os +from pathlib import ( + Path, +) +import pytest +import subprocess +import zipfile + +from vns_utils import ( + is_checksum_address, + is_dict, + to_text, +) + +from .utils import ( + kill_proc_gracefully, +) + +KEYFILE_PW = 'web3py-test' + +GETH_17_ZIP = 'geth-17-fixture.zip' +GETH_1822_ZIP = 'geth-1.8.22-fixture.zip' + + +@pytest.fixture(scope='module') +def geth_binary(): + from geth.install import ( + get_executable_path, + install_geth, + ) + + if 'GETH_BINARY' in os.environ: + return os.environ['GETH_BINARY'] + elif 'GETH_VERSION' in os.environ: + geth_version = os.environ['GETH_VERSION'] + _geth_binary = get_executable_path(geth_version) + if not os.path.exists(_geth_binary): + install_geth(geth_version) + assert os.path.exists(_geth_binary) + return _geth_binary + else: + return 'geth' + + +def absolute_datadir(directory_name): + return os.path.abspath(os.path.join( + os.path.dirname(__file__), + '..', + directory_name, + )) + + +@pytest.fixture(scope="module") +def geth_zipfile_version(geth_binary): + from geth import get_geth_version + version = get_geth_version(geth_executable=os.path.expanduser(geth_binary)) + if version.major == 1: + if version.minor == 7: + return GETH_17_ZIP + elif version.minor == 8: + return GETH_1822_ZIP + assert False, "Unsupported geth version" + + +@pytest.fixture(scope='module') +def datadir(tmpdir_factory, geth_zipfile_version): + zipfile_path = absolute_datadir(geth_zipfile_version) + base_dir = tmpdir_factory.mktemp('goethereum') + tmp_datadir = os.path.join(str(base_dir), 'datadir') + with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: + zip_ref.extractall(tmp_datadir) + return tmp_datadir + + +@pytest.fixture(scope="module") +def geth_fixture_data(datadir): + config_file_path = Path(datadir) / 'config.json' + return json.loads(config_file_path.read_text()) + + +@pytest.fixture(scope='module') +def genesis_file(datadir): + genesis_file_path = os.path.join(datadir, 'genesis.json') + return genesis_file_path + + +@pytest.fixture(scope='module') +def geth_process(geth_binary, datadir, genesis_file, geth_command_arguments): + init_datadir_command = ( + geth_binary, + '--datadir', str(datadir), + 'init', + str(genesis_file), + ) + subprocess.check_output( + init_datadir_command, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + proc = subprocess.Popen( + geth_command_arguments, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=1, + ) + try: + yield proc + finally: + kill_proc_gracefully(proc) + output, errors = proc.communicate() + print( + "Geth Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +@pytest.fixture(scope='module') +def coinbase(web3): + return web3.vns.coinbase + + +@pytest.fixture(scope="module") +def math_contract_deploy_txn_hash(geth_fixture_data): + return geth_fixture_data['math_deploy_txn_hash'] + + +@pytest.fixture(scope="module") +def math_contract(web3, math_contract_factory, geth_fixture_data): + return math_contract_factory(address=geth_fixture_data['math_address']) + + +@pytest.fixture(scope="module") +def math_contract_address(math_contract, address_conversion_func): + return address_conversion_func(math_contract.address) + + +@pytest.fixture(scope="module") +def emitter_contract(web3, emitter_contract_factory, geth_fixture_data): + return emitter_contract_factory(address=geth_fixture_data['emitter_address']) + + +@pytest.fixture(scope="module") +def emitter_contract_address(emitter_contract, address_conversion_func): + return address_conversion_func(emitter_contract.address) + + +@pytest.fixture +def unlocked_account(web3, unlockable_account, unlockable_account_pw): + web3.geth.personal.unlockAccount(unlockable_account, unlockable_account_pw) + yield unlockable_account + web3.geth.personal.lockAccount(unlockable_account) + + +@pytest.fixture(scope='module') +def unlockable_account_pw(geth_fixture_data): + return geth_fixture_data['keyfile_pw'] + + +@pytest.fixture(scope="module") +def unlockable_account(web3, coinbase): + yield coinbase + + +@pytest.fixture() +def unlockable_account_dual_type(unlockable_account, address_conversion_func): + return address_conversion_func(unlockable_account) + + +@pytest.yield_fixture +def unlocked_account_dual_type(web3, unlockable_account_dual_type, unlockable_account_pw): + web3.geth.personal.unlockAccount(unlockable_account_dual_type, unlockable_account_pw) + yield unlockable_account_dual_type + web3.geth.personal.lockAccount(unlockable_account_dual_type) + + +@pytest.fixture(scope="module") +def funded_account_for_raw_txn(geth_fixture_data): + account = geth_fixture_data['raw_txn_account'] + assert is_checksum_address(account) + return account + + +@pytest.fixture(scope="module") +def empty_block(web3, geth_fixture_data): + block = web3.vns.getBlock(geth_fixture_data['empty_block_hash']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def block_with_txn(web3, geth_fixture_data): + block = web3.vns.getBlock(geth_fixture_data['block_with_txn_hash']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def mined_txn_hash(geth_fixture_data): + return geth_fixture_data['mined_txn_hash'] + + +@pytest.fixture(scope="module") +def block_with_txn_with_log(web3, geth_fixture_data): + block = web3.vns.getBlock(geth_fixture_data['block_hash_with_log']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def txn_hash_with_log(geth_fixture_data): + return geth_fixture_data['txn_hash_with_log'] diff --git a/tests/integration/go_ethereum/test_goethereum_http.py b/tests/integration/go_ethereum/test_goethereum_http.py index 0e71b74d18..de24954a11 100644 --- a/tests/integration/go_ethereum/test_goethereum_http.py +++ b/tests/integration/go_ethereum/test_goethereum_http.py @@ -1,93 +1,74 @@ -import pytest - -from tests.utils import ( - get_open_port, -) -from web3 import Web3 - -from .common import ( - CommonGoEthereumShhModuleTest, - GoEthereumAdminModuleTest, - GoEthereumEthModuleTest, - GoEthereumNetModuleTest, - GoEthereumPersonalModuleTest, - GoEthereumTest, - GoEthereumVersionModuleTest, -) -from .utils import ( - wait_for_http, -) - - -@pytest.fixture(scope="module") -def rpc_port(): - return get_open_port() - - -@pytest.fixture(scope="module") -def endpoint_uri(rpc_port): - return 'http://localhost:{0}'.format(rpc_port) - - -@pytest.fixture(scope='module') -def geth_command_arguments(geth_binary, datadir, rpc_port): - return ( - geth_binary, - '--datadir', str(datadir), - '--shh', - '--nodiscover', - '--fakepow', - '--rpc', - '--rpcport', rpc_port, - '--rpcapi', 'admin,db,eth,net,web3,personal,shh,web3', - '--ipcdisable', - ) - - -@pytest.fixture(scope="module") -def web3(geth_process, endpoint_uri): - wait_for_http(endpoint_uri) - _web3 = Web3(Web3.HTTPProvider(endpoint_uri)) - return _web3 - - -class TestGoEthereumTest(GoEthereumTest): - pass - - -class TestGoEthereumAdminModuleTest(GoEthereumAdminModuleTest): - @pytest.mark.xfail(reason="running geth with the --nodiscover flag doesn't allow peer addition") - def test_admin_peers(web3): - super().test_admin_peers(web3) - - @pytest.mark.xfail(reason='Only one RPC endpoint is allowed to be active at any time') - def test_admin_start_stop_rpc(web3): - super().test_admin_start_stop_rpc(web3) - - @pytest.mark.xfail(reason='Only one RPC endpoint is allowed to be active at any time') - def test_admin_startRPC(web3): - super().test_admin_stopRPC(web3) - - @pytest.mark.xfail(reason='Only one RPC endpoint is allowed to be active at any time') - def test_admin_stopRPC(web3): - super().test_admin_stopRPC(web3) - - -class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): - pass - - -class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): - pass - - -class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): - pass - - -class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): - pass - - -class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): - pass +import pytest + +from tests.utils import ( + get_open_port, +) +from web3 import Web3 + +from .common import ( + CommonGoEthereumShhModuleTest, + GoEthereumEthModuleTest, + GoEthereumNetModuleTest, + GoEthereumPersonalModuleTest, + GoEthereumTest, + GoEthereumVersionModuleTest, +) +from .utils import ( + wait_for_http, +) + + +@pytest.fixture(scope="module") +def rpc_port(): + return get_open_port() + + +@pytest.fixture(scope="module") +def endpoint_uri(rpc_port): + return 'http://localhost:{0}'.format(rpc_port) + + +@pytest.fixture(scope='module') +def geth_command_arguments(geth_binary, datadir, rpc_port): + return ( + geth_binary, + '--datadir', str(datadir), + '--shh', + '--nodiscover', + '--fakepow', + '--rpc', + '--rpcport', rpc_port, + '--rpcapi', 'db,vns,net,web3,personal,shh,web3', + '--ipcdisable', + ) + + +@pytest.fixture(scope="module") +def web3(geth_process, endpoint_uri): + wait_for_http(endpoint_uri) + _web3 = Web3 (Web3.HTTPProvider(endpoint_uri)) + return _web3 + + +class TestGoEthereumTest(GoEthereumTest): + pass + + +class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): + pass + + +class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): + pass + + +class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): + pass + + +class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): + pass + + +class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): + pass diff --git a/tests/integration/go_ethereum/test_goethereum_ipc.py b/tests/integration/go_ethereum/test_goethereum_ipc.py index 0baa325e60..8db876b5b8 100644 --- a/tests/integration/go_ethereum/test_goethereum_ipc.py +++ b/tests/integration/go_ethereum/test_goethereum_ipc.py @@ -1,82 +1,75 @@ -import os -import pytest -import tempfile - -from tests.utils import ( - get_open_port, -) -from web3 import Web3 - -from .common import ( - CommonGoEthereumShhModuleTest, - GoEthereumAdminModuleTest, - GoEthereumEthModuleTest, - GoEthereumNetModuleTest, - GoEthereumPersonalModuleTest, - GoEthereumTest, - GoEthereumVersionModuleTest, -) -from .utils import ( - wait_for_socket, -) - - -@pytest.fixture(scope='module') -def geth_command_arguments(geth_binary, datadir, geth_ipc_path): - geth_port = get_open_port() - return ( - geth_binary, - '--datadir', str(datadir), - '--ipcpath', geth_ipc_path, - '--shh', - '--nodiscover', - '--fakepow', - '--port', geth_port, - ) - - -@pytest.fixture(scope='module') -def geth_ipc_path(datadir): - geth_ipc_dir_path = tempfile.mkdtemp() - _geth_ipc_path = os.path.join(geth_ipc_dir_path, 'geth.ipc') - yield _geth_ipc_path - - if os.path.exists(_geth_ipc_path): - os.remove(_geth_ipc_path) - - -@pytest.fixture(scope="module") -def web3(geth_process, geth_ipc_path): - wait_for_socket(geth_ipc_path) - _web3 = Web3(Web3.IPCProvider(geth_ipc_path)) - return _web3 - - -class TestGoEthereumTest(GoEthereumTest): - pass - - -class TestGoEthereumAdminModuleTest(GoEthereumAdminModuleTest): - @pytest.mark.xfail(reason="running geth with the --nodiscover flag doesn't allow peer addition") - def test_admin_peers(web3): - super().test_admin_peers(web3) - - -class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): - pass - - -class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): - pass - - -class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): - pass - - -class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): - pass - - -class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): - pass +import os +import pytest +import tempfile + +from tests.utils import ( + get_open_port, +) +from web3 import Web3 + +from .common import ( + CommonGoEthereumShhModuleTest, + GoEthereumEthModuleTest, + GoEthereumNetModuleTest, + GoEthereumPersonalModuleTest, + GoEthereumTest, + GoEthereumVersionModuleTest, +) +from .utils import ( + wait_for_socket, +) + + +@pytest.fixture(scope='module') +def geth_command_arguments(geth_binary, datadir, geth_ipc_path): + geth_port = get_open_port() + return ( + geth_binary, + '--datadir', str(datadir), + '--ipcpath', geth_ipc_path, + '--shh', + '--nodiscover', + '--fakepow', + '--port', geth_port, + ) + + +@pytest.fixture(scope='module') +def geth_ipc_path(datadir): + geth_ipc_dir_path = tempfile.mkdtemp() + _geth_ipc_path = os.path.join(geth_ipc_dir_path, 'geth.ipc') + yield _geth_ipc_path + + if os.path.exists(_geth_ipc_path): + os.remove(_geth_ipc_path) + + +@pytest.fixture(scope="module") +def web3(geth_process, geth_ipc_path): + wait_for_socket(geth_ipc_path) + _web3 = Web3 (Web3.IPCProvider(geth_ipc_path)) + return _web3 + + +class TestGoEthereumTest(GoEthereumTest): + pass + + +class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): + pass + + +class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): + pass + + +class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): + pass + + +class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): + pass + + +class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): + pass diff --git a/tests/integration/go_ethereum/test_goethereum_ws.py b/tests/integration/go_ethereum/test_goethereum_ws.py index 1081879177..398505e334 100644 --- a/tests/integration/go_ethereum/test_goethereum_ws.py +++ b/tests/integration/go_ethereum/test_goethereum_ws.py @@ -1,105 +1,82 @@ -import pytest - -from tests.integration.common import ( - MiscWebsocketTest, -) -from tests.utils import ( - get_open_port, - wait_for_ws, -) -from web3 import Web3 - -from .common import ( - CommonGoEthereumShhModuleTest, - GoEthereumAdminModuleTest, - GoEthereumEthModuleTest, - GoEthereumNetModuleTest, - GoEthereumPersonalModuleTest, - GoEthereumTest, - GoEthereumVersionModuleTest, -) - - -@pytest.fixture(scope="module") -def ws_port(): - return get_open_port() - - -@pytest.fixture(scope="module") -def endpoint_uri(ws_port): - return 'ws://localhost:{0}'.format(ws_port) - - -@pytest.fixture(scope='module') -def geth_command_arguments(geth_binary, datadir, ws_port): - return ( - geth_binary, - '--datadir', str(datadir), - '--nodiscover', - '--fakepow', - '--ws', - '--shh', - '--wsport', ws_port, - '--wsapi', 'admin,db,eth,net,shh,web3,personal,web3', - '--wsorigins', '*', - '--ipcdisable', - ) - - -@pytest.fixture(scope="module") -def web3(geth_process, endpoint_uri, event_loop): - event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) - _web3 = Web3(Web3.WebsocketProvider(endpoint_uri)) - return _web3 - - -class TestGoEthereumTest(GoEthereumTest): - pass - - -class TestGoEthereumAdminModuleTest(GoEthereumAdminModuleTest): - @pytest.mark.xfail(reason="running geth with the --nodiscover flag doesn't allow peer addition") - def test_admin_peers(web3): - super().test_admin_peers(web3) - - @pytest.mark.xfail(reason='Only one WebSocket endpoint is allowed to be active at any time') - def test_admin_start_stop_ws(web3): - super().test_admin_start_stop_ws(web3) - - @pytest.mark.xfail(reason='Only one WebSocket endpoint is allowed to be active at any time') - def test_admin_startWS(self, web3): - super().test_admin_startWS(web3) - - @pytest.mark.xfail(reason='Only one WebSocket endpoint is allowed to be active at any time') - def test_admin_stopWS(self, web3): - super().test_admin_stopWS(web3) - - -class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): - pass - - -class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): - pass - - -class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): - pass - - -class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): - pass - - -class TestMiscWebsocketTest(MiscWebsocketTest): - pass - - -class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): - def test_shh_async_filter(self, web3): - pytest.xfail("async filter bug in geth ws version") - super().test_shh_async_filter(web3) - - def test_shh_async_filter_deprecated(self, web3): - pytest.xfail("async filter bug in geth ws version") - super().test_shh_async_filter(web3) +import pytest + +from tests.integration.common import ( + MiscWebsocketTest, +) +from tests.utils import ( + get_open_port, + wait_for_ws, +) +from web3 import Web3 + +from .common import ( + CommonGoEthereumShhModuleTest, + GoEthereumEthModuleTest, + GoEthereumNetModuleTest, + GoEthereumPersonalModuleTest, + GoEthereumTest, + GoEthereumVersionModuleTest, +) + + +@pytest.fixture(scope="module") +def ws_port(): + return get_open_port() + + +@pytest.fixture(scope="module") +def endpoint_uri(ws_port): + return 'ws://localhost:{0}'.format(ws_port) + + +@pytest.fixture(scope='module') +def geth_command_arguments(geth_binary, datadir, ws_port): + return ( + geth_binary, + '--datadir', str(datadir), + '--nodiscover', + '--fakepow', + '--ws', + '--shh', + '--wsport', ws_port, + '--wsapi', 'db,vns,net,shh,web3,personal,web3', + '--wsorigins', '*', + '--ipcdisable', + ) + + +@pytest.fixture(scope="module") +def web3(geth_process, endpoint_uri, event_loop): + event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) + _web3 = Web3 (Web3.WebsocketProvider(endpoint_uri)) + return _web3 + + +class TestGoEthereumTest(GoEthereumTest): + pass + + +class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest): + pass + + +class TestGoEthereumVersionModuleTest(GoEthereumVersionModuleTest): + pass + + +class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest): + pass + + +class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest): + pass + + +class TestMiscWebsocketTest(MiscWebsocketTest): + pass + + +class TestGoEthereumShhModuleTest(CommonGoEthereumShhModuleTest): + def test_shh_async_filter(self, web3): + pytest.xfail("async filter bug in geth ws version") + super().test_shh_async_filter(web3) diff --git a/tests/integration/go_ethereum/utils.py b/tests/integration/go_ethereum/utils.py index b9c1176038..6cba7677d3 100644 --- a/tests/integration/go_ethereum/utils.py +++ b/tests/integration/go_ethereum/utils.py @@ -1,52 +1,52 @@ -import signal -import socket -import time - -import requests - - -def wait_for_socket(ipc_path, timeout=30): - start = time.time() - while time.time() < start + timeout: - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(ipc_path) - sock.settimeout(timeout) - except (FileNotFoundError, socket.error): - time.sleep(0.01) - else: - break - - -def wait_for_http(endpoint_uri, timeout=60): - start = time.time() - while time.time() < start + timeout: - try: - requests.get(endpoint_uri) - except requests.ConnectionError: - time.sleep(0.01) - else: - break - - -def wait_for_popen(proc, timeout): - start = time.time() - while time.time() < start + timeout: - if proc.poll() is None: - time.sleep(0.01) - else: - break - - -def kill_proc_gracefully(proc): - if proc.poll() is None: - proc.send_signal(signal.SIGINT) - wait_for_popen(proc, 13) - - if proc.poll() is None: - proc.terminate() - wait_for_popen(proc, 5) - - if proc.poll() is None: - proc.kill() - wait_for_popen(proc, 2) +import signal +import socket +import time + +import requests + + +def wait_for_socket(ipc_path, timeout=30): + start = time.time() + while time.time() < start + timeout: + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(ipc_path) + sock.settimeout(timeout) + except (FileNotFoundError, socket.error): + time.sleep(0.01) + else: + break + + +def wait_for_http(endpoint_uri, timeout=60): + start = time.time() + while time.time() < start + timeout: + try: + requests.get(endpoint_uri) + except requests.ConnectionError: + time.sleep(0.01) + else: + break + + +def wait_for_popen(proc, timeout): + start = time.time() + while time.time() < start + timeout: + if proc.poll() is None: + time.sleep(0.01) + else: + break + + +def kill_proc_gracefully(proc): + if proc.poll() is None: + proc.send_signal(signal.SIGINT) + wait_for_popen(proc, 13) + + if proc.poll() is None: + proc.terminate() + wait_for_popen(proc, 5) + + if proc.poll() is None: + proc.kill() + wait_for_popen(proc, 2) diff --git a/tests/integration/parity/common.py b/tests/integration/parity/common.py index e79cd15052..b5fdaaff01 100644 --- a/tests/integration/parity/common.py +++ b/tests/integration/parity/common.py @@ -1,204 +1,185 @@ -import pytest - -from flaky import ( - flaky, -) - -from web3._utils.module_testing import ( # noqa: F401 - EthModuleTest, - ParityModuleTest, - ParityPersonalModuleTest, - ParitySetModuleTest, - ParityTraceModuleTest, - Web3ModuleTest, -) -from web3._utils.module_testing.eth_module import ( - UNKNOWN_ADDRESS, -) -from web3._utils.module_testing.shh_module import ( - ParityShhModuleTest, -) - -# some tests appear flaky with Parity v1.10.x -MAX_FLAKY_RUNS = 3 - - -class ParityWeb3ModuleTest(Web3ModuleTest): - def _check_web3_clientVersion(self, client_version): - assert client_version.startswith('Parity-Ethereum/') - - -class ParityEthModuleTest(EthModuleTest): - def test_eth_chainId(self, web3): - # Parity will return null if chainId is not available - chain_id = web3.eth.chainId - assert chain_id is None - - @pytest.mark.xfail(reason='Parity dropped "pending" option in 1.11.1') - def test_eth_getBlockByNumber_pending(self, web3): - super().test_eth_getBlockByNumber_pending(web3) - - def test_eth_uninstallFilter(self, web3): - pytest.xfail('eth_uninstallFilter calls to parity always return true') - super().test_eth_uninstallFilter(web3) - - def test_eth_replaceTransaction(self, web3, unlocked_account): - super().test_eth_replaceTransaction(web3, unlocked_account) - - @pytest.mark.xfail(reason='Parity is not setup to auto mine') - def test_eth_replaceTransaction_already_mined(self, web3, unlocked_account): - super().test_eth_replaceTransaction_already_mined(web3, unlocked_account) - - def test_eth_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): - super().test_eth_replaceTransaction_incorrect_nonce(web3, unlocked_account) - - def test_eth_replaceTransaction_gas_price_too_low(self, web3, unlocked_account): - super().test_eth_replaceTransaction_gas_price_too_low(web3, unlocked_account) - - def test_eth_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) - - def test_eth_replaceTransaction_gas_price_defaulting_strategy_higher(self, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_higher( - web3, unlocked_account - ) - - def test_eth_replaceTransaction_gas_price_defaulting_strategy_lower(self, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_lower( - web3, unlocked_account - ) - - def test_eth_modifyTransaction(self, web3, unlocked_account): - super().test_eth_modifyTransaction(web3, unlocked_account) - - @flaky(max_runs=MAX_FLAKY_RUNS) - def test_eth_getTransactionReceipt_unmined(self, web3, unlocked_account): - # Parity diverges from json-rpc spec and retrieves pending block - # transactions with getTransactionReceipt. - txn_hash = web3.eth.sendTransaction({ - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - }) - receipt = web3.eth.getTransactionReceipt(txn_hash) - assert receipt is not None - assert receipt['blockHash'] is None - - @pytest.mark.xfail(reason="Parity matches None to asbent values") - def test_eth_getLogs_with_logs_none_topic_args(self, web3): - super().test_eth_getLogs_with_logs_none_topic_args(web3) - - @flaky(max_runs=MAX_FLAKY_RUNS) - def test_eth_call_old_contract_state(self, web3, math_contract, unlocked_account): - start_block = web3.eth.getBlock('latest') - block_num = start_block.number - block_hash = start_block.hash - - math_contract.functions.increment().transact({'from': unlocked_account}) - - # This isn't an incredibly convincing test since we can't mine, and - # the default resolved block is latest, So if block_identifier was ignored - # we would get the same result. For now, we mostly depend on core tests. - # Ideas to improve this test: - # - Enable on-demand mining in more clients - # - Increment the math contract in all of the fixtures, and check the value in an old block - - block_hash_call_result = math_contract.functions.counter().call(block_identifier=block_hash) - block_num_call_result = math_contract.functions.counter().call(block_identifier=block_num) - latest_call_result = math_contract.functions.counter().call(block_identifier='latest') - default_call_result = math_contract.functions.counter().call() - - assert block_hash_call_result == 0 - assert block_num_call_result == 0 - assert latest_call_result == 0 - assert default_call_result == 0 - - # retrieve this right before using - Parity tests might hit a race otherwise - pending_call_result = math_contract.functions.counter().call(block_identifier='pending') - # should be '1' on first flaky run, '2' on second, or '3' on third - if pending_call_result not in range(1, MAX_FLAKY_RUNS + 1): - raise AssertionError("pending call result was %d!" % pending_call_result) - - def test_eth_getLogs_without_logs(self, web3, block_with_txn_with_log): - # Test with block range - - filter_params = { - "fromBlock": 0, - "toBlock": block_with_txn_with_log['number'] - 1, - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - # the range is wrong, parity returns error message - filter_params = { - "fromBlock": block_with_txn_with_log['number'], - "toBlock": block_with_txn_with_log['number'] - 1, - } - with pytest.raises(ValueError): - web3.eth.getLogs(filter_params) - - # Test with `address` - - # filter with other address - filter_params = { - "fromBlock": 0, - "address": UNKNOWN_ADDRESS, - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - # Test with multiple `address` - - # filter with other address - filter_params = { - "fromBlock": 0, - "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - @pytest.mark.xfail(reason='eth_signTypedData has not been released in Parity') - def test_eth_signTypedData(self, - web3, - unlocked_account_dual_type): - super().test_eth_signTypedData( - web3, unlocked_account_dual_type - ) - - @pytest.mark.xfail(reason='eth_signTypedData has not been released in Parity') - def test_invalid_eth_signTypedData(self, - web3, - unlocked_account_dual_type): - super().test_invalid_eth_signTypedData( - web3, unlocked_account_dual_type - ) - - -class ParityTraceModuleTest(ParityTraceModuleTest): - pass - - -class ParitySetModuleTest(ParitySetModuleTest): - pass - - -class ParityModuleTest(ParityModuleTest): - pass - - -class CommonParityShhModuleTest(ParityShhModuleTest): - def test_shh_sync_filter(self, web3): - # https://github.com/paritytech/parity-ethereum/issues/10565 - pytest.xfail("Skip until parity filter bug is resolved") - super().test_shh_sync_filter(web3) - - def test_shh_async_filter(self, web3): - # https://github.com/paritytech/parity-ethereum/issues/10565 - pytest.xfail("Skip until parity filter bug is resolved") - super().test_shh_async_filter(web3) +import pytest + +from flaky import ( + flaky, +) + +from web3._utils.module_testing import ( # noqa: F401 + EthModuleTest, + ParityModuleTest as TraceModuleTest, + ParityPersonalModuleTest, + Web3ModuleTest, +) +from web3._utils.module_testing.vns_module import ( + UNKNOWN_ADDRESS, +) +from web3._utils.module_testing.shh_module import ( + ParityShhModuleTest, +) + +# some tests appear flaky with Parity v1.10.x +MAX_FLAKY_RUNS = 3 + + +class ParityWeb3ModuleTest (Web3ModuleTest): + def _check_web3_clientVersion(self, client_version): + assert client_version.startswith('Parity-Ethereum/') + + +class ParityEthModuleTest(EthModuleTest): + def test_vns_chainId(self, web3): + # Parity will return null if chainId is not available + chain_id = web3.vns.chainId + assert chain_id is None + + def test_vns_getBlockByNumber_pending(self, web3): + pytest.xfail('Parity dropped "pending" option in 1.11.1') + super().test_vns_getBlockByNumber_pending(web3) + + def test_vns_uninstallFilter(self, web3): + pytest.xfail('vns_uninstallFilter calls to parity always return true') + super().test_vns_uninstallFilter(web3) + + def test_vns_replaceTransaction(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction(web3, unlocked_account) + + def test_vns_replaceTransaction_already_mined(self, web3, unlocked_account): + pytest.xfail('Parity is not setup to auto mine') + super().test_vns_replaceTransaction_already_mined(web3, unlocked_account) + + def test_vns_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_incorrect_nonce(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_too_low(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_too_low(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_higher(self, + web3, + unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_higher( + web3, unlocked_account + ) + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_lower(self, + web3, + unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_lower( + web3, unlocked_account + ) + + def test_vns_modifyTransaction(self, web3, unlocked_account): + pytest.xfail('Needs ability to efficiently control mining') + super().test_vns_modifyTransaction(web3, unlocked_account) + + @flaky(max_runs=MAX_FLAKY_RUNS) + def test_vns_getTransactionReceipt_unmined(self, web3, unlocked_account): + # Parity diverges from json-rpc spec and retrieves pending block + # transactions with getTransactionReceipt. + txn_hash = web3.vns.sendTransaction({ + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + }) + receipt = web3.vns.getTransactionReceipt(txn_hash) + assert receipt is not None + assert receipt['blockHash'] is None + + def test_vns_getLogs_with_logs_none_topic_args(self, web3): + pytest.xfail("Parity matches None to asbent values") + super().test_vns_getLogs_with_logs_none_topic_args(web3) + + @flaky(max_runs=MAX_FLAKY_RUNS) + def test_vns_call_old_contract_state(self, web3, math_contract, unlocked_account): + start_block = web3.vns.getBlock('latest') + block_num = start_block.number + block_hash = start_block.hash + + math_contract.functions.increment().transact({'from': unlocked_account}) + + # This isn't an incredibly convincing test since we can't mine, and + # the default resolved block is latest, So if block_identifier was ignored + # we would get the same result. For now, we mostly depend on core tests. + # Ideas to improve this test: + # - Enable on-demand mining in more clients + # - Increment the math contract in all of the fixtures, and check the value in an old block + + block_hash_call_result = math_contract.functions.counter().call(block_identifier=block_hash) + block_num_call_result = math_contract.functions.counter().call(block_identifier=block_num) + latest_call_result = math_contract.functions.counter().call(block_identifier='latest') + default_call_result = math_contract.functions.counter().call() + + assert block_hash_call_result == 0 + assert block_num_call_result == 0 + assert latest_call_result == 0 + assert default_call_result == 0 + + # retrieve this right before using - Parity tests might hit a race otherwise + pending_call_result = math_contract.functions.counter().call(block_identifier='pending') + # should be '1' on first flaky run, '2' on second, or '3' on third + if pending_call_result not in range(1, MAX_FLAKY_RUNS + 1): + raise AssertionError("pending call result was %d!" % pending_call_result) + + def test_vns_getLogs_without_logs(self, web3, block_with_txn_with_log): + # Test with block range + + filter_params = { + "fromBlock": 0, + "toBlock": block_with_txn_with_log['number'] - 1, + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + # the range is wrong, parity returns error message + filter_params = { + "fromBlock": block_with_txn_with_log['number'], + "toBlock": block_with_txn_with_log['number'] - 1, + } + with pytest.raises(ValueError): + web3.vns.getLogs(filter_params) + + # Test with `address` + + # filter with other address + filter_params = { + "fromBlock": 0, + "address": UNKNOWN_ADDRESS, + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + # Test with multiple `address` + + # filter with other address + filter_params = { + "fromBlock": 0, + "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + +class ParityTraceModuleTest(TraceModuleTest): + pass + + +class CommonParityShhModuleTest(ParityShhModuleTest): + def test_shh_sync_filter(self, web3): + # https://github.com/paritytech/parity-ethereum/issues/10565 + pytest.xfail("Skip until parity filter bug is resolved") + super().test_shh_sync_filter(web3) + + def test_shh_async_filter(self, web3): + # https://github.com/paritytech/parity-ethereum/issues/10565 + pytest.xfail("Skip until parity filter bug is resolved") + super().test_shh_async_filter(web3) diff --git a/tests/integration/parity/conftest.py b/tests/integration/parity/conftest.py index 17f37c71ce..3a20a91390 100644 --- a/tests/integration/parity/conftest.py +++ b/tests/integration/parity/conftest.py @@ -1,206 +1,206 @@ -import os -import pytest -import tempfile -import zipfile - -from eth_utils import ( - is_checksum_address, - is_dict, -) - -from .install_parity import ( - get_executable_path, - install_parity, -) -from .utils import ( - get_process, -) - -KEYFILE_PW = 'web3py-test' - - -PARITY_2_3_5_FIXTURE = { - 'zip': 'parity-235-fixture.zip', - 'coinbase': 'dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd', - 'block_hash_with_log': '0x8633d4b5497e5a7e81356cbe3f0a49b63fb2020ddeb6c8c27bcecec541055a3b', - 'block_with_txn_hash': '0xf474a7b80cb6cb2b728b290ce6a0893f5f85d2998c4b252d73300da56de205de', - 'emitter_address': '0x4aA591a07989b4F810E2F5cE97e769D60710f168', - 'emitter_deploy_txn_hash': '0xa3e3838c01e73dafc7fc9d7e4e6c97523445006ae125ad1085abcf065feed382', - 'empty_block_hash': '0xc46c025ae50a970408a429a5a2baa65f056a173ff1dae0cab4b490d2ee94413f', - 'keyfile_pw': 'web3py-test', - 'math_address': '0xd794C821fCCFF5D96F5Db44af7e29977630A9dc2', - 'math_deploy_txn_hash': '0xb680f86c00d69484ce68b4d3932c27c4e858c91d6ba8a27f8c499006fe4ced4a', - 'mined_txn_hash': '0x6401f455c01f9eb79fa672a4d24cc7cbbe3891e89eea8db8be39969dc9c480bc', - 'raw_txn_account': '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6', - 'txn_hash_with_log': '0xf6ffb7387cd0288cd1db1b1e368e7d4a21bc6e70fd2ef0e9af41f6c32b9e2cc7' -} - - -@pytest.fixture(scope='module') -def parity_binary(): - if 'PARITY_BINARY' in os.environ: - return os.environ['PARITY_BINARY'] - elif 'PARITY_VERSION' in os.environ: - parity_version = os.environ['PARITY_VERSION'] - _parity_binary = get_executable_path(parity_version) - if not os.path.exists(_parity_binary): - install_parity(parity_version) - assert os.path.exists(_parity_binary) - return _parity_binary - else: - return 'parity' - - -def get_parity_version(parity_binary): - pass - - -@pytest.fixture(scope="module") -def parity_fixture_data(parity_binary): - return PARITY_2_3_5_FIXTURE - - -@pytest.fixture(scope='module') -def datadir(tmpdir_factory, parity_fixture_data): - zipfile_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), - '..', - parity_fixture_data['zip'], - )) - base_dir = tmpdir_factory.mktemp('parity') - tmp_datadir = os.path.join(str(base_dir), 'datadir') - with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: - zip_ref.extractall(tmp_datadir) - return tmp_datadir - - -@pytest.fixture(scope="module") -def author(parity_fixture_data): - # need the address to unlock before web3 module has been opened - author = parity_fixture_data['coinbase'] - return author - - -@pytest.fixture(scope="module") -def passwordfile(): - password_dir = tempfile.mkdtemp() - password_path = os.path.join(password_dir, 'password') - with open(password_path, 'w') as f: - f.write(KEYFILE_PW) - - yield password_path - - if os.path.exists(password_path): - os.remove(password_path) - - -@pytest.fixture(scope="module") -def parity_process(parity_command_arguments): - yield from get_process(parity_command_arguments) - - -@pytest.fixture(scope="module") -def parity_import_blocks_process(parity_import_blocks_command): - yield from get_process(parity_import_blocks_command, terminates=True) - - -@pytest.fixture(scope='module') -def coinbase(web3): - return web3.eth.coinbase - - -@pytest.fixture(scope="module") -def math_contract_deploy_txn_hash(parity_fixture_data): - return parity_fixture_data['math_deploy_txn_hash'] - - -@pytest.fixture(scope="module") -def math_contract(web3, math_contract_factory, parity_fixture_data): - return math_contract_factory(address=parity_fixture_data['math_address']) - - -@pytest.fixture() -def math_contract_address(math_contract, address_conversion_func): - return address_conversion_func(math_contract.address) - - -@pytest.fixture(scope="module") -def emitter_contract(web3, emitter_contract_factory, parity_fixture_data): - return emitter_contract_factory(address=parity_fixture_data['emitter_address']) - - -@pytest.fixture() -def emitter_contract_address(emitter_contract, address_conversion_func): - return address_conversion_func(emitter_contract.address) - - -@pytest.fixture(scope="module") -def unlocked_account(web3, unlockable_account, unlockable_account_pw): - yield unlockable_account - - -@pytest.fixture(scope='module') -def unlockable_account_pw(parity_fixture_data): - return parity_fixture_data['keyfile_pw'] - - -@pytest.fixture(scope="module") -def unlockable_account(web3, coinbase): - yield coinbase - - -@pytest.fixture() -def unlockable_account_dual_type(unlockable_account, address_conversion_func): - return address_conversion_func(unlockable_account) - - -@pytest.fixture -def unlocked_account_dual_type(unlockable_account_dual_type): - return unlockable_account_dual_type - - -@pytest.fixture(scope="module") -def funded_account_for_raw_txn(parity_fixture_data): - account = parity_fixture_data['raw_txn_account'] - assert is_checksum_address(account) - return account - - -@pytest.fixture(scope="module") -def empty_block(web3, parity_fixture_data): - block = web3.eth.getBlock(parity_fixture_data['empty_block_hash']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def block_with_txn(web3, parity_fixture_data): - block = web3.eth.getBlock(parity_fixture_data['block_with_txn_hash']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def mined_txn_hash(parity_fixture_data): - return parity_fixture_data['mined_txn_hash'] - - -@pytest.fixture(scope="module") -def block_with_txn_with_log(web3, parity_fixture_data): - block = web3.eth.getBlock(parity_fixture_data['block_hash_with_log']) - assert is_dict(block) - return block - - -@pytest.fixture(scope="module") -def txn_hash_with_log(parity_fixture_data): - return parity_fixture_data['txn_hash_with_log'] - - -@pytest.fixture(scope="module") -def txn_filter_params(coinbase): - return { - "fromBlock": "earliest", - "toBlock": "latest", - "fromAddress": [coinbase], - } +import os +import pytest +import tempfile +import zipfile + +from vns_utils import ( + is_checksum_address, + is_dict, +) + +from .install_parity import ( + get_executable_path, + install_parity, +) +from .utils import ( + get_process, +) + +KEYFILE_PW = 'web3py-test' + + +PARITY_2_3_5_FIXTURE = { + 'zip': 'parity-235-fixture.zip', + 'coinbase': 'dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd', + 'block_hash_with_log': '0x8633d4b5497e5a7e81356cbe3f0a49b63fb2020ddeb6c8c27bcecec541055a3b', + 'block_with_txn_hash': '0xf474a7b80cb6cb2b728b290ce6a0893f5f85d2998c4b252d73300da56de205de', + 'emitter_address': '0x4aA591a07989b4F810E2F5cE97e769D60710f168', + 'emitter_deploy_txn_hash': '0xa3e3838c01e73dafc7fc9d7e4e6c97523445006ae125ad1085abcf065feed382', + 'empty_block_hash': '0xc46c025ae50a970408a429a5a2baa65f056a173ff1dae0cab4b490d2ee94413f', + 'keyfile_pw': 'web3py-test', + 'math_address': '0xd794C821fCCFF5D96F5Db44af7e29977630A9dc2', + 'math_deploy_txn_hash': '0xb680f86c00d69484ce68b4d3932c27c4e858c91d6ba8a27f8c499006fe4ced4a', + 'mined_txn_hash': '0x6401f455c01f9eb79fa672a4d24cc7cbbe3891e89eea8db8be39969dc9c480bc', + 'raw_txn_account': '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6', + 'txn_hash_with_log': '0xf6ffb7387cd0288cd1db1b1e368e7d4a21bc6e70fd2ef0e9af41f6c32b9e2cc7' +} + + +@pytest.fixture(scope='module') +def parity_binary(): + if 'PARITY_BINARY' in os.environ: + return os.environ['PARITY_BINARY'] + elif 'PARITY_VERSION' in os.environ: + parity_version = os.environ['PARITY_VERSION'] + _parity_binary = get_executable_path(parity_version) + if not os.path.exists(_parity_binary): + install_parity(parity_version) + assert os.path.exists(_parity_binary) + return _parity_binary + else: + return 'parity' + + +def get_parity_version(parity_binary): + pass + + +@pytest.fixture(scope="module") +def parity_fixture_data(parity_binary): + return PARITY_2_3_5_FIXTURE + + +@pytest.fixture(scope='module') +def datadir(tmpdir_factory, parity_fixture_data): + zipfile_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), + '..', + parity_fixture_data['zip'], + )) + base_dir = tmpdir_factory.mktemp('parity') + tmp_datadir = os.path.join(str(base_dir), 'datadir') + with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: + zip_ref.extractall(tmp_datadir) + return tmp_datadir + + +@pytest.fixture(scope="module") +def author(parity_fixture_data): + # need the address to unlock before web3 module has been opened + author = parity_fixture_data['coinbase'] + return author + + +@pytest.fixture(scope="module") +def passwordfile(): + password_dir = tempfile.mkdtemp() + password_path = os.path.join(password_dir, 'password') + with open(password_path, 'w') as f: + f.write(KEYFILE_PW) + + yield password_path + + if os.path.exists(password_path): + os.remove(password_path) + + +@pytest.fixture(scope="module") +def parity_process(parity_command_arguments): + yield from get_process(parity_command_arguments) + + +@pytest.fixture(scope="module") +def parity_import_blocks_process(parity_import_blocks_command): + yield from get_process(parity_import_blocks_command, terminates=True) + + +@pytest.fixture(scope='module') +def coinbase(web3): + return web3.vns.coinbase + + +@pytest.fixture(scope="module") +def math_contract_deploy_txn_hash(parity_fixture_data): + return parity_fixture_data['math_deploy_txn_hash'] + + +@pytest.fixture(scope="module") +def math_contract(web3, math_contract_factory, parity_fixture_data): + return math_contract_factory(address=parity_fixture_data['math_address']) + + +@pytest.fixture() +def math_contract_address(math_contract, address_conversion_func): + return address_conversion_func(math_contract.address) + + +@pytest.fixture(scope="module") +def emitter_contract(web3, emitter_contract_factory, parity_fixture_data): + return emitter_contract_factory(address=parity_fixture_data['emitter_address']) + + +@pytest.fixture() +def emitter_contract_address(emitter_contract, address_conversion_func): + return address_conversion_func(emitter_contract.address) + + +@pytest.fixture(scope="module") +def unlocked_account(web3, unlockable_account, unlockable_account_pw): + yield unlockable_account + + +@pytest.fixture(scope='module') +def unlockable_account_pw(parity_fixture_data): + return parity_fixture_data['keyfile_pw'] + + +@pytest.fixture(scope="module") +def unlockable_account(web3, coinbase): + yield coinbase + + +@pytest.fixture() +def unlockable_account_dual_type(unlockable_account, address_conversion_func): + return address_conversion_func(unlockable_account) + + +@pytest.fixture +def unlocked_account_dual_type(unlockable_account_dual_type): + return unlockable_account_dual_type + + +@pytest.fixture(scope="module") +def funded_account_for_raw_txn(parity_fixture_data): + account = parity_fixture_data['raw_txn_account'] + assert is_checksum_address(account) + return account + + +@pytest.fixture(scope="module") +def empty_block(web3, parity_fixture_data): + block = web3.vns.getBlock(parity_fixture_data['empty_block_hash']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def block_with_txn(web3, parity_fixture_data): + block = web3.vns.getBlock(parity_fixture_data['block_with_txn_hash']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def mined_txn_hash(parity_fixture_data): + return parity_fixture_data['mined_txn_hash'] + + +@pytest.fixture(scope="module") +def block_with_txn_with_log(web3, parity_fixture_data): + block = web3.vns.getBlock(parity_fixture_data['block_hash_with_log']) + assert is_dict(block) + return block + + +@pytest.fixture(scope="module") +def txn_hash_with_log(parity_fixture_data): + return parity_fixture_data['txn_hash_with_log'] + + +@pytest.fixture(scope="module") +def txn_filter_params(coinbase): + return { + "fromBlock": "earliest", + "toBlock": "latest", + "fromAddress": [coinbase], + } diff --git a/tests/integration/parity/install_parity.py b/tests/integration/parity/install_parity.py index f223303396..e1f7e79cba 100644 --- a/tests/integration/parity/install_parity.py +++ b/tests/integration/parity/install_parity.py @@ -1,116 +1,116 @@ -import hashlib -from json.decoder import ( - JSONDecodeError, -) -import os -import stat -import sys - -from eth_utils import ( - to_tuple, -) -import requests -import toolz -from tqdm import tqdm - -URI_QUERY_URL = "https://vanity-service.parity.io/parity-binaries" -BASE_BIN_PATH = "~/.parity-bin" -VERSION_STRINGS = { - "v2.3.5": "2_3_5", -} -ARCHITECTURE = 'x86_64' -OS = os.getenv('PARITY_OS', 'linux') - - -@toolz.curry -def fill_default_request_params(version, os=OS, architecture=ARCHITECTURE): - params = {'version': version, 'os': os, 'architecture': architecture} - return params - - -@toolz.functoolz.memoize -def get_parity_release_json(**kwargs): - try: - return requests.get( - URI_QUERY_URL, - params=kwargs).json() - except JSONDecodeError as json_err: - raise EnvironmentError("Could not find parity release for %s with %r" % ( - URI_QUERY_URL, - kwargs, - )) from json_err - - -@to_tuple -def get_binary_uri(releases_json): - for release in releases_json: - binary_uri = ((uri.get('downloadUrl', None), uri.get('checksum', None)) - for uri in release.get('files', tuple()) - if uri.get('name') == 'parity') - yield next(binary_uri) - - -def get_executable_path(version_string): - identifier = VERSION_STRINGS[version_string] - base_path = os.environ.get('PARITY_BASE_INSTALL_PATH', BASE_BIN_PATH) - path = os.path.join(base_path, f'parity-{identifier}') - return os.path.expanduser(path) - - -def install_parity(version_string): - if version_string not in VERSION_STRINGS.keys(): - raise ValueError("{0} is not an accepted version identifier.") - - path = get_executable_path(version_string) - get_uri = toolz.functoolz.compose( - get_binary_uri, - get_parity_release_json) - params = fill_default_request_params(version_string) - uri, checksum = get_uri(**params)[0] - - if not os.path.exists(path): - try: - download_binary(path, uri, checksum) - except AssertionError as exc: - raise Exception("failed to download binary at uri %r with params %r" % ( - uri, - params, - )) from exc - - return path - - -def download_binary(path, uri, checksum): - r = get_binary_stream(uri) - total_size = int(r.headers.get('content-length', 0)) - os.makedirs(os.path.dirname(path), exist_ok=True) - digest = hashlib.sha256() - with open(path, 'wb') as f: - with tqdm(total=total_size, - unit='B', - unit_scale=True, - unit_divisor=1024) as pbar: - for data in r.iter_content(32 * 1024): - f.write(data) - pbar.update(len(data)) - digest.update(data) - hex_digest = digest.hexdigest() - assert hex_digest == checksum, "digest vs checksum: %r vs %r" % (hex_digest, checksum) - chmod_plus_x(path) - - -def chmod_plus_x(executable_path): - current_st = os.stat(executable_path) - os.chmod(executable_path, current_st.st_mode | stat.S_IEXEC) - - -def get_binary_stream(uri): - resp = requests.get(uri, stream=True) - if resp.status_code == 200: - return resp - else: - resp.raise_for_status() - - -if __name__ == '__main__': - install_parity(sys.argv[1]) +import hashlib +from json.decoder import ( + JSONDecodeError, +) +import os +import stat +import sys + +from vns_utils import ( + to_tuple, +) +import requests +import toolz +from tqdm import tqdm + +URI_QUERY_URL = "https://vanity-service.parity.io/parity-binaries" +BASE_BIN_PATH = "~/.parity-bin" +VERSION_STRINGS = { + "v2.3.5": "2_3_5", +} +ARCHITECTURE = 'x86_64' +OS = os.getenv('PARITY_OS', 'linux') + + +@toolz.curry +def fill_default_request_params(version, os=OS, architecture=ARCHITECTURE): + params = {'version': version, 'os': os, 'architecture': architecture} + return params + + +@toolz.functoolz.memoize +def get_parity_release_json(**kwargs): + try: + return requests.get( + URI_QUERY_URL, + params=kwargs).json() + except JSONDecodeError as json_err: + raise EnvironmentError("Could not find parity release for %s with %r" % ( + URI_QUERY_URL, + kwargs, + )) from json_err + + +@to_tuple +def get_binary_uri(releases_json): + for release in releases_json: + binary_uri = ((uri.get('downloadUrl', None), uri.get('checksum', None)) + for uri in release.get('files', tuple()) + if uri.get('name') == 'parity') + yield next(binary_uri) + + +def get_executable_path(version_string): + identifier = VERSION_STRINGS[version_string] + base_path = os.environ.get('PARITY_BASE_INSTALL_PATH', BASE_BIN_PATH) + path = os.path.join(base_path, f'parity-{identifier}') + return os.path.expanduser(path) + + +def install_parity(version_string): + if version_string not in VERSION_STRINGS.keys(): + raise ValueError("{0} is not an accepted version identifier.") + + path = get_executable_path(version_string) + get_uri = toolz.functoolz.compose( + get_binary_uri, + get_parity_release_json) + params = fill_default_request_params(version_string) + uri, checksum = get_uri(**params)[0] + + if not os.path.exists(path): + try: + download_binary(path, uri, checksum) + except AssertionError as exc: + raise Exception("failed to download binary at uri %r with params %r" % ( + uri, + params, + )) from exc + + return path + + +def download_binary(path, uri, checksum): + r = get_binary_stream(uri) + total_size = int(r.headers.get('content-length', 0)) + os.makedirs(os.path.dirname(path), exist_ok=True) + digest = hashlib.sha256() + with open(path, 'wb') as f: + with tqdm(total=total_size, + unit='B', + unit_scale=True, + unit_divisor=1024) as pbar: + for data in r.iter_content(32 * 1024): + f.write(data) + pbar.update(len(data)) + digest.update(data) + hex_digest = digest.hexdigest() + assert hex_digest == checksum, "digest vs checksum: %r vs %r" % (hex_digest, checksum) + chmod_plus_x(path) + + +def chmod_plus_x(executable_path): + current_st = os.stat(executable_path) + os.chmod(executable_path, current_st.st_mode | stat.S_IEXEC) + + +def get_binary_stream(uri): + resp = requests.get(uri, stream=True) + if resp.status_code == 200: + return resp + else: + resp.raise_for_status() + + +if __name__ == '__main__': + install_parity(sys.argv[1]) diff --git a/tests/integration/parity/test_parity_http.py b/tests/integration/parity/test_parity_http.py index 2b58ed677e..cb71cd4d83 100644 --- a/tests/integration/parity/test_parity_http.py +++ b/tests/integration/parity/test_parity_http.py @@ -1,108 +1,106 @@ -import os -import pytest - -from tests.integration.parity.utils import ( - wait_for_http, -) -from tests.utils import ( - get_open_port, -) -from web3 import Web3 -from web3._utils.module_testing import ( - NetModuleTest, - VersionModuleTest, -) - -from .common import ( - CommonParityShhModuleTest, - ParityEthModuleTest, - ParityPersonalModuleTest, - ParityTraceModuleTest, - ParityWeb3ModuleTest, -) - - -@pytest.fixture(scope="module") -def rpc_port(): - return get_open_port() - - -@pytest.fixture(scope="module") -def endpoint_uri(rpc_port): - return 'http://localhost:{0}'.format(rpc_port) - - -@pytest.fixture(scope="module") -def parity_command_arguments( - parity_import_blocks_process, - parity_binary, - datadir, - passwordfile, - author, - rpc_port -): - return ( - parity_binary, - '--chain', os.path.join(datadir, 'chain_config.json'), - '--base-path', datadir, - '--unlock', author, - '--password', passwordfile, - '--jsonrpc-port', rpc_port, - '--jsonrpc-apis', 'all', - '--jsonrpc-experimental', - '--no-ipc', - '--no-ws', - '--whisper', - ) - - -@pytest.fixture(scope="module") -def parity_import_blocks_command(parity_binary, rpc_port, datadir, passwordfile): - return ( - parity_binary, - 'import', os.path.join(datadir, 'blocks_export.rlp'), - '--chain', os.path.join(datadir, 'chain_config.json'), - '--base-path', datadir, - '--password', passwordfile, - '--jsonrpc-port', str(rpc_port), - '--jsonrpc-apis', 'all', - '--jsonrpc-experimental', - '--no-ipc', - '--no-ws', - '--tracing', 'on', - ) - - -@pytest.fixture(scope="module") # noqa: F811 -def web3(parity_process, endpoint_uri): - wait_for_http(endpoint_uri) - _web3 = Web3(Web3.HTTPProvider(endpoint_uri)) - return _web3 - - -class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): - pass - - -class TestParityEthModuleTest(ParityEthModuleTest): - pass - - -class TestParityVersionModule(VersionModuleTest): - pass - - -class TestParityNetModule(NetModuleTest): - pass - - -class TestParityPersonalModuleTest(ParityPersonalModuleTest): - pass - - -class TestParityTraceModuleTest(ParityTraceModuleTest): - pass - - -class TestParityShhModuleTest(CommonParityShhModuleTest): - pass +import os +import pytest + +from tests.integration.parity.utils import ( + wait_for_http, +) +from tests.utils import ( + get_open_port, +) +from web3 import Web3 +from web3._utils.module_testing import ( + NetModuleTest, + VersionModuleTest, +) + +from .common import ( + CommonParityShhModuleTest, + ParityEthModuleTest, + ParityPersonalModuleTest, + ParityTraceModuleTest, + ParityWeb3ModuleTest, +) + + +@pytest.fixture(scope="module") +def rpc_port(): + return get_open_port() + + +@pytest.fixture(scope="module") +def endpoint_uri(rpc_port): + return 'http://localhost:{0}'.format(rpc_port) + + +@pytest.fixture(scope="module") +def parity_command_arguments( + parity_import_blocks_process, + parity_binary, + datadir, + passwordfile, + author, + rpc_port +): + return ( + parity_binary, + '--chain', os.path.join(datadir, 'chain_config.json'), + '--base-path', datadir, + '--unlock', author, + '--password', passwordfile, + '--jsonrpc-port', rpc_port, + '--jsonrpc-apis', 'all', + '--no-ipc', + '--no-ws', + '--whisper', + ) + + +@pytest.fixture(scope="module") +def parity_import_blocks_command(parity_binary, rpc_port, datadir, passwordfile): + return ( + parity_binary, + 'import', os.path.join(datadir, 'blocks_export.rlp'), + '--chain', os.path.join(datadir, 'chain_config.json'), + '--base-path', datadir, + '--password', passwordfile, + '--jsonrpc-port', str(rpc_port), + '--jsonrpc-apis', 'all', + '--no-ipc', + '--no-ws', + '--tracing', 'on', + ) + + +@pytest.fixture(scope="module") # noqa: F811 +def web3(parity_process, endpoint_uri): + wait_for_http(endpoint_uri) + _web3 = Web3 (Web3.HTTPProvider(endpoint_uri)) + return _web3 + + +class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): + pass + + +class TestParityEthModuleTest(ParityEthModuleTest): + pass + + +class TestParityVersionModule(VersionModuleTest): + pass + + +class TestParityNetModule(NetModuleTest): + pass + + +class TestParityPersonalModuleTest(ParityPersonalModuleTest): + pass + + +class TestParityTraceModuleTest(ParityTraceModuleTest): + pass + + +class TestParityShhModuleTest(CommonParityShhModuleTest): + pass diff --git a/tests/integration/parity/test_parity_ipc.py b/tests/integration/parity/test_parity_ipc.py index 84c86524fd..b81dfc6d60 100644 --- a/tests/integration/parity/test_parity_ipc.py +++ b/tests/integration/parity/test_parity_ipc.py @@ -1,116 +1,104 @@ -import os -import pytest -import tempfile - -from tests.integration.parity.utils import ( - wait_for_socket, -) -from web3 import Web3 -from web3._utils.module_testing import ( - NetModuleTest, - VersionModuleTest, -) - -from .common import ( - CommonParityShhModuleTest, - ParityEthModuleTest, - ParityModuleTest, - ParityPersonalModuleTest, - ParitySetModuleTest, - ParityTraceModuleTest, - ParityWeb3ModuleTest, -) - - -@pytest.fixture(scope='module') -def ipc_path(datadir): - ipc_dir_path = tempfile.mkdtemp() - _ipc_path = os.path.join(ipc_dir_path, 'jsonrpc.ipc') - yield _ipc_path - - if os.path.exists(_ipc_path): - os.remove(_ipc_path) - - -@pytest.fixture(scope="module") -def parity_command_arguments( - parity_import_blocks_process, - parity_binary, - ipc_path, - datadir, - passwordfile, - author -): - return ( - parity_binary, - '--chain', os.path.join(datadir, 'chain_config.json'), - '--ipc-path', ipc_path, - '--base-path', datadir, - '--unlock', author, - '--password', passwordfile, - '--ipc-apis', 'all', - '--jsonrpc-experimental', - '--no-jsonrpc', - '--no-ws', - '--whisper', - ) - - -@pytest.fixture(scope="module") -def parity_import_blocks_command(parity_binary, ipc_path, datadir, passwordfile): - return ( - parity_binary, - 'import', os.path.join(datadir, 'blocks_export.rlp'), - '--chain', os.path.join(datadir, 'chain_config.json'), - '--ipc-path', ipc_path, - '--base-path', datadir, - '--password', passwordfile, - '--ipc-apis', 'all', - '--jsonrpc-experimental', - '--no-jsonrpc', - '--no-ws', - '--tracing', 'on', - ) - - -@pytest.fixture(scope="module") # noqa: F811 -def web3(parity_process, ipc_path): - wait_for_socket(ipc_path) - _web3 = Web3(Web3.IPCProvider(ipc_path)) - return _web3 - - -class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): - pass - - -class TestParityEthModuleTest(ParityEthModuleTest): - pass - - -class TestParityVersionModule(VersionModuleTest): - pass - - -class TestParityNetModule(NetModuleTest): - pass - - -class TestParityPersonalModuleTest(ParityPersonalModuleTest): - pass - - -class TestParityTraceModuleTest(ParityTraceModuleTest): - pass - - -class TestParityModuleTest(ParityModuleTest): - pass - - -class TestParitySetModuleTest(ParitySetModuleTest): - pass - - -class TestParityShhModuleTest(CommonParityShhModuleTest): - pass +import os +import pytest +import tempfile + +from tests.integration.parity.utils import ( + wait_for_socket, +) +from web3 import Web3 +from web3._utils.module_testing import ( + NetModuleTest, + VersionModuleTest, +) + +from .common import ( + CommonParityShhModuleTest, + ParityEthModuleTest, + ParityPersonalModuleTest, + ParityTraceModuleTest, + ParityWeb3ModuleTest, +) + + +@pytest.fixture(scope='module') +def ipc_path(datadir): + ipc_dir_path = tempfile.mkdtemp() + _ipc_path = os.path.join(ipc_dir_path, 'jsonrpc.ipc') + yield _ipc_path + + if os.path.exists(_ipc_path): + os.remove(_ipc_path) + + +@pytest.fixture(scope="module") +def parity_command_arguments( + parity_import_blocks_process, + parity_binary, + ipc_path, + datadir, + passwordfile, + author +): + return ( + parity_binary, + '--chain', os.path.join(datadir, 'chain_config.json'), + '--ipc-path', ipc_path, + '--base-path', datadir, + '--unlock', author, + '--password', passwordfile, + '--ipc-apis', 'all', + '--no-jsonrpc', + '--no-ws', + '--whisper', + ) + + +@pytest.fixture(scope="module") +def parity_import_blocks_command(parity_binary, ipc_path, datadir, passwordfile): + return ( + parity_binary, + 'import', os.path.join(datadir, 'blocks_export.rlp'), + '--chain', os.path.join(datadir, 'chain_config.json'), + '--ipc-path', ipc_path, + '--base-path', datadir, + '--password', passwordfile, + '--ipc-apis', 'all', + '--no-jsonrpc', + '--no-ws', + '--tracing', 'on', + ) + + +@pytest.fixture(scope="module") # noqa: F811 +def web3(parity_process, ipc_path): + wait_for_socket(ipc_path) + _web3 = Web3 (Web3.IPCProvider(ipc_path)) + return _web3 + + +class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): + pass + + +class TestParityEthModuleTest(ParityEthModuleTest): + pass + + +class TestParityVersionModule(VersionModuleTest): + pass + + +class TestParityNetModule(NetModuleTest): + pass + + +class TestParityPersonalModuleTest(ParityPersonalModuleTest): + pass + + +class TestParityTraceModuleTest(ParityTraceModuleTest): + pass + + +class TestParityShhModuleTest(CommonParityShhModuleTest): + pass diff --git a/tests/integration/parity/test_parity_ws.py b/tests/integration/parity/test_parity_ws.py index 16597294c1..8b4fb003a5 100644 --- a/tests/integration/parity/test_parity_ws.py +++ b/tests/integration/parity/test_parity_ws.py @@ -1,125 +1,113 @@ -import os -import pytest - -from tests.integration.common import ( - MiscWebsocketTest, -) -from tests.utils import ( - get_open_port, - wait_for_ws, -) -from web3 import Web3 -from web3._utils.module_testing import ( - NetModuleTest, - VersionModuleTest, -) - -from .common import ( - CommonParityShhModuleTest, - ParityEthModuleTest, - ParityModuleTest, - ParityPersonalModuleTest, - ParitySetModuleTest, - ParityTraceModuleTest, - ParityWeb3ModuleTest, -) - - -@pytest.fixture(scope="module") -def ws_port(): - return get_open_port() - - -@pytest.fixture(scope="module") -def endpoint_uri(ws_port): - return 'ws://localhost:{0}'.format(ws_port) - - -@pytest.fixture(scope="module") -def parity_command_arguments( - parity_import_blocks_process, - parity_binary, - datadir, - passwordfile, - author, - ws_port -): - return ( - parity_binary, - '--chain', os.path.join(datadir, 'chain_config.json'), - '--base-path', datadir, - '--unlock', author, - '--password', passwordfile, - '--ws-port', ws_port, - '--ws-origins', '*', - '--ws-apis', 'all', - '--jsonrpc-experimental', - '--no-ipc', - '--no-jsonrpc', - '--whisper', - ) - - -@pytest.fixture(scope="module") -def parity_import_blocks_command(parity_binary, ws_port, datadir, passwordfile): - return ( - parity_binary, - 'import', os.path.join(datadir, 'blocks_export.rlp'), - '--chain', os.path.join(datadir, 'chain_config.json'), - '--base-path', datadir, - '--password', passwordfile, - '--ws-port', str(ws_port), - '--ws-origins', '*', - '--ws-apis', 'all', - '--jsonrpc-experimental', - '--no-ipc', - '--no-jsonrpc', - '--tracing', 'on', - ) - - -@pytest.fixture(scope="module") # noqa: F811 -def web3(parity_process, endpoint_uri, event_loop): - event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) - _web3 = Web3(Web3.WebsocketProvider(endpoint_uri)) - return _web3 - - -class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): - pass - - -class TestParityEthModuleTest(ParityEthModuleTest): - pass - - -class TestParityVersionModule(VersionModuleTest): - pass - - -class TestParityNetModule(NetModuleTest): - pass - - -class TestParityPersonalModuleTest(ParityPersonalModuleTest): - pass - - -class TestParityTraceModuleTest(ParityTraceModuleTest): - pass - - -class TestParityModuleTest(ParityModuleTest): - pass - - -class TestParitySetModuleTest(ParitySetModuleTest): - pass - - -class TestMiscWebsocketTest(MiscWebsocketTest): - pass - - -class TestParityShhModuleTest(CommonParityShhModuleTest): - pass +import os +import pytest + +from tests.integration.common import ( + MiscWebsocketTest, +) +from tests.utils import ( + get_open_port, + wait_for_ws, +) +from web3 import Web3 +from web3._utils.module_testing import ( + NetModuleTest, + VersionModuleTest, +) + +from .common import ( + CommonParityShhModuleTest, + ParityEthModuleTest, + ParityPersonalModuleTest, + ParityTraceModuleTest, + ParityWeb3ModuleTest, +) + + +@pytest.fixture(scope="module") +def ws_port(): + return get_open_port() + + +@pytest.fixture(scope="module") +def endpoint_uri(ws_port): + return 'ws://localhost:{0}'.format(ws_port) + + +@pytest.fixture(scope="module") +def parity_command_arguments( + parity_import_blocks_process, + parity_binary, + datadir, + passwordfile, + author, + ws_port +): + return ( + parity_binary, + '--chain', os.path.join(datadir, 'chain_config.json'), + '--base-path', datadir, + '--unlock', author, + '--password', passwordfile, + '--ws-port', ws_port, + '--ws-origins', '*', + '--ws-apis', 'all', + '--no-ipc', + '--no-jsonrpc', + '--whisper', + ) + + +@pytest.fixture(scope="module") +def parity_import_blocks_command(parity_binary, ws_port, datadir, passwordfile): + return ( + parity_binary, + 'import', os.path.join(datadir, 'blocks_export.rlp'), + '--chain', os.path.join(datadir, 'chain_config.json'), + '--base-path', datadir, + '--password', passwordfile, + '--ws-port', str(ws_port), + '--ws-origins', '*', + '--ws-apis', 'all', + '--no-ipc', + '--no-jsonrpc', + '--tracing', 'on', + ) + + +@pytest.fixture(scope="module") # noqa: F811 +def web3(parity_process, endpoint_uri, event_loop): + event_loop.run_until_complete(wait_for_ws(endpoint_uri, event_loop)) + _web3 = Web3 (Web3.WebsocketProvider(endpoint_uri)) + return _web3 + + +class TestParityWeb3ModuleTest(ParityWeb3ModuleTest): + pass + + +class TestParityEthModuleTest(ParityEthModuleTest): + pass + + +class TestParityVersionModule(VersionModuleTest): + pass + + +class TestParityNetModule(NetModuleTest): + pass + + +class TestParityPersonalModuleTest(ParityPersonalModuleTest): + pass + + +class TestParityTraceModuleTest(ParityTraceModuleTest): + pass + + +class TestMiscWebsocketTest(MiscWebsocketTest): + pass + + +class TestParityShhModuleTest(CommonParityShhModuleTest): + pass diff --git a/tests/integration/parity/utils.py b/tests/integration/parity/utils.py index 0403533c58..572d4c4af3 100644 --- a/tests/integration/parity/utils.py +++ b/tests/integration/parity/utils.py @@ -1,82 +1,82 @@ -import signal -import socket -import subprocess -import time - -from eth_utils import ( - to_text, -) -import requests - - -def wait_for_socket(ipc_path, timeout=60): - start = time.time() - while time.time() < start + timeout: - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(ipc_path) - sock.settimeout(timeout) - except (FileNotFoundError, socket.error): - time.sleep(0.01) - else: - break - - -def wait_for_http(endpoint_uri, timeout=60): - start = time.time() - while time.time() < start + timeout: - try: - requests.get(endpoint_uri) - except requests.ConnectionError: - time.sleep(0.01) - else: - break - - -def get_process(command_list, terminates=False): - - proc = subprocess.Popen( - command_list, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - bufsize=1, - ) - if terminates: - wait_for_popen(proc, 30) - try: - yield proc - finally: - kill_proc_gracefully(proc) - output, errors = proc.communicate() - print( - "Parity Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) - ) - - -def wait_for_popen(proc, timeout): - start = time.time() - while time.time() < start + timeout: - if proc.poll() is None: - time.sleep(0.01) - else: - break - - -def kill_proc_gracefully(proc): - if proc.poll() is None: - proc.send_signal(signal.SIGINT) - wait_for_popen(proc, 13) - - if proc.poll() is None: - proc.terminate() - wait_for_popen(proc, 5) - - if proc.poll() is None: - proc.kill() - wait_for_popen(proc, 2) +import signal +import socket +import subprocess +import time + +from vns_utils import ( + to_text, +) +import requests + + +def wait_for_socket(ipc_path, timeout=60): + start = time.time() + while time.time() < start + timeout: + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(ipc_path) + sock.settimeout(timeout) + except (FileNotFoundError, socket.error): + time.sleep(0.01) + else: + break + + +def wait_for_http(endpoint_uri, timeout=60): + start = time.time() + while time.time() < start + timeout: + try: + requests.get(endpoint_uri) + except requests.ConnectionError: + time.sleep(0.01) + else: + break + + +def get_process(command_list, terminates=False): + + proc = subprocess.Popen( + command_list, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=1, + ) + if terminates: + wait_for_popen(proc, 30) + try: + yield proc + finally: + kill_proc_gracefully(proc) + output, errors = proc.communicate() + print( + "Parity Process Exited:\n" + "stdout:{0}\n\n" + "stderr:{1}\n\n".format( + to_text(output), + to_text(errors), + ) + ) + + +def wait_for_popen(proc, timeout): + start = time.time() + while time.time() < start + timeout: + if proc.poll() is None: + time.sleep(0.01) + else: + break + + +def kill_proc_gracefully(proc): + if proc.poll() is None: + proc.send_signal(signal.SIGINT) + wait_for_popen(proc, 13) + + if proc.poll() is None: + proc.terminate() + wait_for_popen(proc, 5) + + if proc.poll() is None: + proc.kill() + wait_for_popen(proc, 2) diff --git a/tests/integration/test_ethereum_tester.py b/tests/integration/test_ethereum_tester.py index 8df3e15101..2310d93b49 100644 --- a/tests/integration/test_ethereum_tester.py +++ b/tests/integration/test_ethereum_tester.py @@ -1,322 +1,324 @@ -import functools -import pytest - -from eth_tester import ( - EthereumTester, -) -from eth_utils import ( - is_checksum_address, - is_dict, - is_integer, -) - -from web3 import Web3 -from web3._utils.module_testing import ( - EthModuleTest, - GoEthereumPersonalModuleTest, - NetModuleTest, - VersionModuleTest, - Web3ModuleTest, -) -from web3._utils.module_testing.emitter_contract import ( - EMITTER_ENUM, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) - -pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") - - -@pytest.fixture(scope="module") -def eth_tester(): - _eth_tester = EthereumTester() - return _eth_tester - - -@pytest.fixture(scope="module") -def eth_tester_provider(eth_tester): - provider = EthereumTesterProvider(eth_tester) - return provider - - -@pytest.fixture(scope="module") -def web3(eth_tester_provider): - _web3 = Web3(eth_tester_provider) - return _web3 - - -# -# Math Contract Setup -# -@pytest.fixture(scope="module") -def math_contract_deploy_txn_hash(web3, math_contract_factory): - deploy_txn_hash = math_contract_factory.constructor().transact({'from': web3.eth.coinbase}) - return deploy_txn_hash - - -@pytest.fixture(scope="module") -def math_contract(web3, math_contract_factory, math_contract_deploy_txn_hash): - deploy_receipt = web3.eth.waitForTransactionReceipt(math_contract_deploy_txn_hash) - assert is_dict(deploy_receipt) - contract_address = deploy_receipt['contractAddress'] - assert is_checksum_address(contract_address) - return math_contract_factory(contract_address) - - -@pytest.fixture(scope="module") -def math_contract_address(math_contract, address_conversion_func): - return address_conversion_func(math_contract.address) - -# -# Emitter Contract Setup -# - - -@pytest.fixture(scope="module") -def emitter_contract_deploy_txn_hash(web3, emitter_contract_factory): - deploy_txn_hash = emitter_contract_factory.constructor().transact({'from': web3.eth.coinbase}) - return deploy_txn_hash - - -@pytest.fixture(scope="module") -def emitter_contract(web3, emitter_contract_factory, emitter_contract_deploy_txn_hash): - deploy_receipt = web3.eth.waitForTransactionReceipt(emitter_contract_deploy_txn_hash) - assert is_dict(deploy_receipt) - contract_address = deploy_receipt['contractAddress'] - assert is_checksum_address(contract_address) - return emitter_contract_factory(contract_address) - - -@pytest.fixture(scope="module") -def emitter_contract_address(emitter_contract, address_conversion_func): - return address_conversion_func(emitter_contract.address) - - -@pytest.fixture(scope="module") -def empty_block(web3): - web3.testing.mine() - block = web3.eth.getBlock("latest") - assert not block['transactions'] - return block - - -@pytest.fixture(scope="module") -def block_with_txn(web3): - txn_hash = web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': web3.eth.coinbase, - 'value': 1, - 'gas': 21000, - 'gas_price': 1, - }) - txn = web3.eth.getTransaction(txn_hash) - block = web3.eth.getBlock(txn['blockNumber']) - return block - - -@pytest.fixture(scope="module") -def mined_txn_hash(block_with_txn): - return block_with_txn['transactions'][0] - - -@pytest.fixture(scope="module") -def block_with_txn_with_log(web3, emitter_contract): - txn_hash = emitter_contract.functions.logDouble( - which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, - ).transact({ - 'from': web3.eth.coinbase, - }) - txn = web3.eth.getTransaction(txn_hash) - block = web3.eth.getBlock(txn['blockNumber']) - return block - - -@pytest.fixture(scope="module") -def txn_hash_with_log(block_with_txn_with_log): - return block_with_txn_with_log['transactions'][0] - - -UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' - - -@pytest.fixture(scope='module') -def unlockable_account_pw(web3): - return 'web3-testing' - - -@pytest.fixture(scope='module') -def unlockable_account(web3, unlockable_account_pw): - account = web3.geth.personal.importRawKey(UNLOCKABLE_PRIVATE_KEY, unlockable_account_pw) - web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': account, - 'value': web3.toWei(10, 'ether'), - }) - yield account - - -@pytest.fixture -def unlocked_account(web3, unlockable_account, unlockable_account_pw): - web3.geth.personal.unlockAccount(unlockable_account, unlockable_account_pw) - yield unlockable_account - web3.geth.personal.lockAccount(unlockable_account) - - -@pytest.fixture() -def unlockable_account_dual_type(unlockable_account, address_conversion_func): - return address_conversion_func(unlockable_account) - - -@pytest.fixture -def unlocked_account_dual_type(web3, unlockable_account_dual_type, unlockable_account_pw): - web3.geth.personal.unlockAccount(unlockable_account_dual_type, unlockable_account_pw) - yield unlockable_account_dual_type - web3.geth.personal.lockAccount(unlockable_account_dual_type) - - -@pytest.fixture(scope="module") -def funded_account_for_raw_txn(web3): - account = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' - web3.eth.sendTransaction({ - 'from': web3.eth.coinbase, - 'to': account, - 'value': web3.toWei(10, 'ether'), - 'gas': 21000, - 'gas_price': 1, - }) - return account - - -class TestEthereumTesterWeb3Module(Web3ModuleTest): - def _check_web3_clientVersion(self, client_version): - assert client_version.startswith('EthereumTester/') - - -def not_implemented(method, exc_type=NotImplementedError): - @functools.wraps(method) - def inner(*args, **kwargs): - with pytest.raises(exc_type): - method(*args, **kwargs) - return inner - - -def disable_auto_mine(func): - @functools.wraps(func) - def func_wrapper(self, eth_tester, *args, **kwargs): - snapshot = eth_tester.take_snapshot() - eth_tester.disable_auto_mine_transactions() - try: - func(self, eth_tester, *args, **kwargs) - finally: - eth_tester.enable_auto_mine_transactions() - eth_tester.mine_block() - eth_tester.revert_to_snapshot(snapshot) - return func_wrapper - - -class TestEthereumTesterEthModule(EthModuleTest): - test_eth_sign = not_implemented(EthModuleTest.test_eth_sign, ValueError) - test_eth_signTypedData = not_implemented(EthModuleTest.test_eth_signTypedData, ValueError) - test_eth_signTransaction = not_implemented(EthModuleTest.test_eth_signTransaction, ValueError) - test_eth_submitHashrate = not_implemented(EthModuleTest.test_eth_submitHashrate, ValueError) - test_eth_submitWork = not_implemented(EthModuleTest.test_eth_submitWork, ValueError) - - @disable_auto_mine - def test_eth_getTransactionReceipt_unmined(self, eth_tester, web3, unlocked_account): - super().test_eth_getTransactionReceipt_unmined(web3, unlocked_account) - - @disable_auto_mine - def test_eth_replaceTransaction(self, eth_tester, web3, unlocked_account): - super().test_eth_replaceTransaction(web3, unlocked_account) - - @disable_auto_mine - def test_eth_replaceTransaction_incorrect_nonce(self, eth_tester, web3, unlocked_account): - super().test_eth_replaceTransaction_incorrect_nonce(web3, unlocked_account) - - @disable_auto_mine - def test_eth_replaceTransaction_gas_price_too_low(self, eth_tester, web3, unlocked_account): - super().test_eth_replaceTransaction_gas_price_too_low(web3, unlocked_account) - - @disable_auto_mine - def test_eth_replaceTransaction_gas_price_defaulting_minimum(self, - eth_tester, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) - - @disable_auto_mine - def test_eth_replaceTransaction_gas_price_defaulting_strategy_higher(self, - eth_tester, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_higher( - web3, unlocked_account - ) - - @disable_auto_mine - def test_eth_replaceTransaction_gas_price_defaulting_strategy_lower(self, - eth_tester, - web3, - unlocked_account): - super().test_eth_replaceTransaction_gas_price_defaulting_strategy_lower( - web3, unlocked_account - ) - - @disable_auto_mine - def test_eth_modifyTransaction(self, eth_tester, web3, unlocked_account): - super().test_eth_modifyTransaction(web3, unlocked_account) - - @disable_auto_mine - def test_eth_call_old_contract_state(self, eth_tester, web3, math_contract, unlocked_account): - # For now, ethereum tester cannot give call results in the pending block. - # Once that feature is added, then delete the except/else blocks. - try: - super().test_eth_call_old_contract_state(web3, math_contract, unlocked_account) - except AssertionError as err: - if str(err) == "pending call result was 0 instead of 1": - pass - else: - raise err - else: - raise AssertionError("eth-tester was unexpectedly able to give the pending call result") - - @pytest.mark.xfail(reason='json-rpc method is not implemented on eth-tester') - def test_eth_getStorageAt(self, web3, emitter_contract_address): - super().test_eth_getStorageAt(web3, emitter_contract_address) - - @pytest.mark.xfail(reason='Block identifier has not been implemented in eth-tester') - def test_eth_estimateGas_with_block(self, - web3, - unlocked_account_dual_type): - super().test_eth_estimateGas_with_block( - web3, unlocked_account_dual_type - ) - - def test_eth_chainId(self, web3): - chain_id = web3.eth.chainId - assert is_integer(chain_id) - assert chain_id == 61 - - -class TestEthereumTesterVersionModule(VersionModuleTest): - pass - - -class TestEthereumTesterNetModule(NetModuleTest): - pass - - -# Use web3.geth.personal namespace for testing eth-tester -class TestEthereumTesterPersonalModule(GoEthereumPersonalModuleTest): - test_personal_sign_and_ecrecover = not_implemented( - GoEthereumPersonalModuleTest.test_personal_sign_and_ecrecover, - ValueError, - ) - - # Test overridden here since eth-tester returns False rather than None for failed unlock - def test_personal_unlockAccount_failure(self, - web3, - unlockable_account_dual_type): - result = web3.geth.personal.unlockAccount(unlockable_account_dual_type, 'bad-password') - assert result is False +import functools +import pytest + +from vns_tester import ( + EthereumTester, +) +from vns_utils import ( + is_checksum_address, + is_dict, + is_hex, +) + +from web3 import Web3 +from web3._utils.formatters import ( + hex_to_integer, +) +from web3._utils.module_testing import ( + EthModuleTest, + GoEthereumPersonalModuleTest, + NetModuleTest, + VersionModuleTest, + Web3ModuleTest, +) +from web3._utils.module_testing.emitter_contract import ( + EMITTER_ENUM, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) + +pytestmark = pytest.mark.filterwarnings("ignore:implicit cast from 'char *'") + + +@pytest.fixture(scope="module") +def vns_tester(): + _vns_tester = EthereumTester() + return _vns_tester + + +@pytest.fixture(scope="module") +def vns_tester_provider(vns_tester): + provider = EthereumTesterProvider(vns_tester) + return provider + + +@pytest.fixture(scope="module") +def web3(vns_tester_provider): + _web3 = Web3(vns_tester_provider) + return _web3 + + +# +# Math Contract Setup +# +@pytest.fixture(scope="module") +def math_contract_deploy_txn_hash(web3, math_contract_factory): + deploy_txn_hash = math_contract_factory.constructor().transact({'from': web3.vns.coinbase}) + return deploy_txn_hash + + +@pytest.fixture(scope="module") +def math_contract(web3, math_contract_factory, math_contract_deploy_txn_hash): + deploy_receipt = web3.vns.waitForTransactionReceipt(math_contract_deploy_txn_hash) + assert is_dict(deploy_receipt) + contract_address = deploy_receipt['contractAddress'] + assert is_checksum_address(contract_address) + return math_contract_factory(contract_address) + + +@pytest.fixture(scope="module") +def math_contract_address(math_contract, address_conversion_func): + return address_conversion_func(math_contract.address) + +# +# Emitter Contract Setup +# + + +@pytest.fixture(scope="module") +def emitter_contract_deploy_txn_hash(web3, emitter_contract_factory): + deploy_txn_hash = emitter_contract_factory.constructor().transact({'from': web3.vns.coinbase}) + return deploy_txn_hash + + +@pytest.fixture(scope="module") +def emitter_contract(web3, emitter_contract_factory, emitter_contract_deploy_txn_hash): + deploy_receipt = web3.vns.waitForTransactionReceipt(emitter_contract_deploy_txn_hash) + assert is_dict(deploy_receipt) + contract_address = deploy_receipt['contractAddress'] + assert is_checksum_address(contract_address) + return emitter_contract_factory(contract_address) + + +@pytest.fixture(scope="module") +def emitter_contract_address(emitter_contract, address_conversion_func): + return address_conversion_func(emitter_contract.address) + + +@pytest.fixture(scope="module") +def empty_block(web3): + web3.testing.mine() + block = web3.vns.getBlock("latest") + assert not block['transactions'] + return block + + +@pytest.fixture(scope="module") +def block_with_txn(web3): + txn_hash = web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': web3.vns.coinbase, + 'value': 1, + 'gas': 21000, + 'gas_price': 1, + }) + txn = web3.vns.getTransaction(txn_hash) + block = web3.vns.getBlock(txn['blockNumber']) + return block + + +@pytest.fixture(scope="module") +def mined_txn_hash(block_with_txn): + return block_with_txn['transactions'][0] + + +@pytest.fixture(scope="module") +def block_with_txn_with_log(web3, emitter_contract): + txn_hash = emitter_contract.functions.logDouble( + which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321, + ).transact({ + 'from': web3.vns.coinbase, + }) + txn = web3.vns.getTransaction(txn_hash) + block = web3.vns.getBlock(txn['blockNumber']) + return block + + +@pytest.fixture(scope="module") +def txn_hash_with_log(block_with_txn_with_log): + return block_with_txn_with_log['transactions'][0] + + +UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' + + +@pytest.fixture(scope='module') +def unlockable_account_pw(web3): + return 'web3-testing' + + +@pytest.fixture(scope='module') +def unlockable_account(web3, unlockable_account_pw): + account = web3.geth.personal.importRawKey(UNLOCKABLE_PRIVATE_KEY, unlockable_account_pw) + web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': account, + 'value': web3.toWei(10, 'ether'), + }) + yield account + + +@pytest.fixture +def unlocked_account(web3, unlockable_account, unlockable_account_pw): + web3.geth.personal.unlockAccount(unlockable_account, unlockable_account_pw) + yield unlockable_account + web3.geth.personal.lockAccount(unlockable_account) + + +@pytest.fixture() +def unlockable_account_dual_type(unlockable_account, address_conversion_func): + return address_conversion_func(unlockable_account) + + +@pytest.fixture +def unlocked_account_dual_type(web3, unlockable_account_dual_type, unlockable_account_pw): + web3.geth.personal.unlockAccount(unlockable_account_dual_type, unlockable_account_pw) + yield unlockable_account_dual_type + web3.geth.personal.lockAccount(unlockable_account_dual_type) + + +@pytest.fixture(scope="module") +def funded_account_for_raw_txn(web3): + account = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' + web3.vns.sendTransaction({ + 'from': web3.vns.coinbase, + 'to': account, + 'value': web3.toWei(10, 'ether'), + 'gas': 21000, + 'gas_price': 1, + }) + return account + + +class TestEthereumTesterWeb3Module (Web3ModuleTest): + def _check_web3_clientVersion(self, client_version): + assert client_version.startswith('EthereumTester/') + + +def not_implemented(method, exc_type=NotImplementedError): + @functools.wraps(method) + def inner(*args, **kwargs): + with pytest.raises(exc_type): + method(*args, **kwargs) + return inner + + +def disable_auto_mine(func): + @functools.wraps(func) + def func_wrapper(self, vns_tester, *args, **kwargs): + snapshot = vns_tester.take_snapshot() + vns_tester.disable_auto_mine_transactions() + try: + func(self, vns_tester, *args, **kwargs) + finally: + vns_tester.enable_auto_mine_transactions() + vns_tester.mine_block() + vns_tester.revert_to_snapshot(snapshot) + return func_wrapper + + +class TestEthereumTesterEthModule(EthModuleTest): + test_vns_sign = not_implemented(EthModuleTest.test_vns_sign, ValueError) + test_vns_signTransaction = not_implemented(EthModuleTest.test_vns_signTransaction, ValueError) + test_vns_submitHashrate = not_implemented(EthModuleTest.test_vns_submitHashrate, ValueError) + test_vns_submitWork = not_implemented(EthModuleTest.test_vns_submitWork, ValueError) + + @disable_auto_mine + def test_vns_getTransactionReceipt_unmined(self, vns_tester, web3, unlocked_account): + super().test_vns_getTransactionReceipt_unmined(web3, unlocked_account) + + @disable_auto_mine + def test_vns_replaceTransaction(self, vns_tester, web3, unlocked_account): + super().test_vns_replaceTransaction(web3, unlocked_account) + + @disable_auto_mine + def test_vns_replaceTransaction_incorrect_nonce(self, vns_tester, web3, unlocked_account): + super().test_vns_replaceTransaction_incorrect_nonce(web3, unlocked_account) + + @disable_auto_mine + def test_vns_replaceTransaction_gas_price_too_low(self, vns_tester, web3, unlocked_account): + super().test_vns_replaceTransaction_gas_price_too_low(web3, unlocked_account) + + @disable_auto_mine + def test_vns_replaceTransaction_gas_price_defaulting_minimum(self, + vns_tester, + web3, + unlocked_account): + super().test_vns_replaceTransaction_gas_price_defaulting_minimum(web3, unlocked_account) + + @disable_auto_mine + def test_vns_replaceTransaction_gas_price_defaulting_strategy_higher(self, + vns_tester, + web3, + unlocked_account): + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_higher( + web3, unlocked_account + ) + + @disable_auto_mine + def test_vns_replaceTransaction_gas_price_defaulting_strategy_lower(self, + vns_tester, + web3, + unlocked_account): + super().test_vns_replaceTransaction_gas_price_defaulting_strategy_lower( + web3, unlocked_account + ) + + @disable_auto_mine + def test_vns_modifyTransaction(self, vns_tester, web3, unlocked_account): + super().test_vns_modifyTransaction(web3, unlocked_account) + + @disable_auto_mine + def test_vns_call_old_contract_state(self, vns_tester, web3, math_contract, unlocked_account): + # For now, ethereum tester cannot give call results in the pending block. + # Once that feature is added, then delete the except/else blocks. + try: + super().test_vns_call_old_contract_state(web3, math_contract, unlocked_account) + except AssertionError as err: + if str(err) == "pending call result was 0 instead of 1": + pass + else: + raise err + else: + raise AssertionError("vns-tester was unexpectedly able to give the pending call result") + + def test_vns_getStorageAt(self, web3, emitter_contract_address): + pytest.xfail('json-rpc method is not implemented on vns-tester') + super().test_vns_getStorageAt(web3, emitter_contract_address) + + def test_vns_estimateGas_with_block(self, + web3, + unlocked_account_dual_type): + pytest.xfail('Block identifier has not been implemented in vns-tester') + super().test_vns_estimateGas_with_block( + web3, unlocked_account_dual_type + ) + + def test_vns_chainId(self, web3): + chain_id = web3.vns.chainId + assert is_hex(chain_id) + assert hex_to_integer(chain_id) is 61 + + +class TestEthereumTesterVersionModule(VersionModuleTest): + pass + + +class TestEthereumTesterNetModule(NetModuleTest): + pass + + +# Use web3.geth.personal namespace for testing vns-tester +class TestEthereumTesterPersonalModule(GoEthereumPersonalModuleTest): + test_personal_sign_and_ecrecover = not_implemented( + GoEthereumPersonalModuleTest.test_personal_sign_and_ecrecover, + ValueError, + ) + + # Test overridden here since vns-tester returns False rather than None for failed unlock + def test_personal_unlockAccount_failure(self, + web3, + unlockable_account_dual_type): + result = web3.geth.personal.unlockAccount(unlockable_account_dual_type, 'bad-password') + assert result is False diff --git a/tests/utils.py b/tests/utils.py index 05f2899621..cc72a89cf1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,25 +1,25 @@ -import asyncio -import socket -import time - -import websockets - - -def get_open_port(): - sock = socket.socket() - sock.bind(('127.0.0.1', 0)) - port = sock.getsockname()[1] - sock.close() - return str(port) - - -async def wait_for_ws(endpoint_uri, event_loop, timeout=60): - start = time.time() - while time.time() < start + timeout: - try: - async with websockets.connect(uri=endpoint_uri, loop=event_loop): - pass - except (ConnectionRefusedError, OSError): - await asyncio.sleep(0.01) - else: - break +import asyncio +import socket +import time + +import websockets + + +def get_open_port(): + sock = socket.socket() + sock.bind(('127.0.0.1', 0)) + port = sock.getsockname()[1] + sock.close() + return str(port) + + +async def wait_for_ws(endpoint_uri, event_loop, timeout=60): + start = time.time() + while time.time() < start + timeout: + try: + async with websockets.connect(uri=endpoint_uri, loop=event_loop): + pass + except (ConnectionRefusedError, OSError): + await asyncio.sleep(0.01) + else: + break diff --git a/tox.ini b/tox.ini index 2160072986..990c10ecc6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,66 +1,61 @@ -[tox] -envlist= - py{36,37,38}-ens - py{36,37,38}-ethpm - py{36,37,38}-core - py{36,37,38}-integration-{goethereum,ethtester,parity} - lint - docs - -[isort] -combine_as_imports=True -force_sort_within_sections=True -include_trailing_comma=True -known_standard_library=pytest -known_third_party=lru,eth_tester -known_first_party=web3,ens,ethpm -line_length=21 -multi_line_output=3 -skip=web3/main.py,web3/utils/windows.py -use_parentheses=True - -[flake8] -max-line-length= 100 -exclude= venv*,.tox,docs,build -ignore= -[testenv] -install_command=python -m pip install --no-use-pep517 {opts} {packages} -usedevelop=True -commands= - core: pytest {posargs:tests/core} - ens: pytest {posargs:tests/ens} - ethpm: pytest {posargs:tests/ethpm} - integration-goethereum-ipc: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ipc.py} - integration-goethereum-http: pytest {posargs:tests/integration/go_ethereum/test_goethereum_http.py} - integration-goethereum-ws: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ws.py} - integration-ethtester: pytest {posargs:tests/integration/test_ethereum_tester.py} - integration-parity-ipc: pytest -s {posargs:tests/integration/parity/test_parity_ipc.py} - integration-parity-http: pytest -s {posargs:tests/integration/parity/test_parity_http.py} - integration-parity-ws: pytest -s {posargs:tests/integration/parity/test_parity_ws.py} - docs: make -C {toxinidir} validate-docs -deps = - .[dev] - docs: sphinx -passenv = - GETH_BINARY - GETH_VERSION - PARITY_BINARY - PARITY_VERSION - PARITY_OS - GOROOT - GOPATH - WEB3_INFURA_PROJECT_ID - WEB3_INFURA_API_SECRET -basepython = - docs: python3.6 - py36: python3.6 - py37: python3.7 - py38: python3.8 - -[testenv:lint] -basepython=python -extras=linter -commands= - flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/ethpm {toxinidir}/tests - isort --recursive --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/ethpm/ {toxinidir}/tests/ - mypy -p web3._utils.abi -p web3._utils.contracts -p web3._utils.events -p web3.providers -p web3.main -p web3.contract -p web3.datastructures -p web3.eth -p web3.exceptions -p web3.geth -p web3.iban -p web3.logs -p web3.manager -p web3.module -p web3.net -p web3.parity -p web3.middleware -p web3.pm -p web3.auto -p web3.gas_strategies -p web3.testing -p web3.tools -p web3.version -p ethpm -p ens --config-file {toxinidir}/mypy.ini +[tox] +envlist= + py{36,37}-ens + py{36,37}-core + py{36,37}-integration-{goethereum,ethtester,parity} + lint + doctest + +[isort] +combine_as_imports=True +force_sort_within_sections=True +include_trailing_comma=True +known_standard_library=pytest +known_third_party=lru,vns_tester +known_first_party=web3,ens +line_length=21 +multi_line_output=3 +skip=web3/main.py,web3/utils/windows.py +use_parentheses=True + +[flake8] +max-line-length= 100 +exclude= venv*,.tox,docs,build +ignore= + +[testenv] +usedevelop=True +commands= + core: pytest {posargs:tests/core} + ens: pytest {posargs:tests/ens} + integration-goethereum-ipc: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ipc.py} + integration-goethereum-http: pytest {posargs:tests/integration/go_ethereum/test_goethereum_http.py} + integration-goethereum-ws: pytest {posargs:tests/integration/go_ethereum/test_goethereum_ws.py} + integration-ethtester: pytest {posargs:tests/integration/test_ethereum_tester.py} + integration-parity-ipc: pytest -s {posargs:tests/integration/parity/test_parity_ipc.py} + integration-parity-http: pytest -s {posargs:tests/integration/parity/test_parity_http.py} + integration-parity-ws: pytest -s {posargs:tests/integration/parity/test_parity_ws.py} + doctest: make -C {toxinidir}/docs doctest +deps = + .[dev] + doctest: sphinx + doctest: ethtoken +passenv = + GETH_BINARY + GETH_VERSION + PARITY_BINARY + PARITY_VERSION + PARITY_OS + GOROOT + GOPATH +basepython = + doctest: python3.6 + py36: python3.6 + py37: python3.7 + +[testenv:lint] +basepython=python +extras=linter +commands= + flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/tests + isort --recursive --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/tests/ diff --git a/web3/__init__.py b/web3/__init__.py index c0e0e71e4e..14b0ebdbe0 100644 --- a/web3/__init__.py +++ b/web3/__init__.py @@ -1,48 +1,42 @@ -import sys -import warnings - -import pkg_resources - -from eth_account import ( - Account # noqa: E402, -) -from web3.main import ( - Web3 # noqa: E402, -) -from web3.providers.eth_tester import ( # noqa: E402 - EthereumTesterProvider, -) -from web3.providers.ipc import ( # noqa: E402 - IPCProvider, -) -from web3.providers.rpc import ( # noqa: E402 - HTTPProvider, -) -from web3.providers.websocket import ( # noqa: E402 - WebsocketProvider, -) - -if (3, 5) <= sys.version_info < (3, 6): - warnings.warn( - "Support for Python 3.5 will be removed in web3.py v5", - category=DeprecationWarning, - stacklevel=2) - -if sys.version_info < (3, 5): - raise EnvironmentError( - "Python 3.5 or above is required. " - "Note that support for Python 3.5 will be removed in web3.py v5") - - -__version__ = pkg_resources.get_distribution("web3").version - -__all__ = [ - "__version__", - "Web3", - "HTTPProvider", - "IPCProvider", - "WebsocketProvider", - "TestRPCProvider", - "EthereumTesterProvider", - "Account", -] +import pkg_resources +import sys +import warnings + +if (3, 5) <= sys.version_info < (3, 6): + warnings.warn( + "Support for Python 3.5 will be removed in web3.py v5", + category=DeprecationWarning, + stacklevel=2) + +if sys.version_info < (3, 5): + raise EnvironmentError( + "Python 3.5 or above is required. " + "Note that support for Python 3.5 will be removed in web3.py v5") + +from vns_account import Account # noqa: E402 +from web3.main import web3 # noqa: E402 +from web3.providers.rpc import ( # noqa: E402 + HTTPProvider, +) +from web3.providers.vns_tester import ( # noqa: E402 + EthereumTesterProvider, +) +from web3.providers.ipc import ( # noqa: E402 + IPCProvider, +) +from web3.providers.websocket import ( # noqa: E402 + WebsocketProvider, +) + +__version__ = pkg_resources.get_distribution("web3").version + +__all__ = [ + "__version__", + "web3", + "HTTPProvider", + "IPCProvider", + "WebsocketProvider", + "TestRPCProvider", + "EthereumTesterProvider", + "Account", +] diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index 98eac47222..e281988ff4 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -1,956 +1,713 @@ -import binascii -from collections import ( - abc, - namedtuple, -) -import copy -import itertools -import re -from typing import ( - Any, - Callable, - Collection, - Dict, - Iterable, - List, - Mapping, - Optional, - Sequence, - Tuple, - Type, - Union, - cast, -) -import warnings - -from eth_abi import ( - codec, - decoding, - encoding, -) -from eth_abi.base import ( - parse_type_str, -) -from eth_abi.exceptions import ( - ValueOutOfBounds, -) -from eth_abi.grammar import ( - ABIType, - BasicType, - TupleType, - parse, -) -from eth_abi.registry import ( - ABIRegistry, - BaseEquals, - registry as default_registry, -) -from eth_typing import ( - HexStr, - TypeStr, -) -from eth_utils import ( - decode_hex, - is_bytes, - is_list_like, - is_text, - to_text, - to_tuple, -) -from eth_utils.abi import ( - collapse_if_tuple, -) -from eth_utils.toolz import ( - curry, - partial, - pipe, -) - -from web3._utils.decorators import ( - combomethod, -) -from web3._utils.ens import ( - is_ens_name, -) -from web3._utils.formatters import ( - recursive_map, -) -from web3.exceptions import ( - FallbackNotFound, -) -from web3.types import ( - ABI, - ABIEvent, - ABIEventParams, - ABIFunction, - ABIFunctionParams, -) - - -def filter_by_type(_type: str, contract_abi: ABI) -> List[Union[ABIFunction, ABIEvent]]: - # fix for this just landed, not yet released : https://github.com/python/mypy/pull/7917 - # after it's released, update Union[ABIFunction, ABIEvent] -> ABIElement - return [abi for abi in contract_abi if abi['type'] == _type] # type: ignore - - -def filter_by_name(name: str, contract_abi: ABI) -> List[Union[ABIFunction, ABIEvent]]: - return [ - abi - for abi - in contract_abi - if ( - # type ignored b/c see line 91 - abi['type'] not in ('fallback', 'constructor') and # type: ignore - abi['name'] == name # type: ignore - ) - ] - - -def get_abi_input_types(abi: Union[ABIFunction, ABIEvent]) -> List[str]: - if 'inputs' not in abi and abi['type'] == 'fallback': - return [] - else: - return [collapse_if_tuple(arg) for arg in abi['inputs']] - - -def get_abi_output_types(abi: Union[ABIFunction, ABIEvent]) -> List[str]: - if abi['type'] == 'fallback': - return [] - else: - return [collapse_if_tuple(arg) for arg in abi['outputs']] - - -def get_abi_input_names(abi: Union[ABIFunction, ABIEvent]) -> List[str]: - if 'inputs' not in abi and abi['type'] == 'fallback': - return [] - else: - return [arg['name'] for arg in abi['inputs']] - - -def get_fallback_func_abi(contract_abi: ABI) -> ABIFunction: - fallback_abis = filter_by_type('fallback', contract_abi) - if fallback_abis: - return fallback_abis[0] - else: - raise FallbackNotFound("No fallback function was found in the contract ABI.") - - -def fallback_func_abi_exists(contract_abi: ABI) -> List[Union[ABIFunction, ABIEvent]]: - return filter_by_type('fallback', contract_abi) - - -def get_indexed_event_inputs(event_abi: ABIEvent) -> List[ABIEventParams]: - return [arg for arg in event_abi['inputs'] if arg['indexed'] is True] - - -def exclude_indexed_event_inputs(event_abi: ABIEvent) -> List[ABIEventParams]: - return [arg for arg in event_abi['inputs'] if arg['indexed'] is False] - - -def filter_by_argument_count( - num_arguments: int, contract_abi: ABI -) -> List[Union[ABIFunction, ABIEvent]]: - return [ - abi - for abi - in contract_abi - # type ignored b/c see line 91 - if len(abi['inputs']) == num_arguments # type: ignore - ] - - -def filter_by_argument_name( - argument_names: Collection[str], contract_abi: ABI -) -> List[Union[ABIFunction, ABIEvent]]: - return [ - abi - for abi in contract_abi - if set(argument_names).intersection( - get_abi_input_names(abi) - ) == set(argument_names) - ] - - -class AddressEncoder(encoding.AddressEncoder): - @classmethod - def validate_value(cls, value: Any) -> None: - if is_ens_name(value): - return - - super().validate_value(value) - - -class AcceptsHexStrEncoder(encoding.BaseEncoder): - subencoder_cls: Type[encoding.BaseEncoder] = None - is_strict: bool = None - - def __init__(self, subencoder: encoding.BaseEncoder) -> None: - self.subencoder = subencoder - - # type ignored b/c conflict w/ defined BaseEncoder.is_dynamic = False - @property - def is_dynamic(self) -> bool: # type: ignore - return self.subencoder.is_dynamic - - @classmethod - def from_type_str(cls, abi_type: TypeStr, registry: ABIRegistry) -> "AcceptsHexStrEncoder": - subencoder_cls = cls.get_subencoder_class() - # cast b/c expects BaseCoder but `from_type_string` restricted to BaseEncoder subclasses - subencoder = cast(encoding.BaseEncoder, subencoder_cls.from_type_str(abi_type, registry)) - return cls(subencoder) - - @classmethod - def get_subencoder_class(cls) -> Type[encoding.BaseEncoder]: - if cls.subencoder_cls is None: - raise AttributeError(f'No subencoder class is set. {cls.__name__}') - return cls.subencoder_cls - - # type ignored b/c combomethod makes signature conflict w/ defined BaseEncoder.validate_value() - @combomethod - def validate_value(self, value: Any) -> None: # type: ignore - normalized_value = self.validate_and_normalize(value) - return self.subencoder.validate_value(normalized_value) - - def encode(self, value: Any) -> bytes: - normalized_value = self.validate_and_normalize(value) - return self.subencoder.encode(normalized_value) - - def validate_and_normalize(self, value: Any) -> HexStr: - raw_value = value - if is_text(value): - try: - value = decode_hex(value) - except binascii.Error: - self.invalidate_value( - value, - msg=f'{value} is an invalid hex string', - ) - else: - if raw_value[:2] != '0x': - if self.is_strict: - self.invalidate_value( - raw_value, - msg='hex string must be prefixed with 0x' - ) - elif raw_value[:2] != '0x': - warnings.warn( - 'in v6 it will be invalid to pass a hex string without the "0x" prefix', - category=DeprecationWarning - ) - return value - - -class BytesEncoder(AcceptsHexStrEncoder): - subencoder_cls = encoding.BytesEncoder - is_strict = False - - -class ByteStringEncoder(AcceptsHexStrEncoder): - subencoder_cls = encoding.ByteStringEncoder - is_strict = False - - -class StrictByteStringEncoder(AcceptsHexStrEncoder): - subencoder_cls = encoding.ByteStringEncoder - is_strict = True - - -class ExactLengthBytesEncoder(encoding.BaseEncoder): - # TODO: move this to eth-abi once the api is stabilized - is_big_endian = False - value_bit_size = None - data_byte_size = None - - def validate(self) -> None: - super().validate() - - if self.value_bit_size is None: - raise ValueError("`value_bit_size` may not be none") - if self.data_byte_size is None: - raise ValueError("`data_byte_size` may not be none") - if self.encode_fn is None: - raise ValueError("`encode_fn` may not be none") - if self.is_big_endian is None: - raise ValueError("`is_big_endian` may not be none") - - if self.value_bit_size % 8 != 0: - raise ValueError( - "Invalid value bit size: {0}. Must be a multiple of 8".format( - self.value_bit_size, - ) - ) - - if self.value_bit_size > self.data_byte_size * 8: - raise ValueError("Value byte size exceeds data size") - - def encode(self, value: Any) -> bytes: - normalized_value = self.validate_value(value) - return self.encode_fn(normalized_value) - - # type ignored b/c conflict with defined BaseEncoder.validate_value() -> None - def validate_value(self, value: Any) -> bytes: # type: ignore - if not is_bytes(value) and not is_text(value): - self.invalidate_value(value) - - raw_value = value - if is_text(value): - try: - value = decode_hex(value) - except binascii.Error: - self.invalidate_value( - value, - msg=f'{value} is not a valid hex string', - ) - else: - if raw_value[:2] != '0x': - self.invalidate_value( - raw_value, - msg='hex string must be prefixed with 0x' - ) - - byte_size = self.value_bit_size // 8 - if len(value) > byte_size: - self.invalidate_value( - value, - exc=ValueOutOfBounds, - msg="exceeds total byte size for bytes{} encoding".format(byte_size), - ) - elif len(value) < byte_size: - self.invalidate_value( - value, - exc=ValueOutOfBounds, - msg="less than total byte size for bytes{} encoding".format(byte_size), - ) - return value - - @staticmethod - def encode_fn(value: Any) -> bytes: - return value - - @parse_type_str('bytes') - def from_type_str(cls, abi_type: BasicType, registry: ABIRegistry) -> bytes: - # type ignored b/c kwargs are set in superclass init - # Unexpected keyword argument "value_bit_size" for "__call__" of "BaseEncoder" - return cls( # type: ignore - value_bit_size=abi_type.sub * 8, - data_byte_size=abi_type.sub, - ) - - -class BytesDecoder(decoding.FixedByteSizeDecoder): - # FixedByteSizeDecoder.is_big_endian is defined as None - is_big_endian = False # type: ignore - - # FixedByteSizeDecoder.decoder_fn is defined as None - @staticmethod - def decoder_fn(data: bytes) -> bytes: # type: ignore - return data - - @parse_type_str('bytes') - def from_type_str(cls, abi_type: BasicType, registry: ABIRegistry) -> bytes: - # type ignored b/c kwargs are set in superclass init - # Unexpected keyword argument "value_bit_size" for "__call__" of "BaseDecoder" - return cls( # type: ignore - value_bit_size=abi_type.sub * 8, - data_byte_size=abi_type.sub, - ) - - -class TextStringEncoder(encoding.TextStringEncoder): - @classmethod - def validate_value(cls, value: Any) -> None: - if is_bytes(value): - try: - value = to_text(value) - except UnicodeDecodeError: - cls.invalidate_value( - value, - msg='not decodable as unicode string', - ) - - super().validate_value(value) - - -def filter_by_encodability( - abi_codec: codec.ABIEncoder, args: Sequence[Any], kwargs: Dict[str, Any], contract_abi: ABI -) -> List[ABIFunction]: - return [ - function_abi - for function_abi - in contract_abi - if check_if_arguments_can_be_encoded(function_abi, abi_codec, args, kwargs) - ] - - -def check_if_arguments_can_be_encoded( - function_abi: ABIFunction, - abi_codec: codec.ABIEncoder, - args: Sequence[Any], - kwargs: Dict[str, Any], -) -> bool: - try: - arguments = merge_args_and_kwargs(function_abi, args, kwargs) - except TypeError: - return False - - if len(function_abi.get('inputs', [])) != len(arguments): - return False - - try: - types, aligned_args = get_aligned_abi_inputs(function_abi, arguments) - except TypeError: - return False - - return all( - abi_codec.is_encodable(_type, arg) - for _type, arg in zip(types, aligned_args) - ) - - -def merge_args_and_kwargs( - function_abi: ABIFunction, args: Sequence[Any], kwargs: Dict[str, Any] -) -> Tuple[Any, ...]: - """ - Takes a list of positional args (``args``) and a dict of keyword args - (``kwargs``) defining values to be passed to a call to the contract function - described by ``function_abi``. Checks to ensure that the correct number of - args were given, no duplicate args were given, and no unknown args were - given. Returns a list of argument values aligned to the order of inputs - defined in ``function_abi``. - """ - # Ensure the function is being applied to the correct number of args - if len(args) + len(kwargs) != len(function_abi.get('inputs', [])): - raise TypeError( - "Incorrect argument count. Expected '{0}'. Got '{1}'".format( - len(function_abi['inputs']), - len(args) + len(kwargs), - ) - ) - - # If no keyword args were given, we don't need to align them - if not kwargs: - return cast(Tuple[Any, ...], args) - - kwarg_names = set(kwargs.keys()) - sorted_arg_names = tuple(arg_abi['name'] for arg_abi in function_abi['inputs']) - args_as_kwargs = dict(zip(sorted_arg_names, args)) - - # Check for duplicate args - duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) - if duplicate_args: - raise TypeError( - "{fn_name}() got multiple values for argument(s) '{dups}'".format( - fn_name=function_abi['name'], - dups=', '.join(duplicate_args), - ) - ) - - # Check for unknown args - unknown_args = kwarg_names.difference(sorted_arg_names) - if unknown_args: - if function_abi.get('name'): - raise TypeError( - "{fn_name}() got unexpected keyword argument(s) '{dups}'".format( - fn_name=function_abi.get('name'), - dups=', '.join(unknown_args), - ) - ) - raise TypeError( - "Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format( - _type=function_abi.get('type'), - dups=', '.join(unknown_args), - ) - ) - - # Sort args according to their position in the ABI and unzip them from their - # names - sorted_args = tuple(zip( - *sorted( - itertools.chain(kwargs.items(), args_as_kwargs.items()), - key=lambda kv: sorted_arg_names.index(kv[0]), - ) - )) - - if sorted_args: - return sorted_args[1] - else: - return tuple() - - -TUPLE_TYPE_STR_RE = re.compile(r'^(tuple)(\[([1-9][0-9]*)?\])?$') - - -def get_tuple_type_str_parts(s: str) -> Optional[Tuple[str, Optional[str]]]: - """ - Takes a JSON ABI type string. For tuple type strings, returns the separated - prefix and array dimension parts. For all other strings, returns ``None``. - """ - match = TUPLE_TYPE_STR_RE.match(s) - - if match is not None: - tuple_prefix = match.group(1) - tuple_dims = match.group(2) - - return tuple_prefix, tuple_dims - - return None - - -def _align_abi_input(arg_abi: ABIFunctionParams, arg: Any) -> Tuple[Any, ...]: - """ - Aligns the values of any mapping at any level of nesting in ``arg`` - according to the layout of the corresponding abi spec. - """ - tuple_parts = get_tuple_type_str_parts(arg_abi['type']) - - if tuple_parts is None: - # Arg is non-tuple. Just return value. - return arg - - tuple_prefix, tuple_dims = tuple_parts - if tuple_dims is None: - # Arg is non-list tuple. Each sub arg in `arg` will be aligned - # according to its corresponding abi. - sub_abis = arg_abi['components'] - else: - # Arg is list tuple. A non-list version of its abi will be used to - # align each element in `arg`. - new_abi = copy.copy(arg_abi) - new_abi['type'] = tuple_prefix - - sub_abis = itertools.repeat(new_abi) - - if isinstance(arg, abc.Mapping): - # Arg is mapping. Align values according to abi order. - aligned_arg = tuple(arg[abi['name']] for abi in sub_abis) - else: - aligned_arg = arg - - if not is_list_like(aligned_arg): - raise TypeError( - 'Expected non-string sequence for "{}" component type: got {}'.format( - arg_abi['type'], - aligned_arg, - ), - ) - - return type(aligned_arg)( - _align_abi_input(sub_abi, sub_arg) - for sub_abi, sub_arg in zip(sub_abis, aligned_arg) - ) - - -def get_aligned_abi_inputs( - abi: ABIFunction, args: Union[Sequence[Any], Mapping[Any, Any]] -) -> Tuple[Tuple[Any, ...], Sequence[Any]]: - """ - Takes a function ABI (``abi``) and a sequence or mapping of args (``args``). - Returns a list of type strings for the function's inputs and a list of - arguments which have been aligned to the layout of those types. The args - contained in ``args`` may contain nested mappings or sequences corresponding - to tuple-encoded values in ``abi``. - """ - input_abis = abi.get('inputs', []) - - if isinstance(args, abc.Mapping): - # `args` is mapping. Align values according to abi order. - args = tuple(args[abi['name']] for abi in input_abis) - - return ( - tuple(collapse_if_tuple(abi) for abi in input_abis), - # too many arguments for Sequence - type(args)( # type: ignore - _align_abi_input(abi, arg) - for abi, arg in zip(input_abis, args) - ), - ) - - -def get_constructor_abi(contract_abi: ABI) -> ABIFunction: - candidates = [ - # type ignored b/c see line 91 - abi for abi in contract_abi if abi['type'] == 'constructor' # type: ignore - ] - if len(candidates) == 1: - return candidates[0] - elif len(candidates) == 0: - return None - elif len(candidates) > 1: - raise ValueError("Found multiple constructors.") - return None - - -DYNAMIC_TYPES = ['bytes', 'string'] - -INT_SIZES = range(8, 257, 8) -BYTES_SIZES = range(1, 33) -UINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES] -INT_TYPES = ['int{0}'.format(i) for i in INT_SIZES] -BYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte'] - -STATIC_TYPES = list(itertools.chain( - ['address', 'bool'], - UINT_TYPES, - INT_TYPES, - BYTES_TYPES, -)) - -BASE_TYPE_REGEX = '|'.join(( - _type + '(?![a-z0-9])' - for _type - in itertools.chain(STATIC_TYPES, DYNAMIC_TYPES) -)) - -SUB_TYPE_REGEX = ( - r'\[' - '[0-9]*' - r'\]' -) - -TYPE_REGEX = ( - '^' - '(?:{base_type})' - '(?:(?:{sub_type})*)?' - '$' -).format( - base_type=BASE_TYPE_REGEX, - sub_type=SUB_TYPE_REGEX, -) - - -def is_recognized_type(abi_type: TypeStr) -> bool: - return bool(re.match(TYPE_REGEX, abi_type)) - - -def is_bool_type(abi_type: TypeStr) -> bool: - return abi_type == 'bool' - - -def is_uint_type(abi_type: TypeStr) -> bool: - return abi_type in UINT_TYPES - - -def is_int_type(abi_type: TypeStr) -> bool: - return abi_type in INT_TYPES - - -def is_address_type(abi_type: TypeStr) -> bool: - return abi_type == 'address' - - -def is_bytes_type(abi_type: TypeStr) -> bool: - return abi_type in BYTES_TYPES + ['bytes'] - - -def is_string_type(abi_type: TypeStr) -> bool: - return abi_type == 'string' - - -@curry -def is_length(target_length: int, value: abc.Sized) -> bool: - return len(value) == target_length - - -def size_of_type(abi_type: TypeStr) -> int: - """ - Returns size in bits of abi_type - """ - if 'string' in abi_type: - return None - if 'byte' in abi_type: - return None - if '[' in abi_type: - return None - if abi_type == 'bool': - return 8 - if abi_type == 'address': - return 160 - return int(re.sub(r"\D", "", abi_type)) - - -END_BRACKETS_OF_ARRAY_TYPE_REGEX = r"\[[^]]*\]$" - - -def sub_type_of_array_type(abi_type: TypeStr) -> str: - if not is_array_type(abi_type): - raise ValueError( - "Cannot parse subtype of nonarray abi-type: {0}".format(abi_type) - ) - - return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1) - - -def length_of_array_type(abi_type: TypeStr) -> int: - if not is_array_type(abi_type): - raise ValueError( - "Cannot parse length of nonarray abi-type: {0}".format(abi_type) - ) - - inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]") - if not inner_brackets: - return None - else: - return int(inner_brackets) - - -ARRAY_REGEX = ( - "^" - "[a-zA-Z0-9_]+" - "({sub_type})+" - "$" -).format(sub_type=SUB_TYPE_REGEX) - - -def is_array_type(abi_type: TypeStr) -> bool: - return bool(re.match(ARRAY_REGEX, abi_type)) - - -NAME_REGEX = ( - '[a-zA-Z_]' - '[a-zA-Z0-9_]*' -) - - -ENUM_REGEX = ( - '^' - '{lib_name}' - r'\.' - '{enum_name}' - '$' -).format(lib_name=NAME_REGEX, enum_name=NAME_REGEX) - - -def is_probably_enum(abi_type: TypeStr) -> bool: - return bool(re.match(ENUM_REGEX, abi_type)) - - -@to_tuple -def normalize_event_input_types( - abi_args: Collection[Union[ABIFunction, ABIEvent]] -) -> Iterable[Union[TypeStr, Dict[TypeStr, Any]]]: - for arg in abi_args: - if is_recognized_type(arg['type']): - yield arg - elif is_probably_enum(arg['type']): - yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()} - else: - yield arg - - -def abi_to_signature(abi: Union[ABIFunction, ABIEvent]) -> str: - function_signature = "{fn_name}({fn_input_types})".format( - fn_name=abi['name'], - fn_input_types=','.join([ - # type ignored b/c see line 91 - arg['type'] for arg in normalize_event_input_types(abi.get('inputs', [])) # type: ignore # noqa: E501 - ]), - ) - return function_signature - - -######################################################## -# -# Conditionally modifying data, tagged with ABI Types -# -######################################################## - - -@curry -def map_abi_data( - normalizers: Sequence[Callable[[TypeStr, Any], Tuple[TypeStr, Any]]], - types: Sequence[TypeStr], - data: Sequence[Any], -) -> Any: - """ - This function will apply normalizers to your data, in the - context of the relevant types. Each normalizer is in the format: - - def normalizer(datatype, data): - # Conditionally modify data - return (datatype, data) - - Where datatype is a valid ABI type string, like "uint". - - In case of an array, like "bool[2]", normalizer will receive `data` - as an iterable of typed data, like `[("bool", True), ("bool", False)]`. - - Internals - --- - - This is accomplished by: - - 1. Decorating the data tree with types - 2. Recursively mapping each of the normalizers to the data - 3. Stripping the types back out of the tree - """ - pipeline = itertools.chain( - [abi_data_tree(types)], - map(data_tree_map, normalizers), - [partial(recursive_map, strip_abi_type)], - ) - - return pipe(data, *pipeline) - - -@curry -def abi_data_tree(types: Sequence[TypeStr], data: Sequence[Any]) -> List[Any]: - """ - Decorate the data tree with pairs of (type, data). The pair tuple is actually an - ABITypedData, but can be accessed as a tuple. - - As an example: - - >>> abi_data_tree(types=["bool[2]", "uint"], data=[[True, False], 0]) - [("bool[2]", [("bool", True), ("bool", False)]), ("uint256", 0)] - """ - return [ - abi_sub_tree(data_type, data_value) - for data_type, data_value - in zip(types, data) - ] - - -@curry -def data_tree_map( - func: Callable[[TypeStr, Any], Tuple[TypeStr, Any]], data_tree: Any -) -> "ABITypedData": - """ - Map func to every ABITypedData element in the tree. func will - receive two args: abi_type, and data - """ - def map_to_typed_data(elements: Any) -> "ABITypedData": - if isinstance(elements, ABITypedData) and elements.abi_type is not None: - return ABITypedData(func(*elements)) - else: - return elements - return recursive_map(map_to_typed_data, data_tree) - - -class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')): - """ - This class marks data as having a certain ABI-type. - - >>> a1 = ABITypedData(['address', addr1]) - >>> a2 = ABITypedData(['address', addr2]) - >>> addrs = ABITypedData(['address[]', [a1, a2]]) - - You can access the fields using tuple() interface, or with - attributes: - - >>> assert a1.abi_type == a1[0] - >>> assert a1.data == a1[1] - - Unlike a typical `namedtuple`, you initialize with a single - positional argument that is iterable, to match the init - interface of all other relevant collections. - """ - def __new__(cls, iterable: Iterable[Any]) -> "ABITypedData": - return super().__new__(cls, *iterable) - - -def abi_sub_tree( - type_str_or_abi_type: Optional[Union[TypeStr, ABIType]], data_value: Any -) -> ABITypedData: - if type_str_or_abi_type is None: - return ABITypedData([None, data_value]) - - if isinstance(type_str_or_abi_type, TypeStr): - abi_type = parse(type_str_or_abi_type) - else: - abi_type = type_str_or_abi_type - - # In the two special cases below, we rebuild the given data structures with - # annotated items - if abi_type.is_array: - # If type is array, determine item type and annotate all - # items in iterable with that type - item_type_str = abi_type.item_type.to_type_str() - value_to_annotate = [ - abi_sub_tree(item_type_str, item_value) - for item_value in data_value - ] - elif isinstance(abi_type, TupleType): - # Otherwise, if type is tuple, determine component types and annotate - # tuple components in iterable respectively with those types - value_to_annotate = type(data_value)( - abi_sub_tree(comp_type.to_type_str(), comp_value) - for comp_type, comp_value in zip(abi_type.components, data_value) - ) - else: - value_to_annotate = data_value - - return ABITypedData([ - abi_type.to_type_str(), - value_to_annotate, - ]) - - -def strip_abi_type(elements: Any) -> Any: - if isinstance(elements, ABITypedData): - return elements.data - else: - return elements - - -def build_default_registry() -> ABIRegistry: - # We make a copy here just to make sure that eth-abi's default registry is not - # affected by our custom encoder subclasses - registry = default_registry.copy() - - registry.unregister('address') - registry.unregister('bytes') - registry.unregister('bytes') - registry.unregister('string') - - registry.register( - BaseEquals('address'), - AddressEncoder, decoding.AddressDecoder, - label='address', - ) - registry.register( - BaseEquals('bytes', with_sub=True), - BytesEncoder, decoding.BytesDecoder, - label='bytes', - ) - registry.register( - BaseEquals('bytes', with_sub=False), - ByteStringEncoder, decoding.ByteStringDecoder, - label='bytes', - ) - registry.register( - BaseEquals('string'), - TextStringEncoder, decoding.StringDecoder, - label='string', - ) - return registry - - -def build_strict_registry() -> ABIRegistry: - registry = default_registry.copy() - - registry.unregister('address') - registry.unregister('bytes') - registry.unregister('bytes') - registry.unregister('string') - - registry.register( - BaseEquals('address'), - AddressEncoder, decoding.AddressDecoder, - label='address', - ) - registry.register( - BaseEquals('bytes', with_sub=True), - ExactLengthBytesEncoder, BytesDecoder, - label='bytes', - ) - registry.register( - BaseEquals('bytes', with_sub=False), - StrictByteStringEncoder, decoding.ByteStringDecoder, - label='bytes', - ) - registry.register( - BaseEquals('string'), - TextStringEncoder, decoding.StringDecoder, - label='string', - ) - return registry +import binascii +from collections import ( + abc, + namedtuple, +) +import copy +import itertools +import re +from typing import ( + Any, + Optional, + Tuple, + Union, +) + +from vns_abi import ( + decoding, + encoding, +) +from vns_abi.codec import ( + ABICodec, +) +from vns_abi.grammar import ( + ABIType, + TupleType, + parse, +) +from vns_abi.registry import ( + BaseEquals, + registry as default_registry, +) +from vns_typing import ( + TypeStr, +) +from vns_utils import ( + decode_hex, + is_bytes, + is_list_like, + is_text, + to_text, + to_tuple, +) +from vns_utils.abi import ( + collapse_if_tuple, +) + +from web3._utils.ens import ( + is_ens_name, +) +from web3._utils.formatters import ( + recursive_map, +) +from web3._utils.toolz import ( + curry, + partial, + pipe, +) +from web3.exceptions import ( + FallbackNotFound, +) + + +def filter_by_type(_type, contract_abi): + return [abi for abi in contract_abi if abi['type'] == _type] + + +def filter_by_name(name, contract_abi): + return [ + abi + for abi + in contract_abi + if ( + abi['type'] not in ('fallback', 'constructor') and + abi['name'] == name + ) + ] + + +def get_abi_input_types(abi): + if 'inputs' not in abi and abi['type'] == 'fallback': + return [] + else: + return [collapse_if_tuple(arg) for arg in abi['inputs']] + + +def get_abi_output_types(abi): + if abi['type'] == 'fallback': + return [] + else: + return [collapse_if_tuple(arg) for arg in abi['outputs']] + + +def get_abi_input_names(abi): + if 'inputs' not in abi and abi['type'] == 'fallback': + return [] + else: + return [arg['name'] for arg in abi['inputs']] + + +def get_fallback_func_abi(contract_abi): + fallback_abis = filter_by_type('fallback', contract_abi) + if fallback_abis: + return fallback_abis[0] + else: + raise FallbackNotFound("No fallback function was found in the contract ABI.") + + +def fallback_func_abi_exists(contract_abi): + return filter_by_type('fallback', contract_abi) + + +def get_indexed_event_inputs(event_abi): + return [arg for arg in event_abi['inputs'] if arg['indexed'] is True] + + +def exclude_indexed_event_inputs(event_abi): + return [arg for arg in event_abi['inputs'] if arg['indexed'] is False] + + +def filter_by_argument_count(num_arguments, contract_abi): + return [ + abi + for abi + in contract_abi + if len(abi['inputs']) == num_arguments + ] + + +def filter_by_argument_name(argument_names, contract_abi): + return [ + abi + for abi in contract_abi + if set(argument_names).intersection( + get_abi_input_names(abi) + ) == set(argument_names) + ] + + +class AddressEncoder(encoding.AddressEncoder): + @classmethod + def validate_value(cls, value): + if is_ens_name(value): + return + + super().validate_value(value) + + +class AcceptsHexStrMixin: + def validate_value(self, value): + if is_text(value): + try: + value = decode_hex(value) + except binascii.Error: + self.invalidate_value( + value, + msg='invalid hex string', + ) + + super().validate_value(value) + + +class BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder): + pass + + +class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder): + pass + + +class TextStringEncoder(encoding.TextStringEncoder): + @classmethod + def validate_value(cls, value): + if is_bytes(value): + try: + value = to_text(value) + except UnicodeDecodeError: + cls.invalidate_value( + value, + msg='not decodable as unicode string', + ) + + super().validate_value(value) + + +# We make a copy here just to make sure that vns-abi's default registry is not +# affected by our custom encoder subclasses +registry = default_registry.copy() + +registry.unregister('address') +registry.unregister('bytes') +registry.unregister('bytes') +registry.unregister('string') + +registry.register( + BaseEquals('address'), + AddressEncoder, decoding.AddressDecoder, + label='address', +) +registry.register( + BaseEquals('bytes', with_sub=True), + BytesEncoder, decoding.BytesDecoder, + label='bytes', +) +registry.register( + BaseEquals('bytes', with_sub=False), + ByteStringEncoder, decoding.ByteStringDecoder, + label='bytes', +) +registry.register( + BaseEquals('string'), + TextStringEncoder, decoding.StringDecoder, + label='string', +) + +codec = ABICodec(registry) +is_encodable = codec.is_encodable + + +def filter_by_encodability(args, kwargs, contract_abi): + return [ + function_abi + for function_abi + in contract_abi + if check_if_arguments_can_be_encoded(function_abi, args, kwargs) + ] + + +def check_if_arguments_can_be_encoded(function_abi, args, kwargs): + try: + arguments = merge_args_and_kwargs(function_abi, args, kwargs) + except TypeError: + return False + + if len(function_abi.get('inputs', [])) != len(arguments): + return False + + try: + types, aligned_args = get_aligned_abi_inputs(function_abi, arguments) + except TypeError: + return False + + return all( + is_encodable(_type, arg) + for _type, arg in zip(types, aligned_args) + ) + + +def merge_args_and_kwargs(function_abi, args, kwargs): + """ + Takes a list of positional args (``args``) and a dict of keyword args + (``kwargs``) defining values to be passed to a call to the contract function + described by ``function_abi``. Checks to ensure that the correct number of + args were given, no duplicate args were given, and no unknown args were + given. Returns a list of argument values aligned to the order of inputs + defined in ``function_abi``. + """ + # Ensure the function is being applied to the correct number of args + if len(args) + len(kwargs) != len(function_abi.get('inputs', [])): + raise TypeError( + "Incorrect argument count. Expected '{0}'. Got '{1}'".format( + len(function_abi['inputs']), + len(args) + len(kwargs), + ) + ) + + # If no keyword args were given, we don't need to align them + if not kwargs: + return args + + kwarg_names = set(kwargs.keys()) + sorted_arg_names = tuple(arg_abi['name'] for arg_abi in function_abi['inputs']) + args_as_kwargs = dict(zip(sorted_arg_names, args)) + + # Check for duplicate args + duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) + if duplicate_args: + raise TypeError( + "{fn_name}() got multiple values for argument(s) '{dups}'".format( + fn_name=function_abi['name'], + dups=', '.join(duplicate_args), + ) + ) + + # Check for unknown args + unknown_args = kwarg_names.difference(sorted_arg_names) + if unknown_args: + if function_abi.get('name'): + raise TypeError( + "{fn_name}() got unexpected keyword argument(s) '{dups}'".format( + fn_name=function_abi.get('name'), + dups=', '.join(unknown_args), + ) + ) + raise TypeError( + "Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format( + _type=function_abi.get('type'), + dups=', '.join(unknown_args), + ) + ) + + # Sort args according to their position in the ABI and unzip them from their + # names + sorted_args = tuple(zip( + *sorted( + itertools.chain(kwargs.items(), args_as_kwargs.items()), + key=lambda kv: sorted_arg_names.index(kv[0]), + ) + )) + + if sorted_args: + return sorted_args[1] + else: + return tuple() + + +TUPLE_TYPE_STR_RE = re.compile(r'^(tuple)(\[([1-9][0-9]*)?\])?$') + + +def get_tuple_type_str_parts(s: str) -> Optional[Tuple[str, Optional[str]]]: + """ + Takes a JSON ABI type string. For tuple type strings, returns the separated + prefix and array dimension parts. For all other strings, returns ``None``. + """ + match = TUPLE_TYPE_STR_RE.match(s) + + if match is not None: + tuple_prefix = match.group(1) + tuple_dims = match.group(2) + + return tuple_prefix, tuple_dims + + return None + + +def _align_abi_input(arg_abi, arg): + """ + Aligns the values of any mapping at any level of nesting in ``arg`` + according to the layout of the corresponding abi spec. + """ + tuple_parts = get_tuple_type_str_parts(arg_abi['type']) + + if tuple_parts is None: + # Arg is non-tuple. Just return value. + return arg + + tuple_prefix, tuple_dims = tuple_parts + if tuple_dims is None: + # Arg is non-list tuple. Each sub arg in `arg` will be aligned + # according to its corresponding abi. + sub_abis = arg_abi['components'] + else: + # Arg is list tuple. A non-list version of its abi will be used to + # align each element in `arg`. + new_abi = copy.copy(arg_abi) + new_abi['type'] = tuple_prefix + + sub_abis = itertools.repeat(new_abi) + + if isinstance(arg, abc.Mapping): + # Arg is mapping. Align values according to abi order. + aligned_arg = tuple(arg[abi['name']] for abi in sub_abis) + else: + aligned_arg = arg + + if not is_list_like(aligned_arg): + raise TypeError( + 'Expected non-string sequence for "{}" component type: got {}'.format( + arg_abi['type'], + aligned_arg, + ), + ) + + return type(aligned_arg)( + _align_abi_input(sub_abi, sub_arg) + for sub_abi, sub_arg in zip(sub_abis, aligned_arg) + ) + + +def get_aligned_abi_inputs(abi, args): + """ + Takes a function ABI (``abi``) and a sequence or mapping of args (``args``). + Returns a list of type strings for the function's inputs and a list of + arguments which have been aligned to the layout of those types. The args + contained in ``args`` may contain nested mappings or sequences corresponding + to tuple-encoded values in ``abi``. + """ + input_abis = abi.get('inputs', []) + + if isinstance(args, abc.Mapping): + # `args` is mapping. Align values according to abi order. + args = tuple(args[abi['name']] for abi in input_abis) + + return ( + tuple(collapse_if_tuple(abi) for abi in input_abis), + type(args)( + _align_abi_input(abi, arg) + for abi, arg in zip(input_abis, args) + ), + ) + + +def get_constructor_abi(contract_abi): + candidates = [ + abi for abi in contract_abi if abi['type'] == 'constructor' + ] + if len(candidates) == 1: + return candidates[0] + elif len(candidates) == 0: + return None + elif len(candidates) > 1: + raise ValueError("Found multiple constructors.") + + +DYNAMIC_TYPES = ['bytes', 'string'] + +INT_SIZES = range(8, 257, 8) +BYTES_SIZES = range(1, 33) +UINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES] +INT_TYPES = ['int{0}'.format(i) for i in INT_SIZES] +BYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte'] + +STATIC_TYPES = list(itertools.chain( + ['address', 'bool'], + UINT_TYPES, + INT_TYPES, + BYTES_TYPES, +)) + +BASE_TYPE_REGEX = '|'.join(( + _type + '(?![a-z0-9])' + for _type + in itertools.chain(STATIC_TYPES, DYNAMIC_TYPES) +)) + +SUB_TYPE_REGEX = ( + r'\[' + '[0-9]*' + r'\]' +) + +TYPE_REGEX = ( + '^' + '(?:{base_type})' + '(?:(?:{sub_type})*)?' + '$' +).format( + base_type=BASE_TYPE_REGEX, + sub_type=SUB_TYPE_REGEX, +) + + +def is_recognized_type(abi_type): + return bool(re.match(TYPE_REGEX, abi_type)) + + +def is_bool_type(abi_type): + return abi_type == 'bool' + + +def is_uint_type(abi_type): + return abi_type in UINT_TYPES + + +def is_int_type(abi_type): + return abi_type in INT_TYPES + + +def is_address_type(abi_type): + return abi_type == 'address' + + +def is_bytes_type(abi_type): + return abi_type in BYTES_TYPES + ['bytes'] + + +def is_string_type(abi_type): + return abi_type == 'string' + + +@curry +def is_length(target_length, value): + return len(value) == target_length + + +def size_of_type(abi_type): + """ + Returns size in bits of abi_type + """ + if 'string' in abi_type: + return None + if 'byte' in abi_type: + return None + if '[' in abi_type: + return None + if abi_type == 'bool': + return 8 + if abi_type == 'address': + return 160 + return int(re.sub(r"\D", "", abi_type)) + + +END_BRACKETS_OF_ARRAY_TYPE_REGEX = r"\[[^]]*\]$" + + +def sub_type_of_array_type(abi_type): + if not is_array_type(abi_type): + raise ValueError( + "Cannot parse subtype of nonarray abi-type: {0}".format(abi_type) + ) + + return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1) + + +def length_of_array_type(abi_type): + if not is_array_type(abi_type): + raise ValueError( + "Cannot parse length of nonarray abi-type: {0}".format(abi_type) + ) + + inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]") + if not inner_brackets: + return None + else: + return int(inner_brackets) + + +ARRAY_REGEX = ( + "^" + "[a-zA-Z0-9_]+" + "({sub_type})+" + "$" +).format(sub_type=SUB_TYPE_REGEX) + + +def is_array_type(abi_type): + return bool(re.match(ARRAY_REGEX, abi_type)) + + +NAME_REGEX = ( + '[a-zA-Z_]' + '[a-zA-Z0-9_]*' +) + + +ENUM_REGEX = ( + '^' + '{lib_name}' + r'\.' + '{enum_name}' + '$' +).format(lib_name=NAME_REGEX, enum_name=NAME_REGEX) + + +def is_probably_enum(abi_type): + return bool(re.match(ENUM_REGEX, abi_type)) + + +@to_tuple +def normalize_event_input_types(abi_args): + for arg in abi_args: + if is_recognized_type(arg['type']): + yield arg + elif is_probably_enum(arg['type']): + yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()} + else: + yield arg + + +def abi_to_signature(abi): + function_signature = "{fn_name}({fn_input_types})".format( + fn_name=abi['name'], + fn_input_types=','.join([ + arg['type'] for arg in normalize_event_input_types(abi.get('inputs', [])) + ]), + ) + return function_signature + + +######################################################## +# +# Conditionally modifying data, tagged with ABI Types +# +######################################################## + + +@curry +def map_abi_data(normalizers, types, data): + """ + This function will apply normalizers to your data, in the + context of the relevant types. Each normalizer is in the format: + + def normalizer(datatype, data): + # Conditionally modify data + return (datatype, data) + + Where datatype is a valid ABI type string, like "uint". + + In case of an array, like "bool[2]", normalizer will receive `data` + as an iterable of typed data, like `[("bool", True), ("bool", False)]`. + + Internals + --- + + This is accomplished by: + + 1. Decorating the data tree with types + 2. Recursively mapping each of the normalizers to the data + 3. Stripping the types back out of the tree + """ + pipeline = itertools.chain( + [abi_data_tree(types)], + map(data_tree_map, normalizers), + [partial(recursive_map, strip_abi_type)], + ) + + return pipe(data, *pipeline) + + +@curry +def abi_data_tree(types, data): + """ + Decorate the data tree with pairs of (type, data). The pair tuple is actually an + ABITypedData, but can be accessed as a tuple. + + As an example: + + >>> abi_data_tree(types=["bool[2]", "uint"], data=[[True, False], 0]) + [("bool[2]", [("bool", True), ("bool", False)]), ("uint256", 0)] + """ + return [ + abi_sub_tree(data_type, data_value) + for data_type, data_value + in zip(types, data) + ] + + +@curry +def data_tree_map(func, data_tree): + """ + Map func to every ABITypedData element in the tree. func will + receive two args: abi_type, and data + """ + def map_to_typed_data(elements): + if isinstance(elements, ABITypedData) and elements.abi_type is not None: + return ABITypedData(func(*elements)) + else: + return elements + return recursive_map(map_to_typed_data, data_tree) + + +class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')): + """ + This class marks data as having a certain ABI-type. + + >>> a1 = ABITypedData(['address', addr1]) + >>> a2 = ABITypedData(['address', addr2]) + >>> addrs = ABITypedData(['address[]', [a1, a2]]) + + You can access the fields using tuple() interface, or with + attributes: + + >>> assert a1.abi_type == a1[0] + >>> assert a1.data == a1[1] + + Unlike a typical `namedtuple`, you initialize with a single + positional argument that is iterable, to match the init + interface of all other relevant collections. + """ + def __new__(cls, iterable): + return super().__new__(cls, *iterable) + + +def abi_sub_tree(type_str_or_abi_type: Optional[Union[TypeStr, ABIType]], + data_value: Any) -> ABITypedData: + if type_str_or_abi_type is None: + return ABITypedData([None, data_value]) + + if isinstance(type_str_or_abi_type, TypeStr): + abi_type = parse(type_str_or_abi_type) + else: + abi_type = type_str_or_abi_type + + # In the two special cases below, we rebuild the given data structures with + # annotated items + if abi_type.is_array: + # If type is array, determine item type and annotate all + # items in iterable with that type + item_type_str = abi_type.item_type.to_type_str() + value_to_annotate = [ + abi_sub_tree(item_type_str, item_value) + for item_value in data_value + ] + elif isinstance(abi_type, TupleType): + # Otherwise, if type is tuple, determine component types and annotate + # tuple components in iterable respectively with those types + value_to_annotate = type(data_value)( + abi_sub_tree(comp_type.to_type_str(), comp_value) + for comp_type, comp_value in zip(abi_type.components, data_value) + ) + else: + value_to_annotate = data_value + + return ABITypedData([ + abi_type.to_type_str(), + value_to_annotate, + ]) + + +def strip_abi_type(elements): + if isinstance(elements, ABITypedData): + return elements.data + else: + return elements diff --git a/web3/_utils/admin.py b/web3/_utils/admin.py index 5bf7839b77..e207eab5fb 100644 --- a/web3/_utils/admin.py +++ b/web3/_utils/admin.py @@ -1,67 +1,62 @@ -from web3.method import ( - DeprecatedMethod, - Method, - default_root_munger, -) - - -def admin_start_params_munger(module, host='localhost', port='8546', cors='', apis='eth,net,web3'): - return (host, port, cors, apis) - - -add_peer = Method( - "admin_addPeer", - mungers=[default_root_munger], -) - - -datadir = Method( - "admin_datadir", - mungers=None, -) - - -node_info = Method( - "admin_nodeInfo", - mungers=None, -) - - -peers = Method( - "admin_peers", - mungers=None, -) - - -start_rpc = Method( - "admin_startRPC", - mungers=[admin_start_params_munger], -) - - -start_ws = Method( - "admin_startWS", - mungers=[admin_start_params_munger], -) - - -stop_rpc = Method( - "admin_stopRPC", - mungers=None, -) - - -stop_ws = Method( - "admin_stopWS", - mungers=None, -) - -# -# Deprecated Methods -# -addPeer = DeprecatedMethod(add_peer, 'addPeer', 'add_peer') -nodeInfo = DeprecatedMethod(node_info, 'nodeInfo', 'node_info') -startRPC = DeprecatedMethod(start_rpc, 'startRPC', 'start_rpc') -stopRPC = DeprecatedMethod(stop_rpc, 'stopRPC', 'stop_rpc') -startWS = DeprecatedMethod(start_ws, 'startWS', 'start_ws') -stopWS = DeprecatedMethod(stop_ws, 'stopWS', 'stop_ws') +from web3.method import ( + Method, + default_root_munger, +) + + +def admin_start_params_munger(module, host='localhost', port='8546', cors='', apis='vns,net,web3'): + return (host, port, cors, apis) + + +addPeer = Method( + "admin_addPeer", + mungers=[default_root_munger], +) + + +datadir = Method( + "admin_datadir", + mungers=None, +) + + +nodeInfo = Method( + "admin_nodeInfo", + mungers=None, +) + + +peers = Method( + "admin_peers", + mungers=None, +) + + +setSolc = Method( + "admin_setSolc", + mungers=[default_root_munger], +) + + +startRPC = Method( + "admin_startRPC", + mungers=[admin_start_params_munger], +) + + +startWS = Method( + "admin_startWS", + mungers=[admin_start_params_munger], +) + + +stopRPC = Method( + "admin_stopRPC", + mungers=None, +) + + +stopWS = Method( + "admin_stopWS", + mungers=None, +) diff --git a/web3/_utils/blocks.py b/web3/_utils/blocks.py index c6c7843a29..765438eb3b 100644 --- a/web3/_utils/blocks.py +++ b/web3/_utils/blocks.py @@ -1,60 +1,60 @@ -from eth_utils import ( - is_bytes, - is_hex, - is_integer, - is_string, - is_text, - remove_0x_prefix, -) - - -def is_predefined_block_number(value): - if is_text(value): - value_text = value - elif is_bytes(value): - # `value` could either be random bytes or the utf-8 encoding of - # one of the words in: {"latest", "pending", "earliest"} - # We cannot decode the bytes as utf8, because random bytes likely won't be valid. - # So we speculatively decode as 'latin-1', which cannot fail. - value_text = value.decode('latin-1') - elif is_integer(value): - return False - else: - raise TypeError("unrecognized block reference: %r" % value) - - return value_text in {"latest", "pending", "earliest"} - - -def is_hex_encoded_block_hash(value): - if not is_string(value): - return False - return len(remove_0x_prefix(value)) == 64 and is_hex(value) - - -def is_hex_encoded_block_number(value): - if not is_string(value): - return False - elif is_hex_encoded_block_hash(value): - return False - try: - value_as_int = int(value, 16) - except ValueError: - return False - return 0 <= value_as_int < 2**256 - - -def select_method_for_block_identifier(value, if_hash, if_number, if_predefined): - if is_predefined_block_number(value): - return if_predefined - elif isinstance(value, bytes): - return if_hash - elif is_hex_encoded_block_hash(value): - return if_hash - elif is_integer(value) and (0 <= value < 2**256): - return if_number - elif is_hex_encoded_block_number(value): - return if_number - else: - raise ValueError( - "Value did not match any of the recognized block identifiers: {0}".format(value) - ) +from vns_utils import ( + is_bytes, + is_hex, + is_integer, + is_string, + is_text, + remove_0x_prefix, +) + + +def is_predefined_block_number(value): + if is_text(value): + value_text = value + elif is_bytes(value): + # `value` could either be random bytes or the utf-8 encoding of + # one of the words in: {"latest", "pending", "earliest"} + # We cannot decode the bytes as utf8, because random bytes likely won't be valid. + # So we speculatively decode as 'latin-1', which cannot fail. + value_text = value.decode('latin-1') + elif is_integer(value): + return False + else: + raise TypeError("unrecognized block reference: %r" % value) + + return value_text in {"latest", "pending", "earliest"} + + +def is_hex_encoded_block_hash(value): + if not is_string(value): + return False + return len(remove_0x_prefix(value)) == 64 and is_hex(value) + + +def is_hex_encoded_block_number(value): + if not is_string(value): + return False + elif is_hex_encoded_block_hash(value): + return False + try: + value_as_int = int(value, 16) + except ValueError: + return False + return 0 <= value_as_int < 2**256 + + +def select_method_for_block_identifier(value, if_hash, if_number, if_predefined): + if is_predefined_block_number(value): + return if_predefined + elif isinstance(value, bytes): + return if_hash + elif is_hex_encoded_block_hash(value): + return if_hash + elif is_integer(value) and (0 <= value < 2**256): + return if_number + elif is_hex_encoded_block_number(value): + return if_number + else: + raise ValueError( + "Value did not match any of the recognized block identifiers: {0}".format(value) + ) diff --git a/web3/_utils/caching.py b/web3/_utils/caching.py index d123e4506a..bb4c185da7 100644 --- a/web3/_utils/caching.py +++ b/web3/_utils/caching.py @@ -1,42 +1,42 @@ -import collections -import hashlib - -from eth_utils import ( - is_boolean, - is_bytes, - is_dict, - is_list_like, - is_null, - is_number, - is_text, - to_bytes, -) - - -def generate_cache_key(value): - """ - Generates a cache key for the *args and **kwargs - """ - if is_bytes(value): - return hashlib.md5(value).hexdigest() - elif is_text(value): - return generate_cache_key(to_bytes(text=value)) - elif is_boolean(value) or is_null(value) or is_number(value): - return generate_cache_key(repr(value)) - elif is_dict(value): - return generate_cache_key(( - (key, value[key]) - for key - in sorted(value.keys()) - )) - elif is_list_like(value) or isinstance(value, collections.abc.Generator): - return generate_cache_key("".join(( - generate_cache_key(item) - for item - in value - ))) - else: - raise TypeError("Cannot generate cache key for value {0} of type {1}".format( - value, - type(value), - )) +import collections +import hashlib + +from vns_utils import ( + is_boolean, + is_bytes, + is_dict, + is_list_like, + is_null, + is_number, + is_text, + to_bytes, +) + + +def generate_cache_key(value): + """ + Generates a cache key for the *args and **kwargs + """ + if is_bytes(value): + return hashlib.md5(value).hexdigest() + elif is_text(value): + return generate_cache_key(to_bytes(text=value)) + elif is_boolean(value) or is_null(value) or is_number(value): + return generate_cache_key(repr(value)) + elif is_dict(value): + return generate_cache_key(( + (key, value[key]) + for key + in sorted(value.keys()) + )) + elif is_list_like(value) or isinstance(value, collections.abc.Generator): + return generate_cache_key("".join(( + generate_cache_key(item) + for item + in value + ))) + else: + raise TypeError("Cannot generate cache key for value {0} of type {1}".format( + value, + type(value), + )) diff --git a/web3/_utils/compat/__init__.py b/web3/_utils/compat/__init__.py deleted file mode 100644 index 5f330d7b3b..0000000000 --- a/web3/_utils/compat/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# remove once web3 supports python>=3.8 -# TypedDict was added to typing in 3.8 -try: - from typing import Literal, TypedDict # type: ignore -except ImportError: - from mypy_extensions import TypedDict # noqa: F401 - from typing_extensions import Literal # noqa: F401 diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index 36d6b8d328..4da6e48aaa 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -1,303 +1,260 @@ -import functools -from typing import ( - TYPE_CHECKING, - Any, - Sequence, - Tuple, - Type, - Union, -) - -from eth_abi.codec import ( - ABICodec, -) -from eth_typing import ( - ChecksumAddress, - HexStr, -) -from eth_utils import ( - add_0x_prefix, - encode_hex, - function_abi_to_4byte_selector, - is_text, -) -from eth_utils.toolz import ( - pipe, - valmap, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.abi import ( - abi_to_signature, - check_if_arguments_can_be_encoded, - filter_by_argument_count, - filter_by_argument_name, - filter_by_encodability, - filter_by_name, - filter_by_type, - get_abi_input_types, - get_aligned_abi_inputs, - get_fallback_func_abi, - map_abi_data, - merge_args_and_kwargs, -) -from web3._utils.encoding import ( - to_hex, -) -from web3._utils.function_identifiers import ( - FallbackFn, -) -from web3._utils.normalizers import ( - abi_address_to_hex, - abi_bytes_to_bytes, - abi_ens_resolver, - abi_string_to_text, -) -from web3.exceptions import ( - ValidationError, -) -from web3.types import ( - ABI, - ABIEvent, - ABIFunction, - TxParams, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def find_matching_event_abi( - abi: ABI, event_name: str=None, argument_names: Sequence[str]=None -) -> ABIEvent: - - filters = [ - functools.partial(filter_by_type, 'event'), - ] - - if event_name is not None: - filters.append(functools.partial(filter_by_name, event_name)) - - if argument_names is not None: - filters.append( - functools.partial(filter_by_argument_name, argument_names) - ) - - event_abi_candidates = pipe(abi, *filters) - - if len(event_abi_candidates) == 1: - return event_abi_candidates[0] - elif not event_abi_candidates: - raise ValueError("No matching events found") - else: - raise ValueError("Multiple events found") - - -def find_matching_fn_abi( - abi: ABI, - abi_codec: ABICodec, - fn_identifier: Union[str, Type[FallbackFn]]=None, - args: Sequence[Any]=None, - kwargs: Any=None, -) -> ABIFunction: - args = args or tuple() - kwargs = kwargs or dict() - num_arguments = len(args) + len(kwargs) - - if fn_identifier is FallbackFn: - return get_fallback_func_abi(abi) - - if not is_text(fn_identifier): - raise TypeError("Unsupported function identifier") - - name_filter = functools.partial(filter_by_name, fn_identifier) - arg_count_filter = functools.partial(filter_by_argument_count, num_arguments) - encoding_filter = functools.partial(filter_by_encodability, abi_codec, args, kwargs) - - function_candidates = pipe(abi, name_filter, arg_count_filter, encoding_filter) - - if len(function_candidates) == 1: - return function_candidates[0] - else: - matching_identifiers = name_filter(abi) - matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers] - - arg_count_matches = len(arg_count_filter(matching_identifiers)) - encoding_matches = len(encoding_filter(matching_identifiers)) - - if arg_count_matches == 0: - diagnosis = "\nFunction invocation failed due to improper number of arguments." - elif encoding_matches == 0: - diagnosis = "\nFunction invocation failed due to no matching argument types." - elif encoding_matches > 1: - diagnosis = ( - "\nAmbiguous argument encoding. " - "Provided arguments can be encoded to multiple functions matching this call." - ) - - message = ( - "\nCould not identify the intended function with name `{name}`, " - "positional argument(s) of type `{arg_types}` and " - "keyword argument(s) of type `{kwarg_types}`." - "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}" - "{diagnosis}" - ).format( - name=fn_identifier, - arg_types=tuple(map(type, args)), - kwarg_types=valmap(type, kwargs), - num_candidates=len(matching_identifiers), - candidates=matching_function_signatures, - diagnosis=diagnosis, - ) - - raise ValidationError(message) - - -def encode_abi( - web3: "Web3", abi: ABIFunction, arguments: Sequence[Any], data: HexStr=None -) -> HexStr: - argument_types = get_abi_input_types(abi) - - if not check_if_arguments_can_be_encoded(abi, web3.codec, arguments, {}): - raise TypeError( - "One or more arguments could not be encoded to the necessary " - "ABI type. Expected types are: {0}".format( - ', '.join(argument_types), - ) - ) - - normalizers = [ - abi_ens_resolver(web3), - abi_address_to_hex, - abi_bytes_to_bytes, - abi_string_to_text, - ] - normalized_arguments = map_abi_data( - normalizers, - argument_types, - arguments, - ) - encoded_arguments = web3.codec.encode_abi( - argument_types, - normalized_arguments, - ) - - if data: - return to_hex(HexBytes(data) + encoded_arguments) - else: - return encode_hex(encoded_arguments) - - -def prepare_transaction( - address: ChecksumAddress, - web3: "Web3", - fn_identifier: Union[str, Type[FallbackFn]], - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - transaction: TxParams=None, - fn_args: Sequence[Any]=None, - fn_kwargs: Any=None, -) -> HexStr: - """ - :parameter `is_function_abi` is used to distinguish function abi from contract abi - Returns a dictionary of the transaction that could be used to call this - TODO: make this a public API - TODO: add new prepare_deploy_transaction API - """ - if fn_abi is None: - fn_abi = find_matching_fn_abi(contract_abi, web3.codec, fn_identifier, fn_args, fn_kwargs) - - validate_payable(transaction, fn_abi) - - if transaction is None: - prepared_transaction: TxParams = {} - else: - prepared_transaction = dict(**transaction) - - if 'data' in prepared_transaction: - raise ValueError("Transaction parameter may not contain a 'data' key") - - if address: - prepared_transaction.setdefault('to', address) - - prepared_transaction['data'] = encode_transaction_data( - web3, - fn_identifier, - contract_abi, - fn_abi, - fn_args, - fn_kwargs, - ) - return prepared_transaction - - -def encode_transaction_data( - web3: "Web3", - fn_identifier: Union[str, Type[FallbackFn]], - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - args: Sequence[Any]=None, - kwargs: Any=None -) -> HexStr: - if fn_identifier is FallbackFn: - fn_abi, fn_selector, fn_arguments = get_fallback_function_info(contract_abi, fn_abi) - elif is_text(fn_identifier): - fn_abi, fn_selector, fn_arguments = get_function_info( - # type ignored b/c fn_id here is always str b/c FallbackFn is handled above - fn_identifier, web3.codec, contract_abi, fn_abi, args, kwargs, # type: ignore - ) - else: - raise TypeError("Unsupported function identifier") - - return add_0x_prefix(encode_abi(web3, fn_abi, fn_arguments, fn_selector)) - - -def get_fallback_function_info( - contract_abi: ABI=None, fn_abi: ABIFunction=None -) -> Tuple[ABIFunction, HexStr, Sequence[Any]]: - if fn_abi is None: - fn_abi = get_fallback_func_abi(contract_abi) - fn_selector = encode_hex(b'') - fn_arguments: Sequence[Any] = tuple() - return fn_abi, fn_selector, fn_arguments - - -def get_function_info( - fn_name: str, - abi_codec: ABICodec, - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - args: Sequence[Any]=None, - kwargs: Any=None, -) -> Tuple[ABIFunction, HexStr, Sequence[Any]]: - if args is None: - args = tuple() - if kwargs is None: - kwargs = {} - - if fn_abi is None: - fn_abi = find_matching_fn_abi(contract_abi, abi_codec, fn_name, args, kwargs) - - fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi)) - - fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs) - - _, aligned_fn_arguments = get_aligned_abi_inputs(fn_abi, fn_arguments) - - return fn_abi, fn_selector, aligned_fn_arguments - - -def validate_payable(transaction: TxParams, abi: ABIFunction) -> None: - """Raise ValidationError if non-zero ether - is sent to a non payable function. - """ - if 'value' in transaction: - if transaction['value'] != 0: - if "payable" in abi and not abi["payable"]: - raise ValidationError( - "Sending non-zero ether to a contract function " - "with payable=False. Please ensure that " - "transaction's value is 0." - ) +import functools + +from vns_abi import ( + encode_abi as vns_abi_encode_abi, +) +from vns_utils import ( + add_0x_prefix, + encode_hex, + function_abi_to_4byte_selector, + is_text, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.abi import ( + abi_to_signature, + check_if_arguments_can_be_encoded, + filter_by_argument_count, + filter_by_argument_name, + filter_by_encodability, + filter_by_name, + filter_by_type, + get_abi_input_types, + get_aligned_abi_inputs, + get_fallback_func_abi, + map_abi_data, + merge_args_and_kwargs, +) +from web3._utils.encoding import ( + to_hex, +) +from web3._utils.function_identifiers import ( + FallbackFn, +) +from web3._utils.normalizers import ( + abi_address_to_hex, + abi_bytes_to_bytes, + abi_ens_resolver, + abi_string_to_text, +) +from web3._utils.toolz import ( + pipe, + valmap, +) +from web3.exceptions import ( + ValidationError, +) + + +def find_matching_event_abi(abi, event_name=None, argument_names=None): + + filters = [ + functools.partial(filter_by_type, 'event'), + ] + + if event_name is not None: + filters.append(functools.partial(filter_by_name, event_name)) + + if argument_names is not None: + filters.append( + functools.partial(filter_by_argument_name, argument_names) + ) + + event_abi_candidates = pipe(abi, *filters) + + if len(event_abi_candidates) == 1: + return event_abi_candidates[0] + elif not event_abi_candidates: + raise ValueError("No matching events found") + else: + raise ValueError("Multiple events found") + + +def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None): + args = args or tuple() + kwargs = kwargs or dict() + num_arguments = len(args) + len(kwargs) + + if fn_identifier is FallbackFn: + return get_fallback_func_abi(abi) + + if not is_text(fn_identifier): + raise TypeError("Unsupported function identifier") + + name_filter = functools.partial(filter_by_name, fn_identifier) + arg_count_filter = functools.partial(filter_by_argument_count, num_arguments) + encoding_filter = functools.partial(filter_by_encodability, args, kwargs) + + function_candidates = pipe(abi, name_filter, arg_count_filter, encoding_filter) + + if len(function_candidates) == 1: + return function_candidates[0] + else: + matching_identifiers = name_filter(abi) + matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers] + + arg_count_matches = len(arg_count_filter(matching_identifiers)) + encoding_matches = len(encoding_filter(matching_identifiers)) + + if arg_count_matches == 0: + diagnosis = "\nFunction invocation failed due to improper number of arguments." + elif encoding_matches == 0: + diagnosis = "\nFunction invocation failed due to no matching argument types." + elif encoding_matches > 1: + diagnosis = ( + "\nAmbiguous argument encoding. " + "Provided arguments can be encoded to multiple functions matching this call." + ) + + message = ( + "\nCould not identify the intended function with name `{name}`, " + "positional argument(s) of type `{arg_types}` and " + "keyword argument(s) of type `{kwarg_types}`." + "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}" + "{diagnosis}" + ).format( + name=fn_identifier, + arg_types=tuple(map(type, args)), + kwarg_types=valmap(type, kwargs), + num_candidates=len(matching_identifiers), + candidates=matching_function_signatures, + diagnosis=diagnosis, + ) + + raise ValidationError(message) + + +def encode_abi(web3, abi, arguments, data=None): + argument_types = get_abi_input_types(abi) + + if not check_if_arguments_can_be_encoded(abi, arguments, {}): + raise TypeError( + "One or more arguments could not be encoded to the necessary " + "ABI type. Expected types are: {0}".format( + ', '.join(argument_types), + ) + ) + + normalizers = [ + abi_ens_resolver(web3), + abi_address_to_hex, + abi_bytes_to_bytes, + abi_string_to_text, + ] + normalized_arguments = map_abi_data( + normalizers, + argument_types, + arguments, + ) + encoded_arguments = vns_abi_encode_abi( + argument_types, + normalized_arguments, + ) + + if data: + return to_hex(HexBytes(data) + encoded_arguments) + else: + return encode_hex(encoded_arguments) + + +def prepare_transaction( + address, + web3, + fn_identifier, + contract_abi=None, + fn_abi=None, + transaction=None, + fn_args=None, + fn_kwargs=None): + """ + :parameter `is_function_abi` is used to distinguish function abi from contract abi + Returns a dictionary of the transaction that could be used to call this + TODO: make this a public API + TODO: add new prepare_deploy_transaction API + """ + if fn_abi is None: + fn_abi = find_matching_fn_abi(contract_abi, fn_identifier, fn_args, fn_kwargs) + + validate_payable(transaction, fn_abi) + + if transaction is None: + prepared_transaction = {} + else: + prepared_transaction = dict(**transaction) + + if 'data' in prepared_transaction: + raise ValueError("Transaction parameter may not contain a 'data' key") + + if address: + prepared_transaction.setdefault('to', address) + + prepared_transaction['data'] = encode_transaction_data( + web3, + fn_identifier, + contract_abi, + fn_abi, + fn_args, + fn_kwargs, + ) + return prepared_transaction + + +def encode_transaction_data( + web3, + fn_identifier, + contract_abi=None, + fn_abi=None, + args=None, + kwargs=None): + if fn_identifier is FallbackFn: + fn_abi, fn_selector, fn_arguments = get_fallback_function_info(contract_abi, fn_abi) + elif is_text(fn_identifier): + fn_abi, fn_selector, fn_arguments = get_function_info( + fn_identifier, contract_abi, fn_abi, args, kwargs, + ) + else: + raise TypeError("Unsupported function identifier") + + return add_0x_prefix(encode_abi(web3, fn_abi, fn_arguments, fn_selector)) + + +def get_fallback_function_info(contract_abi=None, fn_abi=None): + if fn_abi is None: + fn_abi = get_fallback_func_abi(contract_abi) + fn_selector = encode_hex(b'') + fn_arguments = tuple() + return fn_abi, fn_selector, fn_arguments + + +def get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None, kwargs=None): + if args is None: + args = tuple() + if kwargs is None: + kwargs = {} + + if fn_abi is None: + fn_abi = find_matching_fn_abi(contract_abi, fn_name, args, kwargs) + + fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi)) + + fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs) + + _, aligned_fn_arguments = get_aligned_abi_inputs(fn_abi, fn_arguments) + + return fn_abi, fn_selector, aligned_fn_arguments + + +def validate_payable(transaction, abi): + """Raise ValidationError if non-zero ether + is sent to a non payable function. + """ + if 'value' in transaction: + if transaction['value'] != 0: + if "payable" in abi and not abi["payable"]: + raise ValidationError( + "Sending non-zero ether to a contract function " + "with payable=False. Please ensure that " + "transaction's value is 0." + ) diff --git a/web3/_utils/datatypes.py b/web3/_utils/datatypes.py index 29777f1ae3..85f31afd40 100644 --- a/web3/_utils/datatypes.py +++ b/web3/_utils/datatypes.py @@ -1,39 +1,37 @@ -from eth_utils import ( - apply_formatters_to_dict, -) -from eth_utils.toolz import ( - concat, -) - - -def verify_attr(class_name, key, namespace): - if key not in namespace: - raise AttributeError( - "Property {0} not found on {1} class. " - "`{1}.factory` only accepts keyword arguments which are " - "present on the {1} class".format(key, class_name) - ) - - -class PropertyCheckingFactory(type): - def __init__(cls, name, bases, namespace, **kargs): - # see PEP487. To accept kwargs in __new__, they need to be - # filtered out here. - super().__init__(name, bases, namespace) - - def __new__(mcs, name, bases, namespace, normalizers=None): - all_bases = set(concat(base.__mro__ for base in bases)) - all_keys = set(concat(base.__dict__.keys() for base in all_bases)) - - for key in namespace: - verify_attr(name, key, all_keys) - - if normalizers: - processed_namespace = apply_formatters_to_dict( - normalizers, - namespace, - ) - else: - processed_namespace = namespace - - return super().__new__(mcs, name, bases, processed_namespace) +import web3._utils.formatters +from web3._utils.toolz import ( + concat, +) + + +def verify_attr(class_name, key, namespace): + if key not in namespace: + raise AttributeError( + "Property {0} not found on {1} class. " + "`{1}.factory` only accepts keyword arguments which are " + "present on the {1} class".format(key, class_name) + ) + + +class PropertyCheckingFactory(type): + def __init__(cls, name, bases, namespace, **kargs): + # see PEP487. To accept kwargs in __new__, they need to be + # filtered out here. + super().__init__(name, bases, namespace) + + def __new__(mcs, name, bases, namespace, normalizers=None): + all_bases = set(concat(base.__mro__ for base in bases)) + all_keys = set(concat(base.__dict__.keys() for base in all_bases)) + + for key in namespace: + verify_attr(name, key, all_keys) + + if normalizers: + processed_namespace = web3._utils.formatters.apply_formatters_to_dict( + normalizers, + namespace, + ) + else: + processed_namespace = namespace + + return super().__new__(mcs, name, bases, processed_namespace) diff --git a/web3/_utils/decorators.py b/web3/_utils/decorators.py index 84dd7a2f77..e23861842f 100644 --- a/web3/_utils/decorators.py +++ b/web3/_utils/decorators.py @@ -1,58 +1,59 @@ -import functools -import threading -import warnings - - -class combomethod: - def __init__(self, method): - self.method = method - - def __get__(self, obj=None, objtype=None): - @functools.wraps(self.method) - def _wrapper(*args, **kwargs): - if obj is not None: - return self.method(obj, *args, **kwargs) - else: - return self.method(objtype, *args, **kwargs) - return _wrapper - - -def reject_recursive_repeats(to_wrap): - """ - Prevent simple cycles by returning None when called recursively with same instance - """ - to_wrap.__already_called = {} - - @functools.wraps(to_wrap) - def wrapped(*args): - arg_instances = tuple(map(id, args)) - thread_id = threading.get_ident() - thread_local_args = (thread_id,) + arg_instances - if thread_local_args in to_wrap.__already_called: - raise ValueError('Recursively called %s with %r' % (to_wrap, args)) - to_wrap.__already_called[thread_local_args] = True - try: - wrapped_val = to_wrap(*args) - finally: - del to_wrap.__already_called[thread_local_args] - return wrapped_val - return wrapped - - -def deprecated_for(replace_message): - """ - Decorate a deprecated function, with info about what to use instead, like: - - @deprecated_for("toBytes()") - def toAscii(arg): - ... - """ - def decorator(to_wrap): - @functools.wraps(to_wrap) - def wrapper(*args, **kwargs): - warnings.warn( - f"{to_wrap.__name__} is deprecated in favor of {replace_message}", - category=DeprecationWarning) - return to_wrap(*args, **kwargs) - return wrapper - return decorator +import functools +import threading +import warnings + + +class combomethod: + def __init__(self, method): + self.method = method + + def __get__(self, obj=None, objtype=None): + @functools.wraps(self.method) + def _wrapper(*args, **kwargs): + if obj is not None: + return self.method(obj, *args, **kwargs) + else: + return self.method(objtype, *args, **kwargs) + return _wrapper + + +def reject_recursive_repeats(to_wrap): + """ + Prevent simple cycles by returning None when called recursively with same instance + """ + to_wrap.__already_called = {} + + @functools.wraps(to_wrap) + def wrapped(*args): + arg_instances = tuple(map(id, args)) + thread_id = threading.get_ident() + thread_local_args = (thread_id,) + arg_instances + if thread_local_args in to_wrap.__already_called: + raise ValueError('Recursively called %s with %r' % (to_wrap, args)) + to_wrap.__already_called[thread_local_args] = True + try: + wrapped_val = to_wrap(*args) + finally: + del to_wrap.__already_called[thread_local_args] + return wrapped_val + return wrapped + + +def deprecated_for(replace_message): + """ + Decorate a deprecated function, with info about what to use instead, like: + + @deprecated_for("toBytes()") + def toAscii(arg): + ... + """ + def decorator(to_wrap): + @functools.wraps(to_wrap) + def wrapper(*args, **kwargs): + warnings.warn( + "%s is deprecated in favor of %s" % (to_wrap.__name__, replace_message), + category=DeprecationWarning, + stacklevel=2) + return to_wrap(*args, **kwargs) + return wrapper + return decorator diff --git a/web3/_utils/empty.py b/web3/_utils/empty.py index 5391d017a4..4dae6c815d 100644 --- a/web3/_utils/empty.py +++ b/web3/_utils/empty.py @@ -1,9 +1,9 @@ -class Empty: - def __bool__(self): - return False - - def __nonzero__(self): - return False - - -empty = Empty() +class Empty: + def __bool__(self): + return False + + def __nonzero__(self): + return False + + +empty = Empty() diff --git a/web3/_utils/encoding.py b/web3/_utils/encoding.py index b727278973..012397c9e1 100644 --- a/web3/_utils/encoding.py +++ b/web3/_utils/encoding.py @@ -1,333 +1,333 @@ -# String encodings and numeric representations -import json -import re - -from eth_abi.encoding import ( - BaseArrayEncoder, -) -from eth_utils import ( - add_0x_prefix, - big_endian_to_int, - decode_hex, - encode_hex, - int_to_big_endian, - is_boolean, - is_bytes, - is_hex, - is_integer, - is_list_like, - remove_0x_prefix, - to_hex, -) -from eth_utils.toolz import ( - curry, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.abi import ( - is_address_type, - is_array_type, - is_bool_type, - is_bytes_type, - is_int_type, - is_string_type, - is_uint_type, - size_of_type, - sub_type_of_array_type, -) -from web3._utils.validation import ( - assert_one_val, - validate_abi_type, - validate_abi_value, -) -from web3.datastructures import ( - AttributeDict, -) - - -def hex_encode_abi_type(abi_type, value, force_size=None): - """ - Encodes value into a hex string in format of abi_type - """ - validate_abi_type(abi_type) - validate_abi_value(abi_type, value) - - data_size = force_size or size_of_type(abi_type) - if is_array_type(abi_type): - sub_type = sub_type_of_array_type(abi_type) - return "".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value]) - elif is_bool_type(abi_type): - return to_hex_with_size(value, data_size) - elif is_uint_type(abi_type): - return to_hex_with_size(value, data_size) - elif is_int_type(abi_type): - return to_hex_twos_compliment(value, data_size) - elif is_address_type(abi_type): - return pad_hex(value, data_size) - elif is_bytes_type(abi_type): - if is_bytes(value): - return encode_hex(value) - else: - return value - elif is_string_type(abi_type): - return to_hex(text=value) - else: - raise ValueError( - "Unsupported ABI type: {0}".format(abi_type) - ) - - -def to_hex_twos_compliment(value, bit_size): - """ - Converts integer value to twos compliment hex representation with given bit_size - """ - if value >= 0: - return to_hex_with_size(value, bit_size) - - value = (1 << bit_size) + value - hex_value = hex(value) - hex_value = hex_value.rstrip("L") - return hex_value - - -def to_hex_with_size(value, bit_size): - """ - Converts a value to hex with given bit_size: - """ - return pad_hex(to_hex(value), bit_size) - - -def pad_hex(value, bit_size): - """ - Pads a hex string up to the given bit_size - """ - value = remove_0x_prefix(value) - return add_0x_prefix(value.zfill(int(bit_size / 4))) - - -def trim_hex(hexstr): - if hexstr.startswith('0x0'): - hexstr = re.sub('^0x0+', '0x', hexstr) - if hexstr == '0x': - hexstr = '0x0' - return hexstr - - -def to_int(value=None, hexstr=None, text=None): - """ - Converts value to it's integer representation. - - Values are converted this way: - - * value: - * bytes: big-endian integer - * bool: True => 1, False => 0 - * hexstr: interpret hex as integer - * text: interpret as string of digits, like '12' => 12 - """ - assert_one_val(value, hexstr=hexstr, text=text) - - if hexstr is not None: - return int(hexstr, 16) - elif text is not None: - return int(text) - elif isinstance(value, bytes): - return big_endian_to_int(value) - elif isinstance(value, str): - raise TypeError("Pass in strings with keyword hexstr or text") - else: - return int(value) - - -@curry -def pad_bytes(fill_with, num_bytes, unpadded): - return unpadded.rjust(num_bytes, fill_with) - - -zpad_bytes = pad_bytes(b'\0') - - -def to_bytes(primitive=None, hexstr=None, text=None): - assert_one_val(primitive, hexstr=hexstr, text=text) - - if is_boolean(primitive): - return b'\x01' if primitive else b'\x00' - elif isinstance(primitive, bytes): - return primitive - elif is_integer(primitive): - return to_bytes(hexstr=to_hex(primitive)) - elif hexstr is not None: - if len(hexstr) % 2: - hexstr = '0x0' + remove_0x_prefix(hexstr) - return decode_hex(hexstr) - elif text is not None: - return text.encode('utf-8') - raise TypeError("expected an int in first arg, or keyword of hexstr or text") - - -def to_text(primitive=None, hexstr=None, text=None): - assert_one_val(primitive, hexstr=hexstr, text=text) - - if hexstr is not None: - return to_bytes(hexstr=hexstr).decode('utf-8') - elif text is not None: - return text - elif isinstance(primitive, str): - return to_text(hexstr=primitive) - elif isinstance(primitive, bytes): - return primitive.decode('utf-8') - elif is_integer(primitive): - byte_encoding = int_to_big_endian(primitive) - return to_text(byte_encoding) - raise TypeError("Expected an int, bytes or hexstr.") - - -@curry -def text_if_str(to_type, text_or_primitive): - """ - Convert to a type, assuming that strings can be only unicode text (not a hexstr) - - @param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text), - eg~ to_bytes, to_text, to_hex, to_int, etc - @param hexstr_or_primitive in bytes, str, or int. - """ - if isinstance(text_or_primitive, str): - (primitive, text) = (None, text_or_primitive) - else: - (primitive, text) = (text_or_primitive, None) - return to_type(primitive, text=text) - - -@curry -def hexstr_if_str(to_type, hexstr_or_primitive): - """ - Convert to a type, assuming that strings can be only hexstr (not unicode text) - - @param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text), - eg~ to_bytes, to_text, to_hex, to_int, etc - @param text_or_primitive in bytes, str, or int. - """ - if isinstance(hexstr_or_primitive, str): - (primitive, hexstr) = (None, hexstr_or_primitive) - if remove_0x_prefix(hexstr) and not is_hex(hexstr): - raise ValueError( - "when sending a str, it must be a hex string. Got: {0!r}".format( - hexstr_or_primitive, - ) - ) - else: - (primitive, hexstr) = (hexstr_or_primitive, None) - return to_type(primitive, hexstr=hexstr) - - -class FriendlyJsonSerde: - """ - Friendly JSON serializer & deserializer - - When encoding or decoding fails, this class collects - information on which fields failed, to show more - helpful information in the raised error messages. - """ - def _json_mapping_errors(self, mapping): - for key, val in mapping.items(): - try: - self._friendly_json_encode(val) - except TypeError as exc: - yield "%r: because (%s)" % (key, exc) - - def _json_list_errors(self, iterable): - for index, element in enumerate(iterable): - try: - self._friendly_json_encode(element) - except TypeError as exc: - yield "%d: because (%s)" % (index, exc) - - def _friendly_json_encode(self, obj, cls=None): - try: - encoded = json.dumps(obj, cls=cls) - return encoded - except TypeError as full_exception: - if hasattr(obj, 'items'): - item_errors = '; '.join(self._json_mapping_errors(obj)) - raise TypeError("dict had unencodable value at keys: {{{}}}".format(item_errors)) - elif is_list_like(obj): - element_errors = '; '.join(self._json_list_errors(obj)) - raise TypeError("list had unencodable value at index: [{}]".format(element_errors)) - else: - raise full_exception - - def json_decode(self, json_str): - try: - decoded = json.loads(json_str) - return decoded - except json.decoder.JSONDecodeError as exc: - err_msg = 'Could not decode {} because of {}.'.format(repr(json_str), exc) - # Calling code may rely on catching JSONDecodeError to recognize bad json - # so we have to re-raise the same type. - raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos) - - def json_encode(self, obj, cls=None): - try: - return self._friendly_json_encode(obj, cls=cls) - except TypeError as exc: - raise TypeError("Could not encode to JSON: {}".format(exc)) - - -def to_4byte_hex(hex_or_str_or_bytes): - size_of_4bytes = 4 * 8 - byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes) - if len(byte_str) > 4: - raise ValueError( - 'expected value of size 4 bytes. Got: %d bytes' % len(byte_str) - ) - hex_str = encode_hex(byte_str) - return pad_hex(hex_str, size_of_4bytes) - - -class DynamicArrayPackedEncoder(BaseArrayEncoder): - is_dynamic = True - - def encode(self, value): - encoded_elements = self.encode_elements(value) - encoded_value = encoded_elements - - return encoded_value - - -# TODO: Replace with eth-abi packed encoder once web3 requires eth-abi>=2 -def encode_single_packed(_type, value): - import codecs - from eth_abi import ( - grammar as abi_type_parser, - ) - from eth_abi.registry import has_arrlist, registry - abi_type = abi_type_parser.parse(_type) - if has_arrlist(_type): - item_encoder = registry.get_encoder(abi_type.item_type.to_type_str()) - if abi_type.arrlist[-1] != 1: - return DynamicArrayPackedEncoder(item_encoder=item_encoder).encode(value) - else: - raise NotImplementedError( - "Fixed arrays are not implemented in this packed encoder prototype") - elif abi_type.base == "string": - return codecs.encode(value, 'utf8') - elif abi_type.base == "bytes": - return value - - -class Web3JsonEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, AttributeDict): - return {k: v for k, v in obj.items()} - if isinstance(obj, HexBytes): - return obj.hex() - return json.JSONEncoder.default(self, obj) - - -def to_json(obj): - ''' - Convert a complex object (like a transaction object) to a JSON string - ''' - return FriendlyJsonSerde().json_encode(obj, cls=Web3JsonEncoder) +# String encodings and numeric representations +import json +import re + +from vns_abi.encoding import ( + BaseArrayEncoder, +) +from vns_utils import ( + add_0x_prefix, + big_endian_to_int, + decode_hex, + encode_hex, + int_to_big_endian, + is_boolean, + is_bytes, + is_hex, + is_integer, + is_list_like, + remove_0x_prefix, + to_hex, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.abi import ( + is_address_type, + is_array_type, + is_bool_type, + is_bytes_type, + is_int_type, + is_string_type, + is_uint_type, + size_of_type, + sub_type_of_array_type, +) +from web3._utils.toolz import ( + curry, +) +from web3._utils.validation import ( + assert_one_val, + validate_abi_type, + validate_abi_value, +) +from web3.datastructures import ( + AttributeDict, +) + + +def hex_encode_abi_type(abi_type, value, force_size=None): + """ + Encodes value into a hex string in format of abi_type + """ + validate_abi_type(abi_type) + validate_abi_value(abi_type, value) + + data_size = force_size or size_of_type(abi_type) + if is_array_type(abi_type): + sub_type = sub_type_of_array_type(abi_type) + return "".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value]) + elif is_bool_type(abi_type): + return to_hex_with_size(value, data_size) + elif is_uint_type(abi_type): + return to_hex_with_size(value, data_size) + elif is_int_type(abi_type): + return to_hex_twos_compliment(value, data_size) + elif is_address_type(abi_type): + return pad_hex(value, data_size) + elif is_bytes_type(abi_type): + if is_bytes(value): + return encode_hex(value) + else: + return value + elif is_string_type(abi_type): + return to_hex(text=value) + else: + raise ValueError( + "Unsupported ABI type: {0}".format(abi_type) + ) + + +def to_hex_twos_compliment(value, bit_size): + """ + Converts integer value to twos compliment hex representation with given bit_size + """ + if value >= 0: + return to_hex_with_size(value, bit_size) + + value = (1 << bit_size) + value + hex_value = hex(value) + hex_value = hex_value.rstrip("L") + return hex_value + + +def to_hex_with_size(value, bit_size): + """ + Converts a value to hex with given bit_size: + """ + return pad_hex(to_hex(value), bit_size) + + +def pad_hex(value, bit_size): + """ + Pads a hex string up to the given bit_size + """ + value = remove_0x_prefix(value) + return add_0x_prefix(value.zfill(int(bit_size / 4))) + + +def trim_hex(hexstr): + if hexstr.startswith('0x0'): + hexstr = re.sub('^0x0+', '0x', hexstr) + if hexstr == '0x': + hexstr = '0x0' + return hexstr + + +def to_int(value=None, hexstr=None, text=None): + """ + Converts value to it's integer representation. + + Values are converted this way: + + * value: + * bytes: big-endian integer + * bool: True => 1, False => 0 + * hexstr: interpret hex as integer + * text: interpret as string of digits, like '12' => 12 + """ + assert_one_val(value, hexstr=hexstr, text=text) + + if hexstr is not None: + return int(hexstr, 16) + elif text is not None: + return int(text) + elif isinstance(value, bytes): + return big_endian_to_int(value) + elif isinstance(value, str): + raise TypeError("Pass in strings with keyword hexstr or text") + else: + return int(value) + + +@curry +def pad_bytes(fill_with, num_bytes, unpadded): + return unpadded.rjust(num_bytes, fill_with) + + +zpad_bytes = pad_bytes(b'\0') + + +def to_bytes(primitive=None, hexstr=None, text=None): + assert_one_val(primitive, hexstr=hexstr, text=text) + + if is_boolean(primitive): + return b'\x01' if primitive else b'\x00' + elif isinstance(primitive, bytes): + return primitive + elif is_integer(primitive): + return to_bytes(hexstr=to_hex(primitive)) + elif hexstr is not None: + if len(hexstr) % 2: + hexstr = '0x0' + remove_0x_prefix(hexstr) + return decode_hex(hexstr) + elif text is not None: + return text.encode('utf-8') + raise TypeError("expected an int in first arg, or keyword of hexstr or text") + + +def to_text(primitive=None, hexstr=None, text=None): + assert_one_val(primitive, hexstr=hexstr, text=text) + + if hexstr is not None: + return to_bytes(hexstr=hexstr).decode('utf-8') + elif text is not None: + return text + elif isinstance(primitive, str): + return to_text(hexstr=primitive) + elif isinstance(primitive, bytes): + return primitive.decode('utf-8') + elif is_integer(primitive): + byte_encoding = int_to_big_endian(primitive) + return to_text(byte_encoding) + raise TypeError("Expected an int, bytes or hexstr.") + + +@curry +def text_if_str(to_type, text_or_primitive): + """ + Convert to a type, assuming that strings can be only unicode text (not a hexstr) + + @param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text), + eg~ to_bytes, to_text, to_hex, to_int, etc + @param hexstr_or_primitive in bytes, str, or int. + """ + if isinstance(text_or_primitive, str): + (primitive, text) = (None, text_or_primitive) + else: + (primitive, text) = (text_or_primitive, None) + return to_type(primitive, text=text) + + +@curry +def hexstr_if_str(to_type, hexstr_or_primitive): + """ + Convert to a type, assuming that strings can be only hexstr (not unicode text) + + @param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text), + eg~ to_bytes, to_text, to_hex, to_int, etc + @param text_or_primitive in bytes, str, or int. + """ + if isinstance(hexstr_or_primitive, str): + (primitive, hexstr) = (None, hexstr_or_primitive) + if remove_0x_prefix(hexstr) and not is_hex(hexstr): + raise ValueError( + "when sending a str, it must be a hex string. Got: {0!r}".format( + hexstr_or_primitive, + ) + ) + else: + (primitive, hexstr) = (hexstr_or_primitive, None) + return to_type(primitive, hexstr=hexstr) + + +class FriendlyJsonSerde: + """ + Friendly JSON serializer & deserializer + + When encoding or decoding fails, this class collects + information on which fields failed, to show more + helpful information in the raised error messages. + """ + def _json_mapping_errors(self, mapping): + for key, val in mapping.items(): + try: + self._friendly_json_encode(val) + except TypeError as exc: + yield "%r: because (%s)" % (key, exc) + + def _json_list_errors(self, iterable): + for index, element in enumerate(iterable): + try: + self._friendly_json_encode(element) + except TypeError as exc: + yield "%d: because (%s)" % (index, exc) + + def _friendly_json_encode(self, obj, cls=None): + try: + encoded = json.dumps(obj, cls=cls) + return encoded + except TypeError as full_exception: + if hasattr(obj, 'items'): + item_errors = '; '.join(self._json_mapping_errors(obj)) + raise TypeError("dict had unencodable value at keys: {{{}}}".format(item_errors)) + elif is_list_like(obj): + element_errors = '; '.join(self._json_list_errors(obj)) + raise TypeError("list had unencodable value at index: [{}]".format(element_errors)) + else: + raise full_exception + + def json_decode(self, json_str): + try: + decoded = json.loads(json_str) + return decoded + except json.decoder.JSONDecodeError as exc: + err_msg = 'Could not decode {} because of {}.'.format(repr(json_str), exc) + # Calling code may rely on catching JSONDecodeError to recognize bad json + # so we have to re-raise the same type. + raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos) + + def json_encode(self, obj, cls=None): + try: + return self._friendly_json_encode(obj, cls=cls) + except TypeError as exc: + raise TypeError("Could not encode to JSON: {}".format(exc)) + + +def to_4byte_hex(hex_or_str_or_bytes): + size_of_4bytes = 4 * 8 + byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes) + if len(byte_str) > 4: + raise ValueError( + 'expected value of size 4 bytes. Got: %d bytes' % len(byte_str) + ) + hex_str = encode_hex(byte_str) + return pad_hex(hex_str, size_of_4bytes) + + +class DynamicArrayPackedEncoder(BaseArrayEncoder): + is_dynamic = True + + def encode(self, value): + encoded_elements = self.encode_elements(value) + encoded_value = encoded_elements + + return encoded_value + + +# TODO: Replace with vns-abi packed encoder once web3 requires vns-abi>=2 +def encode_single_packed(_type, value): + import codecs + from vns_abi import ( + grammar as abi_type_parser, + ) + from vns_abi.registry import has_arrlist, registry + abi_type = abi_type_parser.parse(_type) + if has_arrlist(_type): + item_encoder = registry.get_encoder(abi_type.item_type.to_type_str()) + if abi_type.arrlist[-1] != 1: + return DynamicArrayPackedEncoder(item_encoder=item_encoder).encode(value) + else: + raise NotImplementedError( + "Fixed arrays are not implemented in this packed encoder prototype") + elif abi_type.base == "string": + return codecs.encode(value, 'utf8') + elif abi_type.base == "bytes": + return value + + +class Web3JsonEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, AttributeDict): + return {k: v for k, v in obj.items()} + if isinstance(obj, HexBytes): + return obj.hex() + return json.JSONEncoder.default(self, obj) + + +def to_json(obj): + ''' + Convert a complex object (like a transaction object) to a JSON string + ''' + return FriendlyJsonSerde().json_encode(obj, cls=Web3JsonEncoder) diff --git a/web3/_utils/ens.py b/web3/_utils/ens.py index f4fe94a825..a9132bb2a6 100644 --- a/web3/_utils/ens.py +++ b/web3/_utils/ens.py @@ -1,62 +1,62 @@ -from contextlib import ( - contextmanager, -) - -from eth_utils import ( - is_0x_prefixed, - is_hex, - is_hex_address, -) - -from ens import ENS -from web3.exceptions import ( - NameNotFound, -) - - -def is_ens_name(value): - if not isinstance(value, str): - return False - elif is_hex_address(value): - return False - elif is_0x_prefixed(value) and is_hex(value): - return False - else: - return ENS.is_valid_name(value) - - -def validate_name_has_address(ens, name): - addr = ens.address(name) - if addr: - return addr - else: - raise NameNotFound("Could not find address for name %r" % name) - - -class StaticENS: - def __init__(self, name_addr_pairs): - self.registry = dict(name_addr_pairs) - - def address(self, name): - return self.registry.get(name, None) - - -@contextmanager -def ens_addresses(w3, name_addr_pairs): - original_ens = w3.ens - w3.ens = StaticENS(name_addr_pairs) - yield - w3.ens = original_ens - - -@contextmanager -def contract_ens_addresses(contract, name_addr_pairs): - """ - Use this context manager to temporarily resolve name/address pairs - supplied as the argument. For example: - - with contract_ens_addresses(mycontract, [('resolve-as-1s.eth', '0x111...111')]): - # any contract call or transaction in here would only resolve the above ENS pair - """ - with ens_addresses(contract.web3, name_addr_pairs): - yield +from contextlib import ( + contextmanager, +) + +from vns_utils import ( + is_0x_prefixed, + is_hex, + is_hex_address, +) + +from ens import ENS +from web3.exceptions import ( + NameNotFound, +) + + +def is_ens_name(value): + if not isinstance(value, str): + return False + elif is_hex_address(value): + return False + elif is_0x_prefixed(value) and is_hex(value): + return False + else: + return ENS.is_valid_name(value) + + +def validate_name_has_address(ens, name): + addr = ens.address(name) + if addr: + return addr + else: + raise NameNotFound("Could not find address for name %r" % name) + + +class StaticENS: + def __init__(self, name_addr_pairs): + self.registry = dict(name_addr_pairs) + + def address(self, name): + return self.registry.get(name, None) + + +@contextmanager +def ens_addresses(w3, name_addr_pairs): + original_ens = w3.ens + w3.ens = StaticENS(name_addr_pairs) + yield + w3.ens = original_ens + + +@contextmanager +def contract_ens_addresses(contract, name_addr_pairs): + """ + Use this context manager to temporarily resolve name/address pairs + supplied as the argument. For example: + + with contract_ens_addresses(mycontract, [('resolve-as-1s.vns', '0x111...111')]): + # any contract call or transaction in here would only resolve the above ENS pair + """ + with ens_addresses(contract.web3, name_addr_pairs): + yield diff --git a/web3/_utils/events.py b/web3/_utils/events.py index 5cc963bead..1e786cc814 100644 --- a/web3/_utils/events.py +++ b/web3/_utils/events.py @@ -1,509 +1,448 @@ -from abc import ( - ABC, - abstractmethod, -) -from enum import Enum -import itertools -from typing import ( - TYPE_CHECKING, - Any, - Collection, - Dict, - Iterable, - List, - Optional, - Sequence, - Tuple, - Union, - cast, -) - -from eth_abi import ( - grammar, -) -from eth_abi.codec import ( - ABICodec, -) -from eth_typing import ( - ChecksumAddress, - HexStr, - TypeStr, -) -from eth_utils import ( - encode_hex, - event_abi_to_log_topic, - is_list_like, - keccak, - to_dict, - to_hex, - to_tuple, -) -from eth_utils.curried import ( - apply_formatter_if, -) -from eth_utils.toolz import ( - complement, - compose, - cons, - curry, - valfilter, -) - -import web3 -from web3._utils.abi import ( - exclude_indexed_event_inputs, - get_abi_input_names, - get_indexed_event_inputs, - map_abi_data, - normalize_event_input_types, -) -from web3._utils.encoding import ( - encode_single_packed, - hexstr_if_str, - to_bytes, -) -from web3._utils.normalizers import ( - BASE_RETURN_NORMALIZERS, -) -from web3.datastructures import ( - AttributeDict, -) -from web3.exceptions import ( - InvalidEventABI, - LogTopicError, - MismatchedABI, -) -from web3.types import ( - ABIEvent, - ABIEventParams, - BlockIdentifier, - EventData, - FilterParams, - LogReceipt, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - from web3._utils.filters import ( # noqa: F401 - LogFilter, - ) - - -def construct_event_topic_set( - event_abi: ABIEvent, abi_codec: ABICodec, arguments: Union[Sequence[Any], Dict[str, Any]]=None -) -> List[HexStr]: - if arguments is None: - arguments = {} - if isinstance(arguments, (list, tuple)): - if len(arguments) != len(event_abi['inputs']): - raise ValueError( - "When passing an argument list, the number of arguments must " - "match the event constructor." - ) - arguments = { - arg['name']: [arg_value] - for arg, arg_value - in zip(event_abi['inputs'], arguments) - } - - normalized_args = { - key: value if is_list_like(value) else [value] - # type ignored b/c arguments is always a dict at this point - for key, value in arguments.items() # type: ignore - } - - event_topic = encode_hex(event_abi_to_log_topic(event_abi)) - indexed_args = get_indexed_event_inputs(event_abi) - zipped_abi_and_args = [ - (arg, normalized_args.get(arg['name'], [None])) - for arg in indexed_args - ] - encoded_args = [ - [ - None if option is None else encode_hex(abi_codec.encode_single(arg['type'], option)) - for option in arg_options] - for arg, arg_options in zipped_abi_and_args - ] - - topics = list(normalize_topic_list([event_topic] + encoded_args)) # type: ignore - return topics - - -def construct_event_data_set( - event_abi: ABIEvent, abi_codec: ABICodec, arguments: Union[Sequence[Any], Dict[str, Any]]=None -) -> List[List[Optional[HexStr]]]: - if arguments is None: - arguments = {} - if isinstance(arguments, (list, tuple)): - if len(arguments) != len(event_abi['inputs']): - raise ValueError( - "When passing an argument list, the number of arguments must " - "match the event constructor." - ) - arguments = { - arg['name']: [arg_value] - for arg, arg_value - in zip(event_abi['inputs'], arguments) - } - - normalized_args = { - key: value if is_list_like(value) else [value] - # type ignored b/c at this point arguments is always a dict - for key, value in arguments.items() # type: ignore - } - - non_indexed_args = exclude_indexed_event_inputs(event_abi) - zipped_abi_and_args = [ - (arg, normalized_args.get(arg['name'], [None])) - for arg in non_indexed_args - ] - encoded_args = [ - [ - None if option is None else encode_hex(abi_codec.encode_single(arg['type'], option)) - for option in arg_options] - for arg, arg_options in zipped_abi_and_args - ] - - data = [ - list(permutation) - if any(value is not None for value in permutation) - else [] - for permutation in itertools.product(*encoded_args) - ] - return data - - -def is_dynamic_sized_type(type_str: TypeStr) -> bool: - abi_type = grammar.parse(type_str) - return abi_type.is_dynamic - - -@to_tuple -def get_event_abi_types_for_decoding(event_inputs: ABIEventParams) -> Iterable[TypeStr]: - """ - Event logs use the `keccak(value)` for indexed inputs of type `bytes` or - `string`. Because of this we need to modify the types so that we can - decode the log entries using the correct types. - """ - for input_abi in event_inputs: - if input_abi['indexed'] and is_dynamic_sized_type(input_abi['type']): - yield 'bytes32' - else: - yield input_abi['type'] - - -@curry -def get_event_data(abi_codec: ABICodec, event_abi: ABIEvent, log_entry: LogReceipt) -> EventData: - """ - Given an event ABI and a log entry for that event, return the decoded - event data - """ - if event_abi['anonymous']: - log_topics = log_entry['topics'] - elif not log_entry['topics']: - raise MismatchedABI("Expected non-anonymous event to have 1 or more topics") - elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: - raise MismatchedABI("The event signature did not match the provided ABI") - else: - log_topics = log_entry['topics'][1:] - - log_topics_abi = get_indexed_event_inputs(event_abi) - log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) - log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) - log_topic_names = get_abi_input_names(ABIEvent({'inputs': log_topics_abi})) - - if len(log_topics) != len(log_topic_types): - raise LogTopicError("Expected {0} log topics. Got {1}".format( - len(log_topic_types), - len(log_topics), - )) - - log_data = hexstr_if_str(to_bytes, log_entry['data']) - log_data_abi = exclude_indexed_event_inputs(event_abi) - log_data_normalized_inputs = normalize_event_input_types(log_data_abi) - log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) - log_data_names = get_abi_input_names(ABIEvent({'inputs': log_data_abi})) - - # sanity check that there are not name intersections between the topic - # names and the data argument names. - duplicate_names = set(log_topic_names).intersection(log_data_names) - if duplicate_names: - raise InvalidEventABI( - "The following argument names are duplicated " - f"between event inputs: '{', '.join(duplicate_names)}'" - ) - - decoded_log_data = abi_codec.decode_abi(log_data_types, log_data) - normalized_log_data = map_abi_data( - BASE_RETURN_NORMALIZERS, - log_data_types, - decoded_log_data - ) - - decoded_topic_data = [ - abi_codec.decode_single(topic_type, topic_data) - for topic_type, topic_data - in zip(log_topic_types, log_topics) - ] - normalized_topic_data = map_abi_data( - BASE_RETURN_NORMALIZERS, - log_topic_types, - decoded_topic_data - ) - - event_args = dict(itertools.chain( - zip(log_topic_names, normalized_topic_data), - zip(log_data_names, normalized_log_data), - )) - - event_data = { - 'args': event_args, - 'event': event_abi['name'], - 'logIndex': log_entry['logIndex'], - 'transactionIndex': log_entry['transactionIndex'], - 'transactionHash': log_entry['transactionHash'], - 'address': log_entry['address'], - 'blockHash': log_entry['blockHash'], - 'blockNumber': log_entry['blockNumber'], - } - - return AttributeDict.recursive(event_data) - - -@to_tuple -def pop_singlets(seq: Sequence[Any]) -> Iterable[Any]: - yield from (i[0] if is_list_like(i) and len(i) == 1 else i for i in seq) - - -@curry -def remove_trailing_from_seq(seq: Sequence[Any], remove_value: Any=None) -> Sequence[Any]: - index = len(seq) - while index > 0 and seq[index - 1] == remove_value: - index -= 1 - return seq[:index] - - -normalize_topic_list = compose( - remove_trailing_from_seq(remove_value=None), - pop_singlets,) - - -def is_indexed(arg: Any) -> bool: - if isinstance(arg, TopicArgumentFilter) is True: - return True - return False - - -is_not_indexed = complement(is_indexed) - - -class EventFilterBuilder: - formatter = None - _fromBlock = None - _toBlock = None - _address = None - _immutable = False - - def __init__( - self, event_abi: ABIEvent, abi_codec: ABICodec, formatter: EventData=None - ) -> None: - self.event_abi = event_abi - self.abi_codec = abi_codec - self.formatter = formatter - self.event_topic = initialize_event_topics(self.event_abi) - self.args = AttributeDict( - _build_argument_filters_from_event_abi(event_abi, abi_codec)) - self._ordered_arg_names = tuple(arg['name'] for arg in event_abi['inputs']) - - @property - def fromBlock(self) -> BlockIdentifier: - return self._fromBlock - - @fromBlock.setter - def fromBlock(self, value: BlockIdentifier) -> None: - if self._fromBlock is None and not self._immutable: - self._fromBlock = value - else: - raise ValueError( - "fromBlock is already set to {0}. " - "Resetting filter parameters is not permitted".format(self._fromBlock)) - - @property - def toBlock(self) -> BlockIdentifier: - return self._toBlock - - @toBlock.setter - def toBlock(self, value: BlockIdentifier) -> None: - if self._toBlock is None and not self._immutable: - self._toBlock = value - else: - raise ValueError( - "toBlock is already set to {0}. " - "Resetting filter parameters is not permitted".format(self._toBlock)) - - @property - def address(self) -> ChecksumAddress: - return self._address - - @address.setter - def address(self, value: ChecksumAddress) -> None: - if self._address is None and not self._immutable: - self._address = value - else: - raise ValueError( - "address is already set to {0}. " - "Resetting filter parameters is not permitted".format(self.address)) - - @property - def ordered_args(self) -> Tuple[Any, ...]: - return tuple(map(self.args.__getitem__, self._ordered_arg_names)) - - @property # type: ignore - @to_tuple - def indexed_args(self) -> Tuple[Any, ...]: - return tuple(filter(is_indexed, self.ordered_args)) - - @property # type: ignore - @to_tuple - def data_args(self) -> Tuple[Any, ...]: - return tuple(filter(is_not_indexed, self.ordered_args)) - - @property - def topics(self) -> List[HexStr]: - arg_topics = tuple(arg.match_values for arg in self.indexed_args) - return normalize_topic_list(cons(to_hex(self.event_topic), arg_topics)) - - @property - def data_argument_values(self) -> Tuple[Any, ...]: - if self.data_args is not None: - return tuple(arg.match_values for arg in self.data_args) - else: - return (None,) - - @property - def filter_params(self) -> FilterParams: - params = { - "topics": self.topics, - "fromBlock": self.fromBlock, - "toBlock": self.toBlock, - "address": self.address - } - return valfilter(lambda x: x is not None, params) - - def deploy(self, w3: "Web3") -> "LogFilter": - if not isinstance(w3, web3.Web3): - raise ValueError("Invalid web3 argument: got: {0}".format(repr(w3))) - - for arg in self.args.values(): - arg._immutable = True - self._immutable = True - - log_filter = cast("LogFilter", w3.eth.filter(self.filter_params)) - log_filter.filter_params = self.filter_params - log_filter.set_data_filters(self.data_argument_values) - log_filter.builder = self - if self.formatter is not None: - log_filter.log_entry_formatter = self.formatter - return log_filter - - -def initialize_event_topics(event_abi: ABIEvent) -> Union[bytes, List[Any]]: - if event_abi['anonymous'] is False: - return event_abi_to_log_topic(event_abi) - else: - return list() - - -@to_dict -def _build_argument_filters_from_event_abi( - event_abi: ABIEvent, abi_codec: ABICodec -) -> Iterable[Tuple[str, 'BaseArgumentFilter']]: - for item in event_abi['inputs']: - key = item['name'] - value: 'BaseArgumentFilter' - if item['indexed'] is True: - value = TopicArgumentFilter(abi_codec=abi_codec, arg_type=item['type']) - else: - value = DataArgumentFilter(arg_type=item['type']) - yield key, value - - -# type ignored b/c mypy complains about curried fns -array_to_tuple = apply_formatter_if(is_list_like, tuple) # type: ignore - - -@to_tuple -def _normalize_match_values(match_values: Collection[Any]) -> Iterable[Any]: - for value in match_values: - yield array_to_tuple(value) - - -class BaseArgumentFilter(ABC): - _match_values: Tuple[Any, ...] = None - _immutable = False - - def __init__(self, arg_type: TypeStr) -> None: - self.arg_type = arg_type - - def match_single(self, value: Any) -> None: - if self._immutable: - raise ValueError("Setting values is forbidden after filter is deployed.") - if self._match_values is None: - self._match_values = _normalize_match_values((value,)) - else: - raise ValueError("An argument match value/s has already been set.") - - def match_any(self, *values: Collection[Any]) -> None: - if self._immutable: - raise ValueError("Setting values is forbidden after filter is deployed.") - if self._match_values is None: - self._match_values = _normalize_match_values(values) - else: - raise ValueError("An argument match value/s has already been set.") - - @property - @abstractmethod - def match_values(self) -> None: - pass - - -class DataArgumentFilter(BaseArgumentFilter): - # type ignore b/c conflict with BaseArgumentFilter.match_values type - @property - def match_values(self) -> Tuple[TypeStr, Tuple[Any, ...]]: # type: ignore - return (self.arg_type, self._match_values) - - -class TopicArgumentFilter(BaseArgumentFilter): - def __init__(self, arg_type: TypeStr, abi_codec: ABICodec) -> None: - self.abi_codec = abi_codec - self.arg_type = arg_type - - @to_tuple - def _get_match_values(self) -> Iterable[HexStr]: - yield from (self._encode(value) for value in self._match_values) - - # type ignore b/c conflict with BaseArgumentFilter.match_values type - @property - def match_values(self) -> Optional[Tuple[HexStr, ...]]: # type: ignore - if self._match_values is not None: - return self._get_match_values() - else: - return None - - def _encode(self, value: Any) -> HexStr: - if is_dynamic_sized_type(self.arg_type): - return to_hex(keccak(encode_single_packed(self.arg_type, value))) - else: - return to_hex(self.abi_codec.encode_single(self.arg_type, value)) - - -class EventLogErrorFlags(Enum): - Discard = 'discard' - Ignore = 'ignore' - Strict = 'strict' - Warn = 'warn' - - @classmethod - def flag_options(self) -> List[str]: - return [key.upper() for key in self.__members__.keys()] +from abc import ( + ABC, + abstractmethod, +) +import itertools + +from vns_abi import ( + decode_abi, + decode_single, + encode_single, + grammar, +) +from vns_typing import ( + TypeStr, +) +from vns_utils import ( + encode_hex, + event_abi_to_log_topic, + is_list_like, + keccak, + to_dict, + to_hex, + to_tuple, +) + +import web3 +from web3._utils.encoding import ( + encode_single_packed, + hexstr_if_str, + to_bytes, +) +from web3._utils.formatters import ( + apply_formatter_if, +) +from web3._utils.normalizers import ( + BASE_RETURN_NORMALIZERS, +) +from web3._utils.toolz import ( + complement, + compose, + cons, + curry, + valfilter, +) +from web3.datastructures import ( + AttributeDict, +) +from web3.exceptions import ( + MismatchedABI, +) + +from .abi import ( + exclude_indexed_event_inputs, + get_abi_input_names, + get_indexed_event_inputs, + map_abi_data, + normalize_event_input_types, +) + + +def construct_event_topic_set(event_abi, arguments=None): + if arguments is None: + arguments = {} + if isinstance(arguments, (list, tuple)): + if len(arguments) != len(event_abi['inputs']): + raise ValueError( + "When passing an argument list, the number of arguments must " + "match the event constructor." + ) + arguments = { + arg['name']: [arg_value] + for arg, arg_value + in zip(event_abi['inputs'], arguments) + } + + normalized_args = { + key: value if is_list_like(value) else [value] + for key, value in arguments.items() + } + + event_topic = encode_hex(event_abi_to_log_topic(event_abi)) + indexed_args = get_indexed_event_inputs(event_abi) + zipped_abi_and_args = [ + (arg, normalized_args.get(arg['name'], [None])) + for arg in indexed_args + ] + encoded_args = [ + [ + None if option is None else encode_hex(encode_single(arg['type'], option)) + for option in arg_options] + for arg, arg_options in zipped_abi_and_args + ] + + topics = list(normalize_topic_list([event_topic] + encoded_args)) + return topics + + +def construct_event_data_set(event_abi, arguments=None): + if arguments is None: + arguments = {} + if isinstance(arguments, (list, tuple)): + if len(arguments) != len(event_abi['inputs']): + raise ValueError( + "When passing an argument list, the number of arguments must " + "match the event constructor." + ) + arguments = { + arg['name']: [arg_value] + for arg, arg_value + in zip(event_abi['inputs'], arguments) + } + + normalized_args = { + key: value if is_list_like(value) else [value] + for key, value in arguments.items() + } + + non_indexed_args = exclude_indexed_event_inputs(event_abi) + zipped_abi_and_args = [ + (arg, normalized_args.get(arg['name'], [None])) + for arg in non_indexed_args + ] + encoded_args = [ + [ + None if option is None else encode_hex(encode_single(arg['type'], option)) + for option in arg_options] + for arg, arg_options in zipped_abi_and_args + ] + + data = [ + list(permutation) + if any(value is not None for value in permutation) + else [] + for permutation in itertools.product(*encoded_args) + ] + return data + + +def is_dynamic_sized_type(type_str: TypeStr) -> bool: + abi_type = grammar.parse(type_str) + return abi_type.is_dynamic + + +@to_tuple +def get_event_abi_types_for_decoding(event_inputs): + """ + Event logs use the `keccak(value)` for indexed inputs of type `bytes` or + `string`. Because of this we need to modify the types so that we can + decode the log entries using the correct types. + """ + for input_abi in event_inputs: + if input_abi['indexed'] and is_dynamic_sized_type(input_abi['type']): + yield 'bytes32' + else: + yield input_abi['type'] + + +@curry +def get_event_data(event_abi, log_entry): + """ + Given an event ABI and a log entry for that event, return the decoded + event data + """ + if event_abi['anonymous']: + log_topics = log_entry['topics'] + elif not log_entry['topics']: + raise MismatchedABI("Expected non-anonymous event to have 1 or more topics") + elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: + raise MismatchedABI("The event signature did not match the provided ABI") + else: + log_topics = log_entry['topics'][1:] + + log_topics_abi = get_indexed_event_inputs(event_abi) + log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) + log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) + log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) + + if len(log_topics) != len(log_topic_types): + raise ValueError("Expected {0} log topics. Got {1}".format( + len(log_topic_types), + len(log_topics), + )) + + log_data = hexstr_if_str(to_bytes, log_entry['data']) + log_data_abi = exclude_indexed_event_inputs(event_abi) + log_data_normalized_inputs = normalize_event_input_types(log_data_abi) + log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) + log_data_names = get_abi_input_names({'inputs': log_data_abi}) + + # sanity check that there are not name intersections between the topic + # names and the data argument names. + duplicate_names = set(log_topic_names).intersection(log_data_names) + if duplicate_names: + raise ValueError( + "Invalid Event ABI: The following argument names are duplicated " + "between event inputs: '{0}'".format(', '.join(duplicate_names)) + ) + + decoded_log_data = decode_abi(log_data_types, log_data) + normalized_log_data = map_abi_data( + BASE_RETURN_NORMALIZERS, + log_data_types, + decoded_log_data + ) + + decoded_topic_data = [ + decode_single(topic_type, topic_data) + for topic_type, topic_data + in zip(log_topic_types, log_topics) + ] + normalized_topic_data = map_abi_data( + BASE_RETURN_NORMALIZERS, + log_topic_types, + decoded_topic_data + ) + + event_args = dict(itertools.chain( + zip(log_topic_names, normalized_topic_data), + zip(log_data_names, normalized_log_data), + )) + + event_data = { + 'args': event_args, + 'event': event_abi['name'], + 'logIndex': log_entry['logIndex'], + 'transactionIndex': log_entry['transactionIndex'], + 'transactionHash': log_entry['transactionHash'], + 'address': log_entry['address'], + 'blockHash': log_entry['blockHash'], + 'blockNumber': log_entry['blockNumber'], + } + + return AttributeDict.recursive(event_data) + + +@to_tuple +def pop_singlets(seq): + yield from (i[0] if is_list_like(i) and len(i) == 1 else i for i in seq) + + +@curry +def remove_trailing_from_seq(seq, remove_value=None): + index = len(seq) + while index > 0 and seq[index - 1] == remove_value: + index -= 1 + return seq[:index] + + +normalize_topic_list = compose( + remove_trailing_from_seq(remove_value=None), + pop_singlets,) + + +def is_indexed(arg): + if isinstance(arg, TopicArgumentFilter) is True: + return True + return False + + +is_not_indexed = complement(is_indexed) + + +class EventFilterBuilder: + formatter = None + _fromBlock = None + _toBlock = None + _address = None + _immutable = False + + def __init__(self, event_abi, formatter=None): + self.event_abi = event_abi + self.formatter = formatter + self.event_topic = initialize_event_topics(self.event_abi) + self.args = AttributeDict( + _build_argument_filters_from_event_abi(event_abi)) + self._ordered_arg_names = tuple(arg['name'] for arg in event_abi['inputs']) + + @property + def fromBlock(self): + return self._fromBlock + + @fromBlock.setter + def fromBlock(self, value): + if self._fromBlock is None and not self._immutable: + self._fromBlock = value + else: + raise ValueError( + "fromBlock is already set to {0}. " + "Resetting filter parameters is not permitted".format(self._fromBlock)) + + @property + def toBlock(self): + return self._toBlock + + @toBlock.setter + def toBlock(self, value): + if self._toBlock is None and not self._immutable: + self._toBlock = value + else: + raise ValueError( + "toBlock is already set to {0}. " + "Resetting filter parameters is not permitted".format(self._toBlock)) + + @property + def address(self): + return self._address + + @address.setter + def address(self, value): + if self._address is None and not self._immutable: + self._address = value + else: + raise ValueError( + "address is already set to {0}. " + "Resetting filter parameters is not permitted".format(self.address)) + + @property + def ordered_args(self): + return tuple(map(self.args.__getitem__, self._ordered_arg_names)) + + @property + @to_tuple + def indexed_args(self): + return tuple(filter(is_indexed, self.ordered_args)) + + @property + @to_tuple + def data_args(self): + return tuple(filter(is_not_indexed, self.ordered_args)) + + @property + def topics(self): + arg_topics = tuple(arg.match_values for arg in self.indexed_args) + return normalize_topic_list(cons(to_hex(self.event_topic), arg_topics)) + + @property + def data_argument_values(self): + if self.data_args is not None: + return tuple(arg.match_values for arg in self.data_args) + else: + return (None,) + + @property + def filter_params(self): + params = { + "topics": self.topics, + "fromBlock": self.fromBlock, + "toBlock": self.toBlock, + "address": self.address + } + return valfilter(lambda x: x is not None, params) + + def deploy(self, w3): + if not isinstance(w3, web3.Web3): + raise ValueError("Invalid web3 argument: got: {0}".format(repr(w3))) + + for arg in self.args.values(): + arg._immutable = True + self._immutable = True + + log_filter = w3.vns.filter(self.filter_params) + log_filter.filter_params = self.filter_params + log_filter.set_data_filters(self.data_argument_values) + log_filter.builder = self + if self.formatter is not None: + log_filter.log_entry_formatter = self.formatter + return log_filter + + +def initialize_event_topics(event_abi): + if event_abi['anonymous'] is False: + return event_abi_to_log_topic(event_abi) + else: + return list() + + +@to_dict +def _build_argument_filters_from_event_abi(event_abi): + for item in event_abi['inputs']: + key = item['name'] + if item['indexed'] is True: + value = TopicArgumentFilter(arg_type=item['type']) + else: + value = DataArgumentFilter(arg_type=item['type']) + yield key, value + + +array_to_tuple = apply_formatter_if(is_list_like, tuple) + + +@to_tuple +def _normalize_match_values(match_values): + for value in match_values: + yield array_to_tuple(value) + + +class BaseArgumentFilter(ABC): + _match_values = None + _immutable = False + + def __init__(self, arg_type): + self.arg_type = arg_type + + def match_single(self, value): + if self._immutable: + raise ValueError("Setting values is forbidden after filter is deployed.") + if self._match_values is None: + self._match_values = _normalize_match_values((value,)) + else: + raise ValueError("An argument match value/s has already been set.") + + def match_any(self, *values): + if self._immutable: + raise ValueError("Setting values is forbidden after filter is deployed.") + if self._match_values is None: + self._match_values = _normalize_match_values(values) + else: + raise ValueError("An argument match value/s has already been set.") + + @property + @abstractmethod + def match_values(self): + pass + + +class DataArgumentFilter(BaseArgumentFilter): + @property + def match_values(self): + return (self.arg_type, self._match_values) + + +class TopicArgumentFilter(BaseArgumentFilter): + @to_tuple + def _get_match_values(self): + yield from (self._encode(value) for value in self._match_values) + + @property + def match_values(self): + if self._match_values is not None: + return self._get_match_values() + else: + return None + + def _encode(self, value): + if is_dynamic_sized_type(self.arg_type): + return to_hex(keccak(encode_single_packed(self.arg_type, value))) + else: + return to_hex(encode_single(self.arg_type, value)) diff --git a/web3/_utils/filters.py b/web3/_utils/filters.py index e2989c119f..b06b85d90e 100644 --- a/web3/_utils/filters.py +++ b/web3/_utils/filters.py @@ -1,269 +1,262 @@ -from eth_abi.grammar import ( - parse as parse_type_string, -) -from eth_utils import ( - is_list_like, - is_string, - is_text, -) -from eth_utils.curried import ( - apply_formatter_if, -) -from eth_utils.toolz import ( - complement, - curry, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.events import ( - EventFilterBuilder, -) -from web3._utils.threads import ( - TimerClass, -) -from web3._utils.validation import ( - validate_address, -) -from web3.types import ( - FilterParams, -) - -from .events import ( - construct_event_data_set, - construct_event_topic_set, -) - - -def construct_event_filter_params(event_abi, - abi_codec, - contract_address=None, - argument_filters=None, - topics=None, - fromBlock=None, - toBlock=None, - address=None): - filter_params = {} - topic_set = construct_event_topic_set(event_abi, abi_codec, argument_filters) - - if topics is not None: - if len(topic_set) > 1: - raise TypeError( - "Merging the topics argument with topics generated " - "from argument_filters is not supported.") - topic_set = topics - - if len(topic_set) == 1 and is_list_like(topic_set[0]): - filter_params['topics'] = topic_set[0] - else: - filter_params['topics'] = topic_set - - if address and contract_address: - if is_list_like(address): - filter_params['address'] = address + [contract_address] - elif is_string(address): - filter_params['address'] = [address, contract_address] - else: - raise ValueError( - "Unsupported type for `address` parameter: {0}".format(type(address)) - ) - elif address: - filter_params['address'] = address - elif contract_address: - filter_params['address'] = contract_address - - if 'address' not in filter_params: - pass - elif is_list_like(filter_params['address']): - for addr in filter_params['address']: - validate_address(addr) - else: - validate_address(filter_params['address']) - - if fromBlock is not None: - filter_params['fromBlock'] = fromBlock - - if toBlock is not None: - filter_params['toBlock'] = toBlock - - data_filters_set = construct_event_data_set(event_abi, abi_codec, argument_filters) - - return data_filters_set, filter_params - - -class Filter: - callbacks = None - stopped = False - poll_interval = None - filter_id = None - - def __init__(self, web3, filter_id): - self.web3 = web3 - self.filter_id = filter_id - self.callbacks = [] - super().__init__() - - def __str__(self): - return "Filter for {0}".format(self.filter_id) - - def format_entry(self, entry): - """ - Hook for subclasses to change the format of the value that is passed - into the callback functions. - """ - return entry - - def is_valid_entry(self, entry): - """ - Hook for subclasses to implement additional filtering layers. - """ - return True - - def _filter_valid_entries(self, entries): - return filter(self.is_valid_entry, entries) - - def get_new_entries(self): - log_entries = self._filter_valid_entries(self.web3.eth.getFilterChanges(self.filter_id)) - return self._format_log_entries(log_entries) - - def get_all_entries(self): - log_entries = self._filter_valid_entries(self.web3.eth.getFilterLogs(self.filter_id)) - return self._format_log_entries(log_entries) - - def _format_log_entries(self, log_entries=None): - if log_entries is None: - return [] - - formatted_log_entries = [ - self.format_entry(log_entry) for log_entry in log_entries - ] - return formatted_log_entries - - -class BlockFilter(Filter): - pass - - -class TransactionFilter(Filter): - pass - - -class LogFilter(Filter): - data_filter_set = None - data_filter_set_regex = None - log_entry_formatter = None - filter_params: FilterParams = None - builder: EventFilterBuilder = None - - def __init__(self, *args, **kwargs): - self.log_entry_formatter = kwargs.pop( - 'log_entry_formatter', - self.log_entry_formatter, - ) - if 'data_filter_set' in kwargs: - self.set_data_filters(kwargs.pop('data_filter_set')) - super().__init__(*args, **kwargs) - - def format_entry(self, entry): - if self.log_entry_formatter: - return self.log_entry_formatter(entry) - return entry - - def set_data_filters(self, data_filter_set): - """Sets the data filters (non indexed argument filters) - - Expects a set of tuples with the type and value, e.g.: - (('uint256', [12345, 54321]), ('string', ('a-single-string',))) - """ - self.data_filter_set = data_filter_set - if any(data_filter_set): - self.data_filter_set_function = match_fn(self.web3, data_filter_set) - - def is_valid_entry(self, entry): - if not self.data_filter_set: - return True - return bool(self.data_filter_set_function(entry['data'])) - - -def decode_utf8_bytes(value): - return value.decode("utf-8") - - -not_text = complement(is_text) -normalize_to_text = apply_formatter_if(not_text, decode_utf8_bytes) - - -def normalize_data_values(type_string, data_value): - """Decodes utf-8 bytes to strings for abi string values. - - eth-abi v1 returns utf-8 bytes for string values. - This can be removed once eth-abi v2 is required. - """ - _type = parse_type_string(type_string) - if _type.base == "string": - if _type.arrlist is not None: - return tuple((normalize_to_text(value) for value in data_value)) - else: - return normalize_to_text(data_value) - return data_value - - -@curry -def match_fn(w3, match_values_and_abi, data): - """Match function used for filtering non-indexed event arguments. - - Values provided through the match_values_and_abi parameter are - compared to the abi decoded log data. - """ - abi_types, all_match_values = zip(*match_values_and_abi) - - decoded_values = w3.codec.decode_abi(abi_types, HexBytes(data)) - for data_value, match_values, abi_type in zip(decoded_values, all_match_values, abi_types): - if match_values is None: - continue - normalized_data = normalize_data_values(abi_type, data_value) - for value in match_values: - if not w3.is_encodable(abi_type, value): - raise ValueError( - f"Value {value} is of the wrong abi type. " - f"Expected {abi_type} typed value." - ) - if value == normalized_data: - break - else: - return False - - return True - - -class ShhFilter(Filter): - def __init__(self, *args, **kwargs): - self.poll_interval = kwargs.pop( - 'poll_interval', - self.poll_interval, - ) - super().__init__(*args, **kwargs) - - def get_new_entries(self): - all_messages = self.web3.manager.request_blocking( - "shh_getFilterMessages", - [self.filter_id] - ) - log_entries = self._filter_valid_entries(all_messages) - return self._format_log_entries(log_entries) - - def get_all_entries(self): - raise NotImplementedError() - - def watch(self, callback): - def callback_wrapper(): - entries = self.get_new_entries() - - if entries: - callback(entries) - - timer = TimerClass(self.poll_interval, callback_wrapper) - timer.daemon = True - timer.start() - return timer +from vns_abi import ( + decode_abi, + is_encodable, +) +from vns_abi.grammar import ( + parse as parse_type_string, +) +from vns_utils import ( + is_list_like, + is_string, + is_text, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.formatters import ( + apply_formatter_if, +) +from web3._utils.threads import ( + TimerClass, +) +from web3._utils.toolz import ( + complement, + curry, +) +from web3._utils.validation import ( + validate_address, +) + +from .events import ( + construct_event_data_set, + construct_event_topic_set, +) + + +def construct_event_filter_params(event_abi, + contract_address=None, + argument_filters=None, + topics=None, + fromBlock=None, + toBlock=None, + address=None): + filter_params = {} + topic_set = construct_event_topic_set(event_abi, argument_filters) + + if topics is not None: + if len(topic_set) > 1: + raise TypeError( + "Merging the topics argument with topics generated " + "from argument_filters is not supported.") + topic_set = topics + + if len(topic_set) == 1 and is_list_like(topic_set[0]): + filter_params['topics'] = topic_set[0] + else: + filter_params['topics'] = topic_set + + if address and contract_address: + if is_list_like(address): + filter_params['address'] = address + [contract_address] + elif is_string(address): + filter_params['address'] = [address, contract_address] + else: + raise ValueError( + "Unsupported type for `address` parameter: {0}".format(type(address)) + ) + elif address: + filter_params['address'] = address + elif contract_address: + filter_params['address'] = contract_address + + if 'address' not in filter_params: + pass + elif is_list_like(filter_params['address']): + for addr in filter_params['address']: + validate_address(addr) + else: + validate_address(filter_params['address']) + + if fromBlock is not None: + filter_params['fromBlock'] = fromBlock + + if toBlock is not None: + filter_params['toBlock'] = toBlock + + data_filters_set = construct_event_data_set(event_abi, argument_filters) + + return data_filters_set, filter_params + + +class Filter: + callbacks = None + stopped = False + poll_interval = None + filter_id = None + + def __init__(self, web3, filter_id): + self.web3 = web3 + self.filter_id = filter_id + self.callbacks = [] + super().__init__() + + def __str__(self): + return "Filter for {0}".format(self.filter_id) + + def format_entry(self, entry): + """ + Hook for subclasses to change the format of the value that is passed + into the callback functions. + """ + return entry + + def is_valid_entry(self, entry): + """ + Hook for subclasses to implement additional filtering layers. + """ + return True + + def _filter_valid_entries(self, entries): + return filter(self.is_valid_entry, entries) + + def get_new_entries(self): + log_entries = self._filter_valid_entries(self.web3.vns.getFilterChanges(self.filter_id)) + return self._format_log_entries(log_entries) + + def get_all_entries(self): + log_entries = self._filter_valid_entries(self.web3.vns.getFilterLogs(self.filter_id)) + return self._format_log_entries(log_entries) + + def _format_log_entries(self, log_entries=None): + if log_entries is None: + return [] + + formatted_log_entries = [ + self.format_entry(log_entry) for log_entry in log_entries + ] + return formatted_log_entries + + +class BlockFilter(Filter): + pass + + +class TransactionFilter(Filter): + pass + + +class LogFilter(Filter): + data_filter_set = None + data_filter_set_regex = None + log_entry_formatter = None + + def __init__(self, *args, **kwargs): + self.log_entry_formatter = kwargs.pop( + 'log_entry_formatter', + self.log_entry_formatter, + ) + if 'data_filter_set' in kwargs: + self.set_data_filters(kwargs.pop('data_filter_set')) + super().__init__(*args, **kwargs) + + def format_entry(self, entry): + if self.log_entry_formatter: + return self.log_entry_formatter(entry) + return entry + + def set_data_filters(self, data_filter_set): + """Sets the data filters (non indexed argument filters) + + Expects a set of tuples with the type and value, e.g.: + (('uint256', [12345, 54321]), ('string', ('a-single-string',))) + """ + self.data_filter_set = data_filter_set + if any(data_filter_set): + self.data_filter_set_function = match_fn(data_filter_set) + + def is_valid_entry(self, entry): + if not self.data_filter_set: + return True + return bool(self.data_filter_set_function(entry['data'])) + + +def decode_utf8_bytes(value): + return value.decode("utf-8") + + +not_text = complement(is_text) +normalize_to_text = apply_formatter_if(not_text, decode_utf8_bytes) + + +def normalize_data_values(type_string, data_value): + """Decodes utf-8 bytes to strings for abi string values. + + vns-abi v1 returns utf-8 bytes for string values. + This can be removed once vns-abi v2 is required. + """ + _type = parse_type_string(type_string) + if _type.base == "string": + if _type.arrlist is not None: + return tuple((normalize_to_text(value) for value in data_value)) + else: + return normalize_to_text(data_value) + return data_value + + +@curry +def match_fn(match_values_and_abi, data): + """Match function used for filtering non-indexed event arguments. + + Values provided through the match_values_and_abi parameter are + compared to the abi decoded log data. + """ + abi_types, all_match_values = zip(*match_values_and_abi) + decoded_values = decode_abi(abi_types, HexBytes(data)) + for data_value, match_values, abi_type in zip(decoded_values, all_match_values, abi_types): + if match_values is None: + continue + normalized_data = normalize_data_values(abi_type, data_value) + for value in match_values: + if not is_encodable(abi_type, value): + raise ValueError( + "Value {0} is of the wrong abi type. " + "Expected {1} typed value.".format(value, abi_type)) + if value == normalized_data: + break + else: + return False + + return True + + +class ShhFilter(Filter): + def __init__(self, *args, **kwargs): + self.poll_interval = kwargs.pop( + 'poll_interval', + self.poll_interval, + ) + super().__init__(*args, **kwargs) + + def get_new_entries(self): + all_messages = self.web3.manager.request_blocking( + "shh_getFilterMessages", + [self.filter_id] + ) + log_entries = self._filter_valid_entries(all_messages) + return self._format_log_entries(log_entries) + + def get_all_entries(self): + raise NotImplementedError() + + def watch(self, callback): + def callback_wrapper(): + entries = self.get_new_entries() + + if entries: + callback(entries) + + timer = TimerClass(self.poll_interval, callback_wrapper) + timer.daemon = True + timer.start() + return timer diff --git a/web3/_utils/formatters.py b/web3/_utils/formatters.py index 6dc365b02d..46f9ece29e 100644 --- a/web3/_utils/formatters.py +++ b/web3/_utils/formatters.py @@ -1,116 +1,158 @@ -from collections.abc import ( - Iterable, - Mapping, -) - -from eth_utils import ( - is_dict, - is_list_like, - is_string, - to_dict, - to_list, -) -from eth_utils.curried import ( - apply_formatter_at_index, -) -from eth_utils.toolz import ( - compose, - curry, - dissoc, -) - -from web3._utils.decorators import ( - reject_recursive_repeats, -) - - -def hex_to_integer(value): - return int(value, 16) - - -integer_to_hex = hex - - -def apply_formatters_to_args(*formatters): - return compose(*( - apply_formatter_at_index(formatter, index) - for index, formatter - in enumerate(formatters) - )) - - -@curry -@to_list -def apply_formatter_to_array(formatter, value): - for item in value: - yield formatter(item) - - -def map_collection(func, collection): - """ - Apply func to each element of a collection, or value of a dictionary. - If the value is not a collection, return it unmodified - """ - datatype = type(collection) - if isinstance(collection, Mapping): - return datatype((key, func(val)) for key, val in collection.items()) - if is_string(collection): - return collection - elif isinstance(collection, Iterable): - return datatype(map(func, collection)) - else: - return collection - - -@reject_recursive_repeats -def recursive_map(func, data): - """ - Apply func to data, and any collection items inside data (using map_collection). - Define func so that it only applies to the type of value that you want it to apply to. - """ - def recurse(item): - return recursive_map(func, item) - items_mapped = map_collection(recurse, data) - return func(items_mapped) - - -def static_return(value): - def inner(*args, **kwargs): - return value - return inner - - -def static_result(value): - def inner(*args, **kwargs): - return {'result': value} - return inner - - -@curry -@to_dict -def apply_key_map(key_mappings, value): - for key, item in value.items(): - if key in key_mappings: - yield key_mappings[key], item - else: - yield key, item - - -def is_array_of_strings(value): - if not is_list_like(value): - return False - return all((is_string(item) for item in value)) - - -def is_array_of_dicts(value): - if not is_list_like(value): - return False - return all((is_dict(item) for item in value)) - - -@curry -def remove_key_if(key, remove_if, input_dict): - if key in input_dict and remove_if(input_dict): - return dissoc(input_dict, key) - else: - return input_dict +from collections.abc import ( + Iterable, + Mapping, +) + +from vns_utils import ( + is_dict, + is_list_like, + is_string, + to_dict, + to_list, +) + +from web3._utils.decorators import ( + reject_recursive_repeats, +) +from web3._utils.toolz import ( + compose, + curry, + dissoc, +) + + +def hex_to_integer(value): + return int(value, 16) + + +integer_to_hex = hex + + +@curry +@to_list +def apply_formatter_at_index(formatter, at_index, value): + if at_index + 1 > len(value): + raise IndexError( + "Not enough values in iterable to apply formatter. Got: {0}. " + "Need: {1}".format(len(value), at_index + 1) + ) + for index, item in enumerate(value): + if index == at_index: + yield formatter(item) + else: + yield item + + +def apply_formatters_to_args(*formatters): + return compose(*( + apply_formatter_at_index(formatter, index) + for index, formatter + in enumerate(formatters) + )) + + +@curry +def apply_formatter_if(condition, formatter, value): + if condition(value): + return formatter(value) + else: + return value + + +@curry +@to_dict +def apply_formatters_to_dict(formatters, value): + for key, item in value.items(): + if key in formatters: + try: + yield key, formatters[key](item) + except (TypeError, ValueError) as exc: + raise type(exc)("Could not format value %r as field %r" % (item, key)) from exc + else: + yield key, item + + +@curry +@to_list +def apply_formatter_to_array(formatter, value): + for item in value: + yield formatter(item) + + +@curry +def apply_one_of_formatters(formatter_condition_pairs, value): + for formatter, condition in formatter_condition_pairs: + if condition(value): + return formatter(value) + else: + raise ValueError("The provided value did not satisfy any of the formatter conditions") + + +def map_collection(func, collection): + """ + Apply func to each element of a collection, or value of a dictionary. + If the value is not a collection, return it unmodified + """ + datatype = type(collection) + if isinstance(collection, Mapping): + return datatype((key, func(val)) for key, val in collection.items()) + if is_string(collection): + return collection + elif isinstance(collection, Iterable): + return datatype(map(func, collection)) + else: + return collection + + +@reject_recursive_repeats +def recursive_map(func, data): + """ + Apply func to data, and any collection items inside data (using map_collection). + Define func so that it only applies to the type of value that you want it to apply to. + """ + def recurse(item): + return recursive_map(func, item) + items_mapped = map_collection(recurse, data) + return func(items_mapped) + + +def static_return(value): + def inner(*args, **kwargs): + return value + return inner + + +def static_result(value): + def inner(*args, **kwargs): + return {'result': value} + return inner + + +@curry +@to_dict +def apply_key_map(key_mappings, value): + for key, item in value.items(): + if key in key_mappings: + yield key_mappings[key], item + else: + yield key, item + + +def is_array_of_strings(value): + if not is_list_like(value): + return False + return all((is_string(item) for item in value)) + + +def is_array_of_dicts(value): + if not is_list_like(value): + return False + return all((is_dict(item) for item in value)) + + +@curry +def remove_key_if(key, remove_if, input_dict): + if key in input_dict and remove_if(input_dict): + return dissoc(input_dict, key) + else: + return input_dict diff --git a/web3/_utils/function_identifiers.py b/web3/_utils/function_identifiers.py index 29cb738774..ae3414d5ca 100644 --- a/web3/_utils/function_identifiers.py +++ b/web3/_utils/function_identifiers.py @@ -1,2 +1,2 @@ -class FallbackFn: - pass +class FallbackFn: + pass diff --git a/web3/_utils/http.py b/web3/_utils/http.py index 62a253ff93..be16b8b04b 100644 --- a/web3/_utils/http.py +++ b/web3/_utils/http.py @@ -1,8 +1,8 @@ -def construct_user_agent(class_name): - from web3 import __version__ as web3_version - - user_agent = 'Web3.py/{version}/{class_name}'.format( - version=web3_version, - class_name=class_name, - ) - return user_agent +def construct_user_agent(class_name): + from web3 import __version__ as web3_version + + user_agent = 'Web3.py/{version}/{class_name}'.format( + version=web3_version, + class_name=class_name, + ) + return user_agent diff --git a/web3/_utils/hypothesis.py b/web3/_utils/hypothesis.py index 88ee8d1f50..8fe4b532d2 100644 --- a/web3/_utils/hypothesis.py +++ b/web3/_utils/hypothesis.py @@ -1,7 +1,7 @@ -from hypothesis import ( - strategies as st, -) - - -def hexstr_strategy(): - return st.from_regex(r'\A(0[xX])?[0-9a-fA-F]*\Z') +from hypothesis import ( + strategies as st, +) + + +def hexstr_strategy(): + return st.from_regex(r'\A(0[xX])?[0-9a-fA-F]*\Z') diff --git a/web3/_utils/math.py b/web3/_utils/math.py index 130f67a03f..92bfe549da 100644 --- a/web3/_utils/math.py +++ b/web3/_utils/math.py @@ -1,32 +1,32 @@ -from web3.exceptions import ( - InsufficientData, -) - - -def percentile(values=None, percentile=None): - """Calculates a simplified weighted average percentile - """ - if values in [None, tuple(), []] or len(values) < 1: - raise InsufficientData( - "Expected a sequence of at least 1 integers, got {0!r}".format(values)) - if percentile is None: - raise ValueError("Expected a percentile choice, got {0}".format(percentile)) - - sorted_values = sorted(values) - - rank = len(values) * percentile / 100 - if rank > 0: - index = rank - 1 - if index < 0: - return sorted_values[0] - else: - index = rank - - if index % 1 == 0: - return sorted_values[int(index)] - else: - fractional = index % 1 - integer = int(index - fractional) - lower = sorted_values[integer] - higher = sorted_values[integer + 1] - return lower + fractional * (higher - lower) +from web3.exceptions import ( + InsufficientData, +) + + +def percentile(values=None, percentile=None): + """Calculates a simplified weighted average percentile + """ + if values in [None, tuple(), []] or len(values) < 1: + raise InsufficientData( + "Expected a sequence of at least 1 integers, got {0!r}".format(values)) + if percentile is None: + raise ValueError("Expected a percentile choice, got {0}".format(percentile)) + + sorted_values = sorted(values) + + rank = len(values) * percentile / 100 + if rank > 0: + index = rank - 1 + if index < 0: + return sorted_values[0] + else: + index = rank + + if index % 1 == 0: + return sorted_values[int(index)] + else: + fractional = index % 1 + integer = int(index - fractional) + lower = sorted_values[integer] + higher = sorted_values[integer + 1] + return lower + fractional * (higher - lower) diff --git a/web3/_utils/method_formatters.py b/web3/_utils/method_formatters.py deleted file mode 100644 index 970a69d15b..0000000000 --- a/web3/_utils/method_formatters.py +++ /dev/null @@ -1,479 +0,0 @@ -import codecs -import operator -from typing import ( - Any, - Callable, - Dict, -) - -from eth_utils.curried import ( - apply_formatter_at_index, - apply_formatter_if, - apply_formatter_to_array, - apply_formatters_to_dict, - apply_formatters_to_sequence, - apply_one_of_formatters, - is_address, - is_bytes, - is_dict, - is_integer, - is_null, - is_string, - remove_0x_prefix, - text_if_str, - to_checksum_address, - to_list, - to_tuple, -) -from eth_utils.toolz import ( - complement, - compose, - curried, - curry, - partial, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.abi import ( - is_length, -) -from web3._utils.encoding import ( - hexstr_if_str, - to_hex, -) -from web3._utils.formatters import ( - hex_to_integer, - integer_to_hex, - is_array_of_dicts, - is_array_of_strings, - remove_key_if, -) -from web3._utils.normalizers import ( - abi_address_to_hex, - abi_bytes_to_hex, - abi_int_to_hex, - abi_string_to_hex, -) -from web3._utils.rpc_abi import ( - RPC_ABIS, - abi_request_formatters, -) -from web3.datastructures import ( - AttributeDict, -) -from web3.types import ( - RPCEndpoint, -) - - -def bytes_to_ascii(value): - return codecs.decode(value, 'ascii') - - -to_ascii_if_bytes = apply_formatter_if(is_bytes, bytes_to_ascii) -to_integer_if_hex = apply_formatter_if(is_string, hex_to_integer) -block_number_formatter = apply_formatter_if(is_integer, integer_to_hex) - - -is_false = partial(operator.is_, False) - -is_not_false = complement(is_false) -is_not_null = complement(is_null) - - -@curry -def to_hexbytes(num_bytes, val, variable_length=False): - if isinstance(val, (str, int, bytes)): - result = HexBytes(val) - else: - raise TypeError("Cannot convert %r to HexBytes" % val) - - extra_bytes = len(result) - num_bytes - if extra_bytes == 0 or (variable_length and extra_bytes < 0): - return result - elif all(byte == 0 for byte in result[:extra_bytes]): - return HexBytes(result[extra_bytes:]) - else: - raise ValueError( - "The value %r is %d bytes, but should be %d" % ( - result, len(result), num_bytes - ) - ) - - -def is_attrdict(val): - return isinstance(val, AttributeDict) - - -not_attrdict = complement(is_attrdict) - - -TRANSACTION_FORMATTERS = { - 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), - 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), - 'nonce': to_integer_if_hex, - 'gas': to_integer_if_hex, - 'gasPrice': to_integer_if_hex, - 'value': to_integer_if_hex, - 'from': to_checksum_address, - 'publicKey': apply_formatter_if(is_not_null, to_hexbytes(64)), - 'r': to_hexbytes(32, variable_length=True), - 'raw': HexBytes, - 's': to_hexbytes(32, variable_length=True), - 'to': apply_formatter_if(is_address, to_checksum_address), - 'hash': to_hexbytes(32), - 'v': apply_formatter_if(is_not_null, to_integer_if_hex), - 'standardV': apply_formatter_if(is_not_null, to_integer_if_hex), -} - - -transaction_formatter = apply_formatters_to_dict(TRANSACTION_FORMATTERS) - - -WHISPER_LOG_FORMATTERS = { - 'sig': to_hexbytes(130), - 'topic': to_hexbytes(8), - 'payload': HexBytes, - 'padding': apply_formatter_if(is_not_null, HexBytes), - 'hash': to_hexbytes(64), - 'recipientPublicKey': apply_formatter_if(is_not_null, to_hexbytes(130)), -} - - -whisper_log_formatter = apply_formatters_to_dict(WHISPER_LOG_FORMATTERS) - - -def apply_list_to_array_formatter(formatter): - return to_list(apply_formatter_to_array(formatter)) - - -LOG_ENTRY_FORMATTERS = { - 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), - 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), - 'transactionHash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'logIndex': to_integer_if_hex, - 'address': to_checksum_address, - 'topics': apply_list_to_array_formatter(to_hexbytes(32)), - 'data': to_ascii_if_bytes, -} - - -log_entry_formatter = apply_formatters_to_dict(LOG_ENTRY_FORMATTERS) - - -RECEIPT_FORMATTERS = { - 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), - 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), - 'transactionHash': to_hexbytes(32), - 'cumulativeGasUsed': to_integer_if_hex, - 'status': to_integer_if_hex, - 'gasUsed': to_integer_if_hex, - 'contractAddress': apply_formatter_if(is_not_null, to_checksum_address), - 'logs': apply_list_to_array_formatter(log_entry_formatter), - 'logsBloom': to_hexbytes(256), -} - - -receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS) - -BLOCK_FORMATTERS = { - 'extraData': to_hexbytes(32, variable_length=True), - 'gasLimit': to_integer_if_hex, - 'gasUsed': to_integer_if_hex, - 'size': to_integer_if_hex, - 'timestamp': to_integer_if_hex, - 'hash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'logsBloom': apply_formatter_if(is_not_null, to_hexbytes(256)), - 'miner': apply_formatter_if(is_not_null, to_checksum_address), - 'mixHash': to_hexbytes(32), - 'nonce': apply_formatter_if(is_not_null, to_hexbytes(8, variable_length=True)), - 'number': apply_formatter_if(is_not_null, to_integer_if_hex), - 'parentHash': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'sha3Uncles': apply_formatter_if(is_not_null, to_hexbytes(32)), - 'uncles': apply_list_to_array_formatter(to_hexbytes(32)), - 'difficulty': to_integer_if_hex, - 'receiptsRoot': to_hexbytes(32), - 'stateRoot': to_hexbytes(32), - 'totalDifficulty': to_integer_if_hex, - 'transactions': apply_one_of_formatters(( - (is_array_of_dicts, apply_list_to_array_formatter(transaction_formatter)), - (is_array_of_strings, apply_list_to_array_formatter(to_hexbytes(32))), - )), - 'transactionsRoot': to_hexbytes(32), -} - - -block_formatter = apply_formatters_to_dict(BLOCK_FORMATTERS) - - -SYNCING_FORMATTERS = { - 'startingBlock': to_integer_if_hex, - 'currentBlock': to_integer_if_hex, - 'highestBlock': to_integer_if_hex, - 'knownStates': to_integer_if_hex, - 'pulledStates': to_integer_if_hex, -} - - -syncing_formatter = apply_formatters_to_dict(SYNCING_FORMATTERS) - - -TRANSACTION_POOL_CONTENT_FORMATTERS = { - 'pending': compose( - curried.keymap(to_ascii_if_bytes), - curried.valmap(transaction_formatter), - ), - 'queued': compose( - curried.keymap(to_ascii_if_bytes), - curried.valmap(transaction_formatter), - ), -} - - -transaction_pool_content_formatter = apply_formatters_to_dict( - TRANSACTION_POOL_CONTENT_FORMATTERS -) - - -TRANSACTION_POOL_INSPECT_FORMATTERS = { - 'pending': curried.keymap(to_ascii_if_bytes), - 'queued': curried.keymap(to_ascii_if_bytes), -} - - -transaction_pool_inspect_formatter = apply_formatters_to_dict( - TRANSACTION_POOL_INSPECT_FORMATTERS -) - -STORAGE_PROOF_FORMATTERS = { - 'key': HexBytes, - 'value': HexBytes, - 'proof': apply_list_to_array_formatter(HexBytes), -} - -ACCOUNT_PROOF_FORMATTERS = { - 'address': to_checksum_address, - 'accountProof': apply_list_to_array_formatter(HexBytes), - 'balance': to_integer_if_hex, - 'codeHash': to_hexbytes(32), - 'nonce': to_integer_if_hex, - 'storageHash': to_hexbytes(32), - 'storageProof': apply_list_to_array_formatter( - apply_formatters_to_dict(STORAGE_PROOF_FORMATTERS) - ) -} - -proof_formatter = apply_formatters_to_dict(ACCOUNT_PROOF_FORMATTERS) - -FILTER_PARAMS_FORMATTERS = { - 'fromBlock': apply_formatter_if(is_integer, integer_to_hex), - 'toBlock': apply_formatter_if(is_integer, integer_to_hex), -} - - -filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS) - - -filter_result_formatter = apply_one_of_formatters(( - (is_array_of_dicts, apply_list_to_array_formatter(log_entry_formatter)), - (is_array_of_strings, apply_list_to_array_formatter(to_hexbytes(32))), -)) - - -transaction_param_formatter = compose( - remove_key_if('to', lambda txn: txn['to'] in {'', b'', None}), -) - - -estimate_gas_without_block_id = apply_formatter_at_index(transaction_param_formatter, 0) -estimate_gas_with_block_id = apply_formatters_to_sequence([ - transaction_param_formatter, - block_number_formatter, -]) - -SIGNED_TX_FORMATTER = { - 'raw': HexBytes, - 'tx': transaction_formatter, -} - -signed_tx_formatter = apply_formatters_to_dict(SIGNED_TX_FORMATTER) - -FILTER_PARAM_NORMALIZERS = apply_formatters_to_dict({ - 'address': apply_formatter_if(is_string, lambda x: [x]) -}) - -PYTHONIC_REQUEST_FORMATTERS: Dict[RPCEndpoint, Callable[..., Any]] = { - # Eth - 'eth_getBalance': apply_formatter_at_index(block_number_formatter, 1), - 'eth_getBlockByNumber': apply_formatter_at_index(block_number_formatter, 0), - 'eth_getBlockTransactionCountByNumber': apply_formatter_at_index( - block_number_formatter, - 0, - ), - 'eth_getCode': apply_formatter_at_index(block_number_formatter, 1), - 'eth_getStorageAt': apply_formatter_at_index(block_number_formatter, 2), - 'eth_getTransactionByBlockNumberAndIndex': compose( - apply_formatter_at_index(block_number_formatter, 0), - apply_formatter_at_index(integer_to_hex, 1), - ), - 'eth_getTransactionCount': apply_formatter_at_index(block_number_formatter, 1), - 'eth_getUncleCountByBlockNumber': apply_formatter_at_index(block_number_formatter, 0), - 'eth_getUncleByBlockNumberAndIndex': compose( - apply_formatter_at_index(block_number_formatter, 0), - apply_formatter_at_index(integer_to_hex, 1), - ), - 'eth_getUncleByBlockHashAndIndex': apply_formatter_at_index(integer_to_hex, 1), - 'eth_newFilter': apply_formatter_at_index(filter_params_formatter, 0), - 'eth_getLogs': apply_formatter_at_index(filter_params_formatter, 0), - 'eth_call': apply_formatters_to_sequence([ - transaction_param_formatter, - block_number_formatter, - ]), - 'eth_estimateGas': apply_one_of_formatters(( - (is_length(1), estimate_gas_without_block_id), - (is_length(2), estimate_gas_with_block_id), - )), - 'eth_sendTransaction': apply_formatter_at_index(transaction_param_formatter, 0), - 'eth_getProof': apply_formatter_at_index(block_number_formatter, 2), - # personal - 'personal_importRawKey': apply_formatter_at_index( - compose(remove_0x_prefix, hexstr_if_str(to_hex)), - 0, - ), - 'personal_sign': apply_formatter_at_index(text_if_str(to_hex), 0), - 'personal_ecRecover': apply_formatter_at_index(text_if_str(to_hex), 0), - 'personal_sendTransaction': apply_formatter_at_index(transaction_param_formatter, 0), - # Snapshot and Revert - 'evm_revert': apply_formatter_at_index(integer_to_hex, 0), - 'trace_replayBlockTransactions': apply_formatter_at_index(block_number_formatter, 0), - 'trace_block': apply_formatter_at_index(block_number_formatter, 0), - 'trace_call': compose( - apply_formatter_at_index(transaction_param_formatter, 0), - apply_formatter_at_index(block_number_formatter, 2) - ), -} - - -PYTHONIC_RESULT_FORMATTERS: Dict[RPCEndpoint, Callable[..., Any]] = { - # Eth - 'eth_accounts': apply_list_to_array_formatter(to_checksum_address), - 'eth_blockNumber': to_integer_if_hex, - 'eth_chainId': to_integer_if_hex, - 'eth_coinbase': to_checksum_address, - 'eth_estimateGas': to_integer_if_hex, - 'eth_gasPrice': to_integer_if_hex, - 'eth_getBalance': to_integer_if_hex, - 'eth_getBlockByHash': apply_formatter_if(is_not_null, block_formatter), - 'eth_getBlockByNumber': apply_formatter_if(is_not_null, block_formatter), - 'eth_getBlockTransactionCountByHash': to_integer_if_hex, - 'eth_getBlockTransactionCountByNumber': to_integer_if_hex, - 'eth_getCode': HexBytes, - 'eth_getFilterChanges': filter_result_formatter, - 'eth_getFilterLogs': filter_result_formatter, - 'eth_getLogs': filter_result_formatter, - 'eth_getProof': apply_formatter_if(is_not_null, proof_formatter), - 'eth_getStorageAt': HexBytes, - 'eth_getTransactionByBlockHashAndIndex': apply_formatter_if( - is_not_null, - transaction_formatter, - ), - 'eth_getTransactionByBlockNumberAndIndex': apply_formatter_if( - is_not_null, - transaction_formatter, - ), - 'eth_getTransactionByHash': apply_formatter_if(is_not_null, transaction_formatter), - 'eth_getTransactionCount': to_integer_if_hex, - 'eth_getTransactionReceipt': apply_formatter_if( - is_not_null, - receipt_formatter, - ), - 'eth_getUncleCountByBlockHash': to_integer_if_hex, - 'eth_getUncleCountByBlockNumber': to_integer_if_hex, - 'eth_hashrate': to_integer_if_hex, - 'eth_protocolVersion': compose( - apply_formatter_if(is_integer, str), - to_integer_if_hex, - ), - 'eth_sendRawTransaction': to_hexbytes(32), - 'eth_sendTransaction': to_hexbytes(32), - 'eth_sign': HexBytes, - 'eth_signTransaction': apply_formatter_if(is_not_null, signed_tx_formatter), - 'eth_signTypedData': HexBytes, - 'eth_syncing': apply_formatter_if(is_not_false, syncing_formatter), - # personal - 'personal_importRawKey': to_checksum_address, - 'personal_listAccounts': apply_list_to_array_formatter(to_checksum_address), - 'personal_newAccount': to_checksum_address, - 'personal_sendTransaction': to_hexbytes(32), - 'personal_signTypedData': HexBytes, - # SHH - 'shh_getFilterMessages': apply_list_to_array_formatter(whisper_log_formatter), - # Transaction Pool - 'txpool_content': transaction_pool_content_formatter, - 'txpool_inspect': transaction_pool_inspect_formatter, - # Snapshot and Revert - 'evm_snapshot': hex_to_integer, - # Net - 'net_peerCount': to_integer_if_hex, -} - - -ATTRDICT_FORMATTER = { - '*': apply_formatter_if(is_dict and not_attrdict, AttributeDict.recursive) -} - -METHOD_NORMALIZERS: Dict[RPCEndpoint, Callable[..., Any]] = { - 'eth_getLogs': apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0), - 'eth_newFilter': apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0) -} - -STANDARD_NORMALIZERS = [ - abi_bytes_to_hex, - abi_int_to_hex, - abi_string_to_hex, - abi_address_to_hex, -] - - -ABI_REQUEST_FORMATTERS = abi_request_formatters(STANDARD_NORMALIZERS, RPC_ABIS) - - -@to_tuple -def combine_formatters(formatter_maps, method_name): - for formatter_map in formatter_maps: - if method_name in formatter_map: - yield formatter_map[method_name] - - -def get_request_formatters(method_name): - request_formatter_maps = ( - METHOD_NORMALIZERS, - PYTHONIC_REQUEST_FORMATTERS, - ABI_REQUEST_FORMATTERS - ) - formatters = combine_formatters(request_formatter_maps, method_name) - return compose(*formatters) - - -def get_result_formatters(method_name): - formatters = combine_formatters( - (PYTHONIC_RESULT_FORMATTERS,), - method_name - ) - attrdict_formatter = apply_formatter_if(is_dict and not_attrdict, AttributeDict.recursive) - - return compose(*formatters, attrdict_formatter) - - -def get_error_formatters(method_name): - # Note error formatters work on the full response dict - # TODO - test this function - error_formatter_maps = () - formatters = combine_formatters(error_formatter_maps, method_name) - - return compose(*formatters) diff --git a/web3/_utils/miner.py b/web3/_utils/miner.py index d7404820a2..2116be6200 100644 --- a/web3/_utils/miner.py +++ b/web3/_utils/miner.py @@ -1,51 +1,51 @@ -from web3.method import ( - Method, - default_root_munger, -) - -makeDag = Method( - "miner_makeDag", - mungers=[default_root_munger], -) - - -setExtra = Method( - "miner_setExtra", - mungers=[default_root_munger], -) - - -setEtherbase = Method( - "miner_setEtherbase", - mungers=[default_root_munger], -) - - -setGasPrice = Method( - "miner_setGasPrice", - mungers=[default_root_munger], -) - - -start = Method( - "miner_start", - mungers=[default_root_munger], -) - - -stop = Method( - "miner_stop", - mungers=None, -) - - -startAutoDag = Method( - "miner_startAutoDag", - mungers=None, -) - - -stopAutoDag = Method( - "miner_stopAutoDag", - mungers=None, -) +from web3.method import ( + Method, + default_root_munger, +) + +makeDag = Method( + "miner_makeDag", + mungers=[default_root_munger], +) + + +setExtra = Method( + "miner_setExtra", + mungers=[default_root_munger], +) + + +setEtherbase = Method( + "miner_setEtherbase", + mungers=[default_root_munger], +) + + +setGasPrice = Method( + "miner_setGasPrice", + mungers=[default_root_munger], +) + + +start = Method( + "miner_start", + mungers=[default_root_munger], +) + + +stop = Method( + "miner_stop", + mungers=None, +) + + +startAutoDag = Method( + "miner_startAutoDag", + mungers=None, +) + + +stopAutoDag = Method( + "miner_stopAutoDag", + mungers=None, +) diff --git a/web3/_utils/module.py b/web3/_utils/module.py index a0423f2f23..60efd665df 100644 --- a/web3/_utils/module.py +++ b/web3/_utils/module.py @@ -1,16 +1,16 @@ -from web3.exceptions import ( - ValidationError, -) - - -def attach_modules(parent_module, module_definitions): - for module_name, module_info in module_definitions.items(): - module_class = module_info[0] - module_class.attach(parent_module, module_name) - - if len(module_info) == 2: - submodule_definitions = module_info[1] - module = getattr(parent_module, module_name) - attach_modules(module, submodule_definitions) - elif len(module_info) != 1: - raise ValidationError("Module definitions can only have 1 or 2 elements.") +from web3.exceptions import ( + ValidationError, +) + + +def attach_modules(parent_module, module_definitions): + for module_name, module_info in module_definitions.items(): + module_class = module_info[0] + module_class.attach(parent_module, module_name) + + if len(module_info) == 2: + submodule_definitions = module_info[1] + module = getattr(parent_module, module_name) + attach_modules(module, submodule_definitions) + elif len(module_info) != 1: + raise ValidationError("Module definitions can only have 1 or 2 elements.") diff --git a/web3/_utils/module_testing/__init__.py b/web3/_utils/module_testing/__init__.py index 5b763e69ce..0c5ca5a45e 100644 --- a/web3/_utils/module_testing/__init__.py +++ b/web3/_utils/module_testing/__init__.py @@ -1,28 +1,23 @@ -from .eth_module import ( # noqa: F401 - EthModuleTest, -) -from .go_ethereum_admin_module import ( # noqa: F401 - GoEthereumAdminModuleTest, -) -from .net_module import ( # noqa: F401 - NetModuleTest, -) -from .parity_module import ( # noqa: F401 - ParityModuleTest, - ParitySetModuleTest, - ParityTraceModuleTest, -) -from .personal_module import ( # noqa: F401 - GoEthereumPersonalModuleTest, - ParityPersonalModuleTest, -) -from .shh_module import ( # noqa: F401 - GoEthereumShhModuleTest, - ParityShhModuleTest, -) -from .version_module import ( # noqa: F401 - VersionModuleTest, -) -from .web3_module import ( # noqa: F401 - Web3ModuleTest, -) +from .web3_module import ( # noqa: F401 + Web3ModuleTest, +) +from .vns_module import ( # noqa: F401 + EthModuleTest, +) +from .net_module import ( # noqa: F401 + NetModuleTest, +) +from .personal_module import ( # noqa: F401 + GoEthereumPersonalModuleTest, + ParityPersonalModuleTest, +) +from .shh_module import ( # noqa: F401 + GoEthereumShhModuleTest, + ParityShhModuleTest, +) +from .version_module import ( # noqa: F401 + VersionModuleTest, +) +from .parity_module import ( # noqa: F401 + ParityModuleTest, +) diff --git a/web3/_utils/module_testing/emitter_contract.py b/web3/_utils/module_testing/emitter_contract.py index 1cf7eb813f..71638a0502 100644 --- a/web3/_utils/module_testing/emitter_contract.py +++ b/web3/_utils/module_testing/emitter_contract.py @@ -1,666 +1,232 @@ -CONTRACT_EMITTER_CODE = ( - "608060405234801561001057600080fd5b50610aed806100206000396000f300608060405260043" - "6106100ae5763ffffffff7c01000000000000000000000000000000000000000000000000000000" - "006000350416630bb563d681146100b357806317c0c1801461010e57806320f0256e14610129578" - "06390b41d8b14610150578063966b50e0146101715780639c377053146101ff578063aa6fd82214" - "610223578063acabb9ed14610241578063b2ddc449146102d8578063e17bf9561461030c578063f" - "82ef69e14610365575b600080fd5b3480156100bf57600080fd5b50604080516020600480358082" - "0135601f810184900484028501840190955284845261010c9436949293602493928401919081908" - "401838280828437509497506103999650505050505050565b005b34801561011a57600080fd5b50" - "61010c60ff60043516610435565b34801561013557600080fd5b5061010c60ff600435166024356" - "04435606435608435610527565b34801561015c57600080fd5b5061010c60ff6004351660243560" - "44356105e4565b34801561017d57600080fd5b50604080516020600480358082013583810280860" - "1850190965280855261010c95369593946024949385019291829185019084908082843750506040" - "805187358901803560208181028481018201909552818452989b9a9989019892975090820195509" - "350839250850190849080828437509497506106b49650505050505050565b34801561020b576000" - "80fd5b5061010c60ff6004351660243560443560643561076d565b34801561022f57600080fd5b5" - "061010c60ff60043516602435610818565b34801561024d57600080fd5b50604080516020600480" - "3580820135601f810184900484028501840190955284845261010c9436949293602493928401919" - "0819084018382808284375050604080516020601f89358b01803591820183900483028401830190" - "9452808352979a9998810197919650918201945092508291508401838280828437509497506108c" - "a9650505050505050565b3480156102e457600080fd5b5061010c73ffffffffffffffffffffffff" - "ffffffffffffffff600435811690602435166109bb565b34801561031857600080fd5b506040805" - "160206004803580820135601f810184900484028501840190955284845261010c94369492936024" - "9392840191908190840183828082843750949750610a0d9650505050505050565b3480156103715" - "7600080fd5b5061010c73ffffffffffffffffffffffffffffffffffffffff600435811690602435" - "16610a6b565b7fa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c71235aa64fff99f8" - "16040518080602001828103825283818151815260200191508051906020019080838360005b8381" - "10156103f85781810151838201526020016103e0565b50505050905090810190601f16801561042" - "55780820380516001836020036101000a031916815260200191505b509250505060405180910390" - "a150565b600181601181111561044357fe5b1415610477576040517f1e86022f78f8d04f8e3dfd1" - "3a2bdb280403e6632877c0dbee5e4eeb259908a5c90600090a1610524565b600081601181111561" - "048557fe5b141561049757604051600090a0610524565b604080517f08c379a0000000000000000" - "00000000000000000000000000000000000000000815260206004820152602660248201527f4469" - "646e2774206d6174636820616e7920616c6c6f7761626c65206576656e7460448201527f20696e6" - "4657800000000000000000000000000000000000000000000000000006064820152905190819003" - "60840190fd5b50565b600585601181111561053557fe5b141561058757604080518581526020810" - "18590528082018490526060810183905290517ff039d147f23fe975a4254bdf6b1502b8c79132ae" - "1833986b7ccef2638e73fdf99181900360800190a16105dd565b600b85601181111561059557fe5" - "b14156104975780827fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad9" - "55b58686604051808381526020018281526020019250505060405180910390a35b5050505050565" - "b60038360118111156105f257fe5b141561063857604080518381526020810183905281517fdf0c" - "b1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc5929181900390910190a" - "16106af565b600983601181111561064657fe5b14156106875760408051838152905182917f057b" - "c32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca919081900360200190a" - "26106af565b600883601181111561069557fe5b1415610497576040805183815290518291819003" - "60200190a15b505050565b8160405180828051906020019060200280838360005b838110156106e" - "25781810151838201526020016106ca565b5050505090500191505060405180910390207fdbc4c1" - "d1d2f0d84e58d36ca767ec9ba2ec2f933c055e50e5ccdd57697f7b58b0826040518080602001828" - "103825283818151815260200191508051906020019060200280838360005b838110156107565781" - "8101518382015260200161073e565b505050509050019250505060405180910390a25050565b600" - "484601181111561077b57fe5b14156107c657604080518481526020810184905280820183905290" - "517f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f5506291819003606" - "00190a1610812565b600a8460118111156107d457fe5b1415610497576040805184815290518291" - "84917ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec918190036" - "0200190a35b50505050565b600282601181111561082657fe5b1415610864576040805182815290" - "517f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d491819003602" - "00190a16108c6565b600782601181111561087257fe5b14156108a85760405181907ff70fe689e2" - "90d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1590600090a26108c6565b60068" - "260118111156108b657fe5b1415610497576040518190600090a15b5050565b8160405180828051" - "90602001908083835b602083106108fa5780518252601f1990920191602091820191016108db565" - "b51815160209384036101000a600019018019909216911617905260408051929094018290038220" - "81835287518383015287519096507fe77cf33df73da7bc2e253a2dae617e6f15e4e337eaa462a10" - "8903af4643d1b7595508794929350839283019185019080838360005b8381101561097d57818101" - "5183820152602001610965565b50505050905090810190601f1680156109aa57808203805160018" - "36020036101000a031916815260200191505b509250505060405180910390a25050565b60408051" - "73ffffffffffffffffffffffffffffffffffffffff83811682529151918416917ff922c21568954" - "8d72c3d2fe4ea8dafb2a30c43312c9b43fe5d10f713181f991c9181900360200190a25050565b7f" - "532fd6ea96cfb78bb46e09279a26828b8b493de1a2b8b1ee1face527978a15a5816040518080602" - "00182810382528381815181526020019150805190602001908083836000838110156103f8578181" - "0151838201526020016103e0565b6040805173ffffffffffffffffffffffffffffffffffffffff8" - "0851682528316602082015281517f06029e18f16caae06a69281f35b00ed3fcf47950e6c99dafa1" - "bdd8c4b93479a0929181900390910190a150505600a165627a7a72305820962bb0d0c7c052407e6" - "ad0911da133e939baa8783fbd0220169b97b54160a89e0029" -) - -CONTRACT_EMITTER_RUNTIME = ( - "6080604052600436106100ae5763ffffffff7c01000000000000000000000000000000000000000" - "000000000000000006000350416630bb563d681146100b357806317c0c1801461010e57806320f0" - "256e1461012957806390b41d8b14610150578063966b50e0146101715780639c377053146101ff5" - "78063aa6fd82214610223578063acabb9ed14610241578063b2ddc449146102d8578063e17bf956" - "1461030c578063f82ef69e14610365575b600080fd5b3480156100bf57600080fd5b50604080516" - "0206004803580820135601f810184900484028501840190955284845261010c9436949293602493" - "928401919081908401838280828437509497506103999650505050505050565b005b34801561011" - "a57600080fd5b5061010c60ff60043516610435565b34801561013557600080fd5b5061010c60ff" - "60043516602435604435606435608435610527565b34801561015c57600080fd5b5061010c60ff6" - "00435166024356044356105e4565b34801561017d57600080fd5b50604080516020600480358082" - "0135838102808601850190965280855261010c95369593946024949385019291829185019084908" - "082843750506040805187358901803560208181028481018201909552818452989b9a9989019892" - "975090820195509350839250850190849080828437509497506106b49650505050505050565b348" - "01561020b57600080fd5b5061010c60ff6004351660243560443560643561076d565b3480156102" - "2f57600080fd5b5061010c60ff60043516602435610818565b34801561024d57600080fd5b50604" - "0805160206004803580820135601f810184900484028501840190955284845261010c9436949293" - "6024939284019190819084018382808284375050604080516020601f89358b01803591820183900" - "4830284018301909452808352979a99988101979196509182019450925082915084018382808284" - "37509497506108ca9650505050505050565b3480156102e457600080fd5b5061010c73fffffffff" - "fffffffffffffffffffffffffffffff600435811690602435166109bb565b348015610318576000" - "80fd5b506040805160206004803580820135601f810184900484028501840190955284845261010" - "c943694929360249392840191908190840183828082843750949750610a0d965050505050505056" - "5b34801561037157600080fd5b5061010c73ffffffffffffffffffffffffffffffffffffffff600" - "43581169060243516610a6b565b7fa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c7" - "1235aa64fff99f81604051808060200182810382528381815181526020019150805190602001908" - "0838360005b838110156103f85781810151838201526020016103e0565b50505050905090810190" - "601f1680156104255780820380516001836020036101000a031916815260200191505b509250505" - "060405180910390a150565b600181601181111561044357fe5b1415610477576040517f1e86022f" - "78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c90600090a1610524565b600" - "081601181111561048557fe5b141561049757604051600090a0610524565b604080517f08c379a0" - "0000000000000000000000000000000000000000000000000000000081526020600482015260266" - "0248201527f4469646e2774206d6174636820616e7920616c6c6f7761626c65206576656e746044" - "8201527f20696e64657800000000000000000000000000000000000000000000000000006064820" - "15290519081900360840190fd5b50565b600585601181111561053557fe5b141561058757604080" - "51858152602081018590528082018490526060810183905290517ff039d147f23fe975a4254bdf6" - "b1502b8c79132ae1833986b7ccef2638e73fdf99181900360800190a16105dd565b600b85601181" - "111561059557fe5b14156104975780827fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977" - "affb07b98a77ad955b58686604051808381526020018281526020019250505060405180910390a3" - "5b5050505050565b60038360118111156105f257fe5b14156106385760408051838152602081018" - "3905281517fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc59291" - "81900390910190a16106af565b600983601181111561064657fe5b1415610687576040805183815" - "2905182917f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca9190" - "81900360200190a26106af565b600883601181111561069557fe5b1415610497576040805183815" - "29051829181900360200190a15b505050565b816040518082805190602001906020028083836000" - "5b838110156106e25781810151838201526020016106ca565b50505050905001915050604051809" - "10390207fdbc4c1d1d2f0d84e58d36ca767ec9ba2ec2f933c055e50e5ccdd57697f7b58b0826040" - "518080602001828103825283818151815260200191508051906020019060200280838360005b838" - "1101561075657818101518382015260200161073e565b5050505090500192505050604051809103" - "90a25050565b600484601181111561077b57fe5b14156107c657604080518481526020810184905" - "280820183905290517f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f5" - "50629181900360600190a1610812565b600a8460118111156107d457fe5b1415610497576040805" - "18481529051829184917ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd4" - "2ee6ec9181900360200190a35b50505050565b600282601181111561082657fe5b1415610864576" - "040805182815290517f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce9" - "05d49181900360200190a16108c6565b600782601181111561087257fe5b14156108a8576040518" - "1907ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1590600090a2" - "6108c6565b60068260118111156108b657fe5b1415610497576040518190600090a15b5050565b8" - "16040518082805190602001908083835b602083106108fa5780518252601f199092019160209182" - "0191016108db565b51815160209384036101000a600019018019909216911617905260408051929" - "09401829003822081835287518383015287519096507fe77cf33df73da7bc2e253a2dae617e6f15" - "e4e337eaa462a108903af4643d1b7595508794929350839283019185019080838360005b8381101" - "561097d578181015183820152602001610965565b50505050905090810190601f1680156109aa57" - "80820380516001836020036101000a031916815260200191505b509250505060405180910390a25" - "050565b6040805173ffffffffffffffffffffffffffffffffffffffff8381168252915191841691" - "7ff922c215689548d72c3d2fe4ea8dafb2a30c43312c9b43fe5d10f713181f991c9181900360200" - "190a25050565b7f532fd6ea96cfb78bb46e09279a26828b8b493de1a2b8b1ee1face527978a15a5" - "8160405180806020018281038252838181518152602001915080519060200190808383600083811" - "0156103f85781810151838201526020016103e0565b6040805173ffffffffffffffffffffffffff" - "ffffffffffffff80851682528316602082015281517f06029e18f16caae06a69281f35b00ed3fcf" - "47950e6c99dafa1bdd8c4b93479a0929181900390910190a150505600a165627a7a72305820962b" - "b0d0c7c052407e6ad0911da133e939baa8783fbd0220169b97b54160a89e0029" -) - -CONTRACT_EMITTER_ABI = [ - { - "constant": False, - "inputs": [ - { - "name": "v", - "type": "string" - } - ], - "name": "logString", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "which", - "type": "uint8" - } - ], - "name": "logNoArgs", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "which", - "type": "uint8" - }, - { - "name": "arg0", - "type": "uint256" - }, - { - "name": "arg1", - "type": "uint256" - }, - { - "name": "arg2", - "type": "uint256" - }, - { - "name": "arg3", - "type": "uint256" - } - ], - "name": "logQuadruple", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "which", - "type": "uint8" - }, - { - "name": "arg0", - "type": "uint256" - }, - { - "name": "arg1", - "type": "uint256" - } - ], - "name": "logDouble", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "bytes2[]" - }, - { - "name": "arg1", - "type": "bytes2[]" - } - ], - "name": "logListArgs", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "which", - "type": "uint8" - }, - { - "name": "arg0", - "type": "uint256" - }, - { - "name": "arg1", - "type": "uint256" - }, - { - "name": "arg2", - "type": "uint256" - } - ], - "name": "logTriple", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "which", - "type": "uint8" - }, - { - "name": "arg0", - "type": "uint256" - } - ], - "name": "logSingle", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "string" - }, - { - "name": "arg1", - "type": "string" - } - ], - "name": "logDynamicArgs", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "address" - }, - { - "name": "arg1", - "type": "address" - } - ], - "name": "logAddressIndexedArgs", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "v", - "type": "bytes" - } - ], - "name": "logBytes", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "address" - }, - { - "name": "arg1", - "type": "address" - } - ], - "name": "logAddressNotIndexedArgs", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "anonymous": True, - "inputs": [], - "name": "LogAnonymous", - "type": "event" - }, - { - "anonymous": False, - "inputs": [], - "name": "LogNoArguments", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleArg", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg1", - "type": "uint256" - } - ], - "name": "LogDoubleArg", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg1", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg2", - "type": "uint256" - } - ], - "name": "LogTripleArg", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg1", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg2", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg3", - "type": "uint256" - } - ], - "name": "LogQuadrupleArg", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "v", - "type": "string" - } - ], - "name": "LogString", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "v", - "type": "bytes" - } - ], - "name": "LogBytes", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleWithIndex", - "type": "event" - }, - { - "anonymous": True, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleAnonymous", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg1", - "type": "uint256" - } - ], - "name": "LogDoubleWithIndex", - "type": "event" - }, - { - "anonymous": True, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg1", - "type": "uint256" - } - ], - "name": "LogDoubleAnonymous", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg1", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg2", - "type": "uint256" - } - ], - "name": "LogTripleWithIndex", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - }, - { - "indexed": False, - "name": "arg1", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg2", - "type": "uint256" - }, - { - "indexed": True, - "name": "arg3", - "type": "uint256" - } - ], - "name": "LogQuadrupleWithIndex", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "string" - }, - { - "indexed": False, - "name": "arg1", - "type": "string" - } - ], - "name": "LogDynamicArgs", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "bytes2[]" - }, - { - "indexed": False, - "name": "arg1", - "type": "bytes2[]" - } - ], - "name": "LogListArgs", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "address" - }, - { - "indexed": False, - "name": "arg1", - "type": "address" - } - ], - "name": "LogAddressIndexed", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "address" - }, - { - "indexed": False, - "name": "arg1", - "type": "address" - } - ], - "name": "LogAddressNotIndexed", - "type": "event" - } -] - - -EMITTER_ENUM = { - 'LogAnonymous': 0, - 'LogNoArguments': 1, - 'LogSingleArg': 2, - 'LogDoubleArg': 3, - 'LogTripleArg': 4, - 'LogQuadrupleArg': 5, - 'LogSingleAnonymous': 6, - 'LogSingleWithIndex': 7, - 'LogDoubleAnonymous': 8, - 'LogDoubleWithIndex': 9, - 'LogTripleWithIndex': 10, - 'LogQuadrupleWithInde': 11, -} + +EMITTER_BYTECODE = ( + "60606040526104ae806100126000396000f3606060405236156100615760e060020a60003504630b" + "b563d6811461006357806317c0c1801461013657806320f0256e1461017057806390b41d8b146101" + "ca5780639c37705314610215578063aa6fd82214610267578063e17bf956146102a9575b005b6020" + "6004803580820135601f810184900490930260809081016040526060848152610061946024939192" + "918401918190838280828437509496505050505050507fa95e6e2a182411e7a6f9ed114a85c3761d" + "87f9b8f453d842c71235aa64fff99f81604051808060200182810382528381815181526020019150" + "80519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16" + "80156101255780820380516001836020036101000a031916815260200191505b5092505050604051" + "80910390a15b50565b610061600435600181141561037a577f1e86022f78f8d04f8e3dfd13a2bdb2" + "80403e6632877c0dbee5e4eeb259908a5c60006060a1610133565b61006160043560243560443560" + "64356084356005851415610392576060848152608084815260a084905260c08390527ff039d147f2" + "3fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf991a15b5050505050565b610061" + "60043560243560443560038314156103d457606082815260808290527fdf0cb1dea99afceb3ea698" + "d62e705b736f1345a7eee9eb07e63d1f8f556c1bc590604090a15b505050565b6100616004356024" + "356044356064356004841415610428576060838152608083905260a08290527f4a25b279c7c585f2" + "5eda9788ac9420ebadae78ca6b206a0e6ab488fd81f550629080a15b50505050565b610061600435" + "60243560028214156104655760608181527f56d2ef3c5228bf5d88573621e325a4672ab50e033749" + "a601e4f4a5e1dce905d490602090a15b5050565b60206004803580820135601f8101849004909302" + "60809081016040526060848152610061946024939192918401918190838280828437509496505050" + "505050507f532fd6ea96cfb78bb46e09279a26828b8b493de1a2b8b1ee1face527978a15a5816040" + "51808060200182810382528381815181526020019150805190602001908083838290600060046020" + "84601f0104600f02600301f150905090810190601f16801561012557808203805160018360200361" + "01000a03191681526020019150509250505060405180910390a150565b600081141561038d576000" + "6060a0610133565b610002565b600b85141561038d5760608481526080849052819083907fa30ece" + "802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b590604090a36101c3565b60" + "0983141561040f57606082815281907f057bc32826fbe161da1c110afcdcae7c109a8b69149f727f" + "c37a603c60ef94ca90602090a2610210565b600883141561038d5760608281528190602090a16102" + "10565b600a84141561038d576060838152819083907ff16c999b533366ca5138d78e85da51611089" + "cd05749f098d6c225d4cd42ee6ec90602090a3610261565b600782141561049a57807ff70fe689e2" + "90d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560006060a26102a5565b600682" + "141561038d578060006060a16102a556" +) + + +EMITTER_ABI = [ + { + "constant": False, + "inputs": [{"name": "v", "type": "string"}], + "name": "logString", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [{"name": "which", "type": "uint8"}], + "name": "logNoArgs", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "which", "type": "uint8"}, + {"name": "arg0", "type": "uint256"}, + {"name": "arg1", "type": "uint256"}, + {"name": "arg2", "type": "uint256"}, + {"name": "arg3", "type": "uint256"}, + ], + "name": "logQuadruple", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "which", "type": "uint8"}, + {"name": "arg0", "type": "uint256"}, + {"name": "arg1", "type": "uint256"}, + ], + "name": "logDouble", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "which", "type": "uint8"}, + {"name": "arg0", "type": "uint256"}, + {"name": "arg1", "type": "uint256"}, + {"name": "arg2", "type": "uint256"}, + ], + "name": "logTriple", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "which", "type": "uint8"}, + {"name": "arg0", "type": "uint256"}, + ], + "name": "logSingle", + "outputs": [], + "type": "function", + }, + { + "constant": False, + "inputs": [{"name": "v", "type": "bytes"}], + "name": "logBytes", + "outputs": [], + "type": "function", + }, + { + "anonymous": True, + "inputs": [], + "name": "LogAnonymous", + "type": "event", + }, + { + "anonymous": False, + "inputs": [], + "name": "LogNoArguments", + "type": "event", + }, + { + "anonymous": False, + "inputs": [{"indexed": False, "name": "arg0", "type": "uint256"}], + "name": "LogSingleArg", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": False, "name": "arg1", "type": "uint256"}, + ], + "name": "LogDoubleArg", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": False, "name": "arg1", "type": "uint256"}, + {"indexed": False, "name": "arg2", "type": "uint256"}, + ], + "name": "LogTripleArg", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": False, "name": "arg1", "type": "uint256"}, + {"indexed": False, "name": "arg2", "type": "uint256"}, + {"indexed": False, "name": "arg3", "type": "uint256"}, + ], + "name": "LogQuadrupleArg", + "type": "event", + }, + { + "anonymous": True, + "inputs": [{"indexed": True, "name": "arg0", "type": "uint256"}], + "name": "LogSingleAnonymous", + "type": "event", + }, + { + "anonymous": False, + "inputs": [{"indexed": True, "name": "arg0", "type": "uint256"}], + "name": "LogSingleWithIndex", + "type": "event", + }, + { + "anonymous": True, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + ], + "name": "LogDoubleAnonymous", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + ], + "name": "LogDoubleWithIndex", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": True, "name": "arg1", "type": "uint256"}, + {"indexed": True, "name": "arg2", "type": "uint256"}, + ], + "name": "LogTripleWithIndex", + "type": "event", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "arg0", "type": "uint256"}, + {"indexed": False, "name": "arg1", "type": "uint256"}, + {"indexed": True, "name": "arg2", "type": "uint256"}, + {"indexed": True, "name": "arg3", "type": "uint256"}, + ], + "name": "LogQuadrupleWithIndex", + "type": "event", + }, + { + "anonymous": False, + "inputs": [{"indexed": False, "name": "v", "type": "bytes"}], + "name": "LogBytes", + "type": "event", + }, + { + "anonymous": False, + "inputs": [{"indexed": False, "name": "v", "type": "string"}], + "name": "LogString", + "type": "event", + }, +] + + +EMITTER_ENUM = { + 'LogAnonymous': 0, + 'LogNoArguments': 1, + 'LogSingleArg': 2, + 'LogDoubleArg': 3, + 'LogTripleArg': 4, + 'LogQuadrupleArg': 5, + 'LogSingleAnonymous': 6, + 'LogSingleWithIndex': 7, + 'LogDoubleAnonymous': 8, + 'LogDoubleWithIndex': 9, + 'LogTripleWithIndex': 10, + 'LogQuadrupleWithInde': 11, +} diff --git a/web3/_utils/module_testing/event_contract.py b/web3/_utils/module_testing/event_contract.py deleted file mode 100644 index 0bd0224210..0000000000 --- a/web3/_utils/module_testing/event_contract.py +++ /dev/null @@ -1,64 +0,0 @@ - -EVNT_CONTRACT_CODE = ( - "6080604052348015600f57600080fd5b5061010b8061001f6000396000f30060806040526004361" - "0603f576000357c0100000000000000000000000000000000000000000000000000000000900463" - "ffffffff1680635818fad7146044575b600080fd5b348015604f57600080fd5b50606c600480360" - "38101908080359060200190929190505050606e565b005b7ff70fe689e290d8ce2b2a388ac28db3" - "6fbb0e16a6d89c6804c461f65a1b40bb15816040518082815260200191505060405180910390a17" - "f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d481604051808281" - "5260200191505060405180910390a1505600a165627a7a72305820ff79430a04cf654d7b46edc52" - "9ccaa5d7f77607f54bb58210be0c48455292c810029" -) - - -EVNT_CONTRACT_RUNTIME = ( - "608060405260043610603f576000357c01000000000000000000000000000000000000000000000" - "00000000000900463ffffffff1680635818fad7146044575b600080fd5b348015604f57600080fd" - "5b50606c60048036038101908080359060200190929190505050606e565b005b7ff70fe689e290d" - "8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb158160405180828152602001915050" - "60405180910390a17f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce90" - "5d4816040518082815260200191505060405180910390a1505600a165627a7a72305820ff79430a" - "04cf654d7b46edc529ccaa5d7f77607f54bb58210be0c48455292c810029" -) - - -EVNT_CONTRACT_ABI = [ - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "uint256" - } - ], - "name": "logTwoEvents", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleWithIndex", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleArg", - "type": "event" - } -] diff --git a/web3/_utils/module_testing/go_ethereum_admin_module.py b/web3/_utils/module_testing/go_ethereum_admin_module.py deleted file mode 100644 index 55be8401c8..0000000000 --- a/web3/_utils/module_testing/go_ethereum_admin_module.py +++ /dev/null @@ -1,108 +0,0 @@ -import pytest - -from web3.datastructures import ( - AttributeDict, -) - - -class GoEthereumAdminModuleTest: - def test_add_peer(self, web3): - result = web3.geth.admin.add_peer( - 'enode://f1a6b0bdbf014355587c3018454d070ac57801f05d3b39fe85da574f002a32e929f683d72aa5a8318382e4d3c7a05c9b91687b0d997a39619fb8a6e7ad88e512@1.1.1.1:30303', # noqa: E501 - ) - assert result is True - - def test_admin_datadir(self, web3, datadir): - result = web3.geth.admin.datadir() - assert result == datadir - - def test_admin_node_info(self, web3): - result = web3.geth.admin.node_info() - expected = AttributeDict({ - 'id': '', - 'name': '', - 'enode': '', - 'ip': '', - 'ports': AttributeDict({}), - 'listenAddr': '', - 'protocols': AttributeDict({}) - }) - # Test that result gives at least the keys that are listed in `expected` - assert not set(expected.keys()).difference(result.keys()) - - def test_admin_peers(self, web3): - enode = web3.geth.admin.node_info()['enode'] - web3.geth.admin.add_peer(enode) - result = web3.geth.admin.peers() - assert len(result) == 1 - - def test_admin_start_stop_rpc(self, web3): - start = web3.geth.admin.start_rpc("localhost", 8545) - assert start - - stop = web3.geth.admin.stop_rpc() - assert stop - - def test_admin_start_stop_ws(self, web3): - start = web3.geth.admin.start_ws("localhost", 8546) - assert start - - stop = web3.geth.admin.stop_ws() - assert stop - - # - # Deprecated - # - def test_admin_addPeer(self, web3): - with pytest.warns(DeprecationWarning): - result = web3.geth.admin.addPeer( - 'enode://f1a6b0bdbf014355587c3018454d070ac57801f05d3b39fe85da574f002a32e929f683d72aa5a8318382e4d3c7a05c9b91687b0d997a39619fb8a6e7ad88e512@1.1.1.1:30303', # noqa: E501 - ) - assert result is True - - def test_admin_nodeInfo(self, web3): - with pytest.warns(DeprecationWarning): - result = web3.geth.admin.nodeInfo() - expected = AttributeDict({ - 'id': '', - 'name': '', - 'enode': '', - 'ip': '', - 'ports': AttributeDict({}), - 'listenAddr': '', - 'protocols': AttributeDict({}) - }) - # Test that result gives at least the keys that are listed in `expected` - assert not set(expected.keys()).difference(result.keys()) - - def test_admin_startRPC(self, web3): - with pytest.warns(DeprecationWarning): - start = web3.geth.admin.startRPC('localhost', 8545) - assert start - - stop = web3.geth.admin.stop_rpc() - assert stop - - def test_admin_stopRPC(self, web3): - start = web3.geth.admin.start_rpc('localhost', 8545) - assert start - - with pytest.warns(DeprecationWarning): - stop = web3.geth.admin.stopRPC() - assert stop - - def test_admin_startWS(self, web3): - with pytest.warns(DeprecationWarning): - start = web3.geth.admin.startWS('localhost', 8546) - assert start - - stop = web3.geth.admin.stop_ws() - assert stop - - def test_admin_stopWS(self, web3): - start = web3.geth.admin.start_ws('localhost', 8546) - assert start - - with pytest.warns(DeprecationWarning): - stop = web3.geth.admin.stopWS() - assert stop diff --git a/web3/_utils/module_testing/indexed_event_contract.py b/web3/_utils/module_testing/indexed_event_contract.py deleted file mode 100644 index 20010dc60d..0000000000 --- a/web3/_utils/module_testing/indexed_event_contract.py +++ /dev/null @@ -1,64 +0,0 @@ - -IND_EVENT_CONTRACT_CODE = ( - "6080604052348015600f57600080fd5b506101018061001f6000396000f30060806040526004361" - "0603f576000357c0100000000000000000000000000000000000000000000000000000000900463" - "ffffffff1680635818fad7146044575b600080fd5b348015604f57600080fd5b50606c600480360" - "38101908080359060200190929190505050606e565b005b807ff70fe689e290d8ce2b2a388ac28d" - "b36fbb0e16a6d89c6804c461f65a1b40bb1560405160405180910390a27f56d2ef3c5228bf5d885" - "73621e325a4672ab50e033749a601e4f4a5e1dce905d48160405180828152602001915050604051" - "80910390a1505600a165627a7a72305820afa2dc55cd3a55a914793a583c3284fc5f8dc8ebec94e" - "6ec7fe1ad6d604daf350029" -) - - -IND_EVENT_CONTRACT_RUNTIME = ( - "608060405260043610603f576000357c01000000000000000000000000000000000000000000000" - "00000000000900463ffffffff1680635818fad7146044575b600080fd5b348015604f57600080fd" - "5b50606c60048036038101908080359060200190929190505050606e565b005b807ff70fe689e29" - "0d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560405160405180910390a27f56" - "d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d481604051808281526" - "0200191505060405180910390a1505600a165627a7a72305820afa2dc55cd3a55a914793a583c32" - "84fc5f8dc8ebec94e6ec7fe1ad6d604daf350029" -) - - -IND_EVENT_CONTRACT_ABI = [ - { - "constant": False, - "inputs": [ - { - "name": "arg0", - "type": "uint256" - } - ], - "name": "logTwoEvents", - "outputs": [], - "payable": False, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": True, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleWithIndex", - "type": "event" - }, - { - "anonymous": False, - "inputs": [ - { - "indexed": False, - "name": "arg0", - "type": "uint256" - } - ], - "name": "LogSingleArg", - "type": "event" - } -] diff --git a/web3/_utils/module_testing/math_contract.py b/web3/_utils/module_testing/math_contract.py index 7f8173a90d..135f9b393c 100644 --- a/web3/_utils/module_testing/math_contract.py +++ b/web3/_utils/module_testing/math_contract.py @@ -1,91 +1,91 @@ - -MATH_BYTECODE = ( - "606060405261022e806100126000396000f360606040523615610074576000357c01000000000000" - "000000000000000000000000000000000000000000009004806316216f391461007657806361bc22" - "1a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d5780" - "63dcf537b11461014057610074565b005b610083600480505061016c565b60405180828152602001" - "91505060405180910390f35b6100a6600480505061017f565b604051808281526020019150506040" - "5180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191" - "505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea" - "565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b604051" - "8082815260200191505060405180910390f35b610156600480803590602001909190505061021756" - "5b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90" - "565b60006000505481565b6000816000600082828250540192505081905550600060005054905080" - "507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082" - "815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090" - "506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b600060078202" - "90508050809050610229565b91905056" -) - - -MATH_ABI = [ - { - "constant": False, - "inputs": [], - "name": "return13", - "outputs": [ - {"name": "result", "type": "int256"}, - ], - "type": "function", - }, - { - "constant": True, - "inputs": [], - "name": "counter", - "outputs": [ - {"name": "", "type": "uint256"}, - ], - "type": "function", - }, - { - "constant": False, - "inputs": [ - {"name": "amt", "type": "uint256"}, - ], - "name": "increment", - "outputs": [ - {"name": "result", "type": "uint256"}, - ], - "type": "function", - }, - { - "constant": False, - "inputs": [ - {"name": "a", "type": "int256"}, - {"name": "b", "type": "int256"}, - ], - "name": "add", - "outputs": [ - {"name": "result", "type": "int256"}, - ], - "type": "function", - }, - { - "constant": False, - "inputs": [], - "name": "increment", - "outputs": [ - {"name": "", "type": "uint256"}, - ], - "type": "function" - }, - { - "constant": False, - "inputs": [ - {"name": "a", "type": "int256"}, - ], - "name": "multiply7", - "outputs": [ - {"name": "result", "type": "int256"}, - ], - "type": "function", - }, - { - "anonymous": False, - "inputs": [ - {"indexed": False, "name": "value", "type": "uint256"}, - ], - "name": "Increased", - "type": "event", - }, -] + +MATH_BYTECODE = ( + "606060405261022e806100126000396000f360606040523615610074576000357c01000000000000" + "000000000000000000000000000000000000000000009004806316216f391461007657806361bc22" + "1a146100995780637cf5dab0146100bc578063a5f3c23b146100e8578063d09de08a1461011d5780" + "63dcf537b11461014057610074565b005b610083600480505061016c565b60405180828152602001" + "91505060405180910390f35b6100a6600480505061017f565b604051808281526020019150506040" + "5180910390f35b6100d26004808035906020019091905050610188565b6040518082815260200191" + "505060405180910390f35b61010760048080359060200190919080359060200190919050506101ea" + "565b6040518082815260200191505060405180910390f35b61012a6004805050610201565b604051" + "8082815260200191505060405180910390f35b610156600480803590602001909190505061021756" + "5b6040518082815260200191505060405180910390f35b6000600d9050805080905061017c565b90" + "565b60006000505481565b6000816000600082828250540192505081905550600060005054905080" + "507f3496c3ede4ec3ab3686712aa1c238593ea6a42df83f98a5ec7df9834cfa577c5816040518082" + "815260200191505060405180910390a18090506101e5565b919050565b6000818301905080508090" + "506101fb565b92915050565b600061020d6001610188565b9050610214565b90565b600060078202" + "90508050809050610229565b91905056" +) + + +MATH_ABI = [ + { + "constant": False, + "inputs": [], + "name": "return13", + "outputs": [ + {"name": "result", "type": "int256"}, + ], + "type": "function", + }, + { + "constant": True, + "inputs": [], + "name": "counter", + "outputs": [ + {"name": "", "type": "uint256"}, + ], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "amt", "type": "uint256"}, + ], + "name": "increment", + "outputs": [ + {"name": "result", "type": "uint256"}, + ], + "type": "function", + }, + { + "constant": False, + "inputs": [ + {"name": "a", "type": "int256"}, + {"name": "b", "type": "int256"}, + ], + "name": "add", + "outputs": [ + {"name": "result", "type": "int256"}, + ], + "type": "function", + }, + { + "constant": False, + "inputs": [], + "name": "increment", + "outputs": [ + {"name": "", "type": "uint256"}, + ], + "type": "function" + }, + { + "constant": False, + "inputs": [ + {"name": "a", "type": "int256"}, + ], + "name": "multiply7", + "outputs": [ + {"name": "result", "type": "int256"}, + ], + "type": "function", + }, + { + "anonymous": False, + "inputs": [ + {"indexed": False, "name": "value", "type": "uint256"}, + ], + "name": "Increased", + "type": "event", + }, +] diff --git a/web3/_utils/module_testing/net_module.py b/web3/_utils/module_testing/net_module.py index 753c99c9b1..0593617cde 100644 --- a/web3/_utils/module_testing/net_module.py +++ b/web3/_utils/module_testing/net_module.py @@ -1,29 +1,29 @@ -import pytest - -from eth_utils import ( - is_boolean, - is_integer, - is_string, -) - - -class NetModuleTest: - def test_net_version(self, web3): - version = web3.net.version - - assert is_string(version) - assert version.isdigit() - - def test_net_listening(self, web3): - listening = web3.net.listening - - assert is_boolean(listening) - - def test_net_peerCount(self, web3): - peer_count = web3.net.peerCount - - assert is_integer(peer_count) - - def test_net_chainId_deprecation(self, web3): - with pytest.raises(DeprecationWarning): - web3.net.chainId +import pytest + +from vns_utils import ( + is_boolean, + is_integer, + is_string, +) + + +class NetModuleTest: + def test_net_version(self, web3): + version = web3.net.version + + assert is_string(version) + assert version.isdigit() + + def test_net_listening(self, web3): + listening = web3.net.listening + + assert is_boolean(listening) + + def test_net_peerCount(self, web3): + peer_count = web3.net.peerCount + + assert is_integer(peer_count) + + def test_net_chainId_deprecation(self, web3): + with pytest.raises(DeprecationWarning): + web3.net.chainId diff --git a/web3/_utils/module_testing/parity_module.py b/web3/_utils/module_testing/parity_module.py index 02a555cefd..2122638fbd 100644 --- a/web3/_utils/module_testing/parity_module.py +++ b/web3/_utils/module_testing/parity_module.py @@ -1,126 +1,97 @@ -import pytest - -from eth_utils import ( - add_0x_prefix, -) - -from web3._utils.formatters import ( - hex_to_integer, -) - - -class ParityTraceModuleTest: - - def test_trace_replay_transaction(self, web3, parity_fixture_data): - trace = web3.parity.traceReplayTransaction(parity_fixture_data['mined_txn_hash']) - - assert trace['stateDiff'] is None - assert trace['vmTrace'] is None - assert trace['trace'][0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) - - def test_trace_replay_block_with_transactions(self, - web3, - block_with_txn, - parity_fixture_data): - trace = web3.parity.traceReplayBlockTransactions(block_with_txn['number']) - assert len(trace) > 0 - trace_0_action = trace[0]['trace'][0]['action'] - assert trace_0_action['from'] == add_0x_prefix(parity_fixture_data['coinbase']) - - def test_trace_replay_block_without_transactions(self, web3, empty_block): - trace = web3.parity.traceReplayBlockTransactions(empty_block['number']) - assert len(trace) == 0 - - def test_trace_block(self, web3, block_with_txn): - trace = web3.parity.traceBlock(block_with_txn['number']) - assert trace[0]['blockNumber'] == block_with_txn['number'] - - def test_trace_transaction(self, web3, parity_fixture_data): - trace = web3.parity.traceTransaction(parity_fixture_data['mined_txn_hash']) - assert trace[0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) - - def test_trace_call(self, web3, math_contract, math_contract_address): - coinbase = web3.eth.coinbase - txn_params = math_contract._prepare_transaction( - fn_name='add', - fn_args=(7, 11), - transaction={'from': coinbase, 'to': math_contract_address}, - ) - trace = web3.parity.traceCall(txn_params) - assert trace['stateDiff'] is None - assert trace['vmTrace'] is None - result = hex_to_integer(trace['output']) - assert result == 18 - - def test_eth_call_with_0_result(self, web3, math_contract, math_contract_address): - coinbase = web3.eth.coinbase - txn_params = math_contract._prepare_transaction( - fn_name='add', - fn_args=(0, 0), - transaction={'from': coinbase, 'to': math_contract_address}, - ) - trace = web3.parity.traceCall(txn_params) - assert trace['stateDiff'] is None - assert trace['vmTrace'] is None - result = hex_to_integer(trace['output']) - assert result == 0 - - @pytest.mark.parametrize( - 'raw_transaction', - [ - ( - # address 0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6 - '0xf8648085174876e8008252089439eeed73fb1d3855e90cbd42f348b3d7b340aaa601801ba0ec1295f00936acd0c2cb90ab2cdaacb8bf5e11b3d9957833595aca9ceedb7aada05dfc8937baec0e26029057abd3a1ef8c505dca2cdc07ffacb046d090d2bea06a' # noqa: E501 - ), - ] - ) - def test_trace_raw_transaction(self, - web3, - raw_transaction, - funded_account_for_raw_txn): - trace = web3.parity.traceRawTransaction(raw_transaction) - assert trace['stateDiff'] is None - assert trace['vmTrace'] is None - assert trace['trace'][0]['action']['from'] == funded_account_for_raw_txn.lower() - - def test_trace_filter(self, web3, txn_filter_params, parity_fixture_data): - trace = web3.parity.traceFilter(txn_filter_params) - assert isinstance(trace, list) - assert trace[0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) - - -class ParityModuleTest: - - def test_add_reserved_peer(self, web3): - peer_addr = 'enode://f1a6b0bdbf014355587c3018454d070ac57801f05d3b39fe85da574f002a32e929f683d72aa5a8318382e4d3c7a05c9b91687b0d997a39619fb8a6e7ad88e512@1.1.1.1:30300' # noqa: E501 - assert web3.parity.addReservedPeer(peer_addr) - - def test_list_storage_keys_no_support(self, web3, emitter_contract_address): - keys = web3.parity.listStorageKeys(emitter_contract_address, 10, None) - assert keys is None - - def test_mode(self, web3): - assert web3.parity.mode() is not None - - -class ParitySetModuleTest: - - @pytest.mark.parametrize( - 'mode', - [ - ('dark'), - ('offline'), - ('active'), - ('passive'), - ] - ) - def test_set_mode(self, web3, mode): - assert web3.parity.setMode(mode) is True - - def test_set_mode_with_bad_string(self, web3): - with pytest.raises(ValueError, match="Couldn't parse parameters: mode"): - web3.parity.setMode('not a mode') - - def test_set_mode_with_no_argument(self, web3): - with pytest.raises(TypeError, match='missing 1 required positional argument'): - web3.parity.setMode() +import pytest + +from vns_utils import ( + add_0x_prefix, +) + +from web3._utils.formatters import ( + hex_to_integer, +) + + +class ParityModuleTest: + + def test_list_storage_keys_no_support(self, web3, emitter_contract_address): + keys = web3.parity.listStorageKeys(emitter_contract_address, 10, None) + assert keys is None + + def test_trace_replay_transaction(self, web3, parity_fixture_data): + trace = web3.parity.traceReplayTransaction(parity_fixture_data['mined_txn_hash']) + + assert trace['stateDiff'] is None + assert trace['vmTrace'] is None + assert trace['trace'][0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) + + def test_trace_replay_block_with_transactions(self, + web3, + block_with_txn, + parity_fixture_data): + trace = web3.parity.traceReplayBlockTransactions(block_with_txn['number']) + assert len(trace) > 0 + trace_0_action = trace[0]['trace'][0]['action'] + assert trace_0_action['from'] == add_0x_prefix(parity_fixture_data['coinbase']) + + def test_trace_replay_block_without_transactions(self, web3, empty_block): + trace = web3.parity.traceReplayBlockTransactions(empty_block['number']) + assert len(trace) == 0 + + def test_trace_block(self, web3, block_with_txn): + trace = web3.parity.traceBlock(block_with_txn['number']) + assert trace[0]['blockNumber'] == block_with_txn['number'] + + def test_trace_transaction(self, web3, parity_fixture_data): + trace = web3.parity.traceTransaction(parity_fixture_data['mined_txn_hash']) + assert trace[0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) + + def test_trace_call(self, web3, math_contract, math_contract_address): + coinbase = web3.vns.coinbase + txn_params = math_contract._prepare_transaction( + fn_name='add', + fn_args=(7, 11), + transaction={'from': coinbase, 'to': math_contract_address}, + ) + trace = web3.parity.traceCall(txn_params) + assert trace['stateDiff'] is None + assert trace['vmTrace'] is None + result = hex_to_integer(trace['output']) + assert result == 18 + + def test_vns_call_with_0_result(self, web3, math_contract, math_contract_address): + coinbase = web3.vns.coinbase + txn_params = math_contract._prepare_transaction( + fn_name='add', + fn_args=(0, 0), + transaction={'from': coinbase, 'to': math_contract_address}, + ) + trace = web3.parity.traceCall(txn_params) + assert trace['stateDiff'] is None + assert trace['vmTrace'] is None + result = hex_to_integer(trace['output']) + assert result == 0 + + @pytest.mark.parametrize( + 'raw_transaction', + [ + ( + # address 0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6 + '0xf8648085174876e8008252089439eeed73fb1d3855e90cbd42f348b3d7b340aaa601801ba0ec1295f00936acd0c2cb90ab2cdaacb8bf5e11b3d9957833595aca9ceedb7aada05dfc8937baec0e26029057abd3a1ef8c505dca2cdc07ffacb046d090d2bea06a' # noqa: E501 + ), + ] + ) + def test_trace_raw_transaction(self, + web3, + raw_transaction, + funded_account_for_raw_txn): + trace = web3.parity.traceRawTransaction(raw_transaction) + assert trace['stateDiff'] is None + assert trace['vmTrace'] is None + assert trace['trace'][0]['action']['from'] == funded_account_for_raw_txn.lower() + + def test_trace_filter(self, web3, txn_filter_params, parity_fixture_data): + trace = web3.parity.traceFilter(txn_filter_params) + assert isinstance(trace, list) + assert trace[0]['action']['from'] == add_0x_prefix(parity_fixture_data['coinbase']) + + def test_add_reserved_peer(self, web3): + peer_addr = 'enode://f1a6b0bdbf014355587c3018454d070ac57801f05d3b39fe85da574f002a32e929f683d72aa5a8318382e4d3c7a05c9b91687b0d997a39619fb8a6e7ad88e512@1.1.1.1:30300' # noqa: E501 + assert web3.parity.addReservedPeer(peer_addr) diff --git a/web3/_utils/module_testing/personal_module.py b/web3/_utils/module_testing/personal_module.py index 518c1b0a5e..a271f263d5 100644 --- a/web3/_utils/module_testing/personal_module.py +++ b/web3/_utils/module_testing/personal_module.py @@ -1,341 +1,171 @@ -import json -import pytest - -from eth_utils import ( - is_checksum_address, - is_list_like, - is_same_address, -) -from hexbytes import ( - HexBytes, -) - -PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f933673458f' -PASSWORD = 'web3-testing' -ADDRESS = '0x844B417c0C58B02c2224306047B9fb0D3264fE8c' - - -PRIVATE_KEY_FOR_UNLOCK = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' -ACCOUNT_FOR_UNLOCK = '0x12efDc31B1a8FA1A1e756DFD8A1601055C971E13' - - -class GoEthereumPersonalModuleTest: - def test_personal_importRawKey(self, web3): - actual = web3.geth.personal.importRawKey(PRIVATE_KEY_HEX, PASSWORD) - assert actual == ADDRESS - - def test_personal_listAccounts(self, web3): - accounts = web3.geth.personal.listAccounts() - assert is_list_like(accounts) - assert len(accounts) > 0 - assert all(( - is_checksum_address(item) - for item - in accounts - )) - - def test_personal_lockAccount(self, web3, unlockable_account_dual_type): - # TODO: how do we test this better? - web3.geth.personal.lockAccount(unlockable_account_dual_type) - - def test_personal_unlockAccount_success(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - result = web3.geth.personal.unlockAccount( - unlockable_account_dual_type, - unlockable_account_pw - ) - assert result is True - - def test_personal_unlockAccount_failure(self, - web3, - unlockable_account_dual_type): - with pytest.raises(ValueError): - web3.geth.personal.unlockAccount(unlockable_account_dual_type, 'bad-password') - - def test_personal_newAccount(self, web3): - new_account = web3.geth.personal.newAccount(PASSWORD) - assert is_checksum_address(new_account) - - def test_personal_sendTransaction(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - assert web3.eth.getBalance(unlockable_account_dual_type) > web3.toWei(1, 'ether') - txn_params = { - 'from': unlockable_account_dual_type, - 'to': unlockable_account_dual_type, - 'gas': 21000, - 'value': 1, - 'gasPrice': web3.toWei(1, 'gwei'), - } - txn_hash = web3.geth.personal.sendTransaction(txn_params, unlockable_account_pw) - assert txn_hash - transaction = web3.eth.getTransaction(txn_hash) - - assert is_same_address(transaction['from'], txn_params['from']) - assert is_same_address(transaction['to'], txn_params['to']) - assert transaction['gas'] == txn_params['gas'] - assert transaction['value'] == txn_params['value'] - assert transaction['gasPrice'] == txn_params['gasPrice'] - - def test_personal_sign_and_ecrecover(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - message = 'test-web3-geth-personal-sign' - signature = web3.geth.personal.sign( - message, - unlockable_account_dual_type, - unlockable_account_pw - ) - signer = web3.geth.personal.ecRecover(message, signature) - assert is_same_address(signer, unlockable_account_dual_type) - - @pytest.mark.xfail(reason="personal_signTypedData JSON RPC call has not been released in geth") - def test_personal_sign_typed_data(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - typed_message = ''' - { - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } - ''' - signature = HexBytes(web3.geth.personal.signTypedData( - json.loads(typed_message), - unlockable_account_dual_type, - unlockable_account_pw - )) - - expected_signature = HexBytes( - "0xc8b56aaeefd10ab4005c2455daf28d9082af661ac347cd" - "b612d5b5e11f339f2055be831bf57a6e6cb5f6d93448fa35" - "c1bd56fe1d745ffa101e74697108668c401c" - ) - assert signature == expected_signature - assert len(signature) == 32 + 32 + 1 - - -class ParityPersonalModuleTest(): - def test_personal_listAccounts(self, web3): - accounts = web3.parity.personal.listAccounts() - assert is_list_like(accounts) - assert len(accounts) > 0 - assert all(( - is_checksum_address(item) - for item - in accounts - )) - - def test_personal_unlockAccount_success(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - result = web3.parity.personal.unlockAccount( - unlockable_account_dual_type, - unlockable_account_pw, - None - ) - assert result is True - - # Seems to be an issue with Parity since this should return False - def test_personal_unlockAccount_failure(self, - web3, - unlockable_account_dual_type): - result = web3.parity.personal.unlockAccount( - unlockable_account_dual_type, - 'bad-password', - None - ) - assert result is True - - def test_personal_newAccount(self, web3): - new_account = web3.parity.personal.newAccount(PASSWORD) - assert is_checksum_address(new_account) - - @pytest.mark.xfail(reason='this non-standard json-rpc method is not implemented on parity') - def test_personal_lockAccount(self, web3, unlocked_account): - super().test_personal_lockAccount(web3, unlocked_account) - - @pytest.mark.xfail(reason='this non-standard json-rpc method is not implemented on parity') - def test_personal_importRawKey(self, web3): - super().test_personal_importRawKey(web3) - - def test_personal_sendTransaction(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - assert web3.eth.getBalance(unlockable_account_dual_type) > web3.toWei(1, 'ether') - txn_params = { - 'from': unlockable_account_dual_type, - 'to': unlockable_account_dual_type, - 'gas': 21000, - 'value': 1, - 'gasPrice': web3.toWei(1, 'gwei'), - } - txn_hash = web3.parity.personal.sendTransaction(txn_params, unlockable_account_pw) - assert txn_hash - transaction = web3.eth.getTransaction(txn_hash) - - assert is_same_address(transaction['from'], txn_params['from']) - assert is_same_address(transaction['to'], txn_params['to']) - assert transaction['gas'] == txn_params['gas'] - assert transaction['value'] == txn_params['value'] - assert transaction['gasPrice'] == txn_params['gasPrice'] - - def test_personal_sign_and_ecrecover(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - message = 'test-web3-parity-personal-sign' - signature = web3.parity.personal.sign( - message, - unlockable_account_dual_type, - unlockable_account_pw - ) - signer = web3.parity.personal.ecRecover(message, signature) - assert is_same_address(signer, unlockable_account_dual_type) - - def test_personal_sign_typed_data(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - typed_message = ''' - { - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } - ''' - signature = HexBytes(web3.parity.personal.signTypedData( - json.loads(typed_message), - unlockable_account_dual_type, - unlockable_account_pw - )) - - expected_signature = HexBytes( - "0xc8b56aaeefd10ab4005c2455daf28d9082af661ac347cd" - "b612d5b5e11f339f2055be831bf57a6e6cb5f6d93448fa35" - "c1bd56fe1d745ffa101e74697108668c401c" - ) - assert signature == expected_signature - assert len(signature) == 32 + 32 + 1 - - def test_invalid_personal_sign_typed_data(self, - web3, - unlockable_account_dual_type, - unlockable_account_pw): - invalid_typed_message = ''' - { - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person[2]"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": [{ - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }], - "contents": "Hello, Bob!" - } - } - ''' - with pytest.raises(ValueError, - match=r".*Expected 2 items for array type Person\[2\], got 1 items.*"): - web3.parity.personal.signTypedData( - json.loads(invalid_typed_message), - unlockable_account_dual_type, - unlockable_account_pw - ) +import pytest + +from vns_utils import ( + is_checksum_address, + is_list_like, + is_same_address, +) + +PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f933673458f' +PASSWORD = 'web3-testing' +ADDRESS = '0x844B417c0C58B02c2224306047B9fb0D3264fE8c' + + +PRIVATE_KEY_FOR_UNLOCK = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' +ACCOUNT_FOR_UNLOCK = '0x12efDc31B1a8FA1A1e756DFD8A1601055C971E13' + + +class GoEthereumPersonalModuleTest: + def test_personal_importRawKey(self, web3): + actual = web3.geth.personal.importRawKey(PRIVATE_KEY_HEX, PASSWORD) + assert actual == ADDRESS + + def test_personal_listAccounts(self, web3): + accounts = web3.geth.personal.listAccounts() + assert is_list_like(accounts) + assert len(accounts) > 0 + assert all(( + is_checksum_address(item) + for item + in accounts + )) + + def test_personal_lockAccount(self, web3, unlockable_account_dual_type): + # TODO: how do we test this better? + web3.geth.personal.lockAccount(unlockable_account_dual_type) + + def test_personal_unlockAccount_success(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + result = web3.geth.personal.unlockAccount( + unlockable_account_dual_type, + unlockable_account_pw + ) + assert result is True + + def test_personal_unlockAccount_failure(self, + web3, + unlockable_account_dual_type): + with pytest.raises(ValueError): + web3.geth.personal.unlockAccount(unlockable_account_dual_type, 'bad-password') + + def test_personal_newAccount(self, web3): + new_account = web3.geth.personal.newAccount(PASSWORD) + assert is_checksum_address(new_account) + + def test_personal_sendTransaction(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + assert web3.vns.getBalance(unlockable_account_dual_type) > web3.toWei(1, 'ether') + txn_params = { + 'from': unlockable_account_dual_type, + 'to': unlockable_account_dual_type, + 'gas': 21000, + 'value': 1, + 'gasPrice': web3.toWei(1, 'gwei'), + } + txn_hash = web3.geth.personal.sendTransaction(txn_params, unlockable_account_pw) + assert txn_hash + transaction = web3.vns.getTransaction(txn_hash) + + assert is_same_address(transaction['from'], txn_params['from']) + assert is_same_address(transaction['to'], txn_params['to']) + assert transaction['gas'] == txn_params['gas'] + assert transaction['value'] == txn_params['value'] + assert transaction['gasPrice'] == txn_params['gasPrice'] + + def test_personal_sign_and_ecrecover(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + message = 'test-web3-geth-personal-sign' + signature = web3.geth.personal.sign( + message, + unlockable_account_dual_type, + unlockable_account_pw + ) + signer = web3.geth.personal.ecRecover(message, signature) + assert is_same_address(signer, unlockable_account_dual_type) + + +class ParityPersonalModuleTest(): + def test_personal_listAccounts(self, web3): + accounts = web3.parity.personal.listAccounts() + assert is_list_like(accounts) + assert len(accounts) > 0 + assert all(( + is_checksum_address(item) + for item + in accounts + )) + + def test_personal_unlockAccount_success(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + result = web3.parity.personal.unlockAccount( + unlockable_account_dual_type, + unlockable_account_pw, + None + ) + assert result is True + + # Seems to be an issue with Parity since this should return False + def test_personal_unlockAccount_failure(self, + web3, + unlockable_account_dual_type): + result = web3.parity.personal.unlockAccount( + unlockable_account_dual_type, + 'bad-password', + None + ) + assert result is True + + def test_personal_newAccount(self, web3): + new_account = web3.parity.personal.newAccount(PASSWORD) + assert is_checksum_address(new_account) + + def test_personal_lockAccount(self, web3, unlocked_account): + pytest.xfail('this non-standard json-rpc method is not implemented on parity') + super().test_personal_lockAccount(web3, unlocked_account) + + def test_personal_importRawKey(self, web3): + pytest.xfail('this non-standard json-rpc method is not implemented on parity') + super().test_personal_importRawKey(web3) + + def test_personal_sendTransaction(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + assert web3.vns.getBalance(unlockable_account_dual_type) > web3.toWei(1, 'ether') + txn_params = { + 'from': unlockable_account_dual_type, + 'to': unlockable_account_dual_type, + 'gas': 21000, + 'value': 1, + 'gasPrice': web3.toWei(1, 'gwei'), + } + txn_hash = web3.parity.personal.sendTransaction(txn_params, unlockable_account_pw) + assert txn_hash + transaction = web3.vns.getTransaction(txn_hash) + + assert is_same_address(transaction['from'], txn_params['from']) + assert is_same_address(transaction['to'], txn_params['to']) + assert transaction['gas'] == txn_params['gas'] + assert transaction['value'] == txn_params['value'] + assert transaction['gasPrice'] == txn_params['gasPrice'] + + def test_personal_sign_and_ecrecover(self, + web3, + unlockable_account_dual_type, + unlockable_account_pw): + message = 'test-web3-parity-personal-sign' + signature = web3.parity.personal.sign( + message, + unlockable_account_dual_type, + unlockable_account_pw + ) + signer = web3.parity.personal.ecRecover(message, signature) + assert is_same_address(signer, unlockable_account_dual_type) diff --git a/web3/_utils/module_testing/shh_module.py b/web3/_utils/module_testing/shh_module.py index 404d94e239..1ade5a26c2 100644 --- a/web3/_utils/module_testing/shh_module.py +++ b/web3/_utils/module_testing/shh_module.py @@ -1,595 +1,394 @@ -import pytest -import time - -from eth_utils import ( - is_integer, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.filters import ( - ShhFilter, -) - - -class GoEthereumShhModuleTest(): - # - # shh_filter - # - def test_shh_sync_filter(self, web3): - sender = web3.geth.shh.new_key_pair() - sender_pub = web3.geth.shh.get_public_key(sender) - - receiver = web3.geth.shh.new_key_pair() - receiver_pub = web3.geth.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.geth.shh.new_message_filter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - shh_filter = ShhFilter(web3, shh_filter_id) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - received_messages = shh_filter.get_new_entries() - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[1]) - assert message["topic"] == HexBytes(topic) - - def test_shh_sync_filter_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - sender = web3.geth.shh.newKeyPair() - sender_pub = web3.geth.shh.getPublicKey(sender) - receiver = web3.geth.shh.newKeyPair() - receiver_pub = web3.geth.shh.getPublicKey(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.geth.shh.newMessageFilter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - shh_filter = ShhFilter(web3, shh_filter_id) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - received_messages = shh_filter.get_new_entries() - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[1]) - assert message["topic"] == HexBytes(topic) - - def test_shh_async_filter(self, web3): - received_messages = [] - - sender = web3.geth.shh.new_key_pair() - sender_pub = web3.geth.shh.get_public_key(sender) - receiver = web3.geth.shh.new_key_pair() - receiver_pub = web3.geth.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.geth.shh.new_message_filter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - shh_filter = ShhFilter(web3, shh_filter_id, poll_interval=0.5) - watcher = shh_filter.watch(received_messages.extend) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'pubKey': receiver_pub - }) - time.sleep(1) - - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[0]) - assert message["topic"] == HexBytes(topic) - - watcher.stop() - - def test_shh_async_filter_deprecated(self, web3): - received_messages = [] - - with pytest.warns(DeprecationWarning) as warnings: - sender = web3.geth.shh.newKeyPair() - sender_pub = web3.geth.shh.getPublicKey(sender) - - receiver = web3.geth.shh.newKeyPair() - receiver_pub = web3.geth.shh.getPublicKey(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.geth.shh.newMessageFilter({ - 'privateKeyID': receiver, - 'sig': sender_pub, - 'topics': [topic] - }) - - shh_filter = ShhFilter(web3, shh_filter_id, poll_interval=0.5) - watcher = shh_filter.watch(received_messages.extend) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[0], - 'topic': topic, - 'pubKey': receiver_pub - }) - time.sleep(1) - - web3.geth.shh.post({ - 'sig': sender, - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payloads[1], - 'pubKey': receiver_pub - }) - time.sleep(1) - - assert len(received_messages) == 1 - assert len(warnings) == 5 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[0]) - assert message["topic"] == HexBytes(topic) - - watcher.stop() - - def test_shh_remove_filter_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - receiver = web3.geth.shh.newKeyPair() - receiver_pub = web3.geth.shh.getPublicKey(receiver) - payload = web3.toHex(text="test message :)") - shh_filter_id = web3.geth.shh.newMessageFilter({'privateKeyID': receiver}) - shh_filter = ShhFilter(web3, shh_filter_id) - - web3.geth.shh.post({ - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payload, - 'pubKey': receiver_pub - }) - time.sleep(1) - - message = shh_filter.get_new_entries()[0] - assert message["payload"] == HexBytes(payload) - - assert web3.geth.shh.deleteMessageFilter(shh_filter.filter_id) - - try: - web3.geth.shh.getMessages(shh_filter.filter_id) - assert False - except BaseException: - assert True - - def test_shh_remove_filter(self, web3): - - receiver = web3.geth.shh.new_key_pair() - receiver_pub = web3.geth.shh.get_public_key(receiver) - payload = web3.toHex(text="test message :)") - shh_filter_id = web3.geth.shh.new_message_filter({'privateKeyID': receiver}) - shh_filter = ShhFilter(web3, shh_filter_id) - - web3.geth.shh.post({ - 'powTarget': 2.5, - 'powTime': 2, - 'payload': payload, - 'pubKey': receiver_pub - }) - time.sleep(1) - - message = shh_filter.get_new_entries()[0] - assert message["payload"] == HexBytes(payload) - - assert web3.geth.shh.delete_message_filter(shh_filter.filter_id) - - try: - web3.geth.shh.get_filter_messages(shh_filter.filter_id) - assert False - except BaseException: - assert True - - # - # shh_key_pair - # - def test_shh_asymmetric_key_pair(self, web3): - # Test generating key - - key_id = web3.geth.shh.new_key_pair() - assert web3.geth.shh.has_key_pair(key_id) - assert len(web3.geth.shh.get_public_key(key_id)) == 132 - private_key = web3.geth.shh.get_private_key(key_id) - assert len(private_key) == 66 - assert web3.geth.shh.delete_key_pair(key_id) - - # Test adding a key - assert not web3.geth.shh.has_key_pair(key_id) - key_id = web3.geth.shh.add_private_key(private_key) - assert web3.geth.shh.has_key_pair(key_id) - assert web3.geth.shh.delete_key_pair(key_id) - - def test_shh_asymmetric_key_pair_deprecated(self, web3): - # Test generating key - with pytest.warns(DeprecationWarning): - key_id = web3.geth.shh.newKeyPair() - assert web3.geth.shh.hasKeyPair(key_id) - assert len(web3.geth.shh.getPublicKey(key_id)) == 132 - private_key = web3.geth.shh.getPrivateKey(key_id) - assert len(private_key) == 66 - assert web3.geth.shh.deleteKeyPair(key_id) - - # Test adding a key - assert not web3.geth.shh.hasKeyPair(key_id) - key_id = web3.geth.shh.addPrivateKey(private_key) - assert web3.geth.shh.hasKeyPair(key_id) - assert web3.geth.shh.deleteKeyPair(key_id) - - def test_shh_symmetric_key_pair(self, web3): - # Test generating key - key_id = web3.geth.shh.new_sym_key() - assert web3.geth.shh.has_sym_key(key_id) - - key = web3.geth.shh.get_sym_key(key_id) - assert len(key) == 66 - assert web3.geth.shh.delete_sym_key(key_id) - - # Test adding a key - assert not web3.geth.shh.has_sym_key(key_id) - key_id = web3.geth.shh.add_sym_key(key) - assert web3.geth.shh.has_sym_key(key_id) - assert web3.geth.shh.delete_sym_key(key_id) - - def test_shh_symmetric_key_pair_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - # Test generating key - key_id = web3.geth.shh.newSymKey() - assert web3.geth.shh.hasSymKey(key_id) - - key = web3.geth.shh.getSymKey(key_id) - assert len(key) == 66 - assert web3.geth.shh.deleteSymKey(key_id) - - # Test adding a key - assert not web3.geth.shh.hasSymKey(key_id) - key_id = web3.geth.shh.addSymKey(key) - assert web3.geth.shh.hasSymKey(key_id) - assert web3.geth.shh.deleteSymKey(key_id) - - def test_shh_symmetric_key_pair_from_password_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - key_id = web3.geth.shh.generateSymKeyFromPassword('shh be quiet') - - assert web3.geth.shh.hasSymKey(key_id) - assert len(web3.geth.shh.getSymKey(key_id)) == 66 - assert web3.geth.shh.deleteSymKey(key_id) - - def test_shh_symmetric_key_pair_from_password(self, web3): - - key_id = web3.geth.shh.generate_sym_key_from_password('shh be quiet') - - assert web3.geth.shh.has_sym_key(key_id) - assert len(web3.geth.shh.get_sym_key(key_id)) == 66 - assert web3.geth.shh.delete_sym_key(key_id) - - # - # shh_post - # - def test_shh_post(self, web3): - receiver_pub = web3.geth.shh.get_public_key(web3.geth.shh.new_key_pair()) - assert web3.geth.shh.post({ - "topic": "0x12345678", - "powTarget": 2.5, - "powTime": 2, - "payload": web3.toHex(text="testing shh on web3.py"), - "pubKey": receiver_pub, - }) - - def test_shh_post_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - receiver_pub = web3.geth.shh.getPublicKey(web3.geth.shh.newKeyPair()) - assert web3.geth.shh.post({ - "topic": "0x12345678", - "powTarget": 2.5, - "powTime": 2, - "payload": web3.toHex(text="testing shh on web3.py"), - "pubKey": receiver_pub, - }) - - # - # shh_properties - # - def test_shh_version(self, web3): - version = web3.geth.shh.version() - if '1.7' in web3.clientVersion: - assert version == '5.0' - else: - assert version == '6.0' - - def test_shh_info(self, web3): - pre_info = web3.geth.shh.info() - assert pre_info["maxMessageSize"] != 2048 - assert pre_info["minPow"] != 0.6 - - web3.geth.shh.set_max_message_size(2048) - web3.geth.shh.set_min_pow(0.6) - - info = web3.geth.shh.info() - - assert len(info) == 4 - assert info["maxMessageSize"] == 2048 - assert info["minPow"] == 0.6 - - def test_shh_info_deprecated(self, web3): - with pytest.warns(DeprecationWarning): - pre_info = web3.geth.shh.info() - assert pre_info["maxMessageSize"] != 1024 - assert pre_info["minPow"] != 0.5 - - web3.geth.shh.setMaxMessageSize(1024) - web3.geth.shh.setMinPoW(0.5) - - info = web3.geth.shh.info() - - assert len(info) == 4 - assert info["maxMessageSize"] == 1024 - assert info["minPow"] == 0.5 - - -class ParityShhModuleTest(): - # - # shh_filter - # - def test_shh_sync_filter(self, web3): - sender = web3.parity.shh.new_key_pair() - sender_pub = web3.parity.shh.get_public_key(sender) - - receiver = web3.parity.shh.new_key_pair() - receiver_pub = web3.parity.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.parity.shh.new_mssage_filter({ - 'decryptWith': receiver, - 'from': sender_pub, - 'topics': [topic], - }) - - shh_filter = ShhFilter(web3, shh_filter_id) - time.sleep(1) - - assert web3.parity.shh.post({ - 'from': sender, - 'payload': payloads[0], - 'to': {'public': receiver_pub}, - 'topics': [topic], - 'priority': 1000, - 'ttl': 100, - }) - time.sleep(1) - - assert web3.parity.shh.post({ - 'from': receiver, - 'payload': payloads[1], - 'topics': [topic], - 'priority': 1000, - 'ttl': 100, - }) - time.sleep(1) - received_messages = shh_filter.get_new_entries() - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[1]) - assert message["topic"] == HexBytes(topic) - - def test_shh_async_filter(self, web3): - received_messages = [] - - sender = web3.parity.shh.new_key_pair() - sender_pub = web3.parity.shh.get_public_key(sender) - - receiver = web3.parity.shh.new_key_pair() - receiver_pub = web3.parity.shh.get_public_key(receiver) - - topic = '0x13370000' - payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] - - shh_filter_id = web3.parity.shh.new_message_filter({ - 'decryptWith': receiver, - 'from': sender_pub, - 'topics': [topic] - }) - shh_filter = ShhFilter(web3, shh_filter_id, poll_interval=0.5) - watcher = shh_filter.watch(received_messages.extend) - - web3.parity.shh.post({ - 'from': sender, - 'payload': payloads[0], - 'topics': [topic], - 'to': {'public': receiver_pub}, - 'priority': 1000, - 'ttl': 100, - }) - time.sleep(1) - - web3.parity.shh.post({ - 'from': sender, - 'payload': payloads[1], - 'topics': [topic], - 'to': {'identity': receiver}, - 'priority': 1000, - 'ttl': 100, - }) - time.sleep(1) - - assert len(received_messages) == 1 - - message = received_messages[0] - - assert message["payload"] == HexBytes(payloads[0]) - assert message["topic"] == HexBytes(topic) - - watcher.stop() - - # Sometimes the post fails because PoW is too low. - # We don't care if an error or a True response comes back, - # we only care that we're interfacing correctly with Parity - @pytest.mark.xfail(strict=False, raises=ValueError) - def test_shh_remove_filter(self, web3): - receiver = web3.parity.shh.new_key_pair() - receiver_pub = web3.parity.shh.get_public_key(receiver) - - payload = web3.toHex(text="test message :)") - topic = '0x13370000' - shh_filter = web3.parity.shh.new_message_filter({'decryptWith': None, 'topics': [topic]}) - - assert web3.parity.shh.post({ - 'payload': payload, - 'topics': [topic], - 'to': {'public': receiver_pub}, - 'priority': 500, - 'ttl': 400, - }) - time.sleep(1) - - # Commented out until parity filter bug is resolved - # https://github.com/paritytech/parity-ethereum/issues/10565 - # message = ShhFilter(web3, shh_filter).get_new_entries() - # assert message["payload"] == HexBytes(payload) - - assert web3.parity.shh.delete_message_filter(shh_filter) - - try: - web3.parity.shh.get_filter_messages(shh_filter) - assert False - except BaseException: - assert True - - # - # shh_key_pair - # - def test_shh_asymmetric_key_pair(self, web3): - # Test generating key - key_id = web3.parity.shh.new_key_pair() - assert len(web3.parity.shh.get_public_key(key_id)) == 130 - - private_key = web3.parity.shh.get_private_key(key_id) - assert len(private_key) == 66 - assert web3.parity.shh.delete_key(key_id) - - # Test adding a key - assert not web3.parity.shh.delete_key(key_id) - key_id = web3.parity.shh.add_private_key(private_key) - assert web3.parity.shh.delete_key(key_id) - - def test_shh_symmetric_key_pair(self, web3): - # Test generating key - key_id = web3.parity.shh.new_sym_key() - - key = web3.parity.shh.get_sym_key(key_id) - assert len(key) == 66 - assert web3.parity.shh.delete_key(key_id) - - # Test adding a key - assert not web3.parity.shh.delete_key(key_id) - key_id = web3.parity.shh.add_sym_key(key) - assert web3.parity.shh.delete_key(key_id) - - # - # shh_post - # - - # Sometimes the post fails because PoW is too low. - # We don't care if an error or a True response comes back, - # we only care that we're interfacing correctly with Parity - @pytest.mark.xfail(strict=False, raises=ValueError) - def test_shh_post(self, web3): - sender = web3.parity.shh.new_key_pair() - assert web3.parity.shh.post({ - "topics": ["0x12345678"], - "payload": web3.toHex(text="testing shh on web3.py"), - "from": sender, - "priority": 40, - "ttl": 400, - }) - - def test_shh_info(self, web3): - info = web3.parity.shh.info() - - assert len(info) == 3 - assert is_integer(info["memory"]) - assert is_integer(info["messages"]) - assert is_integer(info["targetMemory"]) +import time + +from vns_utils import ( + is_integer, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.filters import ( + ShhFilter, +) + + +class GoEthereumShhModuleTest(): + # + # shh_filter + # + def test_shh_sync_filter(self, web3): + sender = web3.geth.shh.newKeyPair() + sender_pub = web3.geth.shh.getPublicKey(sender) + + receiver = web3.geth.shh.newKeyPair() + receiver_pub = web3.geth.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter_id = web3.geth.shh.newMessageFilter({ + 'privateKeyID': receiver, + 'sig': sender_pub, + 'topics': [topic] + }) + shh_filter = ShhFilter(web3, shh_filter_id) + + web3.geth.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[0], + 'pubKey': receiver_pub + }) + time.sleep(1) + + web3.geth.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[1], + 'topic': topic, + 'pubKey': receiver_pub + }) + time.sleep(1) + + received_messages = shh_filter.get_new_entries() + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[1]) + assert message["topic"] == HexBytes(topic) + + def test_shh_async_filter(self, web3): + received_messages = [] + + sender = web3.geth.shh.newKeyPair() + sender_pub = web3.geth.shh.getPublicKey(sender) + + receiver = web3.geth.shh.newKeyPair() + receiver_pub = web3.geth.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter_id = web3.geth.shh.newMessageFilter({ + 'privateKeyID': receiver, + 'sig': sender_pub, + 'topics': [topic] + }) + shh_filter = ShhFilter(web3, shh_filter_id, poll_interval=0.5) + watcher = shh_filter.watch(received_messages.extend) + + web3.geth.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[0], + 'topic': topic, + 'pubKey': receiver_pub + }) + time.sleep(1) + + web3.geth.shh.post({ + 'sig': sender, + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payloads[1], + 'pubKey': receiver_pub + }) + time.sleep(1) + + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[0]) + assert message["topic"] == HexBytes(topic) + + watcher.stop() + + def test_shh_remove_filter(self, web3): + receiver = web3.geth.shh.newKeyPair() + receiver_pub = web3.geth.shh.getPublicKey(receiver) + + payload = web3.toHex(text="test message :)") + shh_filter_id = web3.geth.shh.newMessageFilter({'privateKeyID': receiver}) + shh_filter = ShhFilter(web3, shh_filter_id) + + web3.geth.shh.post({ + 'powTarget': 2.5, + 'powTime': 2, + 'payload': payload, + 'pubKey': receiver_pub + }) + time.sleep(1) + + message = shh_filter.get_new_entries()[0] + assert message["payload"] == HexBytes(payload) + + assert web3.geth.shh.deleteMessageFilter(shh_filter.filter_id) + + try: + web3.geth.shh.getMessages(shh_filter.filter_id) + assert False + except: + assert True + + # + # shh_key_pair + # + def test_shh_asymmetric_key_pair(self, web3): + # Test generating key + key_id = web3.geth.shh.newKeyPair() + assert web3.geth.shh.hasKeyPair(key_id) + assert len(web3.geth.shh.getPublicKey(key_id)) == 132 + + private_key = web3.geth.shh.getPrivateKey(key_id) + assert len(private_key) == 66 + assert web3.geth.shh.deleteKeyPair(key_id) + + # Test adding a key + assert not web3.geth.shh.hasKeyPair(key_id) + key_id = web3.geth.shh.addPrivateKey(private_key) + assert web3.geth.shh.hasKeyPair(key_id) + assert web3.geth.shh.deleteKeyPair(key_id) + + def test_shh_symmetric_key_pair(self, web3): + # Test generating key + key_id = web3.geth.shh.newSymKey() + assert web3.geth.shh.hasSymKey(key_id) + + key = web3.geth.shh.getSymKey(key_id) + assert len(key) == 66 + assert web3.geth.shh.deleteSymKey(key_id) + + # Test adding a key + assert not web3.geth.shh.hasSymKey(key_id) + key_id = web3.geth.shh.addSymKey(key) + assert web3.geth.shh.hasSymKey(key_id) + assert web3.geth.shh.deleteSymKey(key_id) + + def test_shh_symmetric_key_pair_from_password(self, web3): + key_id = web3.geth.shh.generateSymKeyFromPassword('shh be quiet') + + assert web3.geth.shh.hasSymKey(key_id) + assert len(web3.geth.shh.getSymKey(key_id)) == 66 + assert web3.geth.shh.deleteSymKey(key_id) + + # + # shh_post + # + def test_shh_post(self, web3): + receiver_pub = web3.geth.shh.getPublicKey(web3.geth.shh.newKeyPair()) + assert web3.geth.shh.post({ + "topic": "0x12345678", + "powTarget": 2.5, + "powTime": 2, + "payload": web3.toHex(text="testing shh on web3.py"), + "pubKey": receiver_pub, + }) + + # + # shh_properties + # + def test_shh_version(self, web3): + version = web3.geth.shh.version() + if '1.7' in web3.clientVersion: + assert version == '5.0' + else: + assert version == '6.0' + + def test_shh_info(self, web3): + pre_info = web3.geth.shh.info() + assert pre_info["maxMessageSize"] is not 1024 + assert pre_info["minPow"] is not 0.5 + + web3.geth.shh.setMaxMessageSize(1024) + web3.geth.shh.setMinPoW(0.5) + + info = web3.geth.shh.info() + + assert len(info) == 4 + assert info["maxMessageSize"] == 1024 + assert info["minPow"] == 0.5 + + +class ParityShhModuleTest(): + # + # shh_filter + # + def test_shh_sync_filter(self, web3): + sender = web3.parity.shh.newKeyPair() + sender_pub = web3.parity.shh.getPublicKey(sender) + + receiver = web3.parity.shh.newKeyPair() + receiver_pub = web3.parity.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter_id = web3.parity.shh.newMessageFilter({ + 'decryptWith': receiver, + 'from': sender_pub, + 'topics': [topic], + }) + + shh_filter = ShhFilter(web3, shh_filter_id) + time.sleep(1) + + assert web3.parity.shh.post({ + 'from': sender, + 'payload': payloads[0], + 'to': {'public': receiver_pub}, + 'topics': [topic], + 'priority': 1000, + 'ttl': 100, + }) + time.sleep(1) + + assert web3.parity.shh.post({ + 'from': receiver, + 'payload': payloads[1], + 'topics': [topic], + 'priority': 1000, + 'ttl': 100, + }) + time.sleep(1) + received_messages = shh_filter.get_new_entries() + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[1]) + assert message["topic"] == HexBytes(topic) + + def test_shh_async_filter(self, web3): + received_messages = [] + + sender = web3.parity.shh.newKeyPair() + sender_pub = web3.parity.shh.getPublicKey(sender) + + receiver = web3.parity.shh.newKeyPair() + receiver_pub = web3.parity.shh.getPublicKey(receiver) + + topic = '0x13370000' + payloads = [web3.toHex(text="test message :)"), web3.toHex(text="2nd test message")] + + shh_filter_id = web3.parity.shh.newMessageFilter({ + 'decryptWith': receiver, + 'from': sender_pub, + 'topics': [topic] + }) + shh_filter = ShhFilter(web3, shh_filter_id, poll_interval=0.5) + watcher = shh_filter.watch(received_messages.extend) + + web3.parity.shh.post({ + 'from': sender, + 'payload': payloads[0], + 'topics': [topic], + 'to': {'public': receiver_pub}, + 'priority': 1000, + 'ttl': 100, + }) + time.sleep(1) + + web3.parity.shh.post({ + 'from': sender, + 'payload': payloads[1], + 'topics': [topic], + 'to': {'identity': receiver}, + 'priority': 1000, + 'ttl': 100, + }) + time.sleep(1) + + assert len(received_messages) == 1 + + message = received_messages[0] + + assert message["payload"] == HexBytes(payloads[0]) + assert message["topic"] == HexBytes(topic) + + watcher.stop() + + def test_shh_remove_filter(self, web3): + receiver = web3.parity.shh.newKeyPair() + receiver_pub = web3.parity.shh.getPublicKey(receiver) + + payload = web3.toHex(text="test message :)") + topic = '0x13370000' + shh_filter = web3.parity.shh.newMessageFilter({'decryptWith': None, 'topics': [topic]}) + + assert web3.parity.shh.post({ + 'payload': payload, + 'topics': [topic], + 'to': {'public': receiver_pub}, + 'priority': 500, + 'ttl': 400, + }) + time.sleep(1) + + # Commented out until parity filter bug is resolved + # https://github.com/paritytech/parity-ethereum/issues/10565 + # message = ShhFilter(web3, shh_filter).get_new_entries() + # assert message["payload"] == HexBytes(payload) + + assert web3.parity.shh.deleteMessageFilter(shh_filter) + + try: + web3.parity.shh.getFilterMessages(shh_filter) + assert False + except: + assert True + + # + # shh_key_pair + # + def test_shh_asymmetric_key_pair(self, web3): + # Test generating key + key_id = web3.parity.shh.newKeyPair() + assert len(web3.parity.shh.getPublicKey(key_id)) == 130 + + private_key = web3.parity.shh.getPrivateKey(key_id) + assert len(private_key) == 66 + assert web3.parity.shh.deleteKey(key_id) + + # Test adding a key + assert not web3.parity.shh.deleteKey(key_id) + key_id = web3.parity.shh.addPrivateKey(private_key) + assert web3.parity.shh.deleteKey(key_id) + + def test_shh_symmetric_key_pair(self, web3): + # Test generating key + key_id = web3.parity.shh.newSymKey() + + key = web3.parity.shh.getSymKey(key_id) + assert len(key) == 66 + assert web3.parity.shh.deleteKey(key_id) + + # Test adding a key + assert not web3.parity.shh.deleteKey(key_id) + key_id = web3.parity.shh.addSymKey(key) + assert web3.parity.shh.deleteKey(key_id) + + # + # shh_post + # + def test_shh_post(self, web3): + sender = web3.parity.shh.newKeyPair() + assert web3.parity.shh.post({ + "topics": ["0x12345678"], + "payload": web3.toHex(text="testing shh on web3.py"), + "from": sender, + "priority": 40, + "ttl": 400, + }) + + def test_shh_info(self, web3): + info = web3.parity.shh.info() + + assert len(info) == 3 + assert is_integer(info["memory"]) + assert is_integer(info["messages"]) + assert is_integer(info["targetMemory"]) diff --git a/web3/_utils/module_testing/version_module.py b/web3/_utils/module_testing/version_module.py index b126b6deef..5deb1ffe3f 100644 --- a/web3/_utils/module_testing/version_module.py +++ b/web3/_utils/module_testing/version_module.py @@ -1,11 +1,11 @@ -from eth_utils import ( - is_string, -) - - -class VersionModuleTest: - def test_eth_protocolVersion(self, web3): - protocol_version = web3.eth.protocolVersion - - assert is_string(protocol_version) - assert protocol_version.isdigit() +from vns_utils import ( + is_string, +) + + +class VersionModuleTest: + def test_vns_protocolVersion(self, web3): + protocol_version = web3.vns.protocolVersion + + assert is_string(protocol_version) + assert protocol_version.isdigit() diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/vns_module.py similarity index 58% rename from web3/_utils/module_testing/eth_module.py rename to web3/_utils/module_testing/vns_module.py index a656aac6c6..c6e7fe3ba7 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/vns_module.py @@ -1,944 +1,849 @@ -# -*- coding: utf-8 -*- - -import json -import pytest - -from eth_utils import ( - is_boolean, - is_bytes, - is_checksum_address, - is_dict, - is_integer, - is_list_like, - is_same_address, - is_string, -) -from hexbytes import ( - HexBytes, -) - -from web3.exceptions import ( - BlockNotFound, - InvalidAddress, - TransactionNotFound, -) - -UNKNOWN_ADDRESS = '0xdEADBEeF00000000000000000000000000000000' -UNKNOWN_HASH = '0xdeadbeef00000000000000000000000000000000000000000000000000000000' - - -class EthModuleTest: - def test_eth_protocolVersion(self, web3): - protocol_version = web3.eth.protocolVersion - - assert is_string(protocol_version) - assert protocol_version.isdigit() - - def test_eth_syncing(self, web3): - syncing = web3.eth.syncing - - assert is_boolean(syncing) or is_dict(syncing) - - if is_boolean(syncing): - assert syncing is False - elif is_dict(syncing): - assert 'startingBlock' in syncing - assert 'currentBlock' in syncing - assert 'highestBlock' in syncing - - assert is_integer(syncing['startingBlock']) - assert is_integer(syncing['currentBlock']) - assert is_integer(syncing['highestBlock']) - - def test_eth_coinbase(self, web3): - coinbase = web3.eth.coinbase - assert is_checksum_address(coinbase) - - def test_eth_mining(self, web3): - mining = web3.eth.mining - assert is_boolean(mining) - - def test_eth_hashrate(self, web3): - hashrate = web3.eth.hashrate - assert is_integer(hashrate) - assert hashrate >= 0 - - def test_eth_chainId(self, web3): - chain_id = web3.eth.chainId - # chain id value from geth fixture genesis file - assert chain_id == 131277322940537 - - def test_eth_gasPrice(self, web3): - gas_price = web3.eth.gasPrice - assert is_integer(gas_price) - assert gas_price > 0 - - def test_eth_accounts(self, web3): - accounts = web3.eth.accounts - assert is_list_like(accounts) - assert len(accounts) != 0 - assert all(( - is_checksum_address(account) - for account - in accounts - )) - assert web3.eth.coinbase in accounts - - def test_eth_blockNumber(self, web3): - block_number = web3.eth.blockNumber - assert is_integer(block_number) - assert block_number >= 0 - - def test_eth_getBalance(self, web3): - coinbase = web3.eth.coinbase - - with pytest.raises(InvalidAddress): - web3.eth.getBalance(coinbase.lower()) - - balance = web3.eth.getBalance(coinbase) - - assert is_integer(balance) - assert balance >= 0 - - def test_eth_getStorageAt(self, web3, emitter_contract_address): - storage = web3.eth.getStorageAt(emitter_contract_address, 0) - assert isinstance(storage, HexBytes) - - def test_eth_getStorageAt_invalid_address(self, web3): - coinbase = web3.eth.coinbase - with pytest.raises(InvalidAddress): - web3.eth.getStorageAt(coinbase.lower(), 0) - - def test_eth_getTransactionCount(self, web3, unlocked_account_dual_type): - transaction_count = web3.eth.getTransactionCount(unlocked_account_dual_type) - assert is_integer(transaction_count) - assert transaction_count >= 0 - - def test_eth_getTransactionCount_invalid_address(self, web3): - coinbase = web3.eth.coinbase - with pytest.raises(InvalidAddress): - web3.eth.getTransactionCount(coinbase.lower()) - - def test_eth_getBlockTransactionCountByHash_empty_block(self, web3, empty_block): - transaction_count = web3.eth.getBlockTransactionCount(empty_block['hash']) - - assert is_integer(transaction_count) - assert transaction_count == 0 - - def test_eth_getBlockTransactionCountByNumber_empty_block(self, web3, empty_block): - transaction_count = web3.eth.getBlockTransactionCount(empty_block['number']) - - assert is_integer(transaction_count) - assert transaction_count == 0 - - def test_eth_getBlockTransactionCountByHash_block_with_txn(self, web3, block_with_txn): - transaction_count = web3.eth.getBlockTransactionCount(block_with_txn['hash']) - - assert is_integer(transaction_count) - assert transaction_count >= 1 - - def test_eth_getBlockTransactionCountByNumber_block_with_txn(self, web3, block_with_txn): - transaction_count = web3.eth.getBlockTransactionCount(block_with_txn['number']) - - assert is_integer(transaction_count) - assert transaction_count >= 1 - - def test_eth_getUncleCountByBlockHash(self, web3, empty_block): - uncle_count = web3.eth.getUncleCount(empty_block['hash']) - - assert is_integer(uncle_count) - assert uncle_count == 0 - - def test_eth_getUncleCountByBlockNumber(self, web3, empty_block): - uncle_count = web3.eth.getUncleCount(empty_block['number']) - - assert is_integer(uncle_count) - assert uncle_count == 0 - - def test_eth_getCode(self, web3, math_contract_address): - code = web3.eth.getCode(math_contract_address) - assert isinstance(code, HexBytes) - assert len(code) > 0 - - def test_eth_getCode_invalid_address(self, web3, math_contract): - with pytest.raises(InvalidAddress): - web3.eth.getCode(math_contract.address.lower()) - - def test_eth_getCode_with_block_identifier(self, web3, emitter_contract): - code = web3.eth.getCode(emitter_contract.address, block_identifier=web3.eth.blockNumber) - assert isinstance(code, HexBytes) - assert len(code) > 0 - - def test_eth_sign(self, web3, unlocked_account_dual_type): - signature = web3.eth.sign( - unlocked_account_dual_type, text='Message tö sign. Longer than hash!' - ) - assert is_bytes(signature) - assert len(signature) == 32 + 32 + 1 - - # test other formats - hexsign = web3.eth.sign( - unlocked_account_dual_type, - hexstr='0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' - ) - assert hexsign == signature - - intsign = web3.eth.sign( - unlocked_account_dual_type, - 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 - ) - assert intsign == signature - - bytessign = web3.eth.sign( - unlocked_account_dual_type, b'Message t\xc3\xb6 sign. Longer than hash!' - ) - assert bytessign == signature - - new_signature = web3.eth.sign( - unlocked_account_dual_type, text='different message is different' - ) - assert new_signature != signature - - def test_eth_signTypedData(self, web3, unlocked_account_dual_type, skip_if_testrpc): - validJSONMessage = ''' - { - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } - ''' - skip_if_testrpc(web3) - signature = HexBytes(web3.eth.signTypedData( - unlocked_account_dual_type, - json.loads(validJSONMessage) - )) - assert len(signature) == 32 + 32 + 1 - - def test_invalid_eth_signTypedData(self, - web3, - unlocked_account_dual_type, - skip_if_testrpc): - skip_if_testrpc(web3) - invalid_typed_message = ''' - { - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person[2]"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": [{ - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }], - "contents": "Hello, Bob!" - } - } - ''' - with pytest.raises(ValueError, - match=r".*Expected 2 items for array type Person\[2\], got 1 items.*"): - web3.eth.signTypedData( - unlocked_account_dual_type, - json.loads(invalid_typed_message) - ) - - def test_eth_signTransaction(self, web3, unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - 'nonce': 0, - } - result = web3.eth.signTransaction(txn_params) - signatory_account = web3.eth.account.recoverTransaction(result['raw']) - assert unlocked_account == signatory_account - assert result['tx']['to'] == txn_params['to'] - assert result['tx']['value'] == txn_params['value'] - assert result['tx']['gas'] == txn_params['gas'] - assert result['tx']['gasPrice'] == txn_params['gasPrice'] - assert result['tx']['nonce'] == txn_params['nonce'] - - def test_eth_sendTransaction_addr_checksum_required(self, web3, unlocked_account): - non_checksum_addr = unlocked_account.lower() - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - - with pytest.raises(InvalidAddress): - invalid_params = dict(txn_params, **{'from': non_checksum_addr}) - web3.eth.sendTransaction(invalid_params) - - with pytest.raises(InvalidAddress): - invalid_params = dict(txn_params, **{'to': non_checksum_addr}) - web3.eth.sendTransaction(invalid_params) - - def test_eth_sendTransaction(self, web3, unlocked_account_dual_type): - txn_params = { - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - txn_hash = web3.eth.sendTransaction(txn_params) - txn = web3.eth.getTransaction(txn_hash) - - assert is_same_address(txn['from'], txn_params['from']) - assert is_same_address(txn['to'], txn_params['to']) - assert txn['value'] == 1 - assert txn['gas'] == 21000 - assert txn['gasPrice'] == txn_params['gasPrice'] - - def test_eth_sendTransaction_with_nonce(self, web3, unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - # Increased gas price to ensure transaction hash different from other tests - 'gasPrice': web3.eth.gasPrice * 3, - 'nonce': web3.eth.getTransactionCount(unlocked_account), - } - txn_hash = web3.eth.sendTransaction(txn_params) - txn = web3.eth.getTransaction(txn_hash) - - assert is_same_address(txn['from'], txn_params['from']) - assert is_same_address(txn['to'], txn_params['to']) - assert txn['value'] == 1 - assert txn['gas'] == 21000 - assert txn['gasPrice'] == txn_params['gasPrice'] - assert txn['nonce'] == txn_params['nonce'] - - def test_eth_replaceTransaction(self, web3, unlocked_account_dual_type): - txn_params = { - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - txn_params['gasPrice'] = web3.eth.gasPrice * 2 - replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) - replace_txn = web3.eth.getTransaction(replace_txn_hash) - - assert is_same_address(replace_txn['from'], txn_params['from']) - assert is_same_address(replace_txn['to'], txn_params['to']) - assert replace_txn['value'] == 1 - assert replace_txn['gas'] == 21000 - assert replace_txn['gasPrice'] == txn_params['gasPrice'] - - def test_eth_replaceTransaction_non_existing_transaction( - self, web3, unlocked_account_dual_type): - txn_params = { - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - with pytest.raises(TransactionNotFound): - web3.eth.replaceTransaction( - '0x98e8cc09b311583c5079fa600f6c2a3bea8611af168c52e4b60b5b243a441997', - txn_params - ) - - # auto mine is enabled for this test - def test_eth_replaceTransaction_already_mined(self, web3, unlocked_account_dual_type): - txn_params = { - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - txn_params['gasPrice'] = web3.eth.gasPrice * 2 - with pytest.raises(ValueError): - web3.eth.replaceTransaction(txn_hash, txn_params) - - def test_eth_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - txn_hash = web3.eth.sendTransaction(txn_params) - txn = web3.eth.getTransaction(txn_hash) - - txn_params['gasPrice'] = web3.eth.gasPrice * 2 - txn_params['nonce'] = txn['nonce'] + 1 - with pytest.raises(ValueError): - web3.eth.replaceTransaction(txn_hash, txn_params) - - def test_eth_replaceTransaction_gas_price_too_low(self, web3, unlocked_account_dual_type): - txn_params = { - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': 10, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - txn_params['gasPrice'] = 9 - with pytest.raises(ValueError): - web3.eth.replaceTransaction(txn_hash, txn_params) - - def test_eth_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': 10, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - txn_params.pop('gasPrice') - replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) - replace_txn = web3.eth.getTransaction(replace_txn_hash) - - assert replace_txn['gasPrice'] == 11 # minimum gas price - - def test_eth_replaceTransaction_gas_price_defaulting_strategy_higher(self, - web3, - unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': 10, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - def higher_gas_price_strategy(web3, txn): - return 20 - - web3.eth.setGasPriceStrategy(higher_gas_price_strategy) - - txn_params.pop('gasPrice') - replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) - replace_txn = web3.eth.getTransaction(replace_txn_hash) - assert replace_txn['gasPrice'] == 20 # Strategy provides higher gas price - - def test_eth_replaceTransaction_gas_price_defaulting_strategy_lower(self, - web3, - unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': 10, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - def lower_gas_price_strategy(web3, txn): - return 5 - - web3.eth.setGasPriceStrategy(lower_gas_price_strategy) - - txn_params.pop('gasPrice') - replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) - replace_txn = web3.eth.getTransaction(replace_txn_hash) - # Strategy provices lower gas price - minimum preferred - assert replace_txn['gasPrice'] == 11 - - def test_eth_modifyTransaction(self, web3, unlocked_account): - txn_params = { - 'from': unlocked_account, - 'to': unlocked_account, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - } - txn_hash = web3.eth.sendTransaction(txn_params) - - modified_txn_hash = web3.eth.modifyTransaction( - txn_hash, gasPrice=(txn_params['gasPrice'] * 2), value=2 - ) - modified_txn = web3.eth.getTransaction(modified_txn_hash) - - assert is_same_address(modified_txn['from'], txn_params['from']) - assert is_same_address(modified_txn['to'], txn_params['to']) - assert modified_txn['value'] == 2 - assert modified_txn['gas'] == 21000 - assert modified_txn['gasPrice'] == txn_params['gasPrice'] * 2 - - @pytest.mark.parametrize( - 'raw_transaction, expected_hash', - [ - ( - # address 0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6 - '0xf8648085174876e8008252089439eeed73fb1d3855e90cbd42f348b3d7b340aaa601801ba0ec1295f00936acd0c2cb90ab2cdaacb8bf5e11b3d9957833595aca9ceedb7aada05dfc8937baec0e26029057abd3a1ef8c505dca2cdc07ffacb046d090d2bea06a', # noqa: E501 - '0x1f80f8ab5f12a45be218f76404bda64d37270a6f4f86ededd0eb599f80548c13', - ), - ( - # private key 0x3c2ab4e8f17a7dea191b8c991522660126d681039509dc3bb31af7c9bdb63518 - # This is an unfunded account, but the transaction has a 0 gas price, so is valid. - # It never needs to be mined, we just want the transaction hash back to confirm. - HexBytes('0xf85f808082c35094d898d5e829717c72e7438bad593076686d7d164a80801ba005c2e99ecee98a12fbf28ab9577423f42e9e88f2291b3acc8228de743884c874a077d6bc77a47ad41ec85c96aac2ad27f05a039c4787fca8a1e5ee2d8c7ec1bb6a'), # noqa: E501 - '0x98eeadb99454427f6aad7b558bac13e9d225512a6f5e5c11cf48e8d4067e51b5', - ), - ] - ) - def test_eth_sendRawTransaction(self, - web3, - raw_transaction, - funded_account_for_raw_txn, - expected_hash): - txn_hash = web3.eth.sendRawTransaction(raw_transaction) - assert txn_hash == web3.toBytes(hexstr=expected_hash) - - def test_eth_call(self, web3, math_contract): - coinbase = web3.eth.coinbase - txn_params = math_contract._prepare_transaction( - fn_name='add', - fn_args=(7, 11), - transaction={'from': coinbase, 'to': math_contract.address}, - ) - call_result = web3.eth.call(txn_params) - assert is_string(call_result) - result = web3.codec.decode_single('uint256', call_result) - assert result == 18 - - def test_eth_call_with_0_result(self, web3, math_contract): - coinbase = web3.eth.coinbase - txn_params = math_contract._prepare_transaction( - fn_name='add', - fn_args=(0, 0), - transaction={'from': coinbase, 'to': math_contract.address}, - ) - call_result = web3.eth.call(txn_params) - assert is_string(call_result) - result = web3.codec.decode_single('uint256', call_result) - assert result == 0 - - def test_eth_estimateGas(self, web3, unlocked_account_dual_type): - gas_estimate = web3.eth.estimateGas({ - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - }) - assert is_integer(gas_estimate) - assert gas_estimate > 0 - - def test_eth_estimateGas_with_block(self, - web3, - unlocked_account_dual_type): - gas_estimate = web3.eth.estimateGas({ - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - }, 'latest') - assert is_integer(gas_estimate) - assert gas_estimate > 0 - - def test_eth_getBlockByHash(self, web3, empty_block): - block = web3.eth.getBlock(empty_block['hash']) - assert block['hash'] == empty_block['hash'] - - def test_eth_getBlockByHash_not_found(self, web3, empty_block): - with pytest.raises(BlockNotFound): - web3.eth.getBlock(UNKNOWN_HASH) - - def test_eth_getBlockByNumber_with_integer(self, web3, empty_block): - block = web3.eth.getBlock(empty_block['number']) - assert block['number'] == empty_block['number'] - - def test_eth_getBlockByNumber_latest(self, web3, empty_block): - current_block_number = web3.eth.blockNumber - block = web3.eth.getBlock('latest') - assert block['number'] == current_block_number - - def test_eth_getBlockByNumber_not_found(self, web3, empty_block): - with pytest.raises(BlockNotFound): - web3.eth.getBlock(12345) - - def test_eth_getBlockByNumber_pending(self, web3, empty_block): - current_block_number = web3.eth.blockNumber - block = web3.eth.getBlock('pending') - assert block['number'] == current_block_number + 1 - - def test_eth_getBlockByNumber_earliest(self, web3, empty_block): - genesis_block = web3.eth.getBlock(0) - block = web3.eth.getBlock('earliest') - assert block['number'] == 0 - assert block['hash'] == genesis_block['hash'] - - def test_eth_getBlockByNumber_full_transactions(self, web3, block_with_txn): - block = web3.eth.getBlock(block_with_txn['number'], True) - transaction = block['transactions'][0] - assert transaction['hash'] == block_with_txn['transactions'][0] - - def test_eth_getTransactionByHash(self, web3, mined_txn_hash): - transaction = web3.eth.getTransaction(mined_txn_hash) - assert is_dict(transaction) - assert transaction['hash'] == HexBytes(mined_txn_hash) - - def test_eth_getTransactionByHash_contract_creation(self, - web3, - math_contract_deploy_txn_hash): - transaction = web3.eth.getTransaction(math_contract_deploy_txn_hash) - assert is_dict(transaction) - assert transaction['to'] is None, "to field is %r" % transaction['to'] - - def test_eth_getTransactionByBlockHashAndIndex(self, web3, block_with_txn, mined_txn_hash): - transaction = web3.eth.getTransactionByBlock(block_with_txn['hash'], 0) - assert is_dict(transaction) - assert transaction['hash'] == HexBytes(mined_txn_hash) - - def test_eth_getTransactionByBlockNumberAndIndex(self, web3, block_with_txn, mined_txn_hash): - transaction = web3.eth.getTransactionByBlock(block_with_txn['number'], 0) - assert is_dict(transaction) - assert transaction['hash'] == HexBytes(mined_txn_hash) - - def test_eth_getTransactionReceipt_mined(self, web3, block_with_txn, mined_txn_hash): - receipt = web3.eth.getTransactionReceipt(mined_txn_hash) - assert is_dict(receipt) - assert receipt['blockNumber'] == block_with_txn['number'] - assert receipt['blockHash'] == block_with_txn['hash'] - assert receipt['transactionIndex'] == 0 - assert receipt['transactionHash'] == HexBytes(mined_txn_hash) - - def test_eth_getTransactionReceipt_unmined(self, web3, unlocked_account_dual_type): - txn_hash = web3.eth.sendTransaction({ - 'from': unlocked_account_dual_type, - 'to': unlocked_account_dual_type, - 'value': 1, - 'gas': 21000, - 'gasPrice': web3.eth.gasPrice, - }) - with pytest.raises(TransactionNotFound): - web3.eth.getTransactionReceipt(txn_hash) - - def test_eth_getTransactionReceipt_with_log_entry(self, - web3, - block_with_txn_with_log, - emitter_contract, - txn_hash_with_log): - receipt = web3.eth.getTransactionReceipt(txn_hash_with_log) - assert is_dict(receipt) - assert receipt['blockNumber'] == block_with_txn_with_log['number'] - assert receipt['blockHash'] == block_with_txn_with_log['hash'] - assert receipt['transactionIndex'] == 0 - assert receipt['transactionHash'] == HexBytes(txn_hash_with_log) - - assert len(receipt['logs']) == 1 - log_entry = receipt['logs'][0] - - assert log_entry['blockNumber'] == block_with_txn_with_log['number'] - assert log_entry['blockHash'] == block_with_txn_with_log['hash'] - assert log_entry['logIndex'] == 0 - assert is_same_address(log_entry['address'], emitter_contract.address) - assert log_entry['transactionIndex'] == 0 - assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) - - def test_eth_getUncleByBlockHashAndIndex(self, web3): - # TODO: how do we make uncles.... - pass - - def test_eth_getUncleByBlockNumberAndIndex(self, web3): - # TODO: how do we make uncles.... - pass - - def test_eth_newFilter(self, web3): - filter = web3.eth.filter({}) - - changes = web3.eth.getFilterChanges(filter.filter_id) - assert is_list_like(changes) - assert not changes - - logs = web3.eth.getFilterLogs(filter.filter_id) - assert is_list_like(logs) - assert not logs - - result = web3.eth.uninstallFilter(filter.filter_id) - assert result is True - - def test_eth_newBlockFilter(self, web3): - filter = web3.eth.filter('latest') - assert is_string(filter.filter_id) - - changes = web3.eth.getFilterChanges(filter.filter_id) - assert is_list_like(changes) - assert not changes - - # TODO: figure out why this fails in go-ethereum - # logs = web3.eth.getFilterLogs(filter.filter_id) - # assert is_list_like(logs) - # assert not logs - - result = web3.eth.uninstallFilter(filter.filter_id) - assert result is True - - def test_eth_newPendingTransactionFilter(self, web3): - filter = web3.eth.filter('pending') - assert is_string(filter.filter_id) - - changes = web3.eth.getFilterChanges(filter.filter_id) - assert is_list_like(changes) - assert not changes - - # TODO: figure out why this fails in go-ethereum - # logs = web3.eth.getFilterLogs(filter.filter_id) - # assert is_list_like(logs) - # assert not logs - - result = web3.eth.uninstallFilter(filter.filter_id) - assert result is True - - def test_eth_getLogs_without_logs(self, web3, block_with_txn_with_log): - # Test with block range - - filter_params = { - "fromBlock": 0, - "toBlock": block_with_txn_with_log['number'] - 1, - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - # the range is wrong - filter_params = { - "fromBlock": block_with_txn_with_log['number'], - "toBlock": block_with_txn_with_log['number'] - 1, - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - # Test with `address` - - # filter with other address - filter_params = { - "fromBlock": 0, - "address": UNKNOWN_ADDRESS, - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - # Test with multiple `address` - - # filter with other address - filter_params = { - "fromBlock": 0, - "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], - } - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - def test_eth_getLogs_with_logs( - self, - web3, - block_with_txn_with_log, - emitter_contract_address, - txn_hash_with_log): - - def assert_contains_log(result): - assert len(result) == 1 - log_entry = result[0] - assert log_entry['blockNumber'] == block_with_txn_with_log['number'] - assert log_entry['blockHash'] == block_with_txn_with_log['hash'] - assert log_entry['logIndex'] == 0 - assert is_same_address(log_entry['address'], emitter_contract_address) - assert log_entry['transactionIndex'] == 0 - assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) - - # Test with block range - - # the range includes the block where the log resides in - filter_params = { - "fromBlock": block_with_txn_with_log['number'], - "toBlock": block_with_txn_with_log['number'], - } - result = web3.eth.getLogs(filter_params) - assert_contains_log(result) - - # specify only `from_block`. by default `to_block` should be 'latest' - filter_params = { - "fromBlock": 0, - } - result = web3.eth.getLogs(filter_params) - assert_contains_log(result) - - # Test with `address` - - # filter with emitter_contract.address - filter_params = { - "fromBlock": 0, - "address": emitter_contract_address, - } - - def test_eth_getLogs_with_logs_topic_args( - self, - web3, - block_with_txn_with_log, - emitter_contract_address, - txn_hash_with_log): - def assert_contains_log(result): - assert len(result) == 1 - log_entry = result[0] - assert log_entry['blockNumber'] == block_with_txn_with_log['number'] - assert log_entry['blockHash'] == block_with_txn_with_log['hash'] - assert log_entry['logIndex'] == 0 - assert is_same_address(log_entry['address'], emitter_contract_address) - assert log_entry['transactionIndex'] == 0 - assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) - - # Test with None event sig - - filter_params = { - "fromBlock": 0, - "topics": [ - None, - '0x000000000000000000000000000000000000000000000000000000000000d431'], - } - - result = web3.eth.getLogs(filter_params) - assert_contains_log(result) - - # Test with None indexed arg - filter_params = { - "fromBlock": 0, - "topics": [ - '0x057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca', - None], - } - result = web3.eth.getLogs(filter_params) - assert_contains_log(result) - - def test_eth_getLogs_with_logs_none_topic_args( - self, - web3): - # Test with None overflowing - filter_params = { - "fromBlock": 0, - "topics": [None, None, None], - } - - result = web3.eth.getLogs(filter_params) - assert len(result) == 0 - - def test_eth_call_old_contract_state(self, web3, math_contract, unlocked_account): - start_block = web3.eth.getBlock('latest') - block_num = start_block.number - block_hash = start_block.hash - - math_contract.functions.increment().transact({'from': unlocked_account}) - - # This isn't an incredibly convincing test since we can't mine, and - # the default resolved block is latest, So if block_identifier was ignored - # we would get the same result. For now, we mostly depend on core tests. - # Ideas to improve this test: - # - Enable on-demand mining in more clients - # - Increment the math contract in all of the fixtures, and check the value in an old block - block_hash_call_result = math_contract.functions.counter().call(block_identifier=block_hash) - block_num_call_result = math_contract.functions.counter().call(block_identifier=block_num) - latest_call_result = math_contract.functions.counter().call(block_identifier='latest') - default_call_result = math_contract.functions.counter().call() - pending_call_result = math_contract.functions.counter().call(block_identifier='pending') - - assert block_hash_call_result == 0 - assert block_num_call_result == 0 - assert latest_call_result == 0 - assert default_call_result == 0 - - if pending_call_result != 1: - raise AssertionError("pending call result was %d instead of 1" % pending_call_result) - - def test_eth_uninstallFilter(self, web3): - filter = web3.eth.filter({}) - assert is_string(filter.filter_id) - - success = web3.eth.uninstallFilter(filter.filter_id) - assert success is True - - failure = web3.eth.uninstallFilter(filter.filter_id) - assert failure is False - - def test_eth_getTransactionFromBlock_deprecation(self, web3, block_with_txn): - with pytest.raises(DeprecationWarning): - web3.eth.getTransactionFromBlock(block_with_txn['hash'], 0) - - def test_eth_getCompilers_deprecation(self, web3): - with pytest.raises(DeprecationWarning): - web3.eth.getCompilers() - - def test_eth_submitHashrate(self, web3): - # node_id from EIP 1474: https://github.com/ethereum/EIPs/pull/1474/files - node_id = '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c' - result = web3.eth.submitHashrate(5000, node_id) - assert result is True - - def test_eth_submitWork(self, web3): - nonce = 1 - pow_hash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - mix_digest = '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' - result = web3.eth.submitWork(nonce, pow_hash, mix_digest) - assert result is False +# -*- coding: utf-8 -*- + +import pytest + +from vns_abi import ( + decode_single, +) +from vns_utils import ( + is_boolean, + is_bytes, + is_checksum_address, + is_dict, + is_integer, + is_list_like, + is_same_address, + is_string, + to_int, +) +from hexbytes import ( + HexBytes, +) + +from web3.exceptions import ( + BlockNotFound, + InvalidAddress, + TransactionNotFound, +) + +UNKNOWN_ADDRESS = '0xdEADBEeF00000000000000000000000000000000' +UNKNOWN_HASH = '0xdeadbeef00000000000000000000000000000000000000000000000000000000' + + +class EthModuleTest: + def test_vns_protocolVersion(self, web3): + protocol_version = web3.vns.protocolVersion + + assert is_string(protocol_version) + assert protocol_version.isdigit() + + def test_vns_syncing(self, web3): + syncing = web3.vns.syncing + + assert is_boolean(syncing) or is_dict(syncing) + + if is_boolean(syncing): + assert syncing is False + elif is_dict(syncing): + assert 'startingBlock' in syncing + assert 'currentBlock' in syncing + assert 'highestBlock' in syncing + + assert is_integer(syncing['startingBlock']) + assert is_integer(syncing['currentBlock']) + assert is_integer(syncing['highestBlock']) + + def test_vns_coinbase(self, web3): + coinbase = web3.vns.coinbase + assert is_checksum_address(coinbase) + + def test_vns_mining(self, web3): + mining = web3.vns.mining + assert is_boolean(mining) + + def test_vns_hashrate(self, web3): + hashrate = web3.vns.hashrate + assert is_integer(hashrate) + assert hashrate >= 0 + + def test_vns_chainId(self, web3): + chain_id = web3.vns.chainId + # chain id value from geth fixture genesis file + assert to_int(hexstr=chain_id) == 131277322940537 + + def test_vns_gasPrice(self, web3): + gas_price = web3.vns.gasPrice + assert is_integer(gas_price) + assert gas_price > 0 + + def test_vns_accounts(self, web3): + accounts = web3.vns.accounts + assert is_list_like(accounts) + assert len(accounts) != 0 + assert all(( + is_checksum_address(account) + for account + in accounts + )) + assert web3.vns.coinbase in accounts + + def test_vns_blockNumber(self, web3): + block_number = web3.vns.blockNumber + assert is_integer(block_number) + assert block_number >= 0 + + def test_vns_getBalance(self, web3): + coinbase = web3.vns.coinbase + + with pytest.raises(InvalidAddress): + web3.vns.getBalance(coinbase.lower()) + + balance = web3.vns.getBalance(coinbase) + + assert is_integer(balance) + assert balance >= 0 + + def test_vns_getStorageAt(self, web3, emitter_contract_address): + storage = web3.vns.getStorageAt(emitter_contract_address, 0) + assert isinstance(storage, HexBytes) + + def test_vns_getStorageAt_invalid_address(self, web3): + coinbase = web3.vns.coinbase + with pytest.raises(InvalidAddress): + web3.vns.getStorageAt(coinbase.lower(), 0) + + def test_vns_getTransactionCount(self, web3, unlocked_account_dual_type): + transaction_count = web3.vns.getTransactionCount(unlocked_account_dual_type) + assert is_integer(transaction_count) + assert transaction_count >= 0 + + def test_vns_getTransactionCount_invalid_address(self, web3): + coinbase = web3.vns.coinbase + with pytest.raises(InvalidAddress): + web3.vns.getTransactionCount(coinbase.lower()) + + def test_vns_getBlockTransactionCountByHash_empty_block(self, web3, empty_block): + transaction_count = web3.vns.getBlockTransactionCount(empty_block['hash']) + + assert is_integer(transaction_count) + assert transaction_count == 0 + + def test_vns_getBlockTransactionCountByNumber_empty_block(self, web3, empty_block): + transaction_count = web3.vns.getBlockTransactionCount(empty_block['number']) + + assert is_integer(transaction_count) + assert transaction_count == 0 + + def test_vns_getBlockTransactionCountByHash_block_with_txn(self, web3, block_with_txn): + transaction_count = web3.vns.getBlockTransactionCount(block_with_txn['hash']) + + assert is_integer(transaction_count) + assert transaction_count >= 1 + + def test_vns_getBlockTransactionCountByNumber_block_with_txn(self, web3, block_with_txn): + transaction_count = web3.vns.getBlockTransactionCount(block_with_txn['number']) + + assert is_integer(transaction_count) + assert transaction_count >= 1 + + def test_vns_getUncleCountByBlockHash(self, web3, empty_block): + uncle_count = web3.vns.getUncleCount(empty_block['hash']) + + assert is_integer(uncle_count) + assert uncle_count == 0 + + def test_vns_getUncleCountByBlockNumber(self, web3, empty_block): + uncle_count = web3.vns.getUncleCount(empty_block['number']) + + assert is_integer(uncle_count) + assert uncle_count == 0 + + def test_vns_getCode(self, web3, math_contract_address): + code = web3.vns.getCode(math_contract_address) + assert isinstance(code, HexBytes) + assert len(code) > 0 + + def test_vns_getCode_invalid_address(self, web3, math_contract): + with pytest.raises(InvalidAddress): + web3.vns.getCode(math_contract.address.lower()) + + def test_vns_getCode_with_block_identifier(self, web3, emitter_contract): + code = web3.vns.getCode(emitter_contract.address, block_identifier=web3.vns.blockNumber) + assert isinstance(code, HexBytes) + assert len(code) > 0 + + def test_vns_sign(self, web3, unlocked_account_dual_type): + signature = web3.vns.sign( + unlocked_account_dual_type, text='Message tö sign. Longer than hash!' + ) + assert is_bytes(signature) + assert len(signature) == 32 + 32 + 1 + + # test other formats + hexsign = web3.vns.sign( + unlocked_account_dual_type, + hexstr='0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' + ) + assert hexsign == signature + + intsign = web3.vns.sign( + unlocked_account_dual_type, + 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 + ) + assert intsign == signature + + bytessign = web3.vns.sign( + unlocked_account_dual_type, b'Message t\xc3\xb6 sign. Longer than hash!' + ) + assert bytessign == signature + + new_signature = web3.vns.sign( + unlocked_account_dual_type, text='different message is different' + ) + assert new_signature != signature + + def test_vns_signTransaction(self, web3, unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + 'nonce': 0, + } + result = web3.vns.signTransaction(txn_params) + signatory_account = web3.vns.account.recoverTransaction(result['raw']) + assert unlocked_account == signatory_account + assert result['tx']['to'] == txn_params['to'] + assert result['tx']['value'] == txn_params['value'] + assert result['tx']['gas'] == txn_params['gas'] + assert result['tx']['gasPrice'] == txn_params['gasPrice'] + assert result['tx']['nonce'] == txn_params['nonce'] + + def test_vns_sendTransaction_addr_checksum_required(self, web3, unlocked_account): + non_checksum_addr = unlocked_account.lower() + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + + with pytest.raises(InvalidAddress): + invalid_params = dict(txn_params, **{'from': non_checksum_addr}) + web3.vns.sendTransaction(invalid_params) + + with pytest.raises(InvalidAddress): + invalid_params = dict(txn_params, **{'to': non_checksum_addr}) + web3.vns.sendTransaction(invalid_params) + + def test_vns_sendTransaction(self, web3, unlocked_account_dual_type): + txn_params = { + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + txn_hash = web3.vns.sendTransaction(txn_params) + txn = web3.vns.getTransaction(txn_hash) + + assert is_same_address(txn['from'], txn_params['from']) + assert is_same_address(txn['to'], txn_params['to']) + assert txn['value'] == 1 + assert txn['gas'] == 21000 + assert txn['gasPrice'] == txn_params['gasPrice'] + + def test_vns_sendTransaction_with_nonce(self, web3, unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + # Increased gas price to ensure transaction hash different from other tests + 'gasPrice': web3.vns.gasPrice * 2, + 'nonce': web3.vns.getTransactionCount(unlocked_account), + } + txn_hash = web3.vns.sendTransaction(txn_params) + txn = web3.vns.getTransaction(txn_hash) + + assert is_same_address(txn['from'], txn_params['from']) + assert is_same_address(txn['to'], txn_params['to']) + assert txn['value'] == 1 + assert txn['gas'] == 21000 + assert txn['gasPrice'] == txn_params['gasPrice'] + assert txn['nonce'] == txn_params['nonce'] + + def test_vns_replaceTransaction(self, web3, unlocked_account_dual_type): + txn_params = { + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + txn_params['gasPrice'] = web3.vns.gasPrice * 2 + replace_txn_hash = web3.vns.replaceTransaction(txn_hash, txn_params) + replace_txn = web3.vns.getTransaction(replace_txn_hash) + + assert is_same_address(replace_txn['from'], txn_params['from']) + assert is_same_address(replace_txn['to'], txn_params['to']) + assert replace_txn['value'] == 1 + assert replace_txn['gas'] == 21000 + assert replace_txn['gasPrice'] == txn_params['gasPrice'] + + def test_vns_replaceTransaction_non_existing_transaction( + self, web3, unlocked_account_dual_type): + txn_params = { + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + with pytest.raises(TransactionNotFound): + web3.vns.replaceTransaction( + '0x98e8cc09b311583c5079fa600f6c2a3bea8611af168c52e4b60b5b243a441997', + txn_params + ) + + # auto mine is enabled for this test + def test_vns_replaceTransaction_already_mined(self, web3, unlocked_account_dual_type): + txn_params = { + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + txn_params['gasPrice'] = web3.vns.gasPrice * 2 + with pytest.raises(ValueError): + web3.vns.replaceTransaction(txn_hash, txn_params) + + def test_vns_replaceTransaction_incorrect_nonce(self, web3, unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + txn_hash = web3.vns.sendTransaction(txn_params) + txn = web3.vns.getTransaction(txn_hash) + + txn_params['gasPrice'] = web3.vns.gasPrice * 2 + txn_params['nonce'] = txn['nonce'] + 1 + with pytest.raises(ValueError): + web3.vns.replaceTransaction(txn_hash, txn_params) + + def test_vns_replaceTransaction_gas_price_too_low(self, web3, unlocked_account_dual_type): + txn_params = { + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': 10, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + txn_params['gasPrice'] = 9 + with pytest.raises(ValueError): + web3.vns.replaceTransaction(txn_hash, txn_params) + + def test_vns_replaceTransaction_gas_price_defaulting_minimum(self, web3, unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': 10, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + txn_params.pop('gasPrice') + replace_txn_hash = web3.vns.replaceTransaction(txn_hash, txn_params) + replace_txn = web3.vns.getTransaction(replace_txn_hash) + + assert replace_txn['gasPrice'] == 11 # minimum gas price + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_higher(self, + web3, + unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': 10, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + def higher_gas_price_strategy(web3, txn): + return 20 + + web3.vns.setGasPriceStrategy(higher_gas_price_strategy) + + txn_params.pop('gasPrice') + replace_txn_hash = web3.vns.replaceTransaction(txn_hash, txn_params) + replace_txn = web3.vns.getTransaction(replace_txn_hash) + assert replace_txn['gasPrice'] == 20 # Strategy provides higher gas price + + def test_vns_replaceTransaction_gas_price_defaulting_strategy_lower(self, + web3, + unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': 10, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + def lower_gas_price_strategy(web3, txn): + return 5 + + web3.vns.setGasPriceStrategy(lower_gas_price_strategy) + + txn_params.pop('gasPrice') + replace_txn_hash = web3.vns.replaceTransaction(txn_hash, txn_params) + replace_txn = web3.vns.getTransaction(replace_txn_hash) + # Strategy provices lower gas price - minimum preferred + assert replace_txn['gasPrice'] == 11 + + def test_vns_modifyTransaction(self, web3, unlocked_account): + txn_params = { + 'from': unlocked_account, + 'to': unlocked_account, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + } + txn_hash = web3.vns.sendTransaction(txn_params) + + modified_txn_hash = web3.vns.modifyTransaction( + txn_hash, gasPrice=(txn_params['gasPrice'] * 2), value=2 + ) + modified_txn = web3.vns.getTransaction(modified_txn_hash) + + assert is_same_address(modified_txn['from'], txn_params['from']) + assert is_same_address(modified_txn['to'], txn_params['to']) + assert modified_txn['value'] == 2 + assert modified_txn['gas'] == 21000 + assert modified_txn['gasPrice'] == txn_params['gasPrice'] * 2 + + @pytest.mark.parametrize( + 'raw_transaction, expected_hash', + [ + ( + # address 0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6 + '0xf8648085174876e8008252089439eeed73fb1d3855e90cbd42f348b3d7b340aaa601801ba0ec1295f00936acd0c2cb90ab2cdaacb8bf5e11b3d9957833595aca9ceedb7aada05dfc8937baec0e26029057abd3a1ef8c505dca2cdc07ffacb046d090d2bea06a', # noqa: E501 + '0x1f80f8ab5f12a45be218f76404bda64d37270a6f4f86ededd0eb599f80548c13', + ), + ( + # private key 0x3c2ab4e8f17a7dea191b8c991522660126d681039509dc3bb31af7c9bdb63518 + # This is an unfunded account, but the transaction has a 0 gas price, so is valid. + # It never needs to be mined, we just want the transaction hash back to confirm. + HexBytes('0xf85f808082c35094d898d5e829717c72e7438bad593076686d7d164a80801ba005c2e99ecee98a12fbf28ab9577423f42e9e88f2291b3acc8228de743884c874a077d6bc77a47ad41ec85c96aac2ad27f05a039c4787fca8a1e5ee2d8c7ec1bb6a'), # noqa: E501 + '0x98eeadb99454427f6aad7b558bac13e9d225512a6f5e5c11cf48e8d4067e51b5', + ), + ] + ) + def test_vns_sendRawTransaction(self, + web3, + raw_transaction, + funded_account_for_raw_txn, + expected_hash): + txn_hash = web3.vns.sendRawTransaction(raw_transaction) + assert txn_hash == web3.toBytes(hexstr=expected_hash) + + def test_vns_call(self, web3, math_contract): + coinbase = web3.vns.coinbase + txn_params = math_contract._prepare_transaction( + fn_name='add', + fn_args=(7, 11), + transaction={'from': coinbase, 'to': math_contract.address}, + ) + call_result = web3.vns.call(txn_params) + assert is_string(call_result) + result = decode_single('uint256', call_result) + assert result == 18 + + def test_vns_call_with_0_result(self, web3, math_contract): + coinbase = web3.vns.coinbase + txn_params = math_contract._prepare_transaction( + fn_name='add', + fn_args=(0, 0), + transaction={'from': coinbase, 'to': math_contract.address}, + ) + call_result = web3.vns.call(txn_params) + assert is_string(call_result) + result = decode_single('uint256', call_result) + assert result == 0 + + def test_vns_estimateGas(self, web3, unlocked_account_dual_type): + gas_estimate = web3.vns.estimateGas({ + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + }) + assert is_integer(gas_estimate) + assert gas_estimate > 0 + + def test_vns_estimateGas_with_block(self, + web3, + unlocked_account_dual_type): + gas_estimate = web3.vns.estimateGas({ + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + }, 'latest') + assert is_integer(gas_estimate) + assert gas_estimate > 0 + + def test_vns_getBlockByHash(self, web3, empty_block): + block = web3.vns.getBlock(empty_block['hash']) + assert block['hash'] == empty_block['hash'] + + def test_vns_getBlockByHash_not_found(self, web3, empty_block): + with pytest.raises(BlockNotFound): + web3.vns.getBlock(UNKNOWN_HASH) + + def test_vns_getBlockByNumber_with_integer(self, web3, empty_block): + block = web3.vns.getBlock(empty_block['number']) + assert block['number'] == empty_block['number'] + + def test_vns_getBlockByNumber_latest(self, web3, empty_block): + current_block_number = web3.vns.blockNumber + block = web3.vns.getBlock('latest') + assert block['number'] == current_block_number + + def test_vns_getBlockByNumber_not_found(self, web3, empty_block): + with pytest.raises(BlockNotFound): + web3.vns.getBlock(12345) + + def test_vns_getBlockByNumber_pending(self, web3, empty_block): + current_block_number = web3.vns.blockNumber + block = web3.vns.getBlock('pending') + assert block['number'] == current_block_number + 1 + + def test_vns_getBlockByNumber_earliest(self, web3, empty_block): + genesis_block = web3.vns.getBlock(0) + block = web3.vns.getBlock('earliest') + assert block['number'] == 0 + assert block['hash'] == genesis_block['hash'] + + def test_vns_getBlockByNumber_full_transactions(self, web3, block_with_txn): + block = web3.vns.getBlock(block_with_txn['number'], True) + transaction = block['transactions'][0] + assert transaction['hash'] == block_with_txn['transactions'][0] + + def test_vns_getTransactionByHash(self, web3, mined_txn_hash): + transaction = web3.vns.getTransaction(mined_txn_hash) + assert is_dict(transaction) + assert transaction['hash'] == HexBytes(mined_txn_hash) + + def test_vns_getTransactionByHash_contract_creation(self, + web3, + math_contract_deploy_txn_hash): + transaction = web3.vns.getTransaction(math_contract_deploy_txn_hash) + assert is_dict(transaction) + assert transaction['to'] is None, "to field is %r" % transaction['to'] + + def test_vns_getTransactionByBlockHashAndIndex(self, web3, block_with_txn, mined_txn_hash): + transaction = web3.vns.getTransactionByBlock(block_with_txn['hash'], 0) + assert is_dict(transaction) + assert transaction['hash'] == HexBytes(mined_txn_hash) + + def test_vns_getTransactionByBlockNumberAndIndex(self, web3, block_with_txn, mined_txn_hash): + transaction = web3.vns.getTransactionByBlock(block_with_txn['number'], 0) + assert is_dict(transaction) + assert transaction['hash'] == HexBytes(mined_txn_hash) + + def test_vns_getTransactionReceipt_mined(self, web3, block_with_txn, mined_txn_hash): + receipt = web3.vns.getTransactionReceipt(mined_txn_hash) + assert is_dict(receipt) + assert receipt['blockNumber'] == block_with_txn['number'] + assert receipt['blockHash'] == block_with_txn['hash'] + assert receipt['transactionIndex'] == 0 + assert receipt['transactionHash'] == HexBytes(mined_txn_hash) + + def test_vns_getTransactionReceipt_unmined(self, web3, unlocked_account_dual_type): + txn_hash = web3.vns.sendTransaction({ + 'from': unlocked_account_dual_type, + 'to': unlocked_account_dual_type, + 'value': 1, + 'gas': 21000, + 'gasPrice': web3.vns.gasPrice, + }) + with pytest.raises(TransactionNotFound): + web3.vns.getTransactionReceipt(txn_hash) + + def test_vns_getTransactionReceipt_with_log_entry(self, + web3, + block_with_txn_with_log, + emitter_contract, + txn_hash_with_log): + receipt = web3.vns.getTransactionReceipt(txn_hash_with_log) + assert is_dict(receipt) + assert receipt['blockNumber'] == block_with_txn_with_log['number'] + assert receipt['blockHash'] == block_with_txn_with_log['hash'] + assert receipt['transactionIndex'] == 0 + assert receipt['transactionHash'] == HexBytes(txn_hash_with_log) + + assert len(receipt['logs']) == 1 + log_entry = receipt['logs'][0] + + assert log_entry['blockNumber'] == block_with_txn_with_log['number'] + assert log_entry['blockHash'] == block_with_txn_with_log['hash'] + assert log_entry['logIndex'] == 0 + assert is_same_address(log_entry['address'], emitter_contract.address) + assert log_entry['transactionIndex'] == 0 + assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) + + def test_vns_getUncleByBlockHashAndIndex(self, web3): + # TODO: how do we make uncles.... + pass + + def test_vns_getUncleByBlockNumberAndIndex(self, web3): + # TODO: how do we make uncles.... + pass + + def test_vns_newFilter(self, web3): + filter = web3.vns.filter({}) + + changes = web3.vns.getFilterChanges(filter.filter_id) + assert is_list_like(changes) + assert not changes + + logs = web3.vns.getFilterLogs(filter.filter_id) + assert is_list_like(logs) + assert not logs + + result = web3.vns.uninstallFilter(filter.filter_id) + assert result is True + + def test_vns_newBlockFilter(self, web3): + filter = web3.vns.filter('latest') + assert is_string(filter.filter_id) + + changes = web3.vns.getFilterChanges(filter.filter_id) + assert is_list_like(changes) + assert not changes + + # TODO: figure out why this fails in go-ethereum + # logs = web3.vns.getFilterLogs(filter.filter_id) + # assert is_list_like(logs) + # assert not logs + + result = web3.vns.uninstallFilter(filter.filter_id) + assert result is True + + def test_vns_newPendingTransactionFilter(self, web3): + filter = web3.vns.filter('pending') + assert is_string(filter.filter_id) + + changes = web3.vns.getFilterChanges(filter.filter_id) + assert is_list_like(changes) + assert not changes + + # TODO: figure out why this fails in go-ethereum + # logs = web3.vns.getFilterLogs(filter.filter_id) + # assert is_list_like(logs) + # assert not logs + + result = web3.vns.uninstallFilter(filter.filter_id) + assert result is True + + def test_vns_getLogs_without_logs(self, web3, block_with_txn_with_log): + # Test with block range + + filter_params = { + "fromBlock": 0, + "toBlock": block_with_txn_with_log['number'] - 1, + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + # the range is wrong + filter_params = { + "fromBlock": block_with_txn_with_log['number'], + "toBlock": block_with_txn_with_log['number'] - 1, + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + # Test with `address` + + # filter with other address + filter_params = { + "fromBlock": 0, + "address": UNKNOWN_ADDRESS, + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + # Test with multiple `address` + + # filter with other address + filter_params = { + "fromBlock": 0, + "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], + } + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + def test_vns_getLogs_with_logs( + self, + web3, + block_with_txn_with_log, + emitter_contract_address, + txn_hash_with_log): + + def assert_contains_log(result): + assert len(result) == 1 + log_entry = result[0] + assert log_entry['blockNumber'] == block_with_txn_with_log['number'] + assert log_entry['blockHash'] == block_with_txn_with_log['hash'] + assert log_entry['logIndex'] == 0 + assert is_same_address(log_entry['address'], emitter_contract_address) + assert log_entry['transactionIndex'] == 0 + assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) + + # Test with block range + + # the range includes the block where the log resides in + filter_params = { + "fromBlock": block_with_txn_with_log['number'], + "toBlock": block_with_txn_with_log['number'], + } + result = web3.vns.getLogs(filter_params) + assert_contains_log(result) + + # specify only `from_block`. by default `to_block` should be 'latest' + filter_params = { + "fromBlock": 0, + } + result = web3.vns.getLogs(filter_params) + assert_contains_log(result) + + # Test with `address` + + # filter with emitter_contract.address + filter_params = { + "fromBlock": 0, + "address": emitter_contract_address, + } + + def test_vns_getLogs_with_logs_topic_args( + self, + web3, + block_with_txn_with_log, + emitter_contract_address, + txn_hash_with_log): + def assert_contains_log(result): + assert len(result) == 1 + log_entry = result[0] + assert log_entry['blockNumber'] == block_with_txn_with_log['number'] + assert log_entry['blockHash'] == block_with_txn_with_log['hash'] + assert log_entry['logIndex'] == 0 + assert is_same_address(log_entry['address'], emitter_contract_address) + assert log_entry['transactionIndex'] == 0 + assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) + + # Test with None event sig + + filter_params = { + "fromBlock": 0, + "topics": [ + None, + '0x000000000000000000000000000000000000000000000000000000000000d431'], + } + + result = web3.vns.getLogs(filter_params) + assert_contains_log(result) + + # Test with None indexed arg + filter_params = { + "fromBlock": 0, + "topics": [ + '0x057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca', + None], + } + result = web3.vns.getLogs(filter_params) + assert_contains_log(result) + + def test_vns_getLogs_with_logs_none_topic_args( + self, + web3): + # Test with None overflowing + filter_params = { + "fromBlock": 0, + "topics": [None, None, None], + } + + result = web3.vns.getLogs(filter_params) + assert len(result) == 0 + + def test_vns_call_old_contract_state(self, web3, math_contract, unlocked_account): + start_block = web3.vns.getBlock('latest') + block_num = start_block.number + block_hash = start_block.hash + + math_contract.functions.increment().transact({'from': unlocked_account}) + + # This isn't an incredibly convincing test since we can't mine, and + # the default resolved block is latest, So if block_identifier was ignored + # we would get the same result. For now, we mostly depend on core tests. + # Ideas to improve this test: + # - Enable on-demand mining in more clients + # - Increment the math contract in all of the fixtures, and check the value in an old block + block_hash_call_result = math_contract.functions.counter().call(block_identifier=block_hash) + block_num_call_result = math_contract.functions.counter().call(block_identifier=block_num) + latest_call_result = math_contract.functions.counter().call(block_identifier='latest') + default_call_result = math_contract.functions.counter().call() + pending_call_result = math_contract.functions.counter().call(block_identifier='pending') + + assert block_hash_call_result == 0 + assert block_num_call_result == 0 + assert latest_call_result == 0 + assert default_call_result == 0 + + if pending_call_result != 1: + raise AssertionError("pending call result was %d instead of 1" % pending_call_result) + + def test_vns_uninstallFilter(self, web3): + filter = web3.vns.filter({}) + assert is_string(filter.filter_id) + + success = web3.vns.uninstallFilter(filter.filter_id) + assert success is True + + failure = web3.vns.uninstallFilter(filter.filter_id) + assert failure is False + + def test_vns_getTransactionFromBlock_deprecation(self, web3, block_with_txn): + with pytest.raises(DeprecationWarning): + web3.vns.getTransactionFromBlock(block_with_txn['hash'], 0) + + def test_vns_getCompilers_deprecation(self, web3): + with pytest.raises(DeprecationWarning): + web3.vns.getCompilers() + + def test_vns_submitHashrate(self, web3): + # node_id from EIP 1474: https://github.com/ethereum/EIPs/pull/1474/files + node_id = '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c' + result = web3.vns.submitHashrate(5000, node_id) + assert result is True + + def test_vns_submitWork(self, web3): + nonce = 1 + pow_hash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' + mix_digest = '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' + result = web3.vns.submitWork(nonce, pow_hash, mix_digest) + assert result is False diff --git a/web3/_utils/module_testing/web3_module.py b/web3/_utils/module_testing/web3_module.py index 6bf30c8f15..58e73c5405 100644 --- a/web3/_utils/module_testing/web3_module.py +++ b/web3/_utils/module_testing/web3_module.py @@ -1,217 +1,217 @@ -import pytest - -from hexbytes import ( - HexBytes, -) - -from web3 import Web3 -from web3._utils.ens import ( - ens_addresses, -) -from web3.exceptions import ( - InvalidAddress, -) - - -class Web3ModuleTest: - def test_web3_clientVersion(self, web3): - client_version = web3.clientVersion - self._check_web3_clientVersion(client_version) - - def _check_web3_clientVersion(self, client_version): - raise NotImplementedError("Must be implemented by subclasses") - - # Contract that calculated test values can be found at - # https://kovan.etherscan.io/address/0xb9be06f5b99372cf9afbccadbbb9954ccaf7f4bb#code - @pytest.mark.parametrize( - 'types,values,expected', - ( - ( - ['bool'], - [True], - HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"), - ), - ( - ['uint8', 'uint8', 'uint8'], - [97, 98, 99], - HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"), - ), - ( - ['uint248'], - [30], - HexBytes("0x30f95d210785601eb33ae4d53d405b26f920e765dff87cca8e9a4aec99f82671"), - ), - ( - ['bool', 'uint16'], - [True, 299], - HexBytes("0xed18599ccd80ee9fae9a28b0e34a5573c3233d7468f808fd659bc171cf0b43bd"), - ), - ( - ['int256'], - [-10], - HexBytes("0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b458a304d3d5"), - ), - ( - ['int256'], - [10], - HexBytes("0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8"), - ), - ( - ['int8', 'uint8'], - [-10, 18], - HexBytes("0x5c6ab1e634c08d9c0f4df4d789e8727943ef010dd7ca8e3c89de197a26d148be"), - ), - ( - ['address'], - ["0x49eddd3769c0712032808d86597b84ac5c2f5614"], - InvalidAddress, - ), - ( - ['address'], - ["0x49EdDD3769c0712032808D86597B84ac5c2F5614"], - HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), - ), - ( - ['bytes2'], - ['0x5402'], - HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), - ), - ( - ['bytes3'], - ['0x5402'], - HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), - ), - ( - ['bytes'], - [ - '0x636865636b6c6f6e6762797465737472696e676167' - '61696e7374736f6c6964697479736861336861736866756e6374696f6e' - ], - HexBytes("0xd78a84d65721b67e4011b10c99dafdedcdcd7cb30153064f773e210b4762e22f"), - ), - ( - ['string'], - ['testing a string!'], - HexBytes("0xe8c275c0b4070a5ec6cfcb83f0ba394b30ddd283de785d43f2eabfb04bd96747"), - ), - ( - ['string', 'bool', 'uint16', 'bytes2', 'address'], - [ - 'testing a string!', - False, - 299, - '0x5402', - "0x49eddd3769c0712032808d86597b84ac5c2f5614", - ], - InvalidAddress, - ), - ( - ['string', 'bool', 'uint16', 'bytes2', 'address'], - [ - 'testing a string!', - False, - 299, - '0x5402', - "0x49EdDD3769c0712032808D86597B84ac5c2F5614", - ], - HexBytes("0x8cc6eabb25b842715e8ca39e2524ed946759aa37bfb7d4b81829cf5a7e266103"), - ), - ( - ['bool[2][]'], - [[[True, False], [False, True]]], - HexBytes("0x1eef261f2eb51a8c736d52be3f91ff79e78a9ec5df2b7f50d0c6f98ed1e2bc06"), - ), - ( - ['bool[]'], - [[True, False, True]], - HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), - ), - ( - ['uint24[]'], - [[1, 0, 1]], - HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), - ), - ( - ['uint8[2]'], - [[8, 9]], - HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), - ), - ( - ['uint256[2]'], - [[8, 9]], - HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), - ), - ( - ['uint8[]'], - [[8]], - HexBytes("0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3"), - ), - ( - ['address[]'], - [[ - "0x49EdDD3769c0712032808D86597B84ac5c2F5614", - "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", - ]], - HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), - ), - ( - ['address[]'], - [[ - "0x49EdDD3769c0712032808D86597B84ac5c2F5614", - "0xa6b759bbbf4b59d24acf7e06e79f3a5d104fdce5", - ]], - InvalidAddress, - ), - ), - ) - def test_solidityKeccak(self, web3, types, values, expected): - if isinstance(expected, type) and issubclass(expected, Exception): - with pytest.raises(expected): - web3.solidityKeccak(types, values) - return - - actual = web3.solidityKeccak(types, values) - assert actual == expected - - @pytest.mark.parametrize( - 'types, values, expected', - ( - ( - ['address'], - ['one.eth'], - HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), - ), - ( - ['address[]'], - [['one.eth', 'two.eth']], - HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), - ), - ), - ) - def test_solidityKeccak_ens(self, web3, types, values, expected): - with ens_addresses(web3, { - 'one.eth': "0x49EdDD3769c0712032808D86597B84ac5c2F5614", - 'two.eth': "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", - }): - # when called as class method, any name lookup attempt will fail - with pytest.raises(InvalidAddress): - Web3.solidityKeccak(types, values) - - # when called as instance method, ens lookups can succeed - actual = web3.solidityKeccak(types, values) - assert actual == expected - - @pytest.mark.parametrize( - 'types,values', - ( - (['address'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5', True]), - (['address', 'bool'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), - ([], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), - ) - ) - def test_solidityKeccak_same_number_of_types_and_values(self, web3, types, values): - with pytest.raises(ValueError): - web3.solidityKeccak(types, values) - - def test_is_connected(self, web3): - assert web3.isConnected() +import pytest + +from hexbytes import ( + HexBytes, +) + +from web3 import Web3 +from web3._utils.ens import ( + ens_addresses, +) +from web3.exceptions import ( + InvalidAddress, +) + + +class Web3ModuleTest: + def test_web3_clientVersion(self, web3): + client_version = web3.clientVersion + self._check_web3_clientVersion(client_version) + + def _check_web3_clientVersion(self, client_version): + raise NotImplementedError("Must be implemented by subclasses") + + # Contract that calculated test values can be found at + # https://kovan.etherscan.io/address/0xb9be06f5b99372cf9afbccadbbb9954ccaf7f4bb#code + @pytest.mark.parametrize( + 'types,values,expected', + ( + ( + ['bool'], + [True], + HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"), + ), + ( + ['uint8', 'uint8', 'uint8'], + [97, 98, 99], + HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"), + ), + ( + ['uint248'], + [30], + HexBytes("0x30f95d210785601eb33ae4d53d405b26f920e765dff87cca8e9a4aec99f82671"), + ), + ( + ['bool', 'uint16'], + [True, 299], + HexBytes("0xed18599ccd80ee9fae9a28b0e34a5573c3233d7468f808fd659bc171cf0b43bd"), + ), + ( + ['int256'], + [-10], + HexBytes("0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b458a304d3d5"), + ), + ( + ['int256'], + [10], + HexBytes("0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8"), + ), + ( + ['int8', 'uint8'], + [-10, 18], + HexBytes("0x5c6ab1e634c08d9c0f4df4d789e8727943ef010dd7ca8e3c89de197a26d148be"), + ), + ( + ['address'], + ["0x49eddd3769c0712032808d86597b84ac5c2f5614"], + InvalidAddress, + ), + ( + ['address'], + ["0x49EdDD3769c0712032808D86597B84ac5c2F5614"], + HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), + ), + ( + ['bytes2'], + ['0x5402'], + HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), + ), + ( + ['bytes3'], + ['0x5402'], + HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), + ), + ( + ['bytes'], + [ + '0x636865636b6c6f6e6762797465737472696e676167' + '61696e7374736f6c6964697479736861336861736866756e6374696f6e' + ], + HexBytes("0xd78a84d65721b67e4011b10c99dafdedcdcd7cb30153064f773e210b4762e22f"), + ), + ( + ['string'], + ['testing a string!'], + HexBytes("0xe8c275c0b4070a5ec6cfcb83f0ba394b30ddd283de785d43f2eabfb04bd96747"), + ), + ( + ['string', 'bool', 'uint16', 'bytes2', 'address'], + [ + 'testing a string!', + False, + 299, + '0x5402', + "0x49eddd3769c0712032808d86597b84ac5c2f5614", + ], + InvalidAddress, + ), + ( + ['string', 'bool', 'uint16', 'bytes2', 'address'], + [ + 'testing a string!', + False, + 299, + '0x5402', + "0x49EdDD3769c0712032808D86597B84ac5c2F5614", + ], + HexBytes("0x8cc6eabb25b842715e8ca39e2524ed946759aa37bfb7d4b81829cf5a7e266103"), + ), + ( + ['bool[2][]'], + [[[True, False], [False, True]]], + HexBytes("0x1eef261f2eb51a8c736d52be3f91ff79e78a9ec5df2b7f50d0c6f98ed1e2bc06"), + ), + ( + ['bool[]'], + [[True, False, True]], + HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), + ), + ( + ['uint24[]'], + [[1, 0, 1]], + HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), + ), + ( + ['uint8[2]'], + [[8, 9]], + HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), + ), + ( + ['uint256[2]'], + [[8, 9]], + HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), + ), + ( + ['uint8[]'], + [[8]], + HexBytes("0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3"), + ), + ( + ['address[]'], + [[ + "0x49EdDD3769c0712032808D86597B84ac5c2F5614", + "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", + ]], + HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), + ), + ( + ['address[]'], + [[ + "0x49EdDD3769c0712032808D86597B84ac5c2F5614", + "0xa6b759bbbf4b59d24acf7e06e79f3a5d104fdce5", + ]], + InvalidAddress, + ), + ), + ) + def test_solidityKeccak(self, web3, types, values, expected): + if isinstance(expected, type) and issubclass(expected, Exception): + with pytest.raises(expected): + web3.solidityKeccak(types, values) + return + + actual = web3.solidityKeccak(types, values) + assert actual == expected + + @pytest.mark.parametrize( + 'types, values, expected', + ( + ( + ['address'], + ['one.vns'], + HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), + ), + ( + ['address[]'], + [['one.vns', 'two.vns']], + HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), + ), + ), + ) + def test_solidityKeccak_ens(self, web3, types, values, expected): + with ens_addresses(web3, { + 'one.vns': "0x49EdDD3769c0712032808D86597B84ac5c2F5614", + 'two.vns': "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", + }): + # when called as class method, any name lookup attempt will fail + with pytest.raises(InvalidAddress): + Web3.solidityKeccak(types, values) + + # when called as instance method, ens lookups can succeed + actual = web3.solidityKeccak(types, values) + assert actual == expected + + @pytest.mark.parametrize( + 'types,values', + ( + (['address'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5', True]), + (['address', 'bool'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), + ([], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), + ) + ) + def test_solidityKeccak_same_number_of_types_and_values(self, web3, types, values): + with pytest.raises(ValueError): + web3.solidityKeccak(types, values) + + def test_is_connected(self, web3): + assert web3.isConnected() diff --git a/web3/_utils/normalizers.py b/web3/_utils/normalizers.py index 80e189ac0d..c72826b982 100644 --- a/web3/_utils/normalizers.py +++ b/web3/_utils/normalizers.py @@ -1,219 +1,219 @@ - -import codecs -from distutils.version import ( - LooseVersion, -) -import functools -import json - -import eth_abi -from eth_abi.exceptions import ( - ParseError, -) -from eth_abi.grammar import ( - BasicType, - parse, -) -from eth_utils import ( - to_checksum_address, -) -from eth_utils.address import ( - is_binary_address, -) -from eth_utils.toolz import ( - curry, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.encoding import ( - hexstr_if_str, - text_if_str, - to_bytes, - to_hex, - to_text, -) -from web3._utils.ens import ( - StaticENS, - is_ens_name, - validate_name_has_address, -) -from web3._utils.validation import ( - validate_abi, - validate_address, -) -from web3.exceptions import ( - InvalidAddress, -) - - -def implicitly_identity(to_wrap): - @functools.wraps(to_wrap) - def wrapper(type_str, data): - modified = to_wrap(type_str, data) - if modified is None: - return type_str, data - else: - return modified - return wrapper - - -# -# Return Normalizers -# - - -@implicitly_identity -def addresses_checksummed(type_str, data): - if type_str == 'address': - return type_str, to_checksum_address(data) - - -@implicitly_identity -def decode_abi_strings(type_str, data): - if type_str == 'string': - return type_str, codecs.decode(data, 'utf8', 'backslashreplace') - - -# -# Argument Normalizers -# - - -def parse_basic_type_str(old_normalizer): - """ - Modifies a normalizer to automatically parse the incoming type string. If - that type string does not represent a basic type (i.e. non-tuple type) or is - not parsable, the normalizer does nothing. - """ - @functools.wraps(old_normalizer) - def new_normalizer(type_str, data): - try: - abi_type = parse(type_str) - except ParseError: - # If type string is not parsable, do nothing - return type_str, data - - if not isinstance(abi_type, BasicType): - return type_str, data - - return old_normalizer(abi_type, type_str, data) - - return new_normalizer - - -@implicitly_identity -@parse_basic_type_str -def abi_bytes_to_hex(abi_type, type_str, data): - if abi_type.base != 'bytes' or abi_type.is_array: - return - - bytes_data = hexstr_if_str(to_bytes, data) - if abi_type.sub is None: - return type_str, to_hex(bytes_data) - - num_bytes = abi_type.sub - if len(bytes_data) > num_bytes: - raise ValueError( - "This value was expected to be at most %d bytes, but instead was %d: %r" % ( - (num_bytes, len(bytes_data), data) - ) - ) - - padded = bytes_data.ljust(num_bytes, b'\0') - return type_str, to_hex(padded) - - -@implicitly_identity -@parse_basic_type_str -def abi_int_to_hex(abi_type, type_str, data): - if abi_type.base == 'uint' and not abi_type.is_array: - return abi_type, hexstr_if_str(to_hex, data) - - -@implicitly_identity -def abi_string_to_hex(type_str, data): - if type_str == 'string': - return type_str, text_if_str(to_hex, data) - - -@implicitly_identity -def abi_string_to_text(type_str, data): - if type_str == 'string': - return type_str, text_if_str(to_text, data) - - -@implicitly_identity -@parse_basic_type_str -def abi_bytes_to_bytes(abi_type, type_str, data): - if abi_type.base == 'bytes' and not abi_type.is_array: - return type_str, hexstr_if_str(to_bytes, data) - - -@implicitly_identity -def abi_address_to_hex(type_str, data): - if type_str == 'address': - validate_address(data) - if is_binary_address(data): - return type_str, to_checksum_address(data) - - -@curry -def abi_ens_resolver(w3, type_str, val): - if type_str == 'address' and is_ens_name(val): - if w3 is None: - raise InvalidAddress( - "Could not look up name %r because no web3" - " connection available" % (val) - ) - elif w3.ens is None: - raise InvalidAddress( - "Could not look up name %r because ENS is" - " set to None" % (val) - ) - elif int(w3.net.version) != 1 and not isinstance(w3.ens, StaticENS): - raise InvalidAddress( - "Could not look up name %r because web3 is" - " not connected to mainnet" % (val) - ) - else: - return type_str, validate_name_has_address(w3.ens, val) - else: - return type_str, val - - -BASE_RETURN_NORMALIZERS = [ - addresses_checksummed, -] - - -if LooseVersion(eth_abi.__version__) < LooseVersion("2"): - BASE_RETURN_NORMALIZERS.append(decode_abi_strings) - - -# -# Property Normalizers -# - - -def normalize_abi(abi): - if isinstance(abi, str): - abi = json.loads(abi) - validate_abi(abi) - return abi - - -def normalize_address(ens, address): - if address: - if is_ens_name(address): - validate_name_has_address(ens, address) - else: - validate_address(address) - return address - - -def normalize_bytecode(bytecode): - if bytecode: - bytecode = HexBytes(bytecode) - return bytecode + +import codecs +from distutils.version import ( + LooseVersion, +) +import functools +import json + +import vns_abi +from vns_abi.exceptions import ( + ParseError, +) +from vns_abi.grammar import ( + BasicType, + parse, +) +from vns_utils import ( + to_checksum_address, +) +from vns_utils.address import ( + is_binary_address, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.encoding import ( + hexstr_if_str, + text_if_str, + to_bytes, + to_hex, + to_text, +) +from web3._utils.ens import ( + StaticENS, + is_ens_name, + validate_name_has_address, +) +from web3._utils.toolz import ( + curry, +) +from web3._utils.validation import ( + validate_abi, + validate_address, +) +from web3.exceptions import ( + InvalidAddress, +) + + +def implicitly_identity(to_wrap): + @functools.wraps(to_wrap) + def wrapper(type_str, data): + modified = to_wrap(type_str, data) + if modified is None: + return type_str, data + else: + return modified + return wrapper + + +# +# Return Normalizers +# + + +@implicitly_identity +def addresses_checksummed(type_str, data): + if type_str == 'address': + return type_str, to_checksum_address(data) + + +@implicitly_identity +def decode_abi_strings(type_str, data): + if type_str == 'string': + return type_str, codecs.decode(data, 'utf8', 'backslashreplace') + + +# +# Argument Normalizers +# + + +def parse_basic_type_str(old_normalizer): + """ + Modifies a normalizer to automatically parse the incoming type string. If + that type string does not represent a basic type (i.e. non-tuple type) or is + not parsable, the normalizer does nothing. + """ + @functools.wraps(old_normalizer) + def new_normalizer(type_str, data): + try: + abi_type = parse(type_str) + except ParseError: + # If type string is not parsable, do nothing + return type_str, data + + if not isinstance(abi_type, BasicType): + return type_str, data + + return old_normalizer(abi_type, type_str, data) + + return new_normalizer + + +@implicitly_identity +@parse_basic_type_str +def abi_bytes_to_hex(abi_type, type_str, data): + if abi_type.base != 'bytes' or abi_type.is_array: + return + + bytes_data = hexstr_if_str(to_bytes, data) + if abi_type.sub is None: + return type_str, to_hex(bytes_data) + + num_bytes = abi_type.sub + if len(bytes_data) > num_bytes: + raise ValueError( + "This value was expected to be at most %d bytes, but instead was %d: %r" % ( + (num_bytes, len(bytes_data), data) + ) + ) + + padded = bytes_data.ljust(num_bytes, b'\0') + return type_str, to_hex(padded) + + +@implicitly_identity +@parse_basic_type_str +def abi_int_to_hex(abi_type, type_str, data): + if abi_type.base == 'uint' and not abi_type.is_array: + return abi_type, hexstr_if_str(to_hex, data) + + +@implicitly_identity +def abi_string_to_hex(type_str, data): + if type_str == 'string': + return type_str, text_if_str(to_hex, data) + + +@implicitly_identity +def abi_string_to_text(type_str, data): + if type_str == 'string': + return type_str, text_if_str(to_text, data) + + +@implicitly_identity +@parse_basic_type_str +def abi_bytes_to_bytes(abi_type, type_str, data): + if abi_type.base == 'bytes' and not abi_type.is_array: + return type_str, hexstr_if_str(to_bytes, data) + + +@implicitly_identity +def abi_address_to_hex(type_str, data): + if type_str == 'address': + validate_address(data) + if is_binary_address(data): + return type_str, to_checksum_address(data) + + +@curry +def abi_ens_resolver(w3, type_str, val): + if type_str == 'address' and is_ens_name(val): + if w3 is None: + raise InvalidAddress( + "Could not look up name %r because no web3" + " connection available" % (val) + ) + elif w3.ens is None: + raise InvalidAddress( + "Could not look up name %r because ENS is" + " set to None" % (val) + ) + elif int(w3.net.version) is not 1 and not isinstance(w3.ens, StaticENS): + raise InvalidAddress( + "Could not look up name %r because web3 is" + " not connected to mainnet" % (val) + ) + else: + return type_str, validate_name_has_address(w3.ens, val) + else: + return type_str, val + + +BASE_RETURN_NORMALIZERS = [ + addresses_checksummed, +] + + +if LooseVersion(vns_abi.__version__) < LooseVersion("2"): + BASE_RETURN_NORMALIZERS.append(decode_abi_strings) + + +# +# Property Normalizers +# + + +def normalize_abi(abi): + if isinstance(abi, str): + abi = json.loads(abi) + validate_abi(abi) + return abi + + +def normalize_address(ens, address): + if address: + if is_ens_name(address): + validate_name_has_address(ens, address) + else: + validate_address(address) + return address + + +def normalize_bytecode(bytecode): + if bytecode: + bytecode = HexBytes(bytecode) + return bytecode diff --git a/web3/_utils/personal.py b/web3/_utils/personal.py index 7796483e39..032b822bbe 100644 --- a/web3/_utils/personal.py +++ b/web3/_utils/personal.py @@ -1,57 +1,51 @@ -from web3.method import ( - Method, - default_root_munger, -) - -importRawKey = Method( - "personal_importRawKey", - mungers=[default_root_munger], -) - - -newAccount = Method( - "personal_newAccount", - mungers=[default_root_munger], -) - - -listAccounts = Method( - "personal_listAccounts", - mungers=None, -) - - -sendTransaction = Method( - "personal_sendTransaction", - mungers=[default_root_munger], -) - - -lockAccount = Method( - "personal_lockAccount", - mungers=[default_root_munger], -) - - -unlockAccount = Method( - "personal_unlockAccount", - mungers=[default_root_munger], -) - - -sign = Method( - "personal_sign", - mungers=[default_root_munger], -) - - -signTypedData = Method( - "personal_signTypedData", - mungers=[default_root_munger], -) - - -ecRecover = Method( - "personal_ecRecover", - mungers=[default_root_munger], -) +from web3.method import ( + Method, + default_root_munger, +) + +importRawKey = Method( + "personal_importRawKey", + mungers=[default_root_munger], +) + + +newAccount = Method( + "personal_newAccount", + mungers=[default_root_munger], +) + + +listAccounts = Method( + "personal_listAccounts", + mungers=None, +) + + +sendTransaction = Method( + "personal_sendTransaction", + mungers=[default_root_munger], +) + + +lockAccount = Method( + "personal_lockAccount", + mungers=[default_root_munger], +) + + +unlockAccount = Method( + "personal_unlockAccount", + mungers=[default_root_munger], +) + + +sign = Method( + "personal_sign", + mungers=[default_root_munger], +) + + +ecRecover = Method( + "personal_ecRecover", + mungers=[default_root_munger], +) diff --git a/web3/_utils/request.py b/web3/_utils/request.py index c43157d6db..464d0368be 100644 --- a/web3/_utils/request.py +++ b/web3/_utils/request.py @@ -1,29 +1,29 @@ -import lru -import requests - -from web3._utils.caching import ( - generate_cache_key, -) - - -def _remove_session(key, session): - session.close() - - -_session_cache = lru.LRU(8, callback=_remove_session) - - -def _get_session(*args, **kwargs): - cache_key = generate_cache_key((args, kwargs)) - if cache_key not in _session_cache: - _session_cache[cache_key] = requests.Session() - return _session_cache[cache_key] - - -def make_post_request(endpoint_uri, data, *args, **kwargs): - kwargs.setdefault('timeout', 10) - session = _get_session(endpoint_uri) - response = session.post(endpoint_uri, data=data, *args, **kwargs) - response.raise_for_status() - - return response.content +import lru +import requests + +from web3._utils.caching import ( + generate_cache_key, +) + + +def _remove_session(key, session): + session.close() + + +_session_cache = lru.LRU(8, callback=_remove_session) + + +def _get_session(*args, **kwargs): + cache_key = generate_cache_key((args, kwargs)) + if cache_key not in _session_cache: + _session_cache[cache_key] = requests.Session() + return _session_cache[cache_key] + + +def make_post_request(endpoint_uri, data, *args, **kwargs): + kwargs.setdefault('timeout', 10) + session = _get_session(endpoint_uri) + response = session.post(endpoint_uri, data=data, *args, **kwargs) + response.raise_for_status() + + return response.content diff --git a/web3/_utils/rpc_abi.py b/web3/_utils/rpc_abi.py index 407cfc9274..d5d7374bd3 100644 --- a/web3/_utils/rpc_abi.py +++ b/web3/_utils/rpc_abi.py @@ -1,92 +1,88 @@ -from eth_utils import ( - to_dict, -) -from eth_utils.curried import ( - apply_formatter_at_index, -) -from eth_utils.toolz import ( - curry, -) - -from web3._utils.abi import ( - map_abi_data, -) - -TRANSACTION_PARAMS_ABIS = { - 'data': 'bytes', - 'from': 'address', - 'gas': 'uint', - 'gasPrice': 'uint', - 'nonce': 'uint', - 'to': 'address', - 'value': 'uint', -} - -FILTER_PARAMS_ABIS = { - 'to': 'address', - 'address': 'address[]', -} - -TRACE_PARAMS_ABIS = { - 'to': 'address', - 'from': 'address', -} - -RPC_ABIS = { - # eth - 'eth_call': TRANSACTION_PARAMS_ABIS, - 'eth_estimateGas': TRANSACTION_PARAMS_ABIS, - 'eth_getBalance': ['address', None], - 'eth_getBlockByHash': ['bytes32', 'bool'], - 'eth_getBlockTransactionCountByHash': ['bytes32'], - 'eth_getCode': ['address', None], - 'eth_getLogs': FILTER_PARAMS_ABIS, - 'eth_getStorageAt': ['address', 'uint', None], - 'eth_getProof': ['address', 'uint[]', None], - 'eth_getTransactionByBlockHashAndIndex': ['bytes32', 'uint'], - 'eth_getTransactionByHash': ['bytes32'], - 'eth_getTransactionCount': ['address', None], - 'eth_getTransactionReceipt': ['bytes32'], - 'eth_getUncleCountByBlockHash': ['bytes32'], - 'eth_newFilter': FILTER_PARAMS_ABIS, - 'eth_sendRawTransaction': ['bytes'], - 'eth_sendTransaction': TRANSACTION_PARAMS_ABIS, - 'eth_signTransaction': TRANSACTION_PARAMS_ABIS, - 'eth_sign': ['address', 'bytes'], - 'eth_signTypedData': ['address', None], - 'eth_submitHashrate': ['uint', 'bytes32'], - 'eth_submitWork': ['bytes8', 'bytes32', 'bytes32'], - # personal - 'personal_sendTransaction': TRANSACTION_PARAMS_ABIS, - 'personal_lockAccount': ['address'], - 'personal_unlockAccount': ['address', None, None], - 'personal_sign': [None, 'address', None], - 'personal_signTypedData': [None, 'address', None], - 'trace_call': TRACE_PARAMS_ABIS, - # parity - 'parity_listStorageKeys': ['address', None, None, None], -} - - -@curry -def apply_abi_formatters_to_dict(normalizers, abi_dict, data): - fields = list(set(abi_dict.keys()) & set(data.keys())) - formatted_values = map_abi_data( - normalizers, - [abi_dict[field] for field in fields], - [data[field] for field in fields], - ) - formatted_dict = dict(zip(fields, formatted_values)) - return dict(data, **formatted_dict) - - -@to_dict -def abi_request_formatters(normalizers, abis): - for method, abi_types in abis.items(): - if isinstance(abi_types, list): - yield method, map_abi_data(normalizers, abi_types) - elif isinstance(abi_types, dict): - single_dict_formatter = apply_abi_formatters_to_dict(normalizers, abi_types) - yield method, apply_formatter_at_index(single_dict_formatter, 0) - else: - raise TypeError("ABI definitions must be a list or dictionary, got %r" % abi_types) +from vns_utils import ( + to_dict, +) + +from web3._utils.abi import ( + map_abi_data, +) +from web3._utils.formatters import ( + apply_formatter_at_index, +) +from web3._utils.toolz import ( + curry, +) + +TRANSACTION_PARAMS_ABIS = { + 'data': 'bytes', + 'from': 'address', + 'gas': 'uint', + 'gasPrice': 'uint', + 'nonce': 'uint', + 'to': 'address', + 'value': 'uint', +} + +FILTER_PARAMS_ABIS = { + 'to': 'address', + 'address': 'address[]', +} + +TRACE_PARAMS_ABIS = { + 'to': 'address', + 'from': 'address', +} + +RPC_ABIS = { + # vns 'vns_call': TRANSACTION_PARAMS_ABIS, + 'vns_estimateGas': TRANSACTION_PARAMS_ABIS, + 'vns_getBalance': ['address', None], + 'vns_getBlockByHash': ['bytes32', 'bool'], + 'vns_getBlockTransactionCountByHash': ['bytes32'], + 'vns_getCode': ['address', None], + 'vns_getLogs': FILTER_PARAMS_ABIS, + 'vns_getStorageAt': ['address', 'uint', None], + 'vns_getTransactionByBlockHashAndIndex': ['bytes32', 'uint'], + 'vns_getTransactionByHash': ['bytes32'], + 'vns_getTransactionCount': ['address', None], + 'vns_getTransactionReceipt': ['bytes32'], + 'vns_getUncleCountByBlockHash': ['bytes32'], + 'vns_newFilter': FILTER_PARAMS_ABIS, + 'vns_sendRawTransaction': ['bytes'], + 'vns_sendTransaction': TRANSACTION_PARAMS_ABIS, + 'vns_signTransaction': TRANSACTION_PARAMS_ABIS, + 'vns_sign': ['address', 'bytes'], + 'vns_submitHashrate': ['uint', 'bytes32'], + 'vns_submitWork': ['bytes8', 'bytes32', 'bytes32'], + # personal + 'personal_sendTransaction': TRANSACTION_PARAMS_ABIS, + 'personal_lockAccount': ['address'], + 'personal_unlockAccount': ['address', None, None], + 'personal_sign': [None, 'address', None], + 'trace_call': TRACE_PARAMS_ABIS, + # parity + 'parity_listStorageKeys': ['address', None, None, None], +} + + +@curry +def apply_abi_formatters_to_dict(normalizers, abi_dict, data): + fields = list(set(abi_dict.keys()) & set(data.keys())) + formatted_values = map_abi_data( + normalizers, + [abi_dict[field] for field in fields], + [data[field] for field in fields], + ) + formatted_dict = dict(zip(fields, formatted_values)) + return dict(data, **formatted_dict) + + +@to_dict +def abi_request_formatters(normalizers, abis): + for method, abi_types in abis.items(): + if isinstance(abi_types, list): + yield method, map_abi_data(normalizers, abi_types) + elif isinstance(abi_types, dict): + single_dict_formatter = apply_abi_formatters_to_dict(normalizers, abi_types) + yield method, apply_formatter_at_index(single_dict_formatter, 0) + else: + raise TypeError("ABI definitions must be a list or dictionary, got %r" % abi_types) diff --git a/web3/_utils/shh.py b/web3/_utils/shh.py index f0358bcdee..231218b25b 100644 --- a/web3/_utils/shh.py +++ b/web3/_utils/shh.py @@ -1,188 +1,154 @@ -from web3.method import ( - DeprecatedMethod, - Method, - default_root_munger, -) - -version = Method( - "shh_version", - mungers=None, -) - - -info = Method( - "shh_info", - mungers=None, -) - - -set_max_message_size = Method( - "shh_setMaxMessageSize", - mungers=[default_root_munger], -) - - -set_min_pow = Method( - "shh_setMinPoW", - mungers=[default_root_munger], -) - - -mark_trusted_peer = Method( - "shh_markTrustedPeer", - mungers=[default_root_munger], -) - - -new_key_pair = Method( - "shh_newKeyPair", - mungers=None, -) - - -add_private_key = Method( - "shh_addPrivateKey", - mungers=[default_root_munger], -) - - -delete_key_pair = Method( - "shh_deleteKeyPair", - mungers=[default_root_munger], -) - - -delete_key = Method( - "shh_deleteKey", - mungers=[default_root_munger], -) - - -has_key_pair = Method( - "shh_hasKeyPair", - mungers=[default_root_munger], -) - - -get_public_key = Method( - "shh_getPublicKey", - mungers=[default_root_munger], -) - - -get_private_key = Method( - "shh_getPrivateKey", - mungers=[default_root_munger], -) - - -new_sym_key = Method( - "shh_newSymKey", - mungers=None, -) - - -add_sym_key = Method( - "shh_addSymKey", - mungers=[default_root_munger], -) - - -generate_sym_key_from_password = Method( - "shh_generateSymKeyFromPassword", - mungers=[default_root_munger], -) - - -has_sym_key = Method( - "shh_hasSymKey", - mungers=[default_root_munger], -) - - -get_sym_key = Method( - "shh_getSymKey", - mungers=[default_root_munger], -) - - -delete_sym_key = Method( - "shh_deleteSymKey", - mungers=[default_root_munger], -) - - -def post_munger(module, message): - if message and ("payload" in message): - return (message,) - else: - raise ValueError("Message cannot be None or does not contain field 'payload'") - - -post = Method( - "shh_post", - mungers=[post_munger], -) - - -new_message_filter = Method( - "shh_newMessageFilter", - mungers=[default_root_munger], -) - - -delete_message_filter = Method( - "shh_deleteMessageFilter", - mungers=[default_root_munger], -) - - -get_filter_messages = Method( - "shh_getFilterMessages", - mungers=[default_root_munger], -) - - -subscribe = Method( - "shh_subscribe", - mungers=[default_root_munger], -) - - -unsubscribe = Method( - "shh_unsubscribe", - mungers=[default_root_munger], -) - -# DeprecatedMethods -setMaxMessageSize = DeprecatedMethod( - set_max_message_size, - 'setMaxMessageSize', - 'set_max_message_size') -setMinPoW = DeprecatedMethod(set_min_pow, 'setMinPoW', 'set_min_pow') -markTrustedPeer = DeprecatedMethod(mark_trusted_peer, 'markTrustedPeer', 'mark_trusted_peer') -newKeyPair = DeprecatedMethod(new_key_pair, 'newKeyPair', new_key_pair) -addPrivateKey = DeprecatedMethod(add_private_key, 'addPrivateKey', 'add_private_key') -deleteKeyPair = DeprecatedMethod(delete_key_pair, 'deleteKeyPair', 'delete_key_pair') -deleteKey = DeprecatedMethod(delete_key, 'deleteKey', 'delete_key') -hasKeyPair = DeprecatedMethod(has_key_pair, 'hasKeyPair', 'has_key_pair') -getPublicKey = DeprecatedMethod(get_public_key, 'getPublicKey', 'get_public_key') -getPrivateKey = DeprecatedMethod(get_private_key, 'getPrivateKey', 'get_private_key') -newSymKey = DeprecatedMethod(new_sym_key, 'newSymKey', 'new_sym_key') -addSymKey = DeprecatedMethod(add_sym_key, 'addSymKey', 'add_sym_key') -generateSymKeyFromPassword = DeprecatedMethod( - generate_sym_key_from_password, - 'generateSymKeyFromPassword', - 'generate_sym_key_from_password') -hasSymKey = DeprecatedMethod(has_sym_key, 'hasSymKey', 'has_sym_key') -getSymKey = DeprecatedMethod(get_sym_key, 'getSymKey', 'get_sym_key') -deleteSymKey = DeprecatedMethod(delete_sym_key, 'deleteSymKey', 'delete_sym_key') -newMessageFilter = DeprecatedMethod(new_message_filter, 'newMessageFilter', 'new_message_filter') -deleteMessageFilter = DeprecatedMethod( - delete_message_filter, - 'deleteMessageFilter', - 'delete_message_filter') -getFilterMessages = DeprecatedMethod( - get_filter_messages, - 'getFilterMessages', - 'get_filter_messages') +from web3.method import ( + Method, + default_root_munger, +) + +version = Method( + "shh_version", + mungers=None, +) + + +info = Method( + "shh_info", + mungers=None, +) + + +setMaxMessageSize = Method( + "shh_setMaxMessageSize", + mungers=[default_root_munger], +) + + +setMinPoW = Method( + "shh_setMinPoW", + mungers=[default_root_munger], +) + + +markTrustedPeer = Method( + "shh_markTrustedPeer", + mungers=[default_root_munger], +) + + +newKeyPair = Method( + "shh_newKeyPair", + mungers=None, +) + + +addPrivateKey = Method( + "shh_addPrivateKey", + mungers=[default_root_munger], +) + + +deleteKeyPair = Method( + "shh_deleteKeyPair", + mungers=[default_root_munger], +) + + +deleteKey = Method( + "shh_deleteKey", + mungers=[default_root_munger], +) + + +hasKeyPair = Method( + "shh_hasKeyPair", + mungers=[default_root_munger], +) + + +getPublicKey = Method( + "shh_getPublicKey", + mungers=[default_root_munger], +) + + +getPrivateKey = Method( + "shh_getPrivateKey", + mungers=[default_root_munger], +) + + +newSymKey = Method( + "shh_newSymKey", + mungers=None, +) + + +addSymKey = Method( + "shh_addSymKey", + mungers=[default_root_munger], +) + + +generateSymKeyFromPassword = Method( + "shh_generateSymKeyFromPassword", + mungers=[default_root_munger], +) + + +hasSymKey = Method( + "shh_hasSymKey", + mungers=[default_root_munger], +) + + +getSymKey = Method( + "shh_getSymKey", + mungers=[default_root_munger], +) + + +deleteSymKey = Method( + "shh_deleteSymKey", + mungers=[default_root_munger], +) + + +def post_munger(module, message): + if message and ("payload" in message): + return (message,) + else: + raise ValueError("Message cannot be None or does not contain field 'payload'") + + +post = Method( + "shh_post", + mungers=[post_munger], +) + + +newMessageFilter = Method( + "shh_newMessageFilter", + mungers=[default_root_munger], +) + + +deleteMessageFilter = Method( + "shh_deleteMessageFilter", + mungers=[default_root_munger], +) + + +getFilterMessages = Method( + "shh_getFilterMessages", + mungers=[default_root_munger], +) + + +subscribe = Method( + "shh_subscribe", + mungers=[default_root_munger], +) + + +unsubscribe = Method( + "shh_unsubscribe", + mungers=[default_root_munger], +) diff --git a/web3/_utils/threads.py b/web3/_utils/threads.py index 1231ff04da..68563b8375 100644 --- a/web3/_utils/threads.py +++ b/web3/_utils/threads.py @@ -1,118 +1,118 @@ -""" -A minimal implementation of the various gevent APIs used within this codebase. -""" -import threading -import time - - -class Timeout(Exception): - """ - A limited subset of the `gevent.Timeout` context manager. - """ - seconds = None - exception = None - begun_at = None - is_running = None - - def __init__(self, seconds=None, exception=None, *args, **kwargs): - self.seconds = seconds - self.exception = exception - - def __enter__(self): - self.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - return False - - def __str__(self): - if self.seconds is None: - return '' - return "{0} seconds".format(self.seconds) - - @property - def expire_at(self): - if self.seconds is None: - raise ValueError("Timeouts with `seconds == None` do not have an expiration time") - elif self.begun_at is None: - raise ValueError("Timeout has not been started") - return self.begun_at + self.seconds - - def start(self): - if self.is_running is not None: - raise ValueError("Timeout has already been started") - self.begun_at = time.time() - self.is_running = True - - def check(self): - if self.is_running is None: - raise ValueError("Timeout has not been started") - elif self.is_running is False: - raise ValueError("Timeout has already been cancelled") - elif self.seconds is None: - return - elif time.time() > self.expire_at: - self.is_running = False - if isinstance(self.exception, type): - raise self.exception(str(self)) - elif isinstance(self.exception, Exception): - raise self.exception - else: - raise self - - def cancel(self): - self.is_running = False - - def sleep(self, seconds): - time.sleep(seconds) - self.check() - - -class ThreadWithReturn(threading.Thread): - def __init__(self, target=None, args=None, kwargs=None): - super().__init__( - target=target, - args=args or tuple(), - kwargs=kwargs or {}, - ) - self.target = target - self.args = args - self.kwargs = kwargs - - def run(self): - self._return = self.target(*self.args, **self.kwargs) - - def get(self, timeout=None): - self.join(timeout) - try: - return self._return - except AttributeError: - raise RuntimeError("Something went wrong. No `_return` property was set") - - -class TimerClass(threading.Thread): - def __init__(self, interval, callback, *args): - threading.Thread.__init__(self) - self.callback = callback - self.terminate_event = threading.Event() - self.interval = interval - self.args = args - - def run(self): - while not self.terminate_event.is_set(): - self.callback(*self.args) - self.terminate_event.wait(self.interval) - - def stop(self): - self.terminate_event.set() - - -def spawn(target, *args, thread_class=ThreadWithReturn, **kwargs): - thread = thread_class( - target=target, - args=args, - kwargs=kwargs, - ) - thread.daemon = True - thread.start() - return thread +""" +A minimal implementation of the various gevent APIs used within this codebase. +""" +import threading +import time + + +class Timeout(Exception): + """ + A limited subset of the `gevent.Timeout` context manager. + """ + seconds = None + exception = None + begun_at = None + is_running = None + + def __init__(self, seconds=None, exception=None, *args, **kwargs): + self.seconds = seconds + self.exception = exception + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + def __str__(self): + if self.seconds is None: + return '' + return "{0} seconds".format(self.seconds) + + @property + def expire_at(self): + if self.seconds is None: + raise ValueError("Timeouts with `seconds == None` do not have an expiration time") + elif self.begun_at is None: + raise ValueError("Timeout has not been started") + return self.begun_at + self.seconds + + def start(self): + if self.is_running is not None: + raise ValueError("Timeout has already been started") + self.begun_at = time.time() + self.is_running = True + + def check(self): + if self.is_running is None: + raise ValueError("Timeout has not been started") + elif self.is_running is False: + raise ValueError("Timeout has already been cancelled") + elif self.seconds is None: + return + elif time.time() > self.expire_at: + self.is_running = False + if isinstance(self.exception, type): + raise self.exception(str(self)) + elif isinstance(self.exception, Exception): + raise self.exception + else: + raise self + + def cancel(self): + self.is_running = False + + def sleep(self, seconds): + time.sleep(seconds) + self.check() + + +class ThreadWithReturn(threading.Thread): + def __init__(self, target=None, args=None, kwargs=None): + super().__init__( + target=target, + args=args or tuple(), + kwargs=kwargs or {}, + ) + self.target = target + self.args = args + self.kwargs = kwargs + + def run(self): + self._return = self.target(*self.args, **self.kwargs) + + def get(self, timeout=None): + self.join(timeout) + try: + return self._return + except AttributeError: + raise RuntimeError("Something went wrong. No `_return` property was set") + + +class TimerClass(threading.Thread): + def __init__(self, interval, callback, *args): + threading.Thread.__init__(self) + self.callback = callback + self.terminate_event = threading.Event() + self.interval = interval + self.args = args + + def run(self): + while not self.terminate_event.is_set(): + self.callback(*self.args) + self.terminate_event.wait(self.interval) + + def stop(self): + self.terminate_event.set() + + +def spawn(target, *args, thread_class=ThreadWithReturn, **kwargs): + thread = thread_class( + target=target, + args=args, + kwargs=kwargs, + ) + thread.daemon = True + thread.start() + return thread diff --git a/web3/_utils/toolz/__init__.py b/web3/_utils/toolz/__init__.py new file mode 100644 index 0000000000..faf8bbae86 --- /dev/null +++ b/web3/_utils/toolz/__init__.py @@ -0,0 +1,44 @@ +try: + from cytoolz import ( + assoc, + complement, + compose, + concat, + cons, + curry, + dicttoolz, + dissoc, + excepts, + functoolz, + groupby, + identity, + itertoolz, + merge, + partial, + pipe, + sliding_window, + valfilter, + valmap, + ) +except ImportError: + from toolz import ( # noqa: F401 + assoc, + complement, + compose, + concat, + cons, + curry, + dicttoolz, + dissoc, + excepts, + functoolz, + groupby, + identity, + itertoolz, + merge, + partial, + pipe, + sliding_window, + valfilter, + valmap, + ) diff --git a/web3/_utils/toolz/curried.py b/web3/_utils/toolz/curried.py new file mode 100644 index 0000000000..61677c0ced --- /dev/null +++ b/web3/_utils/toolz/curried.py @@ -0,0 +1,10 @@ +try: + from cytoolz.curried import ( + keymap, + valmap, + ) +except ImportError: + from toolz.curried import ( # noqa: F401 + keymap, + valmap, + ) diff --git a/web3/_utils/transactions.py b/web3/_utils/transactions.py index 26e2de5922..0cfcba66c4 100644 --- a/web3/_utils/transactions.py +++ b/web3/_utils/transactions.py @@ -1,175 +1,174 @@ -import math - -from eth_utils.toolz import ( - assoc, - curry, - merge, -) - -from web3._utils.threads import ( - Timeout, -) -from web3.exceptions import ( - TransactionNotFound, -) - -VALID_TRANSACTION_PARAMS = [ - 'from', - 'to', - 'gas', - 'gasPrice', - 'value', - 'data', - 'nonce', - 'chainId', -] - -TRANSACTION_DEFAULTS = { - 'value': 0, - 'data': b'', - 'gas': lambda web3, tx: web3.eth.estimateGas(tx), - 'gasPrice': lambda web3, tx: web3.eth.generateGasPrice(tx) or web3.eth.gasPrice, - 'chainId': lambda web3, tx: web3.eth.chainId, -} - - -@curry -def fill_nonce(web3, transaction): - if 'from' in transaction and 'nonce' not in transaction: - return assoc( - transaction, - 'nonce', - web3.eth.getTransactionCount( - transaction['from'], - block_identifier='pending')) - else: - return transaction - - -@curry -def fill_transaction_defaults(web3, transaction): - """ - if web3 is None, fill as much as possible while offline - """ - defaults = {} - for key, default_getter in TRANSACTION_DEFAULTS.items(): - if key not in transaction: - if callable(default_getter): - if web3 is not None: - default_val = default_getter(web3, transaction) - else: - raise ValueError("You must specify %s in the transaction" % key) - else: - default_val = default_getter - defaults[key] = default_val - return merge(defaults, transaction) - - -def wait_for_transaction_receipt(web3, txn_hash, timeout, poll_latency): - with Timeout(timeout) as _timeout: - while True: - try: - txn_receipt = web3.eth.getTransactionReceipt(txn_hash) - except TransactionNotFound: - txn_receipt = None - # FIXME: The check for a null `blockHash` is due to parity's - # non-standard implementation of the JSON-RPC API and should - # be removed once the formal spec for the JSON-RPC endpoints - # has been finalized. - if txn_receipt is not None and txn_receipt['blockHash'] is not None: - break - _timeout.sleep(poll_latency) - return txn_receipt - - -def get_block_gas_limit(web3, block_identifier=None): - if block_identifier is None: - block_identifier = web3.eth.blockNumber - block = web3.eth.getBlock(block_identifier) - return block['gasLimit'] - - -def get_buffered_gas_estimate(web3, transaction, gas_buffer=100000): - gas_estimate_transaction = dict(**transaction) - - gas_estimate = web3.eth.estimateGas(gas_estimate_transaction) - - gas_limit = get_block_gas_limit(web3) - - if gas_estimate > gas_limit: - raise ValueError( - "Contract does not appear to be deployable within the " - "current network gas limits. Estimated: {0}. Current gas " - "limit: {1}".format(gas_estimate, gas_limit) - ) - - return min(gas_limit, gas_estimate + gas_buffer) - - -def get_required_transaction(web3, transaction_hash): - current_transaction = web3.eth.getTransaction(transaction_hash) - if not current_transaction: - raise ValueError('Supplied transaction with hash {} does not exist' - .format(transaction_hash)) - return current_transaction - - -def extract_valid_transaction_params(transaction_params): - extracted_params = {key: transaction_params[key] - for key in VALID_TRANSACTION_PARAMS if key in transaction_params} - - if extracted_params.get('data') is not None: - if transaction_params.get('input') is not None: - if extracted_params['data'] != transaction_params['input']: - msg = 'failure to handle this transaction due to both "input: {}" and' - msg += ' "data: {}" are populated. You need to resolve this conflict.' - err_vals = (transaction_params['input'], extracted_params['data']) - raise AttributeError(msg.format(*err_vals)) - else: - return extracted_params - else: - return extracted_params - elif extracted_params.get('data') is None: - if transaction_params.get('input') is not None: - return assoc(extracted_params, 'data', transaction_params['input']) - else: - return extracted_params - else: - raise Exception("Unreachable path: transaction's 'data' is either set or not set") - - -def assert_valid_transaction_params(transaction_params): - for param in transaction_params: - if param not in VALID_TRANSACTION_PARAMS: - raise ValueError('{} is not a valid transaction parameter'.format(param)) - - -def prepare_replacement_transaction(web3, current_transaction, new_transaction): - if current_transaction['blockHash'] is not None: - raise ValueError('Supplied transaction with hash {} has already been mined' - .format(current_transaction['hash'])) - if 'nonce' in new_transaction and new_transaction['nonce'] != current_transaction['nonce']: - raise ValueError('Supplied nonce in new_transaction must match the pending transaction') - - if 'nonce' not in new_transaction: - new_transaction = assoc(new_transaction, 'nonce', current_transaction['nonce']) - - if 'gasPrice' in new_transaction: - if new_transaction['gasPrice'] <= current_transaction['gasPrice']: - raise ValueError('Supplied gas price must exceed existing transaction gas price') - else: - generated_gas_price = web3.eth.generateGasPrice(new_transaction) - minimum_gas_price = int(math.ceil(current_transaction['gasPrice'] * 1.1)) - if generated_gas_price and generated_gas_price > minimum_gas_price: - new_transaction = assoc(new_transaction, 'gasPrice', generated_gas_price) - else: - new_transaction = assoc(new_transaction, 'gasPrice', minimum_gas_price) - - return new_transaction - - -def replace_transaction(web3, current_transaction, new_transaction): - new_transaction = prepare_replacement_transaction( - web3, current_transaction, new_transaction - ) - return web3.eth.sendTransaction(new_transaction) +import math + +from web3._utils.threads import ( + Timeout, +) +from web3._utils.toolz import ( + assoc, + curry, + merge, +) +from web3.exceptions import ( + TransactionNotFound, +) + +VALID_TRANSACTION_PARAMS = [ + 'from', + 'to', + 'gas', + 'gasPrice', + 'value', + 'data', + 'nonce', + 'chainId', +] + +TRANSACTION_DEFAULTS = { + 'value': 0, + 'data': b'', + 'gas': lambda web3, tx: web3.vns.estimateGas(tx), + 'gasPrice': lambda web3, tx: web3.vns.generateGasPrice(tx) or web3.vns.gasPrice, + 'chainId': lambda web3, tx: int(web3.net.version), +} + + +@curry +def fill_nonce(web3, transaction): + if 'from' in transaction and 'nonce' not in transaction: + return assoc( + transaction, + 'nonce', + web3.vns.getTransactionCount( + transaction['from'], + block_identifier='pending')) + else: + return transaction + + +@curry +def fill_transaction_defaults(web3, transaction): + """ + if web3 is None, fill as much as possible while offline + """ + defaults = {} + for key, default_getter in TRANSACTION_DEFAULTS.items(): + if key not in transaction: + if callable(default_getter): + if web3 is not None: + default_val = default_getter(web3, transaction) + else: + raise ValueError("You must specify %s in the transaction" % key) + else: + default_val = default_getter + defaults[key] = default_val + return merge(defaults, transaction) + + +def wait_for_transaction_receipt(web3, txn_hash, timeout=120, poll_latency=0.1): + with Timeout(timeout) as _timeout: + while True: + try: + txn_receipt = web3.vns.getTransactionReceipt(txn_hash) + except TransactionNotFound: + txn_receipt = None + # FIXME: The check for a null `blockHash` is due to parity's + # non-standard implementation of the JSON-RPC API and should + # be removed once the formal spec for the JSON-RPC endpoints + # has been finalized. + if txn_receipt is not None and txn_receipt['blockHash'] is not None: + break + _timeout.sleep(poll_latency) + return txn_receipt + + +def get_block_gas_limit(web3, block_identifier=None): + if block_identifier is None: + block_identifier = web3.vns.blockNumber + block = web3.vns.getBlock(block_identifier) + return block['gasLimit'] + + +def get_buffered_gas_estimate(web3, transaction, gas_buffer=100000): + gas_estimate_transaction = dict(**transaction) + + gas_estimate = web3.vns.estimateGas(gas_estimate_transaction) + + gas_limit = get_block_gas_limit(web3) + + if gas_estimate > gas_limit: + raise ValueError( + "Contract does not appear to be deployable within the " + "current network gas limits. Estimated: {0}. Current gas " + "limit: {1}".format(gas_estimate, gas_limit) + ) + + return min(gas_limit, gas_estimate + gas_buffer) + + +def get_required_transaction(web3, transaction_hash): + current_transaction = web3.vns.getTransaction(transaction_hash) + if not current_transaction: + raise ValueError('Supplied transaction with hash {} does not exist' + .format(transaction_hash)) + return current_transaction + + +def extract_valid_transaction_params(transaction_params): + extracted_params = {key: transaction_params[key] + for key in VALID_TRANSACTION_PARAMS if key in transaction_params} + + if extracted_params.get('data') is not None: + if transaction_params.get('input') is not None: + if extracted_params['data'] != transaction_params['input']: + msg = 'failure to handle this transaction due to both "input: {}" and' + msg += ' "data: {}" are populated. You need to resolve this conflict.' + err_vals = (transaction_params['input'], extracted_params['data']) + raise AttributeError(msg.format(*err_vals)) + else: + return extracted_params + else: + return extracted_params + elif extracted_params.get('data') is None: + if transaction_params.get('input') is not None: + return assoc(extracted_params, 'data', transaction_params['input']) + else: + return extracted_params + else: + raise Exception("Unreachable path: transaction's 'data' is either set or not set") + + +def assert_valid_transaction_params(transaction_params): + for param in transaction_params: + if param not in VALID_TRANSACTION_PARAMS: + raise ValueError('{} is not a valid transaction parameter'.format(param)) + + +def prepare_replacement_transaction(web3, current_transaction, new_transaction): + if current_transaction['blockHash'] is not None: + raise ValueError('Supplied transaction with hash {} has already been mined' + .format(current_transaction['hash'])) + if 'nonce' in new_transaction and new_transaction['nonce'] != current_transaction['nonce']: + raise ValueError('Supplied nonce in new_transaction must match the pending transaction') + + if 'nonce' not in new_transaction: + new_transaction = assoc(new_transaction, 'nonce', current_transaction['nonce']) + + if 'gasPrice' in new_transaction: + if new_transaction['gasPrice'] <= current_transaction['gasPrice']: + raise ValueError('Supplied gas price must exceed existing transaction gas price') + else: + generated_gas_price = web3.vns.generateGasPrice(new_transaction) + minimum_gas_price = int(math.ceil(current_transaction['gasPrice'] * 1.1)) + if generated_gas_price and generated_gas_price > minimum_gas_price: + new_transaction = assoc(new_transaction, 'gasPrice', generated_gas_price) + else: + new_transaction = assoc(new_transaction, 'gasPrice', minimum_gas_price) + + return new_transaction + + +def replace_transaction(web3, current_transaction, new_transaction): + new_transaction = prepare_replacement_transaction( + web3, current_transaction, new_transaction + ) + return web3.vns.sendTransaction(new_transaction) diff --git a/web3/_utils/txpool.py b/web3/_utils/txpool.py index 0a637db507..9f9069d308 100644 --- a/web3/_utils/txpool.py +++ b/web3/_utils/txpool.py @@ -1,20 +1,20 @@ -from web3.method import ( - Method, -) - -content = Method( - "txpool_content", - mungers=None, -) - - -inspect = Method( - "txpool_inspect", - mungers=None, -) - - -status = Method( - "txpool_status", - mungers=None, -) +from web3.method import ( + Method, +) + +content = Method( + "txpool_content", + mungers=None, +) + + +inspect = Method( + "txpool_inspect", + mungers=None, +) + + +status = Method( + "txpool_status", + mungers=None, +) diff --git a/web3/_utils/validation.py b/web3/_utils/validation.py index 93fb2f96fd..9255e9f3cf 100644 --- a/web3/_utils/validation.py +++ b/web3/_utils/validation.py @@ -1,184 +1,184 @@ -import itertools - -from eth_utils import ( - function_abi_to_4byte_selector, - is_0x_prefixed, - is_binary_address, - is_boolean, - is_bytes, - is_checksum_address, - is_dict, - is_hex_address, - is_integer, - is_list_like, - is_string, -) -from eth_utils.curried import ( - apply_formatter_to_array, -) -from eth_utils.hexadecimal import ( - encode_hex, -) -from eth_utils.toolz import ( - compose, - groupby, - valfilter, - valmap, -) - -from web3._utils.abi import ( - abi_to_signature, - filter_by_type, - is_address_type, - is_array_type, - is_bool_type, - is_bytes_type, - is_int_type, - is_recognized_type, - is_string_type, - is_uint_type, - length_of_array_type, - sub_type_of_array_type, -) -from web3.exceptions import ( - InvalidAddress, -) - - -def _prepare_selector_collision_msg(duplicates): - dup_sel = valmap(apply_formatter_to_array(abi_to_signature), duplicates) - joined_funcs = valmap(lambda funcs: ', '.join(funcs), dup_sel) - func_sel_msg_list = [funcs + ' have selector ' + sel for sel, funcs in joined_funcs.items()] - return ' and\n'.join(func_sel_msg_list) - - -def validate_abi(abi): - """ - Helper function for validating an ABI - """ - if not is_list_like(abi): - raise ValueError("'abi' is not a list") - - if not all(is_dict(e) for e in abi): - raise ValueError("'abi' is not a list of dictionaries") - - functions = filter_by_type('function', abi) - selectors = groupby( - compose(encode_hex, function_abi_to_4byte_selector), - functions - ) - duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors) - if duplicates: - raise ValueError( - 'Abi contains functions with colliding selectors. ' - 'Functions {0}'.format(_prepare_selector_collision_msg(duplicates)) - ) - - -def validate_abi_type(abi_type): - """ - Helper function for validating an abi_type - """ - if not is_recognized_type(abi_type): - raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type)) - - -def validate_abi_value(abi_type, value): - """ - Helper function for validating a value against the expected abi_type - Note: abi_type 'bytes' must either be python3 'bytes' object or '' - """ - if is_array_type(abi_type) and is_list_like(value): - # validate length - specified_length = length_of_array_type(abi_type) - if specified_length is not None: - if specified_length < 1: - raise TypeError( - "Invalid abi-type: {abi_type}. Length of fixed sized arrays" - "must be greater than 0." - .format(abi_type=abi_type) - ) - if specified_length != len(value): - raise TypeError( - "The following array length does not the length specified" - "by the abi-type, {abi_type}: {value}" - .format(abi_type=abi_type, value=value) - ) - - # validate sub_types - sub_type = sub_type_of_array_type(abi_type) - for v in value: - validate_abi_value(sub_type, v) - return - elif is_bool_type(abi_type) and is_boolean(value): - return - elif is_uint_type(abi_type) and is_integer(value) and value >= 0: - return - elif is_int_type(abi_type) and is_integer(value): - return - elif is_address_type(abi_type): - validate_address(value) - return - elif is_bytes_type(abi_type): - if is_bytes(value): - return - elif is_string(value): - if is_0x_prefixed(value): - return - else: - raise TypeError( - "ABI values of abi-type 'bytes' must be either" - "a python3 'bytes' object or an '0x' prefixed string." - ) - elif is_string_type(abi_type) and is_string(value): - return - - raise TypeError( - "The following abi value is not a '{abi_type}': {value}" - .format(abi_type=abi_type, value=value) - ) - - -def validate_address(value): - """ - Helper function for validating an address - """ - if is_bytes(value): - if not is_binary_address(value): - raise InvalidAddress("Address must be 20 bytes when input type is bytes", value) - return - - if not isinstance(value, str): - raise TypeError('Address {} must be provided as a string'.format(value)) - if not is_hex_address(value): - raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) - if not is_checksum_address(value): - if value == value.lower(): - raise InvalidAddress( - "Web3.py only accepts checksum addresses. " - "The software that gave you this non-checksum address should be considered unsafe, " - "please file it as a bug on their platform. " - "Try using an ENS name instead. Or, if you must accept lower safety, " - "use Web3.toChecksumAddress(lower_case_address).", - value, - ) - else: - raise InvalidAddress( - "Address has an invalid EIP-55 checksum. " - "After looking up the address from the original source, try again.", - value, - ) - - -def has_one_val(*args, **kwargs): - vals = itertools.chain(args, kwargs.values()) - not_nones = list(filter(lambda val: val is not None, vals)) - return len(not_nones) == 1 - - -def assert_one_val(*args, **kwargs): - if not has_one_val(*args, **kwargs): - raise TypeError( - "Exactly one of the passed values can be specified. " - "Instead, values were: %r, %r" % (args, kwargs) - ) +import itertools + +from vns_utils import ( + function_abi_to_4byte_selector, + is_0x_prefixed, + is_binary_address, + is_boolean, + is_bytes, + is_checksum_address, + is_dict, + is_hex_address, + is_integer, + is_list_like, + is_string, +) +from vns_utils.hexadecimal import ( + encode_hex, +) + +from web3._utils.abi import ( + abi_to_signature, + filter_by_type, + is_address_type, + is_array_type, + is_bool_type, + is_bytes_type, + is_int_type, + is_recognized_type, + is_string_type, + is_uint_type, + length_of_array_type, + sub_type_of_array_type, +) +from web3._utils.formatters import ( + apply_formatter_to_array, +) +from web3._utils.toolz import ( + compose, + groupby, + valfilter, + valmap, +) +from web3.exceptions import ( + InvalidAddress, +) + + +def _prepare_selector_collision_msg(duplicates): + dup_sel = valmap(apply_formatter_to_array(abi_to_signature), duplicates) + joined_funcs = valmap(lambda funcs: ', '.join(funcs), dup_sel) + func_sel_msg_list = [funcs + ' have selector ' + sel for sel, funcs in joined_funcs.items()] + return ' and\n'.join(func_sel_msg_list) + + +def validate_abi(abi): + """ + Helper function for validating an ABI + """ + if not is_list_like(abi): + raise ValueError("'abi' is not a list") + + if not all(is_dict(e) for e in abi): + raise ValueError("'abi' is not a list of dictionaries") + + functions = filter_by_type('function', abi) + selectors = groupby( + compose(encode_hex, function_abi_to_4byte_selector), + functions + ) + duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors) + if duplicates: + raise ValueError( + 'Abi contains functions with colliding selectors. ' + 'Functions {0}'.format(_prepare_selector_collision_msg(duplicates)) + ) + + +def validate_abi_type(abi_type): + """ + Helper function for validating an abi_type + """ + if not is_recognized_type(abi_type): + raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type)) + + +def validate_abi_value(abi_type, value): + """ + Helper function for validating a value against the expected abi_type + Note: abi_type 'bytes' must either be python3 'bytes' object or '' + """ + if is_array_type(abi_type) and is_list_like(value): + # validate length + specified_length = length_of_array_type(abi_type) + if specified_length is not None: + if specified_length < 1: + raise TypeError( + "Invalid abi-type: {abi_type}. Length of fixed sized arrays" + "must be greater than 0." + .format(abi_type=abi_type) + ) + if specified_length != len(value): + raise TypeError( + "The following array length does not the length specified" + "by the abi-type, {abi_type}: {value}" + .format(abi_type=abi_type, value=value) + ) + + # validate sub_types + sub_type = sub_type_of_array_type(abi_type) + for v in value: + validate_abi_value(sub_type, v) + return + elif is_bool_type(abi_type) and is_boolean(value): + return + elif is_uint_type(abi_type) and is_integer(value) and value >= 0: + return + elif is_int_type(abi_type) and is_integer(value): + return + elif is_address_type(abi_type): + validate_address(value) + return + elif is_bytes_type(abi_type): + if is_bytes(value): + return + elif is_string(value): + if is_0x_prefixed(value): + return + else: + raise TypeError( + "ABI values of abi-type 'bytes' must be either" + "a python3 'bytes' object or an '0x' prefixed string." + ) + elif is_string_type(abi_type) and is_string(value): + return + + raise TypeError( + "The following abi value is not a '{abi_type}': {value}" + .format(abi_type=abi_type, value=value) + ) + + +def validate_address(value): + """ + Helper function for validating an address + """ + if is_bytes(value): + if not is_binary_address(value): + raise InvalidAddress("Address must be 20 bytes when input type is bytes", value) + return + + if not isinstance(value, str): + raise TypeError('Address {} must be provided as a string'.format(value)) + if not is_hex_address(value): + raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) + if not is_checksum_address(value): + if value == value.lower(): + raise InvalidAddress( + "Web3.py only accepts checksum addresses. " + "The software that gave you this non-checksum address should be considered unsafe, " + "please file it as a bug on their platform. " + "Try using an ENS name instead. Or, if you must accept lower safety, " + "use Web3.toChecksumAddress(lower_case_address).", + value, + ) + else: + raise InvalidAddress( + "Address has an invalid EIP-55 checksum. " + "After looking up the address from the original source, try again.", + value, + ) + + +def has_one_val(*args, **kwargs): + vals = itertools.chain(args, kwargs.values()) + not_nones = list(filter(lambda val: val is not None, vals)) + return len(not_nones) == 1 + + +def assert_one_val(*args, **kwargs): + if not has_one_val(*args, **kwargs): + raise TypeError( + "Exactly one of the passed values can be specified. " + "Instead, values were: %r, %r" % (args, kwargs) + ) diff --git a/web3/_utils/windows.py b/web3/_utils/windows.py index a0df47e7b7..491c381d21 100644 --- a/web3/_utils/windows.py +++ b/web3/_utils/windows.py @@ -1,29 +1,29 @@ -import sys - -import pywintypes # noqa: E402 -import win32file # noqa: E402 - -if sys.platform != 'win32': - raise ImportError("This module should not be imported on non `win32` platforms") - - -class NamedPipe: - def __init__(self, ipc_path): - try: - self.handle = win32file.CreateFile( - ipc_path, win32file.GENERIC_READ | win32file.GENERIC_WRITE, - 0, None, win32file.OPEN_EXISTING, 0, None) - except pywintypes.error as err: - raise IOError(err) - - def recv(self, max_length): - (err, data) = win32file.ReadFile(self.handle, max_length) - if err: - raise IOError(err) - return data - - def sendall(self, data): - return win32file.WriteFile(self.handle, data) - - def close(self): - self.handle.close() +import sys + +import pywintypes # noqa: E402 +import win32file # noqa: E402 + +if sys.platform != 'win32': + raise ImportError("This module should not be imported on non `win32` platforms") + + +class NamedPipe: + def __init__(self, ipc_path): + try: + self.handle = win32file.CreateFile( + ipc_path, win32file.GENERIC_READ | win32file.GENERIC_WRITE, + 0, None, win32file.OPEN_EXISTING, 0, None) + except pywintypes.error as err: + raise IOError(err) + + def recv(self, max_length): + (err, data) = win32file.ReadFile(self.handle, max_length) + if err: + raise IOError(err) + return data + + def sendall(self, data): + return win32file.WriteFile(self.handle, data) + + def close(self): + self.handle.close() diff --git a/web3/auto/__init__.py b/web3/auto/__init__.py index f4c2172dfc..5839f52da7 100644 --- a/web3/auto/__init__.py +++ b/web3/auto/__init__.py @@ -1,3 +1,3 @@ -from web3 import Web3 - -w3 = Web3() +from web3 import Web3 + +w3 = Web3() diff --git a/web3/auto/gethdev.py b/web3/auto/gethdev.py index ebba276991..f9bf19cec6 100644 --- a/web3/auto/gethdev.py +++ b/web3/auto/gethdev.py @@ -1,13 +1,13 @@ -from web3 import ( - IPCProvider, - Web3, -) -from web3.middleware import ( - geth_poa_middleware, -) -from web3.providers.ipc import ( - get_dev_ipc_path, -) - -w3 = Web3(IPCProvider(get_dev_ipc_path())) -w3.middleware_onion.inject(geth_poa_middleware, layer=0) +from web3 import ( + IPCProvider, + Web3, +) +from web3.middleware import ( + geth_poa_middleware, +) +from web3.providers.ipc import ( + get_dev_ipc_path, +) + +w3 = Web3(IPCProvider(get_dev_ipc_path())) +w3.middleware_onion.inject(geth_poa_middleware, layer=0) diff --git a/web3/auto/http.py b/web3/auto/http.py index d21014a75e..6f7ad95b1e 100644 --- a/web3/auto/http.py +++ b/web3/auto/http.py @@ -1,6 +1,6 @@ -from web3 import ( - HTTPProvider, - Web3, -) - -w3 = Web3(HTTPProvider()) +from web3 import ( + HTTPProvider, + Web3, +) + +w3 = Web3(HTTPProvider()) diff --git a/web3/auto/infura/__init__.py b/web3/auto/infura/__init__.py index b04e8ba37d..d298975539 100644 --- a/web3/auto/infura/__init__.py +++ b/web3/auto/infura/__init__.py @@ -1,15 +1,15 @@ -from web3 import Web3 -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_MAINNET_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_MAINNET_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +from web3 import Web3 +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_MAINNET_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_MAINNET_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) diff --git a/web3/auto/infura/endpoints.py b/web3/auto/infura/endpoints.py index c8de5c78d2..17849ac34d 100644 --- a/web3/auto/infura/endpoints.py +++ b/web3/auto/infura/endpoints.py @@ -1,65 +1,54 @@ -import os -from typing import ( - Dict, - Optional, - Tuple, -) - -from eth_typing import ( - URI, -) -from eth_utils import ( - ValidationError, -) - -from web3.exceptions import ( - InfuraKeyNotFound, -) - -INFURA_MAINNET_DOMAIN = 'mainnet.infura.io' -INFURA_ROPSTEN_DOMAIN = 'ropsten.infura.io' -INFURA_GOERLI_DOMAIN = 'goerli.infura.io' -INFURA_RINKEBY_DOMAIN = 'rinkeby.infura.io' -INFURA_KOVAN_DOMAIN = 'kovan.infura.io' - -WEBSOCKET_SCHEME = 'wss' -HTTP_SCHEME = 'https' - - -def load_api_key() -> str: - # in web3py v6 remove outdated WEB3_INFURA_API_KEY - key = os.environ.get('WEB3_INFURA_PROJECT_ID', - os.environ.get('WEB3_INFURA_API_KEY', '')) - if key == '': - raise InfuraKeyNotFound( - "No Infura Project ID found. Please ensure " - "that the environment variable WEB3_INFURA_PROJECT_ID is set." - ) - return key - - -def load_secret() -> str: - return os.environ.get('WEB3_INFURA_API_SECRET', '') - - -def build_http_headers() -> Optional[Dict[str, Tuple[str, str]]]: - secret = load_secret() - if secret: - headers = {'auth': ('', secret)} - return headers - return None - - -def build_infura_url(domain: str) -> URI: - scheme = os.environ.get('WEB3_INFURA_SCHEME', WEBSOCKET_SCHEME) - key = load_api_key() - secret = load_secret() - - if scheme == WEBSOCKET_SCHEME and secret != '': - return URI("%s://:%s@%s/ws/v3/%s" % (scheme, secret, domain, key)) - elif scheme == WEBSOCKET_SCHEME and secret == '': - return URI("%s://%s/ws/v3/%s" % (scheme, domain, key)) - elif scheme == HTTP_SCHEME: - return URI("%s://%s/v3/%s" % (scheme, domain, key)) - else: - raise ValidationError("Cannot connect to Infura with scheme %r" % scheme) +import os + +from vns_utils import ( + ValidationError, +) + +from web3.exceptions import ( + InfuraKeyNotFound, +) + +INFURA_MAINNET_DOMAIN = 'mainnet.infura.io' +INFURA_ROPSTEN_DOMAIN = 'ropsten.infura.io' +INFURA_GOERLI_DOMAIN = 'goerli.infura.io' +INFURA_RINKEBY_DOMAIN = 'rinkeby.infura.io' +INFURA_KOVAN_DOMAIN = 'kovan.infura.io' + +WEBSOCKET_SCHEME = 'wss' +HTTP_SCHEME = 'https' + + +def load_api_key(): + # in web3py v6 remove outdated WEB3_INFURA_API_KEY + key = os.environ.get('WEB3_INFURA_PROJECT_ID', + os.environ.get('WEB3_INFURA_API_KEY', '')) + if key == '': + raise InfuraKeyNotFound( + "No Infura Project ID found. Please ensure " + "that the environment variable WEB3_INFURA_PROJECT_ID is set." + ) + return key + + +def load_secret(): + return os.environ.get('WEB3_INFURA_API_SECRET', '') + + +def build_http_headers(): + secret = load_secret() + if secret: + headers = {'auth': ('', secret)} + return headers + + +def build_infura_url(domain): + scheme = os.environ.get('WEB3_INFURA_SCHEME', WEBSOCKET_SCHEME) + key = load_api_key() + secret = load_secret() + + if scheme == WEBSOCKET_SCHEME: + return "%s://:%s@%s/ws/v3/%s" % (scheme, secret, domain, key) + elif scheme == HTTP_SCHEME: + return "%s://%s/v3/%s" % (scheme, domain, key) + else: + raise ValidationError("Cannot connect to Infura with scheme %r" % scheme) diff --git a/web3/auto/infura/goerli.py b/web3/auto/infura/goerli.py index cffc1250af..96eccb96b8 100644 --- a/web3/auto/infura/goerli.py +++ b/web3/auto/infura/goerli.py @@ -1,19 +1,19 @@ -from web3 import Web3 -from web3.middleware import ( - geth_poa_middleware, -) -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_GOERLI_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_GOERLI_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) -w3.middleware_onion.inject(geth_poa_middleware, layer=0) +from web3 import Web3 +from web3.middleware import ( + geth_poa_middleware, +) +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_GOERLI_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_GOERLI_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +w3.middleware_onion.inject(geth_poa_middleware, layer=0) diff --git a/web3/auto/infura/kovan.py b/web3/auto/infura/kovan.py index e9b12005f4..dd130f1880 100644 --- a/web3/auto/infura/kovan.py +++ b/web3/auto/infura/kovan.py @@ -1,15 +1,15 @@ -from web3 import Web3 -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_KOVAN_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_KOVAN_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +from web3 import Web3 +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_KOVAN_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_KOVAN_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) diff --git a/web3/auto/infura/mainnet.py b/web3/auto/infura/mainnet.py index b04e8ba37d..d298975539 100644 --- a/web3/auto/infura/mainnet.py +++ b/web3/auto/infura/mainnet.py @@ -1,15 +1,15 @@ -from web3 import Web3 -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_MAINNET_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_MAINNET_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +from web3 import Web3 +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_MAINNET_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_MAINNET_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) diff --git a/web3/auto/infura/rinkeby.py b/web3/auto/infura/rinkeby.py index 972df83dfb..d6d3fea792 100644 --- a/web3/auto/infura/rinkeby.py +++ b/web3/auto/infura/rinkeby.py @@ -1,19 +1,19 @@ -from web3 import Web3 -from web3.middleware import ( - geth_poa_middleware, -) -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_RINKEBY_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_RINKEBY_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) -w3.middleware_onion.inject(geth_poa_middleware, layer=0) +from web3 import Web3 +from web3.middleware import ( + geth_poa_middleware, +) +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_RINKEBY_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_RINKEBY_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +w3.middleware_onion.inject(geth_poa_middleware, layer=0) diff --git a/web3/auto/infura/ropsten.py b/web3/auto/infura/ropsten.py index d82356bcaa..1ba2b3b593 100644 --- a/web3/auto/infura/ropsten.py +++ b/web3/auto/infura/ropsten.py @@ -1,15 +1,15 @@ -from web3 import Web3 -from web3.providers.auto import ( - load_provider_from_uri, -) - -from .endpoints import ( - INFURA_ROPSTEN_DOMAIN, - build_http_headers, - build_infura_url, -) - -_headers = build_http_headers() -_infura_url = build_infura_url(INFURA_ROPSTEN_DOMAIN) - -w3 = Web3(load_provider_from_uri(_infura_url, _headers)) +from web3 import Web3 +from web3.providers.auto import ( + load_provider_from_uri, +) + +from .endpoints import ( + INFURA_ROPSTEN_DOMAIN, + build_http_headers, + build_infura_url, +) + +_headers = build_http_headers() +_infura_url = build_infura_url(INFURA_ROPSTEN_DOMAIN) + +w3 = Web3(load_provider_from_uri(_infura_url, _headers)) diff --git a/web3/auto/ipc.py b/web3/auto/ipc.py index a5ca6abc3a..fcfb230bc4 100644 --- a/web3/auto/ipc.py +++ b/web3/auto/ipc.py @@ -1,6 +1,6 @@ -from web3 import ( - IPCProvider, - Web3, -) - -w3 = Web3(IPCProvider()) +from web3 import ( + IPCProvider, + Web3, +) + +w3 = Web3(IPCProvider()) diff --git a/web3/auto/websocket.py b/web3/auto/websocket.py index 39a4556fb7..2d5ba32586 100644 --- a/web3/auto/websocket.py +++ b/web3/auto/websocket.py @@ -1,6 +1,6 @@ -from web3 import ( - Web3, - WebsocketProvider, -) - -w3 = Web3(WebsocketProvider()) +from web3 import ( + Web3, + WebsocketProvider, +) + +w3 = Web3(WebsocketProvider()) diff --git a/web3/contract.py b/web3/contract.py index 69677210cb..7b6c67f578 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -1,1607 +1,1483 @@ -"""Interaction with smart contracts over Web3 connector. - -""" -import copy -import itertools -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Dict, - Generator, - Iterable, - List, - NoReturn, - Optional, - Sequence, - Tuple, - Type, - Union, - cast, -) -import warnings - -from eth_abi.exceptions import ( - DecodingError, -) -from eth_typing import ( - BlockNumber, - ChecksumAddress, - Hash32, - HexStr, -) -from eth_utils import ( - add_0x_prefix, - encode_hex, - function_abi_to_4byte_selector, - is_list_like, - is_text, - to_tuple, -) -from eth_utils.toolz import ( - compose, - partial, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.abi import ( - abi_to_signature, - check_if_arguments_can_be_encoded, - fallback_func_abi_exists, - filter_by_type, - get_abi_input_names, - get_abi_input_types, - get_abi_output_types, - get_constructor_abi, - is_array_type, - map_abi_data, - merge_args_and_kwargs, -) -from web3._utils.blocks import ( - is_hex_encoded_block_hash, -) -from web3._utils.contracts import ( - encode_abi, - find_matching_event_abi, - find_matching_fn_abi, - get_function_info, - prepare_transaction, -) -from web3._utils.datatypes import ( - PropertyCheckingFactory, -) -from web3._utils.decorators import ( - combomethod, - deprecated_for, -) -from web3._utils.empty import ( - empty, -) -from web3._utils.encoding import ( - to_4byte_hex, - to_hex, -) -from web3._utils.events import ( - EventFilterBuilder, - get_event_data, - is_dynamic_sized_type, -) -from web3._utils.filters import ( - LogFilter, - construct_event_filter_params, -) -from web3._utils.function_identifiers import ( - FallbackFn, -) -from web3._utils.normalizers import ( - BASE_RETURN_NORMALIZERS, - normalize_abi, - normalize_address, - normalize_bytecode, -) -from web3._utils.transactions import ( - fill_transaction_defaults, -) -from web3.datastructures import ( - AttributeDict, - MutableAttributeDict, -) -from web3.exceptions import ( - BadFunctionCallOutput, - BlockNumberOutofRange, - FallbackNotFound, - InvalidEventABI, - LogTopicError, - MismatchedABI, - NoABIEventsFound, - NoABIFound, - NoABIFunctionsFound, - ValidationError, -) -from web3.logs import ( - DISCARD, - IGNORE, - STRICT, - WARN, - EventLogErrorFlags, -) -from web3.types import ( # noqa: F401 - ABI, - ABIEvent, - ABIFunction, - BlockIdentifier, - EventData, - LogReceipt, - TxParams, - TxReceipt, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -ACCEPTABLE_EMPTY_STRINGS = ["0x", b"0x", "", b""] - - -class ContractFunctions: - """Class containing contract function objects - """ - - def __init__(self, abi: ABI, web3: 'Web3', address: ChecksumAddress=None) -> None: - self.abi = abi - self.web3 = web3 - self.address = address - - if self.abi: - self._functions = filter_by_type('function', self.abi) - for func in self._functions: - setattr( - self, - func['name'], - ContractFunction.factory( - func['name'], - web3=self.web3, - contract_abi=self.abi, - address=self.address, - function_identifier=func['name'])) - - def __iter__(self) -> Generator[str, None, None]: - if not hasattr(self, '_functions') or not self._functions: - return - - for func in self._functions: - yield func['name'] - - def __getattr__(self, function_name: str) -> ABIFunction: - if self.abi is None: - raise NoABIFound( - "There is no ABI found for this contract.", - ) - if '_functions' not in self.__dict__: - raise NoABIFunctionsFound( - "The abi for this contract contains no function definitions. ", - "Are you sure you provided the correct contract abi?" - ) - elif function_name not in self.__dict__['_functions']: - raise MismatchedABI( - "The function '{}' was not found in this contract's abi. ".format(function_name), - "Are you sure you provided the correct contract abi?" - ) - else: - return super().__getattribute__(function_name) - - def __getitem__(self, function_name: str) -> ABIFunction: - return getattr(self, function_name) - - -class ContractEvents: - """Class containing contract event objects - - This is available via: - - .. code-block:: python - - >>> mycontract.events - - - To get list of all supported events in the contract ABI. - This allows you to iterate over :class:`ContractEvent` proxy classes. - - .. code-block:: python - - >>> for e in mycontract.events: print(e) - - ... - - """ - - def __init__(self, abi: ABI, web3: 'Web3', address: ChecksumAddress=None) -> None: - if abi: - self.abi = abi - self._events = filter_by_type('event', self.abi) - for event in self._events: - setattr( - self, - event['name'], - ContractEvent.factory( - event['name'], - web3=web3, - contract_abi=self.abi, - address=address, - event_name=event['name'])) - - def __getattr__(self, event_name: str) -> ABIEvent: - if '_events' not in self.__dict__: - raise NoABIEventsFound( - "The abi for this contract contains no event definitions. ", - "Are you sure you provided the correct contract abi?" - ) - elif event_name not in self.__dict__['_events']: - raise MismatchedABI( - "The event '{}' was not found in this contract's abi. ".format(event_name), - "Are you sure you provided the correct contract abi?" - ) - else: - return super().__getattribute__(event_name) - - def __getitem__(self, event_name: str) -> ABIEvent: - return getattr(self, event_name) - - def __iter__(self) -> Iterable[str]: - """Iterate over supported - - :return: Iterable of :class:`ContractEvent` - """ - for event in self._events: - yield self[event['name']] - - -class Contract: - """Base class for Contract proxy classes. - - First you need to create your Contract classes using - :meth:`web3.eth.Eth.contract` that takes compiled Solidity contract - ABI definitions as input. The created class object will be a subclass of - this base class. - - After you have your Contract proxy class created you can interact with - smart contracts - - * Create a Contract proxy object for an existing deployed smart contract by - its address using :meth:`__init__` - - * Deploy a new smart contract using :py:meth:`Contract.constructor.transact()` - """ - - # set during class construction - web3: 'Web3' = None - - # instance level properties - address: ChecksumAddress = None - - # class properties (overridable at instance level) - abi: ABI = None - - asm = None - ast = None - - bytecode = None - bytecode_runtime = None - clone_bin = None - - functions: ContractFunctions = None - caller: 'ContractCaller' = None - - #: Instance of :class:`ContractEvents` presenting available Event ABIs - events: ContractEvents = None - - dev_doc = None - interface = None - metadata = None - opcodes = None - src_map = None - src_map_runtime = None - user_doc = None - - def __init__(self, address: ChecksumAddress=None) -> None: - """Create a new smart contract proxy object. - - :param address: Contract address as 0x hex string - """ - if self.web3 is None: - raise AttributeError( - 'The `Contract` class has not been initialized. Please use the ' - '`web3.contract` interface to create your contract class.' - ) - - if address: - self.address = normalize_address(self.web3.ens, address) - - if not self.address: - raise TypeError("The address argument is required to instantiate a contract.") - - self.functions = ContractFunctions(self.abi, self.web3, self.address) - self.caller = ContractCaller(self.abi, self.web3, self.address) - self.events = ContractEvents(self.abi, self.web3, self.address) - self.fallback = Contract.get_fallback_function(self.abi, self.web3, self.address) - - @classmethod - def factory(cls, web3: 'Web3', class_name: str=None, **kwargs: Any) -> 'Contract': - - kwargs['web3'] = web3 - - normalizers = { - 'abi': normalize_abi, - 'address': partial(normalize_address, kwargs['web3'].ens), - 'bytecode': normalize_bytecode, - 'bytecode_runtime': normalize_bytecode, - } - - contract = cast(Contract, PropertyCheckingFactory( - class_name or cls.__name__, - (cls,), - kwargs, - normalizers=normalizers, - )) - contract.functions = ContractFunctions(contract.abi, contract.web3) - contract.caller = ContractCaller(contract.abi, contract.web3, contract.address) - contract.events = ContractEvents(contract.abi, contract.web3) - contract.fallback = Contract.get_fallback_function(contract.abi, contract.web3) - - return contract - - # - # Contract Methods - # - @classmethod - def constructor(cls, *args: Any, **kwargs: Any) -> 'ContractConstructor': - """ - :param args: The contract constructor arguments as positional arguments - :param kwargs: The contract constructor arguments as keyword arguments - :return: a contract constructor object - """ - if cls.bytecode is None: - raise ValueError( - "Cannot call constructor on a contract that does not have 'bytecode' associated " - "with it" - ) - - return ContractConstructor(cls.web3, - cls.abi, - cls.bytecode, - *args, - **kwargs) - - # Public API - # - @combomethod - def encodeABI(cls, fn_name: str, args: Any=None, kwargs: Any=None, data: HexStr=None) -> HexStr: - """ - Encodes the arguments using the Ethereum ABI for the contract function - that matches the given name and arguments.. - - :param data: defaults to function selector - """ - fn_abi, fn_selector, fn_arguments = get_function_info( - fn_name, cls.web3.codec, contract_abi=cls.abi, args=args, kwargs=kwargs, - ) - - if data is None: - data = fn_selector - - return encode_abi(cls.web3, fn_abi, fn_arguments, data) - - @combomethod - def all_functions(self) -> List['ContractFunction']: - return find_functions_by_identifier( - self.abi, self.web3, self.address, lambda _: True - ) - - @combomethod - def get_function_by_signature(self, signature: str) -> 'ContractFunction': - if ' ' in signature: - raise ValueError( - 'Function signature should not contain any spaces. ' - 'Found spaces in input: %s' % signature - ) - - def callable_check(fn_abi: ABIFunction) -> bool: - return abi_to_signature(fn_abi) == signature - - fns = find_functions_by_identifier(self.abi, self.web3, self.address, callable_check) - return get_function_by_identifier(fns, 'signature') - - @combomethod - def find_functions_by_name(self, fn_name: str) -> List['ContractFunction']: - def callable_check(fn_abi: ABIFunction) -> bool: - return fn_abi['name'] == fn_name - - return find_functions_by_identifier( - self.abi, self.web3, self.address, callable_check - ) - - @combomethod - def get_function_by_name(self, fn_name: str) -> 'ContractFunction': - fns = self.find_functions_by_name(fn_name) - return get_function_by_identifier(fns, 'name') - - @combomethod - def get_function_by_selector(self, selector: Union[bytes, int, HexStr]) -> 'ContractFunction': - def callable_check(fn_abi: ABIFunction) -> bool: - return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(selector) - - fns = find_functions_by_identifier(self.abi, self.web3, self.address, callable_check) - return get_function_by_identifier(fns, 'selector') - - @combomethod - def decode_function_input(self, data: HexStr) -> Tuple['ContractFunction', Dict[str, Any]]: - # type ignored b/c expects data arg to be HexBytes - data = HexBytes(data) # type: ignore - selector, params = data[:4], data[4:] - func = self.get_function_by_selector(selector) - - names = get_abi_input_names(func.abi) - types = get_abi_input_types(func.abi) - - decoded = self.web3.codec.decode_abi(types, cast(HexBytes, params)) - normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded) - - return func, dict(zip(names, normalized)) - - @combomethod - def find_functions_by_args(self, *args: Any) -> List['ContractFunction']: - def callable_check(fn_abi: ABIFunction) -> bool: - return check_if_arguments_can_be_encoded(fn_abi, self.web3.codec, args=args, kwargs={}) - - return find_functions_by_identifier( - self.abi, self.web3, self.address, callable_check - ) - - @combomethod - def get_function_by_args(self, *args: Any) -> 'ContractFunction': - fns = self.find_functions_by_args(*args) - return get_function_by_identifier(fns, 'args') - - # - # Private Helpers - # - _return_data_normalizers: Tuple[Callable[..., Any], ...] = tuple() - - @classmethod - def _prepare_transaction(cls, - fn_name: str, - fn_args: Any=None, - fn_kwargs: Any=None, - transaction: TxParams=None) -> TxParams: - - return prepare_transaction( - cls.address, - cls.web3, - fn_identifier=fn_name, - contract_abi=cls.abi, - transaction=transaction, - fn_args=fn_args, - fn_kwargs=fn_kwargs, - ) - - @classmethod - def _find_matching_fn_abi( - cls, fn_identifier: str=None, args: Any=None, kwargs: Any=None - ) -> ABIFunction: - return find_matching_fn_abi(cls.abi, - cls.web3.codec, - fn_identifier=fn_identifier, - args=args, - kwargs=kwargs) - - @classmethod - def _find_matching_event_abi( - cls, event_name: str=None, argument_names: Sequence[str]=None - ) -> ABIEvent: - return find_matching_event_abi( - abi=cls.abi, - event_name=event_name, - argument_names=argument_names) - - @staticmethod - def get_fallback_function( - abi: ABI, web3: 'Web3', address: ChecksumAddress=None - ) -> 'ContractFunction': - if abi and fallback_func_abi_exists(abi): - return ContractFunction.factory( - 'fallback', - web3=web3, - contract_abi=abi, - address=address, - function_identifier=FallbackFn)() - - return cast('ContractFunction', NonExistentFallbackFunction()) - - @combomethod - def _encode_constructor_data(cls, args: Any=None, kwargs: Any=None) -> HexStr: - constructor_abi = get_constructor_abi(cls.abi) - - if constructor_abi: - if args is None: - args = tuple() - if kwargs is None: - kwargs = {} - - arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) - - deploy_data = add_0x_prefix( - encode_abi(cls.web3, constructor_abi, arguments, data=cls.bytecode) - ) - else: - if args is not None or kwargs is not None: - msg = "Constructor args were provided, but no constructor function was provided." - raise TypeError(msg) - - deploy_data = to_hex(cls.bytecode) - - return deploy_data - - -def mk_collision_prop(fn_name: str) -> Callable[[], None]: - def collision_fn() -> NoReturn: - msg = "Namespace collision for function name {0} with ConciseContract API.".format(fn_name) - raise AttributeError(msg) - collision_fn.__name__ = fn_name - return collision_fn - - -class ContractConstructor: - """ - Class for contract constructor API. - """ - def __init__( - self, web3: 'Web3', abi: ABI, bytecode: HexStr, *args: Any, **kwargs: Any - ) -> None: - self.web3 = web3 - self.abi = abi - self.bytecode = bytecode - self.data_in_transaction = self._encode_data_in_transaction(*args, **kwargs) - - @combomethod - def _encode_data_in_transaction(self, *args: Any, **kwargs: Any) -> HexStr: - constructor_abi = get_constructor_abi(self.abi) - - if constructor_abi: - if not args: - args = tuple() - if not kwargs: - kwargs = {} - - arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) - data = add_0x_prefix( - encode_abi(self.web3, constructor_abi, arguments, data=self.bytecode) - ) - else: - data = to_hex(self.bytecode) - - return data - - @combomethod - def estimateGas(self, transaction: TxParams=None) -> int: - if transaction is None: - estimate_gas_transaction: TxParams = {} - else: - estimate_gas_transaction = dict(**transaction) - self.check_forbidden_keys_in_transaction(estimate_gas_transaction, - ["data", "to"]) - - if self.web3.eth.defaultAccount is not empty: - estimate_gas_transaction.setdefault('from', self.web3.eth.defaultAccount) - - estimate_gas_transaction['data'] = self.data_in_transaction - - return self.web3.eth.estimateGas(estimate_gas_transaction) - - @combomethod - def transact(self, transaction: TxParams=None) -> Hash32: - if transaction is None: - transact_transaction: TxParams = {} - else: - transact_transaction = dict(**transaction) - self.check_forbidden_keys_in_transaction(transact_transaction, - ["data", "to"]) - - if self.web3.eth.defaultAccount is not empty: - transact_transaction.setdefault('from', self.web3.eth.defaultAccount) - - transact_transaction['data'] = self.data_in_transaction - - # TODO: handle asynchronous contract creation - return self.web3.eth.sendTransaction(transact_transaction) - - @combomethod - def buildTransaction(self, transaction: TxParams=None) -> TxParams: - """ - Build the transaction dictionary without sending - """ - - if transaction is None: - built_transaction: TxParams = {} - else: - built_transaction = dict(**transaction) - self.check_forbidden_keys_in_transaction(built_transaction, - ["data", "to"]) - - if self.web3.eth.defaultAccount is not empty: - built_transaction.setdefault('from', self.web3.eth.defaultAccount) - - built_transaction['data'] = self.data_in_transaction - built_transaction['to'] = b'' - return fill_transaction_defaults(self.web3, built_transaction) - - @staticmethod - def check_forbidden_keys_in_transaction( - transaction: TxParams, forbidden_keys: Collection[str]=None - ) -> None: - keys_found = set(transaction.keys()) & set(forbidden_keys) - if keys_found: - raise ValueError("Cannot set {} in transaction".format(', '.join(keys_found))) - - -class ConciseMethod: - ALLOWED_MODIFIERS = {'call', 'estimateGas', 'transact', 'buildTransaction'} - - def __init__( - self, function: 'ContractFunction', normalizers: Tuple[Callable[..., Any], ...]=None - ) -> None: - self._function = function - self._function._return_data_normalizers = normalizers - - def __call__(self, *args: Any, **kwargs: Any) -> 'ContractFunction': - return self.__prepared_function(*args, **kwargs) - - def __prepared_function(self, *args: Any, **kwargs: Any) -> 'ContractFunction': - modifier_dict: Dict[Any, Any] - if not kwargs: - modifier, modifier_dict = 'call', {} - elif len(kwargs) == 1: - modifier, modifier_dict = kwargs.popitem() - if modifier not in self.ALLOWED_MODIFIERS: - raise TypeError( - "The only allowed keyword arguments are: %s" % self.ALLOWED_MODIFIERS) - else: - raise TypeError("Use up to one keyword argument, one of: %s" % self.ALLOWED_MODIFIERS) - - return getattr(self._function(*args), modifier)(modifier_dict) - - -class ConciseContract: - """ - An alternative Contract Factory which invokes all methods as `call()`, - unless you add a keyword argument. The keyword argument assigns the prep method. - - This call - - > contract.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...}) - - is equivalent to this call in the classic contract: - - > contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...}) - """ - @deprecated_for( - "contract.caller. or contract.caller({transaction_dict})." - ) - def __init__( - self, - classic_contract: Contract, - method_class: Union[Type['ConciseMethod'], Type['ImplicitMethod']]=ConciseMethod - ) -> None: - classic_contract._return_data_normalizers += CONCISE_NORMALIZERS - self._classic_contract = classic_contract - self.address = self._classic_contract.address - - protected_fn_names = [fn for fn in dir(self) if not fn.endswith('__')] - - for fn_name in self._classic_contract.functions: - - # Override namespace collisions - if fn_name in protected_fn_names: - _concise_method = cast('ConciseMethod', mk_collision_prop(fn_name)) - - else: - _classic_method = getattr( - self._classic_contract.functions, - fn_name) - - _concise_method = method_class( - _classic_method, - self._classic_contract._return_data_normalizers - ) - - setattr(self, fn_name, _concise_method) - - @classmethod - def factory(cls, *args: Any, **kwargs: Any) -> Contract: - return compose(cls, Contract.factory(*args, **kwargs)) - - -def _none_addr(datatype: str, data: ChecksumAddress) -> Tuple[str, Optional[ChecksumAddress]]: - if datatype == 'address' and int(data, base=16) == 0: - return (datatype, None) - else: - return (datatype, data) - - -CONCISE_NORMALIZERS: Tuple[Callable[..., Any]] = ( - _none_addr, -) - - -class ImplicitMethod(ConciseMethod): - def __call_by_default(self, args: Any) -> bool: - function_abi = find_matching_fn_abi(self._function.contract_abi, - self._function.web3.codec, - fn_identifier=self._function.function_identifier, - args=args) - - return function_abi['constant'] if 'constant' in function_abi.keys() else False - - @deprecated_for("classic contract syntax. Ex: contract.functions.withdraw(amount).transact({})") - def __call__(self, *args: Any, **kwargs: Any) -> 'ContractFunction': - # Modifier is not provided and method is not constant/pure do a transaction instead - if not kwargs and not self.__call_by_default(args): - return super().__call__(*args, transact={}) - else: - return super().__call__(*args, **kwargs) - - -class ImplicitContract(ConciseContract): - """ - ImplicitContract class is similar to the ConciseContract class - however it performs a transaction instead of a call if no modifier - is given and the method is not marked 'constant' in the ABI. - - The transaction will use the default account to send the transaction. - - This call - - > contract.withdraw(amount) - - is equivalent to this call in the classic contract: - - > contract.functions.withdraw(amount).transact({}) - """ - def __init__( - self, - classic_contract: Contract, - method_class: Union[Type[ImplicitMethod], Type[ConciseMethod]]=ImplicitMethod - ) -> None: - super().__init__(classic_contract, method_class=method_class) - - -class NonExistentFallbackFunction: - @staticmethod - def _raise_exception() -> NoReturn: - raise FallbackNotFound("No fallback function was found in the contract ABI.") - - def __getattr__(self, attr: Any) -> Callable[[], None]: - return NonExistentFallbackFunction._raise_exception - - -class ContractFunction: - """Base class for contract functions - - A function accessed via the api contract.functions.myMethod(*args, **kwargs) - is a subclass of this class. - """ - address: ChecksumAddress = None - function_identifier: Union[str, Type[FallbackFn]] = None - web3: 'Web3' = None - contract_abi: ABI = None - abi: ABIFunction = None - transaction: TxParams = None - arguments: Tuple[Any, ...] = None - args: Any = None - kwargs: Any = None - - def __init__(self, abi: ABIFunction=None) -> None: - self.abi = abi - self.fn_name = type(self).__name__ - - def __call__(self, *args: Any, **kwargs: Any) -> 'ContractFunction': - clone = copy.copy(self) - if args is None: - clone.args = tuple() - else: - clone.args = args - - if kwargs is None: - clone.kwargs = {} - else: - clone.kwargs = kwargs - clone._set_function_info() - return clone - - def _set_function_info(self) -> None: - if not self.abi: - self.abi = find_matching_fn_abi( - self.contract_abi, - self.web3.codec, - self.function_identifier, - self.args, - self.kwargs - ) - if self.function_identifier is FallbackFn: - self.selector = encode_hex(b'') - elif is_text(self.function_identifier): - self.selector = encode_hex(function_abi_to_4byte_selector(self.abi)) - else: - raise TypeError("Unsupported function identifier") - - self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs) - - def call( - self, transaction: TxParams=None, block_identifier: BlockIdentifier='latest' - ) -> Sequence[Any]: - """ - Execute a contract function call using the `eth_call` interface. - - This method prepares a ``Caller`` object that exposes the contract - functions and public variables as callable Python functions. - - Reading a public ``owner`` address variable example: - - .. code-block:: python - - ContractFactory = w3.eth.contract( - abi=wallet_contract_definition["abi"] - ) - - # Not a real contract address - contract = ContractFactory("0x2f70d3d26829e412A602E83FE8EeBF80255AEeA5") - - # Read "owner" public variable - addr = contract.functions.owner().call() - - :param transaction: Dictionary of transaction info for web3 interface - :return: ``Caller`` object that has contract public functions - and variables exposed as Python methods - """ - if transaction is None: - call_transaction: Dict[str, Any] = {} - else: - call_transaction = dict(**transaction) - - if 'data' in call_transaction: - raise ValueError("Cannot set data in call transaction") - - if self.address: - call_transaction.setdefault('to', self.address) - if self.web3.eth.defaultAccount is not empty: - call_transaction.setdefault('from', self.web3.eth.defaultAccount) - - if 'to' not in call_transaction: - if isinstance(self, type): - raise ValueError( - "When using `Contract.[methodtype].[method].call()` from" - " a contract factory you " - "must provide a `to` address with the transaction" - ) - else: - raise ValueError( - "Please ensure that this contract instance has an address." - ) - - block_id = parse_block_identifier(self.web3, block_identifier) - - return call_contract_function( - self.web3, - self.address, - self._return_data_normalizers, - self.function_identifier, - call_transaction, - block_id, - self.contract_abi, - self.abi, - *self.args, - **self.kwargs - ) - - def transact(self, transaction: TxParams=None) -> Hash32: - if transaction is None: - transact_transaction: Dict[str, Any] = {} - else: - transact_transaction = dict(**transaction) - - if 'data' in transact_transaction: - raise ValueError("Cannot set data in transact transaction") - - if self.address is not None: - transact_transaction.setdefault('to', self.address) - if self.web3.eth.defaultAccount is not empty: - transact_transaction.setdefault('from', self.web3.eth.defaultAccount) - - if 'to' not in transact_transaction: - if isinstance(self, type): - raise ValueError( - "When using `Contract.transact` from a contract factory you " - "must provide a `to` address with the transaction" - ) - else: - raise ValueError( - "Please ensure that this contract instance has an address." - ) - - return transact_with_contract_function( - self.address, - self.web3, - self.function_identifier, - transact_transaction, - self.contract_abi, - self.abi, - *self.args, - **self.kwargs - ) - - def estimateGas(self, transaction: TxParams=None) -> int: - if transaction is None: - estimate_gas_transaction: Dict[str, Any] = {} - else: - estimate_gas_transaction = dict(**transaction) - - if 'data' in estimate_gas_transaction: - raise ValueError("Cannot set data in estimateGas transaction") - if 'to' in estimate_gas_transaction: - raise ValueError("Cannot set to in estimateGas transaction") - - if self.address: - estimate_gas_transaction.setdefault('to', self.address) - if self.web3.eth.defaultAccount is not empty: - estimate_gas_transaction.setdefault('from', self.web3.eth.defaultAccount) - - if 'to' not in estimate_gas_transaction: - if isinstance(self, type): - raise ValueError( - "When using `Contract.estimateGas` from a contract factory " - "you must provide a `to` address with the transaction" - ) - else: - raise ValueError( - "Please ensure that this contract instance has an address." - ) - - return estimate_gas_for_function( - self.address, - self.web3, - self.function_identifier, - estimate_gas_transaction, - self.contract_abi, - self.abi, - *self.args, - **self.kwargs - ) - - def buildTransaction(self, transaction: TxParams=None) -> TxParams: - """ - Build the transaction dictionary without sending - """ - if transaction is None: - built_transaction: Dict[str, Any] = {} - else: - built_transaction = dict(**transaction) - - if 'data' in built_transaction: - raise ValueError("Cannot set data in build transaction") - - if not self.address and 'to' not in built_transaction: - raise ValueError( - "When using `ContractFunction.buildTransaction` from a contract factory " - "you must provide a `to` address with the transaction" - ) - if self.address and 'to' in built_transaction: - raise ValueError("Cannot set to in contract call build transaction") - - if self.address: - built_transaction.setdefault('to', self.address) - - if 'to' not in built_transaction: - raise ValueError( - "Please ensure that this contract instance has an address." - ) - - return build_transaction_for_function( - self.address, - self.web3, - self.function_identifier, - built_transaction, - self.contract_abi, - self.abi, - *self.args, - **self.kwargs - ) - - @combomethod - def _encode_transaction_data(cls) -> HexStr: - return add_0x_prefix(encode_abi(cls.web3, cls.abi, cls.arguments, cls.selector)) - - _return_data_normalizers: Optional[Tuple[Callable[..., Any], ...]] = tuple() - - @classmethod - def factory(cls, class_name: str, **kwargs: Any) -> 'ContractFunction': - return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get('abi')) - - def __repr__(self) -> str: - if self.abi: - _repr = '' - return '' % self.fn_name - - -class ContractEvent: - """Base class for contract events - - An event accessed via the api contract.events.myEvents(*args, **kwargs) - is a subclass of this class. - """ - address: ChecksumAddress = None - event_name: str = None - web3: 'Web3' = None - contract_abi: ABI = None - abi: ABIEvent = None - - def __init__(self, *argument_names: Tuple[str]) -> None: - - if argument_names is None: - # https://github.com/python/mypy/issues/6283 - self.argument_names = tuple() # type: ignore - else: - self.argument_names = argument_names - - self.abi = self._get_event_abi() - - @classmethod - def _get_event_abi(cls) -> ABIEvent: - return find_matching_event_abi( - cls.contract_abi, - event_name=cls.event_name) - - @combomethod - def processReceipt( - self, txn_receipt: TxReceipt, errors: EventLogErrorFlags=WARN - ) -> Iterable[EventData]: - return self._parse_logs(txn_receipt, errors) - - @to_tuple - def _parse_logs(self, txn_receipt: TxReceipt, errors: EventLogErrorFlags) -> Iterable[ABIEvent]: - try: - errors.name - except AttributeError: - raise AttributeError(f'Error flag must be one of: {EventLogErrorFlags.flag_options()}') - - for log in txn_receipt['logs']: - try: - rich_log = get_event_data(self.web3.codec, self.abi, log) - except (MismatchedABI, LogTopicError, InvalidEventABI, TypeError) as e: - if errors == DISCARD: - continue - elif errors == IGNORE: - new_log: LogReceipt = MutableAttributeDict(log) - new_log['errors'] = e - rich_log = AttributeDict(new_log) - elif errors == STRICT: - raise e - else: - warnings.warn( - f'The log with transaction hash: {log.transactionHash} and ' - f'logIndex: {log.logIndex} encountered the following error ' - f'during processing: {type(e).__name__}({e}). It has been discarded.' - ) - continue - yield rich_log - - @combomethod - def processLog(self, log: HexStr) -> EventData: - return get_event_data(self.web3.codec, self.abi, log) - - @combomethod - def createFilter( - self, *, # PEP 3102 - argument_filters: Dict[str, Any]=None, - fromBlock: Union[int, str]=None, - toBlock: Union[int, str]="latest", - address: ChecksumAddress=None, - topics: Sequence[Any]=None) -> LogFilter: - """ - Create filter object that tracks logs emitted by this contract event. - :param filter_params: other parameters to limit the events - """ - if fromBlock is None: - raise TypeError("Missing mandatory keyword argument to createFilter: fromBlock") - - if argument_filters is None: - argument_filters = dict() - - _filters = dict(**argument_filters) - - event_abi = self._get_event_abi() - - check_for_forbidden_api_filter_arguments(event_abi, _filters) - - _, event_filter_params = construct_event_filter_params( - self._get_event_abi(), - self.web3.codec, - contract_address=self.address, - argument_filters=_filters, - fromBlock=fromBlock, - toBlock=toBlock, - address=address, - topics=topics, - ) - - filter_builder = EventFilterBuilder(event_abi, self.web3.codec) - filter_builder.address = event_filter_params.get('address') - filter_builder.fromBlock = event_filter_params.get('fromBlock') - filter_builder.toBlock = event_filter_params.get('toBlock') - match_any_vals = { - arg: value for arg, value in _filters.items() - if not is_array_type(filter_builder.args[arg].arg_type) and is_list_like(value) - } - for arg, value in match_any_vals.items(): - filter_builder.args[arg].match_any(*value) - - match_single_vals = { - arg: value for arg, value in _filters.items() - if not is_array_type(filter_builder.args[arg].arg_type) and not is_list_like(value) - } - for arg, value in match_single_vals.items(): - filter_builder.args[arg].match_single(value) - - log_filter = filter_builder.deploy(self.web3) - log_filter.log_entry_formatter = get_event_data(self.web3.codec, self._get_event_abi()) - log_filter.builder = filter_builder - - return log_filter - - @combomethod - def build_filter(self) -> EventFilterBuilder: - builder = EventFilterBuilder( - self._get_event_abi(), - self.web3.codec, - formatter=get_event_data(self.web3.codec, self._get_event_abi())) - builder.address = self.address - return builder - - @combomethod - def getLogs(self, - argument_filters: Dict[str, Any]=None, - fromBlock: Union[int, str]=None, - toBlock: Union[int, str]=None, - blockHash: Hash32=None) -> Iterable[EventData]: - """Get events for this contract instance using eth_getLogs API. - - This is a stateless method, as opposed to createFilter. - It can be safely called against nodes which do not provide - eth_newFilter API, like Infura nodes. - - If there are many events, - like ``Transfer`` events for a popular token, - the Ethereum node might be overloaded and timeout - on the underlying JSON-RPC call. - - Example - how to get all ERC-20 token transactions - for the latest 10 blocks: - - .. code-block:: python - - from = max(mycontract.web3.eth.blockNumber - 10, 1) - to = mycontract.web3.eth.blockNumber - - events = mycontract.events.Transfer.getLogs(fromBlock=from, toBlock=to) - - for e in events: - print(e["args"]["from"], - e["args"]["to"], - e["args"]["value"]) - - The returned processed log values will look like: - - .. code-block:: python - - ( - AttributeDict({ - 'args': AttributeDict({}), - 'event': 'LogNoArguments', - 'logIndex': 0, - 'transactionIndex': 0, - 'transactionHash': HexBytes('...'), - 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', - 'blockHash': HexBytes('...'), - 'blockNumber': 3 - }), - AttributeDict(...), - ... - ) - - See also: :func:`web3.middleware.filter.local_filter_middleware`. - - :param argument_filters: - :param fromBlock: block number or "latest", defaults to "latest" - :param toBlock: block number or "latest". Defaults to "latest" - :param blockHash: block hash. blockHash cannot be set at the - same time as fromBlock or toBlock - :yield: Tuple of :class:`AttributeDict` instances - """ - - if not self.address: - raise TypeError("This method can be only called on " - "an instated contract with an address") - - abi = self._get_event_abi() - - if argument_filters is None: - argument_filters = dict() - - _filters = dict(**argument_filters) - - blkhash_set = blockHash is not None - blknum_set = fromBlock is not None or toBlock is not None - if blkhash_set and blknum_set: - raise ValidationError( - 'blockHash cannot be set at the same' - ' time as fromBlock or toBlock') - - # Construct JSON-RPC raw filter presentation based on human readable Python descriptions - # Namely, convert event names to their keccak signatures - data_filter_set, event_filter_params = construct_event_filter_params( - abi, - self.web3.codec, - contract_address=self.address, - argument_filters=_filters, - fromBlock=fromBlock, - toBlock=toBlock, - address=self.address, - ) - - if blockHash is not None: - event_filter_params['blockHash'] = blockHash - - # Call JSON-RPC API - logs = self.web3.eth.getLogs(event_filter_params) - - # Convert raw binary data to Python proxy objects as described by ABI - return tuple(get_event_data(self.web3.codec, abi, entry) for entry in logs) - - @classmethod - def factory(cls, class_name: str, **kwargs: Any) -> PropertyCheckingFactory: - return PropertyCheckingFactory(class_name, (cls,), kwargs) - - -class ContractCaller: - """ - An alternative Contract API. - - This call: - - > contract.caller({'from': eth.accounts[1], 'gas': 100000, ...}).add(2, 3) - is equivalent to this call in the classic contract: - > contract.functions.add(2, 3).call({'from': eth.accounts[1], 'gas': 100000, ...}) - - Other options for invoking this class include: - - > contract.caller.add(2, 3) - - or - - > contract.caller().add(2, 3) - - or - - > contract.caller(transaction={'from': eth.accounts[1], 'gas': 100000, ...}).add(2, 3) - """ - def __init__(self, - abi: ABI, - web3: 'Web3', - address: ChecksumAddress, - transaction: TxParams=None, - block_identifier: BlockIdentifier='latest') -> None: - self.web3 = web3 - self.address = address - self.abi = abi - self._functions = None - - if self.abi: - if transaction is None: - transaction = {} - - self._functions = filter_by_type('function', self.abi) - for func in self._functions: - fn: ContractFunction = ContractFunction.factory( - func['name'], - web3=self.web3, - contract_abi=self.abi, - address=self.address, - function_identifier=func['name']) - - block_id = parse_block_identifier(self.web3, block_identifier) - caller_method = partial(self.call_function, - fn, - transaction=transaction, - block_identifier=block_id) - - setattr(self, func['name'], caller_method) - - def __getattr__(self, function_name: str) -> Any: - if self.abi is None: - raise NoABIFound( - "There is no ABI found for this contract.", - ) - elif not self._functions or len(self._functions) == 0: - raise NoABIFunctionsFound( - "The ABI for this contract contains no function definitions. ", - "Are you sure you provided the correct contract ABI?" - ) - elif function_name not in self._functions: - functions_available = ', '.join([fn['name'] for fn in self._functions]) - raise MismatchedABI( - "The function '{}' was not found in this contract's ABI. ".format(function_name), - "Here is a list of all of the function names found: ", - "{}. ".format(functions_available), - "Did you mean to call one of those functions?" - ) - else: - return super().__getattribute__(function_name) - - def __call__( - self, transaction: TxParams=None, block_identifier: BlockIdentifier='latest' - ) -> 'ContractCaller': - if transaction is None: - transaction = {} - return type(self)(self.abi, - self.web3, - self.address, - transaction=transaction, - block_identifier=block_identifier) - - @staticmethod - def call_function( - fn: ContractFunction, - *args: Any, - transaction: TxParams=None, - block_identifier: BlockIdentifier='latest', - **kwargs: Any - ) -> Sequence[Any]: - if transaction is None: - transaction = {} - return fn(*args, **kwargs).call(transaction, block_identifier) - - -def check_for_forbidden_api_filter_arguments( - event_abi: ABIEvent, _filters: Dict[str, Any] -) -> None: - name_indexed_inputs = {_input['name']: _input for _input in event_abi['inputs']} - - for filter_name, filter_value in _filters.items(): - _input = name_indexed_inputs[filter_name] - if is_array_type(_input['type']): - raise TypeError( - "createFilter no longer supports array type filter arguments. " - "see the build_filter method for filtering array type filters.") - if is_list_like(filter_value) and is_dynamic_sized_type(_input['type']): - raise TypeError( - "createFilter no longer supports setting filter argument options for dynamic sized " - "types. See the build_filter method for setting filters with the match_any " - "method.") - - -def call_contract_function( - web3: 'Web3', - address: ChecksumAddress, - normalizers: Tuple[Callable[..., Any], ...], - function_identifier: Union[str, Type[FallbackFn]], - transaction: TxParams, - block_id: BlockIdentifier=None, - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - *args: Any, - **kwargs: Any) -> Sequence[Any]: - """ - Helper function for interacting with a contract function using the - `eth_call` API. - """ - call_transaction = prepare_transaction( - address, - web3, - fn_identifier=function_identifier, - contract_abi=contract_abi, - fn_abi=fn_abi, - transaction=transaction, - fn_args=args, - fn_kwargs=kwargs, - ) - - if block_id is None: - return_data = web3.eth.call(call_transaction) - else: - return_data = web3.eth.call(call_transaction, block_identifier=block_id) - - if fn_abi is None: - fn_abi = find_matching_fn_abi(contract_abi, web3.codec, function_identifier, args, kwargs) - - output_types = get_abi_output_types(fn_abi) - - try: - output_data = web3.codec.decode_abi(output_types, return_data) - except DecodingError as e: - # Provide a more helpful error message than the one provided by - # eth-abi-utils - is_missing_code_error = ( - return_data in ACCEPTABLE_EMPTY_STRINGS and - web3.eth.getCode(address) in ACCEPTABLE_EMPTY_STRINGS - ) - if is_missing_code_error: - msg = ( - "Could not transact with/call contract function, is contract " - "deployed correctly and chain synced?" - ) - else: - msg = ( - "Could not decode contract function call {} return data {} for " - "output_types {}".format( - function_identifier, - return_data, - output_types - ) - ) - raise BadFunctionCallOutput(msg) from e - - _normalizers = itertools.chain( - BASE_RETURN_NORMALIZERS, - normalizers, - ) - normalized_data = map_abi_data(_normalizers, output_types, output_data) - - if len(normalized_data) == 1: - return normalized_data[0] - else: - return normalized_data - - -def parse_block_identifier(web3: 'Web3', block_identifier: BlockIdentifier) -> BlockIdentifier: - if isinstance(block_identifier, int): - return parse_block_identifier_int(web3, block_identifier) - elif block_identifier in ['latest', 'earliest', 'pending']: - return block_identifier - elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash(block_identifier): - return web3.eth.getBlock(block_identifier)['number'] - else: - raise BlockNumberOutofRange - - -def parse_block_identifier_int(web3: 'Web3', block_identifier_int: int) -> BlockNumber: - if block_identifier_int >= 0: - block_num = block_identifier_int - else: - last_block = web3.eth.getBlock('latest')['number'] - block_num = last_block + block_identifier_int + 1 - if block_num < 0: - raise BlockNumberOutofRange - return BlockNumber(block_num) - - -def transact_with_contract_function( - address: ChecksumAddress, - web3: 'Web3', - function_name: Union[str, Type[FallbackFn]]=None, - transaction: TxParams=None, - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - *args: Any, - **kwargs: Any) -> Hash32: - """ - Helper function for interacting with a contract function by sending a - transaction. - """ - transact_transaction = prepare_transaction( - address, - web3, - fn_identifier=function_name, - contract_abi=contract_abi, - transaction=transaction, - fn_abi=fn_abi, - fn_args=args, - fn_kwargs=kwargs, - ) - - txn_hash = web3.eth.sendTransaction(transact_transaction) - return txn_hash - - -def estimate_gas_for_function( - address: ChecksumAddress, - web3: 'Web3', - fn_identifier: Union[str, Type[FallbackFn]]=None, - transaction: TxParams=None, - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - *args: Any, - **kwargs: Any) -> int: - """Estimates gas cost a function call would take. - - Don't call this directly, instead use :meth:`Contract.estimateGas` - on your contract instance. - """ - estimate_transaction = prepare_transaction( - address, - web3, - fn_identifier=fn_identifier, - contract_abi=contract_abi, - fn_abi=fn_abi, - transaction=transaction, - fn_args=args, - fn_kwargs=kwargs, - ) - - gas_estimate = web3.eth.estimateGas(estimate_transaction) - return gas_estimate - - -def build_transaction_for_function( - address: ChecksumAddress, - web3: 'Web3', - function_name: Union[str, Type[FallbackFn]]=None, - transaction: TxParams=None, - contract_abi: ABI=None, - fn_abi: ABIFunction=None, - *args: Any, - **kwargs: Any) -> TxParams: - """Builds a dictionary with the fields required to make the given transaction - - Don't call this directly, instead use :meth:`Contract.buildTransaction` - on your contract instance. - """ - prepared_transaction = prepare_transaction( - address, - web3, - fn_identifier=function_name, - contract_abi=contract_abi, - fn_abi=fn_abi, - transaction=transaction, - fn_args=args, - fn_kwargs=kwargs, - ) - - prepared_transaction = fill_transaction_defaults(web3, prepared_transaction) - - return prepared_transaction - - -def find_functions_by_identifier( - contract_abi: ABI, web3: 'Web3', address: ChecksumAddress, callable_check: Callable[..., Any] -) -> List[ContractFunction]: - fns_abi = filter_by_type('function', contract_abi) - return [ - ContractFunction.factory( - fn_abi['name'], - web3=web3, - contract_abi=contract_abi, - address=address, - function_identifier=fn_abi['name'], - abi=fn_abi - ) - for fn_abi in fns_abi - if callable_check(fn_abi) - ] - - -def get_function_by_identifier( - fns: Sequence[ContractFunction], identifier: str -) -> ContractFunction: - if len(fns) > 1: - raise ValueError( - 'Found multiple functions with matching {0}. ' - 'Found: {1!r}'.format(identifier, fns) - ) - elif len(fns) == 0: - raise ValueError( - 'Could not find any function with matching {0}'.format(identifier) - ) - return fns[0] +"""Interaction with smart contracts over Web3 connector. + +""" +import copy +import itertools + +from vns_abi import ( + decode_abi, +) +from vns_abi.exceptions import ( + DecodingError, +) +from vns_utils import ( + add_0x_prefix, + encode_hex, + function_abi_to_4byte_selector, + is_list_like, + is_text, + to_tuple, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.abi import ( + abi_to_signature, + check_if_arguments_can_be_encoded, + fallback_func_abi_exists, + filter_by_type, + get_abi_output_types, + get_constructor_abi, + is_array_type, + map_abi_data, + merge_args_and_kwargs, +) +from web3._utils.blocks import ( + is_hex_encoded_block_hash, +) +from web3._utils.contracts import ( + encode_abi, + find_matching_event_abi, + find_matching_fn_abi, + get_function_info, + prepare_transaction, +) +from web3._utils.datatypes import ( + PropertyCheckingFactory, +) +from web3._utils.decorators import ( + combomethod, + deprecated_for, +) +from web3._utils.empty import ( + empty, +) +from web3._utils.encoding import ( + to_4byte_hex, + to_hex, +) +from web3._utils.events import ( + EventFilterBuilder, + get_event_data, + is_dynamic_sized_type, +) +from web3._utils.filters import ( + construct_event_filter_params, +) +from web3._utils.function_identifiers import ( + FallbackFn, +) +from web3._utils.normalizers import ( + BASE_RETURN_NORMALIZERS, + normalize_abi, + normalize_address, + normalize_bytecode, +) +from web3._utils.toolz import ( + compose, + partial, +) +from web3._utils.transactions import ( + fill_transaction_defaults, +) +from web3.exceptions import ( + BadFunctionCallOutput, + BlockNumberOutofRange, + FallbackNotFound, + MismatchedABI, + NoABIEventsFound, + NoABIFound, + NoABIFunctionsFound, + ValidationError, +) + +ACCEPTABLE_EMPTY_STRINGS = ["0x", b"0x", "", b""] + + +class ContractFunctions: + """Class containing contract function objects + """ + + def __init__(self, abi, web3, address=None): + self.abi = abi + self.web3 = web3 + self.address = address + + if self.abi: + self._functions = filter_by_type('function', self.abi) + for func in self._functions: + setattr( + self, + func['name'], + ContractFunction.factory( + func['name'], + web3=self.web3, + contract_abi=self.abi, + address=self.address, + function_identifier=func['name'])) + + def __iter__(self): + if not hasattr(self, '_functions') or not self._functions: + return + + for func in self._functions: + yield func['name'] + + def __getattr__(self, function_name): + if self.abi is None: + raise NoABIFound( + "There is no ABI found for this contract.", + ) + if '_functions' not in self.__dict__: + raise NoABIFunctionsFound( + "The abi for this contract contains no function definitions. ", + "Are you sure you provided the correct contract abi?" + ) + elif function_name not in self.__dict__['_functions']: + raise MismatchedABI( + "The function '{}' was not found in this contract's abi. ".format(function_name), + "Are you sure you provided the correct contract abi?" + ) + else: + return super().__getattribute__(function_name) + + def __getitem__(self, function_name): + return getattr(self, function_name) + + +class ContractEvents: + """Class containing contract event objects + + This is available via: + + .. code-block:: python + + >>> mycontract.events + + + To get list of all supported events in the contract ABI. + This allows you to iterate over :class:`ContractEvent` proxy classes. + + .. code-block:: python + + >>> for e in mycontract.events: print(e) + + ... + + """ + + def __init__(self, abi, web3, address=None): + if abi: + self.abi = abi + self._events = filter_by_type('event', self.abi) + for event in self._events: + setattr( + self, + event['name'], + ContractEvent.factory( + event['name'], + web3=web3, + contract_abi=self.abi, + address=address, + event_name=event['name'])) + + def __getattr__(self, event_name): + if '_events' not in self.__dict__: + raise NoABIEventsFound( + "The abi for this contract contains no event definitions. ", + "Are you sure you provided the correct contract abi?" + ) + elif event_name not in self.__dict__['_events']: + raise MismatchedABI( + "The event '{}' was not found in this contract's abi. ".format(event_name), + "Are you sure you provided the correct contract abi?" + ) + else: + return super().__getattribute__(event_name) + + def __getitem__(self, event_name): + return getattr(self, event_name) + + def __iter__(self): + """Iterate over supported + + :return: Iterable of :class:`ContractEvent` + """ + for event in self._events: + yield self[event['name']] + + +class Contract: + """Base class for Contract proxy classes. + + First you need to create your Contract classes using + :meth:`web3.vns.Bbbbbbbb.contract` that takes compiled Solidity contract + ABI definitions as input. The created class object will be a subclass of + this base class. + + After you have your Contract proxy class created you can interact with + smart contracts + + * Create a Contract proxy object for an existing deployed smart contract by + its address using :meth:`__init__` + + * Deploy a new smart contract using :py:meth:`Contract.deploy` + """ + + # set during class construction + web3 = None + + # instance level properties + address = None + + # class properties (overridable at instance level) + abi = None + + asm = None + ast = None + + bytecode = None + bytecode_runtime = None + clone_bin = None + + functions = None + caller = None + + #: Instance of :class:`ContractEvents` presenting available Event ABIs + events = None + + dev_doc = None + interface = None + metadata = None + opcodes = None + src_map = None + src_map_runtime = None + user_doc = None + + def __init__(self, address=None): + """Create a new smart contract proxy object. + + :param address: Contract address as 0x hex string + """ + if self.web3 is None: + raise AttributeError( + 'The `Contract` class has not been initialized. Please use the ' + '`web3.contract` interface to create your contract class.' + ) + + if address: + self.address = normalize_address(self.web3.ens, address) + + if not self.address: + raise TypeError("The address argument is required to instantiate a contract.") + + self.functions = ContractFunctions(self.abi, self.web3, self.address) + self.caller = ContractCaller(self.abi, self.web3, self.address) + self.events = ContractEvents(self.abi, self.web3, self.address) + self.fallback = Contract.get_fallback_function(self.abi, self.web3, self.address) + + @classmethod + def factory(cls, web3, class_name=None, **kwargs): + + kwargs['web3'] = web3 + + normalizers = { + 'abi': normalize_abi, + 'address': partial(normalize_address, kwargs['web3'].ens), + 'bytecode': normalize_bytecode, + 'bytecode_runtime': normalize_bytecode, + } + + contract = PropertyCheckingFactory( + class_name or cls.__name__, + (cls,), + kwargs, + normalizers=normalizers, + ) + contract.functions = ContractFunctions(contract.abi, contract.web3) + contract.caller = ContractCaller(contract.abi, contract.web3, contract.address) + contract.events = ContractEvents(contract.abi, contract.web3) + contract.fallback = Contract.get_fallback_function(contract.abi, contract.web3) + + return contract + + # + # Contract Methods + # + @classmethod + def constructor(cls, *args, **kwargs): + """ + :param args: The contract constructor arguments as positional arguments + :param kwargs: The contract constructor arguments as keyword arguments + :return: a contract constructor object + """ + if cls.bytecode is None: + raise ValueError( + "Cannot call constructor on a contract that does not have 'bytecode' associated " + "with it" + ) + + return ContractConstructor(cls.web3, + cls.abi, + cls.bytecode, + *args, + **kwargs) + + # Public API + # + @combomethod + def encodeABI(cls, fn_name, args=None, kwargs=None, data=None): + """ + Encodes the arguments using the Ethereum ABI for the contract function + that matches the given name and arguments.. + + :param data: defaults to function selector + """ + fn_abi, fn_selector, fn_arguments = get_function_info( + fn_name, contract_abi=cls.abi, args=args, kwargs=kwargs, + ) + + if data is None: + data = fn_selector + + return encode_abi(cls.web3, fn_abi, fn_arguments, data) + + @combomethod + def all_functions(self): + return find_functions_by_identifier( + self.abi, self.web3, self.address, lambda _: True + ) + + @combomethod + def get_function_by_signature(self, signature): + if ' ' in signature: + raise ValueError( + 'Function signature should not contain any spaces. ' + 'Found spaces in input: %s' % signature + ) + + def callable_check(fn_abi): + return abi_to_signature(fn_abi) == signature + + fns = find_functions_by_identifier(self.abi, self.web3, self.address, callable_check) + return get_function_by_identifier(fns, 'signature') + + @combomethod + def find_functions_by_name(self, fn_name): + def callable_check(fn_abi): + return fn_abi['name'] == fn_name + + return find_functions_by_identifier( + self.abi, self.web3, self.address, callable_check + ) + + @combomethod + def get_function_by_name(self, fn_name): + fns = self.find_functions_by_name(fn_name) + return get_function_by_identifier(fns, 'name') + + @combomethod + def get_function_by_selector(self, selector): + def callable_check(fn_abi): + return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(selector) + + fns = find_functions_by_identifier(self.abi, self.web3, self.address, callable_check) + return get_function_by_identifier(fns, 'selector') + + @combomethod + def decode_function_input(self, data): + data = HexBytes(data) + selector, params = data[:4], data[4:] + func = self.get_function_by_selector(selector) + names = [x['name'] for x in func.abi['inputs']] + types = [x['type'] for x in func.abi['inputs']] + decoded = decode_abi(types, params) + normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded) + return func, dict(zip(names, normalized)) + + @combomethod + def find_functions_by_args(self, *args): + def callable_check(fn_abi): + return check_if_arguments_can_be_encoded(fn_abi, args=args, kwargs={}) + + return find_functions_by_identifier( + self.abi, self.web3, self.address, callable_check + ) + + @combomethod + def get_function_by_args(self, *args): + fns = self.find_functions_by_args(*args) + return get_function_by_identifier(fns, 'args') + + # + # Private Helpers + # + _return_data_normalizers = tuple() + + @classmethod + def _prepare_transaction(cls, + fn_name, + fn_args=None, + fn_kwargs=None, + transaction=None): + + return prepare_transaction( + cls.address, + cls.web3, + fn_identifier=fn_name, + contract_abi=cls.abi, + transaction=transaction, + fn_args=fn_args, + fn_kwargs=fn_kwargs, + ) + + @classmethod + def _find_matching_fn_abi(cls, fn_identifier=None, args=None, kwargs=None): + return find_matching_fn_abi(cls.abi, + fn_identifier=fn_identifier, + args=args, + kwargs=kwargs) + + @classmethod + def _find_matching_event_abi(cls, event_name=None, argument_names=None): + return find_matching_event_abi( + abi=cls.abi, + event_name=event_name, + argument_names=argument_names) + + @staticmethod + def get_fallback_function(abi, web3, address=None): + if abi and fallback_func_abi_exists(abi): + return ContractFunction.factory( + 'fallback', + web3=web3, + contract_abi=abi, + address=address, + function_identifier=FallbackFn)() + + return NonExistentFallbackFunction() + + @combomethod + def _encode_constructor_data(cls, args=None, kwargs=None): + constructor_abi = get_constructor_abi(cls.abi) + + if constructor_abi: + if args is None: + args = tuple() + if kwargs is None: + kwargs = {} + + arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) + + deploy_data = add_0x_prefix( + encode_abi(cls.web3, constructor_abi, arguments, data=cls.bytecode) + ) + else: + if args is not None or kwargs is not None: + msg = "Constructor args were provided, but no constructor function was provided." + raise TypeError(msg) + + deploy_data = to_hex(cls.bytecode) + + return deploy_data + + +def mk_collision_prop(fn_name): + def collision_fn(): + msg = "Namespace collision for function name {0} with ConciseContract API.".format(fn_name) + raise AttributeError(msg) + collision_fn.__name__ = fn_name + return collision_fn + + +class ContractConstructor: + """ + Class for contract constructor API. + """ + def __init__(self, web3, abi, bytecode, *args, **kwargs): + self.web3 = web3 + self.abi = abi + self.bytecode = bytecode + self.data_in_transaction = self._encode_data_in_transaction(*args, **kwargs) + + @combomethod + def _encode_data_in_transaction(self, *args, **kwargs): + constructor_abi = get_constructor_abi(self.abi) + + if constructor_abi: + if not args: + args = tuple() + if not kwargs: + kwargs = {} + + arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) + data = add_0x_prefix( + encode_abi(self.web3, constructor_abi, arguments, data=self.bytecode) + ) + else: + data = to_hex(self.bytecode) + + return data + + @combomethod + def estimateGas(self, transaction=None): + if transaction is None: + estimate_gas_transaction = {} + else: + estimate_gas_transaction = dict(**transaction) + self.check_forbidden_keys_in_transaction(estimate_gas_transaction, + ["data", "to"]) + + if self.web3.vns.defaultAccount is not empty: + estimate_gas_transaction.setdefault('from', self.web3.vns.defaultAccount) + + estimate_gas_transaction['data'] = self.data_in_transaction + + return self.web3.vns.estimateGas(estimate_gas_transaction) + + @combomethod + def transact(self, transaction=None): + if transaction is None: + transact_transaction = {} + else: + transact_transaction = dict(**transaction) + self.check_forbidden_keys_in_transaction(transact_transaction, + ["data", "to"]) + + if self.web3.vns.defaultAccount is not empty: + transact_transaction.setdefault('from', self.web3.vns.defaultAccount) + + transact_transaction['data'] = self.data_in_transaction + + # TODO: handle asynchronous contract creation + return self.web3.vns.sendTransaction(transact_transaction) + + @combomethod + def buildTransaction(self, transaction=None): + """ + Build the transaction dictionary without sending + """ + + if transaction is None: + built_transaction = {} + else: + built_transaction = dict(**transaction) + self.check_forbidden_keys_in_transaction(built_transaction, + ["data", "to"]) + + if self.web3.vns.defaultAccount is not empty: + built_transaction.setdefault('from', self.web3.vns.defaultAccount) + + built_transaction['data'] = self.data_in_transaction + built_transaction['to'] = b'' + return fill_transaction_defaults(self.web3, built_transaction) + + @staticmethod + def check_forbidden_keys_in_transaction(transaction, forbidden_keys=None): + keys_found = set(transaction.keys()) & set(forbidden_keys) + if keys_found: + raise ValueError("Cannot set {} in transaction".format(', '.join(keys_found))) + + +class ConciseMethod: + ALLOWED_MODIFIERS = {'call', 'estimateGas', 'transact', 'buildTransaction'} + + def __init__(self, function, normalizers=None): + self._function = function + self._function._return_data_normalizers = normalizers + + def __call__(self, *args, **kwargs): + return self.__prepared_function(*args, **kwargs) + + def __prepared_function(self, *args, **kwargs): + if not kwargs: + modifier, modifier_dict = 'call', {} + elif len(kwargs) == 1: + modifier, modifier_dict = kwargs.popitem() + if modifier not in self.ALLOWED_MODIFIERS: + raise TypeError( + "The only allowed keyword arguments are: %s" % self.ALLOWED_MODIFIERS) + else: + raise TypeError("Use up to one keyword argument, one of: %s" % self.ALLOWED_MODIFIERS) + + return getattr(self._function(*args), modifier)(modifier_dict) + + +class ConciseContract: + """ + An alternative Contract Factory which invokes all methods as `call()`, + unless you add a keyword argument. The keyword argument assigns the prep method. + + This call + + > contract.withdraw(amount, transact={'from': vns.accounts[1], 'gas': 100000, ...}) + + is equivalent to this call in the classic contract: + + > contract.functions.withdraw(amount).transact({'from': vns.accounts[1], 'gas': 100000, ...}) + """ + @deprecated_for( + "contract.caller. or contract.caller({transaction_dict})." + ) + def __init__(self, classic_contract, method_class=ConciseMethod): + + classic_contract._return_data_normalizers += CONCISE_NORMALIZERS + self._classic_contract = classic_contract + self.address = self._classic_contract.address + + protected_fn_names = [fn for fn in dir(self) if not fn.endswith('__')] + + for fn_name in self._classic_contract.functions: + + # Override namespace collisions + if fn_name in protected_fn_names: + _concise_method = mk_collision_prop(fn_name) + + else: + _classic_method = getattr( + self._classic_contract.functions, + fn_name) + + _concise_method = method_class( + _classic_method, + self._classic_contract._return_data_normalizers + ) + + setattr(self, fn_name, _concise_method) + + @classmethod + def factory(cls, *args, **kwargs): + return compose(cls, Contract.factory(*args, **kwargs)) + + +def _none_addr(datatype, data): + if datatype == 'address' and int(data, base=16) == 0: + return (datatype, None) + else: + return (datatype, data) + + +CONCISE_NORMALIZERS = ( + _none_addr, +) + + +class ImplicitMethod(ConciseMethod): + def __call_by_default(self, args): + function_abi = find_matching_fn_abi(self._function.contract_abi, + fn_identifier=self._function.function_identifier, + args=args) + + return function_abi['constant'] if 'constant' in function_abi.keys() else False + + @deprecated_for("classic contract syntax. Ex: contract.functions.withdraw(amount).transact({})") + def __call__(self, *args, **kwargs): + # Modifier is not provided and method is not constant/pure do a transaction instead + if not kwargs and not self.__call_by_default(args): + return super().__call__(*args, transact={}) + else: + return super().__call__(*args, **kwargs) + + +class ImplicitContract(ConciseContract): + """ + ImplicitContract class is similar to the ConciseContract class + however it performs a transaction instead of a call if no modifier + is given and the method is not marked 'constant' in the ABI. + + The transaction will use the default account to send the transaction. + + This call + + > contract.withdraw(amount) + + is equivalent to this call in the classic contract: + + > contract.functions.withdraw(amount).transact({}) + """ + def __init__(self, classic_contract, method_class=ImplicitMethod): + super().__init__(classic_contract, method_class=method_class) + + +class NonExistentFallbackFunction: + @staticmethod + def _raise_exception(): + raise FallbackNotFound("No fallback function was found in the contract ABI.") + + def __getattr__(self, attr): + return NonExistentFallbackFunction._raise_exception + + +class ContractFunction: + """Base class for contract functions + + A function accessed via the api contract.functions.myMethod(*args, **kwargs) + is a subclass of this class. + """ + address = None + function_identifier = None + web3 = None + contract_abi = None + abi = None + transaction = None + arguments = None + + def __init__(self, abi=None): + self.abi = abi + self.fn_name = type(self).__name__ + + def __call__(self, *args, **kwargs): + clone = copy.copy(self) + if args is None: + clone.args = tuple() + else: + clone.args = args + + if kwargs is None: + clone.kwargs = {} + else: + clone.kwargs = kwargs + clone._set_function_info() + return clone + + def _set_function_info(self): + if not self.abi: + self.abi = find_matching_fn_abi( + self.contract_abi, + self.function_identifier, + self.args, + self.kwargs + ) + if self.function_identifier is FallbackFn: + self.selector = encode_hex(b'') + elif is_text(self.function_identifier): + self.selector = encode_hex(function_abi_to_4byte_selector(self.abi)) + else: + raise TypeError("Unsupported function identifier") + + self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs) + + def call(self, transaction=None, block_identifier='latest'): + """ + Execute a contract function call using the `vns_call` interface. + + This method prepares a ``Caller`` object that exposes the contract + functions and public variables as callable Python functions. + + Reading a public ``owner`` address variable example: + + .. code-block:: python + + ContractFactory = w3.vns.contract( + abi=wallet_contract_definition["abi"] + ) + + # Not a real contract address + contract = ContractFactory("0x2f70d3d26829e412A602E83FE8EeBF80255AEeA5") + + # Read "owner" public variable + addr = contract.functions.owner().call() + + :param transaction: Dictionary of transaction info for web3 interface + :return: ``Caller`` object that has contract public functions + and variables exposed as Python methods + """ + if transaction is None: + call_transaction = {} + else: + call_transaction = dict(**transaction) + + if 'data' in call_transaction: + raise ValueError("Cannot set data in call transaction") + + if self.address: + call_transaction.setdefault('to', self.address) + if self.web3.vns.defaultAccount is not empty: + call_transaction.setdefault('from', self.web3.vns.defaultAccount) + + if 'to' not in call_transaction: + if isinstance(self, type): + raise ValueError( + "When using `Contract.[methodtype].[method].call()` from" + " a contract factory you " + "must provide a `to` address with the transaction" + ) + else: + raise ValueError( + "Please ensure that this contract instance has an address." + ) + + block_id = parse_block_identifier(self.web3, block_identifier) + + return call_contract_function( + self.web3, + self.address, + self._return_data_normalizers, + self.function_identifier, + call_transaction, + block_id, + self.contract_abi, + self.abi, + *self.args, + **self.kwargs + ) + + def transact(self, transaction=None): + if transaction is None: + transact_transaction = {} + else: + transact_transaction = dict(**transaction) + + if 'data' in transact_transaction: + raise ValueError("Cannot set data in transact transaction") + + if self.address is not None: + transact_transaction.setdefault('to', self.address) + if self.web3.vns.defaultAccount is not empty: + transact_transaction.setdefault('from', self.web3.vns.defaultAccount) + + if 'to' not in transact_transaction: + if isinstance(self, type): + raise ValueError( + "When using `Contract.transact` from a contract factory you " + "must provide a `to` address with the transaction" + ) + else: + raise ValueError( + "Please ensure that this contract instance has an address." + ) + + return transact_with_contract_function( + self.address, + self.web3, + self.function_identifier, + transact_transaction, + self.contract_abi, + self.abi, + *self.args, + **self.kwargs + ) + + def estimateGas(self, transaction=None): + if transaction is None: + estimate_gas_transaction = {} + else: + estimate_gas_transaction = dict(**transaction) + + if 'data' in estimate_gas_transaction: + raise ValueError("Cannot set data in estimateGas transaction") + if 'to' in estimate_gas_transaction: + raise ValueError("Cannot set to in estimateGas transaction") + + if self.address: + estimate_gas_transaction.setdefault('to', self.address) + if self.web3.vns.defaultAccount is not empty: + estimate_gas_transaction.setdefault('from', self.web3.vns.defaultAccount) + + if 'to' not in estimate_gas_transaction: + if isinstance(self, type): + raise ValueError( + "When using `Contract.estimateGas` from a contract factory " + "you must provide a `to` address with the transaction" + ) + else: + raise ValueError( + "Please ensure that this contract instance has an address." + ) + + return estimate_gas_for_function( + self.address, + self.web3, + self.function_identifier, + estimate_gas_transaction, + self.contract_abi, + self.abi, + *self.args, + **self.kwargs + ) + + def buildTransaction(self, transaction=None): + """ + Build the transaction dictionary without sending + """ + if transaction is None: + built_transaction = {} + else: + built_transaction = dict(**transaction) + + if 'data' in built_transaction: + raise ValueError("Cannot set data in build transaction") + + if not self.address and 'to' not in built_transaction: + raise ValueError( + "When using `ContractFunction.buildTransaction` from a contract factory " + "you must provide a `to` address with the transaction" + ) + if self.address and 'to' in built_transaction: + raise ValueError("Cannot set to in contract call build transaction") + + if self.address: + built_transaction.setdefault('to', self.address) + + if 'to' not in built_transaction: + raise ValueError( + "Please ensure that this contract instance has an address." + ) + + return build_transaction_for_function( + self.address, + self.web3, + self.function_identifier, + built_transaction, + self.contract_abi, + self.abi, + *self.args, + **self.kwargs + ) + + @combomethod + def _encode_transaction_data(cls): + return add_0x_prefix(encode_abi(cls.web3, cls.abi, cls.arguments, cls.selector)) + + _return_data_normalizers = tuple() + + @classmethod + def factory(cls, class_name, **kwargs): + return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get('abi')) + + def __repr__(self): + if self.abi: + _repr = '' + return '' % self.fn_name + + +class ContractEvent: + """Base class for contract events + + An event accessed via the api contract.events.myEvents(*args, **kwargs) + is a subclass of this class. + """ + address = None + event_name = None + web3 = None + contract_abi = None + abi = None + + def __init__(self, *argument_names): + + if argument_names is None: + self.argument_names = tuple() + else: + self.argument_names = argument_names + + self.abi = self._get_event_abi() + + @classmethod + def _get_event_abi(cls): + return find_matching_event_abi( + cls.contract_abi, + event_name=cls.event_name) + + @combomethod + def processReceipt(self, txn_receipt): + return self._parse_logs(txn_receipt) + + @to_tuple + def _parse_logs(self, txn_receipt): + for log in txn_receipt['logs']: + try: + decoded_log = get_event_data(self.abi, log) + except MismatchedABI: + continue + yield decoded_log + + @combomethod + def createFilter( + self, *, # PEP 3102 + argument_filters=None, + fromBlock=None, + toBlock="latest", + address=None, + topics=None): + """ + Create filter object that tracks logs emitted by this contract event. + :param filter_params: other parameters to limit the events + """ + if fromBlock is None: + raise TypeError("Missing mandatory keyword argument to createFilter: fromBlock") + + if argument_filters is None: + argument_filters = dict() + + _filters = dict(**argument_filters) + + event_abi = self._get_event_abi() + + check_for_forbidden_api_filter_arguments(event_abi, _filters) + + _, event_filter_params = construct_event_filter_params( + self._get_event_abi(), + contract_address=self.address, + argument_filters=_filters, + fromBlock=fromBlock, + toBlock=toBlock, + address=address, + topics=topics, + ) + + filter_builder = EventFilterBuilder(event_abi) + filter_builder.address = event_filter_params.get('address') + filter_builder.fromBlock = event_filter_params.get('fromBlock') + filter_builder.toBlock = event_filter_params.get('toBlock') + match_any_vals = { + arg: value for arg, value in _filters.items() + if not is_array_type(filter_builder.args[arg].arg_type) and is_list_like(value) + } + for arg, value in match_any_vals.items(): + filter_builder.args[arg].match_any(*value) + + match_single_vals = { + arg: value for arg, value in _filters.items() + if not is_array_type(filter_builder.args[arg].arg_type) and not is_list_like(value) + } + for arg, value in match_single_vals.items(): + filter_builder.args[arg].match_single(value) + + log_filter = filter_builder.deploy(self.web3) + log_filter.log_entry_formatter = get_event_data(self._get_event_abi()) + log_filter.builder = filter_builder + + return log_filter + + @combomethod + def build_filter(self): + builder = EventFilterBuilder( + self._get_event_abi(), + formatter=get_event_data(self._get_event_abi())) + builder.address = self.address + return builder + + @combomethod + def getLogs(self, + argument_filters=None, + fromBlock=None, + toBlock=None, + blockHash=None): + """Get events for this contract instance using vns_getLogs API. + + This is a stateless method, as opposed to createFilter. + It can be safely called against nodes which do not provide + vns_newFilter API, like Infura nodes. + + If there are many events, + like ``Transfer`` events for a popular token, + the Ethereum node might be overloaded and timeout + on the underlying JSON-RPC call. + + Example - how to get all ERC-20 token transactions + for the latest 10 blocks: + + .. code-block:: python + + from = max(mycontract.web3.vns.blockNumber - 10, 1) + to = mycontract.web3.vns.blockNumber + + events = mycontract.events.Transfer.getLogs(fromBlock=from, toBlock=to) + + for e in events: + print(e["args"]["from"], + e["args"]["to"], + e["args"]["value"]) + + The returned processed log values will look like: + + .. code-block:: python + + ( + AttributeDict({ + 'args': AttributeDict({}), + 'event': 'LogNoArguments', + 'logIndex': 0, + 'transactionIndex': 0, + 'transactionHash': HexBytes('...'), + 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', + 'blockHash': HexBytes('...'), + 'blockNumber': 3 + }), + AttributeDict(...), + ... + ) + + See also: :func:`web3.middleware.filter.local_filter_middleware`. + + :param argument_filters: + :param fromBlock: block number or "latest", defaults to "latest" + :param toBlock: block number or "latest". Defaults to "latest" + :param blockHash: block hash. blockHash cannot be set at the + same time as fromBlock or toBlock + :yield: Tuple of :class:`AttributeDict` instances + """ + + if not self.address: + raise TypeError("This method can be only called on " + "an instated contract with an address") + + abi = self._get_event_abi() + + if argument_filters is None: + argument_filters = dict() + + _filters = dict(**argument_filters) + + blkhash_set = blockHash is not None + blknum_set = fromBlock is not None or toBlock is not None + if blkhash_set and blknum_set: + raise ValidationError( + 'blockHash cannot be set at the same' + ' time as fromBlock or toBlock') + + # Construct JSON-RPC raw filter presentation based on human readable Python descriptions + # Namely, convert event names to their keccak signatures + data_filter_set, event_filter_params = construct_event_filter_params( + abi, + contract_address=self.address, + argument_filters=_filters, + fromBlock=fromBlock, + toBlock=toBlock, + address=self.address, + ) + + if blockHash is not None: + event_filter_params['blockHash'] = blockHash + + # Call JSON-RPC API + logs = self.web3.vns.getLogs(event_filter_params) + + # Convert raw binary data to Python proxy objects as described by ABI + return tuple(get_event_data(abi, entry) for entry in logs) + + @classmethod + def factory(cls, class_name, **kwargs): + return PropertyCheckingFactory(class_name, (cls,), kwargs) + + +class ContractCaller: + """ + An alternative Contract API. + + This call: + + > contract.caller({'from': vns.accounts[1], 'gas': 100000, ...}).add(2, 3) + is equivalent to this call in the classic contract: + > contract.functions.add(2, 3).call({'from': vns.accounts[1], 'gas': 100000, ...}) + + Other options for invoking this class include: + + > contract.caller.add(2, 3) + + or + + > contract.caller().add(2, 3) + + or + + > contract.caller(transaction={'from': vns.accounts[1], 'gas': 100000, ...}).add(2, 3) + """ + def __init__(self, + abi, + web3, + address, + transaction=None, + block_identifier='latest'): + self.web3 = web3 + self.address = address + self.abi = abi + self._functions = None + + if self.abi: + if transaction is None: + transaction = {} + + self._functions = filter_by_type('function', self.abi) + for func in self._functions: + fn = ContractFunction.factory( + func['name'], + web3=self.web3, + contract_abi=self.abi, + address=self.address, + function_identifier=func['name']) + + block_id = parse_block_identifier(self.web3, block_identifier) + caller_method = partial(self.call_function, + fn, + transaction=transaction, + block_identifier=block_id) + + setattr(self, func['name'], caller_method) + + def __getattr__(self, function_name): + if self.abi is None: + raise NoABIFound( + "There is no ABI found for this contract.", + ) + elif not self._functions or len(self._functions) == 0: + raise NoABIFunctionsFound( + "The ABI for this contract contains no function definitions. ", + "Are you sure you provided the correct contract ABI?" + ) + elif function_name not in self._functions: + functions_available = ', '.join([fn['name'] for fn in self._functions]) + raise MismatchedABI( + "The function '{}' was not found in this contract's ABI. ".format(function_name), + "Here is a list of all of the function names found: ", + "{}. ".format(functions_available), + "Did you mean to call one of those functions?" + ) + else: + return super().__getattribute__(function_name) + + def __call__(self, transaction=None, block_identifier='latest'): + if transaction is None: + transaction = {} + return type(self)(self.abi, + self.web3, + self.address, + transaction=transaction, + block_identifier=block_identifier) + + @staticmethod + def call_function(fn, *args, transaction=None, block_identifier='latest', **kwargs): + if transaction is None: + transaction = {} + return fn(*args, **kwargs).call(transaction, block_identifier) + + +def check_for_forbidden_api_filter_arguments(event_abi, _filters): + name_indexed_inputs = {_input['name']: _input for _input in event_abi['inputs']} + + for filter_name, filter_value in _filters.items(): + _input = name_indexed_inputs[filter_name] + if is_array_type(_input['type']): + raise TypeError( + "createFilter no longer supports array type filter arguments. " + "see the build_filter method for filtering array type filters.") + if is_list_like(filter_value) and is_dynamic_sized_type(_input['type']): + raise TypeError( + "createFilter no longer supports setting filter argument options for dynamic sized " + "types. See the build_filter method for setting filters with the match_any " + "method.") + + +def call_contract_function( + web3, + address, + normalizers, + function_identifier, + transaction, + block_id=None, + contract_abi=None, + fn_abi=None, + *args, + **kwargs): + """ + Helper function for interacting with a contract function using the + `vns_call` API. + """ + call_transaction = prepare_transaction( + address, + web3, + fn_identifier=function_identifier, + contract_abi=contract_abi, + fn_abi=fn_abi, + transaction=transaction, + fn_args=args, + fn_kwargs=kwargs, + ) + + if block_id is None: + return_data = web3.vns.call(call_transaction) + else: + return_data = web3.vns.call(call_transaction, block_identifier=block_id) + + if fn_abi is None: + fn_abi = find_matching_fn_abi(contract_abi, function_identifier, args, kwargs) + + output_types = get_abi_output_types(fn_abi) + + try: + output_data = decode_abi(output_types, return_data) + except DecodingError as e: + # Provide a more helpful error message than the one provided by + # vns-abi-utils + is_missing_code_error = ( + return_data in ACCEPTABLE_EMPTY_STRINGS and + web3.vns.getCode(address) in ACCEPTABLE_EMPTY_STRINGS + ) + if is_missing_code_error: + msg = ( + "Could not transact with/call contract function, is contract " + "deployed correctly and chain synced?" + ) + else: + msg = ( + "Could not decode contract function call {} return data {} for " + "output_types {}".format( + function_identifier, + return_data, + output_types + ) + ) + raise BadFunctionCallOutput(msg) from e + + _normalizers = itertools.chain( + BASE_RETURN_NORMALIZERS, + normalizers, + ) + normalized_data = map_abi_data(_normalizers, output_types, output_data) + + if len(normalized_data) == 1: + return normalized_data[0] + else: + return normalized_data + + +def parse_block_identifier(web3, block_identifier): + if isinstance(block_identifier, int): + return parse_block_identifier_int(web3, block_identifier) + elif block_identifier in ['latest', 'earliest', 'pending']: + return block_identifier + elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash(block_identifier): + return web3.vns.getBlock(block_identifier)['number'] + else: + raise BlockNumberOutofRange + + +def parse_block_identifier_int(web3, block_identifier_int): + if block_identifier_int >= 0: + block_num = block_identifier_int + else: + last_block = web3.vns.getBlock('latest')['number'] + block_num = last_block + block_identifier_int + 1 + if block_num < 0: + raise BlockNumberOutofRange + return block_num + + +def transact_with_contract_function( + address, + web3, + function_name=None, + transaction=None, + contract_abi=None, + fn_abi=None, + *args, + **kwargs): + """ + Helper function for interacting with a contract function by sending a + transaction. + """ + transact_transaction = prepare_transaction( + address, + web3, + fn_identifier=function_name, + contract_abi=contract_abi, + transaction=transaction, + fn_abi=fn_abi, + fn_args=args, + fn_kwargs=kwargs, + ) + + txn_hash = web3.vns.sendTransaction(transact_transaction) + return txn_hash + + +def estimate_gas_for_function( + address, + web3, + fn_identifier=None, + transaction=None, + contract_abi=None, + fn_abi=None, + *args, + **kwargs): + """Estimates gas cost a function call would take. + + Don't call this directly, instead use :meth:`Contract.estimateGas` + on your contract instance. + """ + estimate_transaction = prepare_transaction( + address, + web3, + fn_identifier=fn_identifier, + contract_abi=contract_abi, + fn_abi=fn_abi, + transaction=transaction, + fn_args=args, + fn_kwargs=kwargs, + ) + + gas_estimate = web3.vns.estimateGas(estimate_transaction) + return gas_estimate + + +def build_transaction_for_function( + address, + web3, + function_name=None, + transaction=None, + contract_abi=None, + fn_abi=None, + *args, + **kwargs): + """Builds a dictionary with the fields required to make the given transaction + + Don't call this directly, instead use :meth:`Contract.buildTransaction` + on your contract instance. + """ + prepared_transaction = prepare_transaction( + address, + web3, + fn_identifier=function_name, + contract_abi=contract_abi, + fn_abi=fn_abi, + transaction=transaction, + fn_args=args, + fn_kwargs=kwargs, + ) + + prepared_transaction = fill_transaction_defaults(web3, prepared_transaction) + + return prepared_transaction + + +def find_functions_by_identifier(contract_abi, web3, address, callable_check): + fns_abi = filter_by_type('function', contract_abi) + return [ + ContractFunction.factory( + fn_abi['name'], + web3=web3, + contract_abi=contract_abi, + address=address, + function_identifier=fn_abi['name'], + abi=fn_abi + ) + for fn_abi in fns_abi + if callable_check(fn_abi) + ] + + +def get_function_by_identifier(fns, identifier): + if len(fns) > 1: + raise ValueError( + 'Found multiple functions with matching {0}. ' + 'Found: {1!r}'.format(identifier, fns) + ) + elif len(fns) == 0: + raise ValueError( + 'Could not find any function with matching {0}'.format(identifier) + ) + return fns[0] diff --git a/web3/datastructures.py b/web3/datastructures.py index 8f1eb4696b..229f43f653 100644 --- a/web3/datastructures.py +++ b/web3/datastructures.py @@ -1,236 +1,215 @@ -from collections import ( - OrderedDict, -) -from collections.abc import ( - Hashable, -) -from typing import ( - Any, - Callable, - Dict, - Iterator, - List, - Mapping, - MutableMapping, - Sequence, - Type, - TypeVar, - Union, - cast, -) - -from eth_utils import ( - is_integer, -) - -from web3._utils.formatters import ( - recursive_map, -) - -# Hashable must be immutable: -# "the implementation of hashable collections requires that a key's hash value is immutable" -# https://docs.python.org/3/reference/datamodel.html#object.__hash__ - -T = TypeVar("T") -TKey = TypeVar("TKey", bound=Hashable) -TValue = TypeVar("TValue") - - -class ReadableAttributeDict(Mapping[TKey, TValue]): - """ - The read attributes for the AttributeDict types - """ - - def __init__(self, dictionary: Dict[TKey, TValue], *args: Any, **kwargs: Any) -> None: - # type ignored on 46/50 b/c dict() expects str index type not TKey - self.__dict__ = dict(dictionary) # type: ignore - self.__dict__.update(dict(*args, **kwargs)) - - def __getitem__(self, key: TKey) -> TValue: - return self.__dict__[key] # type: ignore - - def __iter__(self) -> Iterator[Any]: - return iter(self.__dict__) - - def __len__(self) -> int: - return len(self.__dict__) - - def __repr__(self) -> str: - return self.__class__.__name__ + "(%r)" % self.__dict__ - - def _repr_pretty_(self, builder: Any, cycle: bool) -> None: - """ - Custom pretty output for the IPython console - https://ipython.readthedocs.io/en/stable/api/generated/IPython.lib.pretty.html#extending - """ - builder.text(self.__class__.__name__ + "(") - if cycle: - builder.text("") - else: - builder.pretty(self.__dict__) - builder.text(")") - - @classmethod - def _apply_if_mapping(cls: Type[T], value: TValue) -> Union[T, TValue]: - if isinstance(value, Mapping): - # error: Too many arguments for "object" - return cls(value) # type: ignore - else: - return value - - @classmethod - def recursive(cls, value: TValue) -> 'ReadableAttributeDict[TKey, TValue]': - return recursive_map(cls._apply_if_mapping, value) - - -class MutableAttributeDict(MutableMapping[TKey, TValue], ReadableAttributeDict[TKey, TValue]): - - def __setitem__(self, key: Any, val: Any) -> None: - self.__dict__[key] = val - - def __delitem__(self, key: Any) -> None: - del self.__dict__[key] - - -class AttributeDict(ReadableAttributeDict[TKey, TValue], Hashable): - """ - This provides superficial immutability, someone could hack around it - """ - - def __setattr__(self, attr: str, val: TValue) -> None: - if attr == '__dict__': - super().__setattr__(attr, val) - else: - raise TypeError('This data is immutable -- create a copy instead of modifying') - - def __delattr__(self, key: str) -> None: - raise TypeError('This data is immutable -- create a copy instead of modifying') - - def __hash__(self) -> int: - return hash(tuple(sorted(self.items()))) - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Mapping): - return self.__dict__ == dict(other) - else: - return False - - -class NamedElementOnion(Mapping[TKey, TValue]): - """ - Add layers to an onion-shaped structure. Optionally, inject to a specific layer. - This structure is iterable, where the outermost layer is first, and innermost is last. - """ - - def __init__( - self, init_elements: Sequence[Any], valid_element: Callable[..., bool]=callable - ) -> None: - self._queue: 'OrderedDict[Any, Any]' = OrderedDict() - for element in reversed(init_elements): - if valid_element(element): - self.add(element) - else: - self.add(*element) - - def add(self, element: TValue, name: TKey=None) -> None: - if name is None: - name = cast(TKey, element) - - if name in self._queue: - if name is element: - raise ValueError("You can't add the same un-named instance twice") - else: - raise ValueError("You can't add the same name again, use replace instead") - - self._queue[name] = element - - def inject(self, element: TValue, name: TKey=None, layer: int=None) -> None: - """ - Inject a named element to an arbitrary layer in the onion. - - The current implementation only supports insertion at the innermost layer, - or at the outermost layer. Note that inserting to the outermost is equivalent - to calling :meth:`add` . - """ - if not is_integer(layer): - raise TypeError("The layer for insertion must be an int.") - elif layer != 0 and layer != len(self._queue): - raise NotImplementedError( - "You can only insert to the beginning or end of a %s, currently. " - "You tried to insert to %d, but only 0 and %d are permitted. " % ( - type(self), - layer, - len(self._queue), - ) - ) - - self.add(element, name=name) - - if layer == 0: - if name is None: - name = cast(TKey, element) - self._queue.move_to_end(name, last=False) - elif layer == len(self._queue): - return - else: - raise AssertionError("Impossible to reach: earlier validation raises an error") - - def clear(self) -> None: - self._queue.clear() - - def replace(self, old: TKey, new: TKey) -> TValue: - if old not in self._queue: - raise ValueError("You can't replace unless one already exists, use add instead") - to_be_replaced = self._queue[old] - if to_be_replaced is old: - # re-insert with new name in old slot - self._replace_with_new_name(old, new) - else: - self._queue[old] = new - return to_be_replaced - - def remove(self, old: TKey) -> None: - if old not in self._queue: - raise ValueError("You can only remove something that has been added") - del self._queue[old] - - def _replace_with_new_name(self, old: TKey, new: TKey) -> None: - self._queue[new] = new - found_old = False - for key in list(self._queue.keys()): - if not found_old: - if key == old: - found_old = True - continue - elif key != new: - self._queue.move_to_end(key) - del self._queue[old] - - def __iter__(self) -> Iterator[TKey]: - elements = self._queue.values() - if not isinstance(elements, Sequence): - # type ignored b/c elements is set as _OrderedDictValuesView[Any] on 210 - elements = list(elements) # type: ignore - return iter(reversed(elements)) - - def __add__(self, other: Any) -> 'NamedElementOnion[TKey, TValue]': - if not isinstance(other, NamedElementOnion): - raise NotImplementedError("You can only combine with another NamedElementOnion") - combined = self._queue.copy() - combined.update(other._queue) - return NamedElementOnion(cast(List[Any], combined.items())) - - def __contains__(self, element: Any) -> bool: - return element in self._queue - - def __getitem__(self, element: TKey) -> TValue: - return self._queue[element] - - def __len__(self) -> int: - return len(self._queue) - - def __reversed__(self) -> Iterator[TValue]: - elements = cast(List[Any], self._queue.values()) - if not isinstance(elements, Sequence): - elements = list(elements) - return iter(elements) +from collections import ( + OrderedDict, +) +from collections.abc import ( + Hashable, + Mapping, + MutableMapping, + Sequence, +) + +from vns_utils import ( + is_integer, +) + +from web3._utils.formatters import ( + recursive_map, +) + +# Hashable must be immutable: +# "the implementation of hashable collections requires that a key's hash value is immutable" +# https://docs.python.org/3/reference/datamodel.html#object.__hash__ + + +class ReadableAttributeDict(Mapping): + """ + The read attributes for the AttributeDict types + """ + + def __init__(self, dictionary, *args, **kwargs): + self.__dict__ = dict(dictionary) + self.__dict__.update(dict(*args, **kwargs)) + + def __getitem__(self, key): + return self.__dict__[key] + + def __iter__(self): + return iter(self.__dict__) + + def __len__(self): + return len(self.__dict__) + + def __repr__(self): + return self.__class__.__name__ + "(%r)" % self.__dict__ + + def _repr_pretty_(self, builder, cycle): + """ + Custom pretty output for the IPython console + """ + builder.text(self.__class__.__name__ + "(") + if cycle: + builder.text("") + else: + builder.pretty(self.__dict__) + builder.text(")") + + @classmethod + def _apply_if_mapping(cls, value): + if isinstance(value, Mapping): + return cls(value) + else: + return value + + @classmethod + def recursive(cls, value): + return recursive_map(cls._apply_if_mapping, value) + + +class MutableAttributeDict(MutableMapping, ReadableAttributeDict): + + def __setitem__(self, key, val): + self.__dict__[key] = val + + def __delitem__(self, key): + del self.__dict__[key] + + +class AttributeDict(ReadableAttributeDict, Hashable): + """ + This provides superficial immutability, someone could hack around it + """ + + def __setattr__(self, attr, val): + if attr == '__dict__': + super().__setattr__(attr, val) + else: + raise TypeError('This data is immutable -- create a copy instead of modifying') + + def __delattr__(self, key): + raise TypeError('This data is immutable -- create a copy instead of modifying') + + def __hash__(self): + return hash(tuple(sorted(self.items()))) + + def __eq__(self, other): + if isinstance(other, Mapping): + return self.__dict__ == dict(other) + else: + return False + + +class NamedElementOnion(Mapping): + """ + Add layers to an onion-shaped structure. Optionally, inject to a specific layer. + This structure is iterable, where the outermost layer is first, and innermost is last. + """ + + def __init__(self, init_elements, valid_element=callable): + self._queue = OrderedDict() + for element in reversed(init_elements): + if valid_element(element): + self.add(element) + else: + self.add(*element) + + def add(self, element, name=None): + if name is None: + name = element + + if name in self._queue: + if name is element: + raise ValueError("You can't add the same un-named instance twice") + else: + raise ValueError("You can't add the same name again, use replace instead") + + self._queue[name] = element + + def inject(self, element, name=None, layer=None): + """ + Inject a named element to an arbitrary layer in the onion. + + The current implementation only supports insertion at the innermost layer, + or at the outermost layer. Note that inserting to the outermost is equivalent + to calling :meth:`add` . + """ + if not is_integer(layer): + raise TypeError("The layer for insertion must be an int.") + elif layer != 0 and layer != len(self._queue): + raise NotImplementedError( + "You can only insert to the beginning or end of a %s, currently. " + "You tried to insert to %d, but only 0 and %d are permitted. " % ( + type(self), + layer, + len(self._queue), + ) + ) + + self.add(element, name=name) + + if layer == 0: + if name is None: + name = element + self._queue.move_to_end(name, last=False) + elif layer == len(self._queue): + return + else: + raise AssertionError("Impossible to reach: earlier validation raises an error") + + def clear(self): + self._queue.clear() + + def replace(self, old, new): + if old not in self._queue: + raise ValueError("You can't replace unless one already exists, use add instead") + to_be_replaced = self._queue[old] + if to_be_replaced is old: + # re-insert with new name in old slot + self._replace_with_new_name(old, new) + else: + self._queue[old] = new + return to_be_replaced + + def remove(self, old): + if old not in self._queue: + raise ValueError("You can only remove something that has been added") + del self._queue[old] + + def _replace_with_new_name(self, old, new): + self._queue[new] = new + found_old = False + for key in list(self._queue.keys()): + if not found_old: + if key == old: + found_old = True + continue + elif key != new: + self._queue.move_to_end(key) + del self._queue[old] + + def __iter__(self): + elements = self._queue.values() + if not isinstance(elements, Sequence): + elements = list(elements) + return iter(reversed(elements)) + + def __add__(self, other): + if not isinstance(other, NamedElementOnion): + raise NotImplementedError("You can only combine with another NamedElementOnion") + combined = self._queue.copy() + combined.update(other._queue) + return NamedElementOnion(combined.items()) + + def __contains__(self, element): + return element in self._queue + + def __getitem__(self, element): + return self._queue[element] + + def __len__(self): + return len(self._queue) + + def __reversed__(self): + elements = self._queue.values() + if not isinstance(elements, Sequence): + elements = list(elements) + return iter(elements) diff --git a/web3/exceptions.py b/web3/exceptions.py index 473ba281fe..5d8c36c248 100644 --- a/web3/exceptions.py +++ b/web3/exceptions.py @@ -1,171 +1,151 @@ -import datetime -import time - -from web3.types import ( - BlockData, -) - - -class BadFunctionCallOutput(Exception): - """ - We failed to decode ABI output. - - Most likely ABI mismatch. - """ - pass - - -class BlockNumberOutofRange(Exception): - """ - block_identifier passed does not match known block. - """ - pass - - -class CannotHandleRequest(Exception): - """ - Raised by a provider to signal that it cannot handle an RPC request and - that the manager should proceed to the next provider. - """ - pass - - -class InvalidAddress(ValueError): - """ - The supplied address does not have a valid checksum, as defined in EIP-55 - """ - pass - - -class NameNotFound(ValueError): - """ - Raised when a caller provides an Ethereum Name Service name that - does not resolve to an address. - """ - pass - - -class StaleBlockchain(Exception): - """ - Raised by the stalecheck_middleware when the latest block is too old. - """ - def __init__(self, block: BlockData, allowable_delay: int) -> None: - last_block_date = datetime.datetime.fromtimestamp(block.timestamp).strftime('%c') - message = ( - "The latest block, #%d, is %d seconds old, but is only allowed to be %d s old. " - "The date of the most recent block is %s. Continue syncing and try again..." % - (block.number, time.time() - block.timestamp, allowable_delay, last_block_date) - ) - super().__init__(message, block, allowable_delay) - - def __str__(self) -> str: - return self.args[0] - - -class MismatchedABI(Exception): - """ - Raised when an ABI does not match with supplied parameters, or when an - attempt is made to access a function/event that does not exist in the ABI. - """ - pass - - -class FallbackNotFound(Exception): - """ - Raised when fallback function doesn't exist in contract. - """ - pass - - -class ValidationError(Exception): - """ - Raised when a supplied value is invalid. - """ - pass - - -class NoABIFunctionsFound(AttributeError): - """ - Raised when an ABI is present, but doesn't contain any functions. - """ - pass - - -class NoABIFound(AttributeError): - """ - Raised when no ABI is present. - """ - pass - - -class NoABIEventsFound(AttributeError): - """ - Raised when an ABI doesn't contain any events. - """ - pass - - -class InsufficientData(Exception): - """ - Raised when there are insufficient data points to - complete a calculation - """ - pass - - -class TimeExhausted(Exception): - """ - Raised when a method has not retrieved the desired result within a specified timeout. - """ - pass - - -class PMError(Exception): - """ - Raised when an error occurs in the PM module. - """ - pass - - -class ManifestValidationError(PMError): - """ - Raised when a provided manifest cannot be published, since it's invalid. - """ - pass - - -class TransactionNotFound(Exception): - """ - Raised when a tx hash used to lookup a tx in a jsonrpc call cannot be found. - """ - pass - - -class BlockNotFound(Exception): - """ - Raised when the block id used to lookup a block in a jsonrpc call cannot be found. - """ - pass - - -class InfuraKeyNotFound(Exception): - """ - Raised when there is no Infura Project Id set. - """ - pass - - -class LogTopicError(ValueError): - # Inherits from ValueError for backwards compatibility - """ - Raised when the number of log topics is mismatched. - """ - pass - - -class InvalidEventABI(ValueError): - # Inherits from ValueError for backwards compatibility - """ - Raised when the event ABI is invalid. - """ - pass +import datetime +import time + + +class BadFunctionCallOutput(Exception): + """ + We failed to decode ABI output. + + Most likely ABI mismatch. + """ + pass + + +class BlockNumberOutofRange(Exception): + """ + block_identifier passed does not match known block. + """ + pass + + +class CannotHandleRequest(Exception): + """ + Raised by a provider to signal that it cannot handle an RPC request and + that the manager should proceed to the next provider. + """ + pass + + +class InvalidAddress(ValueError): + """ + The supplied address does not have a valid checksum, as defined in EIP-55 + """ + pass + + +class NameNotFound(ValueError): + """ + Raised when a caller provides an Ethereum Name Service name that + does not resolve to an address. + """ + pass + + +class StaleBlockchain(Exception): + """ + Raised by the stalecheck_middleware when the latest block is too old. + """ + def __init__(self, block, allowable_delay): + last_block_date = datetime.datetime.fromtimestamp(block.timestamp).strftime('%c') + message = ( + "The latest block, #%d, is %d seconds old, but is only allowed to be %d s old. " + "The date of the most recent block is %s. Continue syncing and try again..." % + (block.number, time.time() - block.timestamp, allowable_delay, last_block_date) + ) + super().__init__(message, block, allowable_delay) + + def __str__(self): + return self.args[0] + + +class MismatchedABI(Exception): + """ + Raised when an ABI does not match with supplied parameters, or when an + attempt is made to access a function/event that does not exist in the ABI. + """ + pass + + +class FallbackNotFound(Exception): + """ + Raised when fallback function doesn't exist in contract. + """ + pass + + +class ValidationError(Exception): + """ + Raised when a supplied value is invalid. + """ + pass + + +class NoABIFunctionsFound(AttributeError): + """ + Raised when an ABI is present, but doesn't contain any functions. + """ + pass + + +class NoABIFound(AttributeError): + """ + Raised when no ABI is present. + """ + pass + + +class NoABIEventsFound(AttributeError): + """ + Raised when an ABI doesn't contain any events. + """ + pass + + +class InsufficientData(Exception): + """ + Raised when there are insufficient data points to + complete a calculation + """ + pass + + +class TimeExhausted(Exception): + """ + Raised when a method has not retrieved the desired result within a specified timeout. + """ + pass + + +class PMError(Exception): + """ + Raised when an error occurs in the PM module. + """ + pass + + +class ManifestValidationError(PMError): + """ + Raised when a provided manifest cannot be published, since it's invalid. + """ + pass + + +class TransactionNotFound(Exception): + """ + Raised when a tx hash used to lookup a tx in a jsonrpc call cannot be found. + """ + pass + + +class BlockNotFound(Exception): + """ + Raised when the block id used to lookup a block in a jsonrpc call cannot be found. + """ + pass + + +class InfuraKeyNotFound(Exception): + """ + Raised when there is no Infura Project Id set. + """ + pass diff --git a/web3/gas_strategies/rpc.py b/web3/gas_strategies/rpc.py index 2b7bd56b4d..ad2ac30a13 100644 --- a/web3/gas_strategies/rpc.py +++ b/web3/gas_strategies/rpc.py @@ -1,11 +1,5 @@ -from web3 import Web3 -from web3.types import ( - TxParams, -) - - -def rpc_gas_price_strategy(web3: Web3, transaction_params: TxParams=None) -> int: - """ - A simple gas price strategy deriving it's value from the eth_gasPrice JSON-RPC call. - """ - return web3.manager.request_blocking("eth_gasPrice", []) +def rpc_gas_price_strategy(web3, transaction_params=None): + """ + A simple gas price strategy deriving it's value from the vns_gasPrice JSON-RPC call. + """ + return web3.manager.request_blocking("vns_gasPrice", []) diff --git a/web3/gas_strategies/time_based.py b/web3/gas_strategies/time_based.py index 622a9e32ae..7c86da3109 100644 --- a/web3/gas_strategies/time_based.py +++ b/web3/gas_strategies/time_based.py @@ -1,213 +1,192 @@ -import collections -import math -import operator -from typing import ( - Iterable, - Sequence, - Tuple, -) - -from eth_typing import ( - ChecksumAddress, - Hash32, -) -from eth_utils import ( - to_tuple, -) -from eth_utils.toolz import ( - curry, - groupby, - sliding_window, -) - -from web3 import Web3 -from web3._utils.math import ( - percentile, -) -from web3.exceptions import ( - InsufficientData, - ValidationError, -) -from web3.types import ( - GasPriceStrategy, - TxParams, - Wei, -) - -MinerData = collections.namedtuple( - 'MinerData', - ['miner', 'num_blocks', 'min_gas_price', 'low_percentile_gas_price']) - -Probability = collections.namedtuple('Probability', ['gas_price', 'prob']) - - -def _get_avg_block_time(w3: Web3, sample_size: int) -> int: - latest = w3.eth.getBlock('latest') - - constrained_sample_size = min(sample_size, latest['number']) - if constrained_sample_size == 0: - raise ValidationError('Constrained sample size is 0') - - oldest = w3.eth.getBlock(latest['number'] - constrained_sample_size) - return (latest['timestamp'] - oldest['timestamp']) / constrained_sample_size - - -def _get_raw_miner_data( - w3: Web3, sample_size: int -) -> Iterable[Tuple[ChecksumAddress, Hash32, Wei]]: - latest = w3.eth.getBlock('latest', full_transactions=True) - - for transaction in latest['transactions']: - yield (latest['miner'], latest['hash'], transaction['gasPrice']) - - block = latest - - for _ in range(sample_size - 1): - if block['number'] == 0: - break - - # we intentionally trace backwards using parent hashes rather than - # block numbers to make caching the data easier to implement. - block = w3.eth.getBlock(block['parentHash'], full_transactions=True) - for transaction in block['transactions']: - yield (block['miner'], block['hash'], transaction['gasPrice']) - - -def _aggregate_miner_data( - raw_data: Iterable[Tuple[ChecksumAddress, Hash32, Wei]] -) -> Iterable[MinerData]: - data_by_miner = groupby(0, raw_data) - - for miner, miner_data in data_by_miner.items(): - _, block_hashes, gas_prices = map(set, zip(*miner_data)) - try: - price_percentile = percentile(gas_prices, percentile=20) - except InsufficientData: - price_percentile = min(gas_prices) - yield MinerData( - miner, - len(set(block_hashes)), - min(gas_prices), - price_percentile) - - -@to_tuple -def _compute_probabilities( - miner_data: Iterable[MinerData], wait_blocks: int, sample_size: int -) -> Iterable[Probability]: - """ - Computes the probabilities that a txn will be accepted at each of the gas - prices accepted by the miners. - """ - miner_data_by_price = tuple(sorted( - miner_data, - key=operator.attrgetter('low_percentile_gas_price'), - reverse=True, - )) - for idx in range(len(miner_data_by_price)): - low_percentile_gas_price = miner_data_by_price[idx].low_percentile_gas_price - num_blocks_accepting_price = sum(m.num_blocks for m in miner_data_by_price[idx:]) - inv_prob_per_block = (sample_size - num_blocks_accepting_price) / sample_size - probability_accepted = 1 - inv_prob_per_block ** wait_blocks - yield Probability(low_percentile_gas_price, probability_accepted) - - -def _compute_gas_price(probabilities: Sequence[Probability], desired_probability: float) -> Wei: - """ - Given a sorted range of ``Probability`` named-tuples returns a gas price - computed based on where the ``desired_probability`` would fall within the - range. - - :param probabilities: An iterable of `Probability` named-tuples sorted in reverse order. - :param desired_probability: An floating point representation of the desired - probability. (e.g. ``85% -> 0.85``) - """ - first = probabilities[0] - last = probabilities[-1] - - if desired_probability >= first.prob: - return Wei(int(first.gas_price)) - elif desired_probability <= last.prob: - return Wei(int(last.gas_price)) - - for left, right in sliding_window(2, probabilities): - if desired_probability < right.prob: - continue - elif desired_probability > left.prob: - # This code block should never be reachable as it would indicate - # that we already passed by the probability window in which our - # `desired_probability` is located. - raise Exception('Invariant') - - adj_prob = desired_probability - right.prob - window_size = left.prob - right.prob - position = adj_prob / window_size - gas_window_size = left.gas_price - right.gas_price - gas_price = int(math.ceil(right.gas_price + gas_window_size * position)) - return Wei(gas_price) - else: - # The initial `if/else` clause in this function handles the case where - # the `desired_probability` is either above or below the min/max - # probability found in the `probabilities`. - # - # With these two cases handled, the only way this code block should be - # reachable would be if the `probabilities` were not sorted correctly. - # Otherwise, the `desired_probability` **must** fall between two of the - # values in the `probabilities``. - raise Exception('Invariant') - - -@curry -def construct_time_based_gas_price_strategy( - max_wait_seconds: int, sample_size: int=120, probability: int=98 -) -> GasPriceStrategy: - """ - A gas pricing strategy that uses recently mined block data to derive a gas - price for which a transaction is likely to be mined within X seconds with - probability P. - - :param max_wait_seconds: The desired maxiumum number of seconds the - transaction should take to mine. - :param sample_size: The number of recent blocks to sample - :param probability: An integer representation of the desired probability - that the transaction will be mined within ``max_wait_seconds``. 0 means 0% - and 100 means 100%. - """ - def time_based_gas_price_strategy(web3: Web3, transaction_params: TxParams) -> Wei: - avg_block_time = _get_avg_block_time(web3, sample_size=sample_size) - wait_blocks = int(math.ceil(max_wait_seconds / avg_block_time)) - - raw_miner_data = _get_raw_miner_data(web3, sample_size=sample_size) - miner_data = _aggregate_miner_data(raw_miner_data) - - probabilities = _compute_probabilities( - miner_data, - wait_blocks=wait_blocks, - sample_size=sample_size, - ) - - gas_price = _compute_gas_price(probabilities, probability / 100) - return gas_price - return time_based_gas_price_strategy - - -# fast: mine within 1 minute -fast_gas_price_strategy = construct_time_based_gas_price_strategy( - max_wait_seconds=60, - sample_size=120, -) -# medium: mine within 10 minutes -medium_gas_price_strategy = construct_time_based_gas_price_strategy( - max_wait_seconds=600, - sample_size=120, -) -# slow: mine within 1 hour (60 minutes) -slow_gas_price_strategy = construct_time_based_gas_price_strategy( - max_wait_seconds=60 * 60, - sample_size=120, -) -# glacial: mine within the next 24 hours. -glacial_gas_price_strategy = construct_time_based_gas_price_strategy( - max_wait_seconds=24 * 60 * 60, - sample_size=720, -) +import collections +import math +import operator + +from vns_utils import ( + to_tuple, +) + +from web3._utils.math import ( + percentile, +) +from web3._utils.toolz import ( + curry, + groupby, + sliding_window, +) +from web3.exceptions import ( + InsufficientData, + ValidationError, +) + +MinerData = collections.namedtuple( + 'MinerData', + ['miner', 'num_blocks', 'min_gas_price', 'low_percentile_gas_price']) + +Probability = collections.namedtuple('Probability', ['gas_price', 'prob']) + + +def _get_avg_block_time(w3, sample_size): + latest = w3.vns.getBlock('latest') + + constrained_sample_size = min(sample_size, latest['number']) + if constrained_sample_size == 0: + raise ValidationError('Constrained sample size is 0') + + oldest = w3.vns.getBlock(latest['number'] - constrained_sample_size) + return (latest['timestamp'] - oldest['timestamp']) / constrained_sample_size + + +def _get_raw_miner_data(w3, sample_size): + latest = w3.vns.getBlock('latest', full_transactions=True) + + for transaction in latest['transactions']: + yield (latest['miner'], latest['hash'], transaction['gasPrice']) + + block = latest + + for _ in range(sample_size - 1): + if block['number'] == 0: + break + + # we intentionally trace backwards using parent hashes rather than + # block numbers to make caching the data easier to implement. + block = w3.vns.getBlock(block['parentHash'], full_transactions=True) + for transaction in block['transactions']: + yield (block['miner'], block['hash'], transaction['gasPrice']) + + +def _aggregate_miner_data(raw_data): + data_by_miner = groupby(0, raw_data) + + for miner, miner_data in data_by_miner.items(): + _, block_hashes, gas_prices = map(set, zip(*miner_data)) + try: + price_percentile = percentile(gas_prices, percentile=20) + except InsufficientData: + price_percentile = min(gas_prices) + yield MinerData( + miner, + len(set(block_hashes)), + min(gas_prices), + price_percentile) + + +@to_tuple +def _compute_probabilities(miner_data, wait_blocks, sample_size): + """ + Computes the probabilities that a txn will be accepted at each of the gas + prices accepted by the miners. + """ + miner_data_by_price = tuple(sorted( + miner_data, + key=operator.attrgetter('low_percentile_gas_price'), + reverse=True, + )) + for idx in range(len(miner_data_by_price)): + low_percentile_gas_price = miner_data_by_price[idx].low_percentile_gas_price + num_blocks_accepting_price = sum(m.num_blocks for m in miner_data_by_price[idx:]) + inv_prob_per_block = (sample_size - num_blocks_accepting_price) / sample_size + probability_accepted = 1 - inv_prob_per_block ** wait_blocks + yield Probability(low_percentile_gas_price, probability_accepted) + + +def _compute_gas_price(probabilities, desired_probability): + """ + Given a sorted range of ``Probability`` named-tuples returns a gas price + computed based on where the ``desired_probability`` would fall within the + range. + + :param probabilities: An iterable of `Probability` named-tuples sorted in reverse order. + :param desired_probability: An floating point representation of the desired + probability. (e.g. ``85% -> 0.85``) + """ + first = probabilities[0] + last = probabilities[-1] + + if desired_probability >= first.prob: + return int(first.gas_price) + elif desired_probability <= last.prob: + return int(last.gas_price) + + for left, right in sliding_window(2, probabilities): + if desired_probability < right.prob: + continue + elif desired_probability > left.prob: + # This code block should never be reachable as it would indicate + # that we already passed by the probability window in which our + # `desired_probability` is located. + raise Exception('Invariant') + + adj_prob = desired_probability - right.prob + window_size = left.prob - right.prob + position = adj_prob / window_size + gas_window_size = left.gas_price - right.gas_price + gas_price = int(math.ceil(right.gas_price + gas_window_size * position)) + return gas_price + else: + # The initial `if/else` clause in this function handles the case where + # the `desired_probability` is either above or below the min/max + # probability found in the `probabilities`. + # + # With these two cases handled, the only way this code block should be + # reachable would be if the `probabilities` were not sorted correctly. + # Otherwise, the `desired_probability` **must** fall between two of the + # values in the `probabilities``. + raise Exception('Invariant') + + +@curry +def construct_time_based_gas_price_strategy(max_wait_seconds, + sample_size=120, + probability=98): + """ + A gas pricing strategy that uses recently mined block data to derive a gas + price for which a transaction is likely to be mined within X seconds with + probability P. + + :param max_wait_seconds: The desired maxiumum number of seconds the + transaction should take to mine. + :param sample_size: The number of recent blocks to sample + :param probability: An integer representation of the desired probability + that the transaction will be mined within ``max_wait_seconds``. 0 means 0% + and 100 means 100%. + """ + def time_based_gas_price_strategy(web3, transaction_params): + avg_block_time = _get_avg_block_time(web3, sample_size=sample_size) + wait_blocks = int(math.ceil(max_wait_seconds / avg_block_time)) + + raw_miner_data = _get_raw_miner_data(web3, sample_size=sample_size) + miner_data = _aggregate_miner_data(raw_miner_data) + + probabilities = _compute_probabilities( + miner_data, + wait_blocks=wait_blocks, + sample_size=sample_size, + ) + + gas_price = _compute_gas_price(probabilities, probability / 100) + return gas_price + return time_based_gas_price_strategy + + +# fast: mine within 1 minute +fast_gas_price_strategy = construct_time_based_gas_price_strategy( + max_wait_seconds=60, + sample_size=120, +) +# medium: mine within 10 minutes +medium_gas_price_strategy = construct_time_based_gas_price_strategy( + max_wait_seconds=600, + sample_size=120, +) +# slow: mine within 1 hour (60 minutes) +slow_gas_price_strategy = construct_time_based_gas_price_strategy( + max_wait_seconds=60 * 60, + sample_size=120, +) +# glacial: mine within the next 24 hours. +glacial_gas_price_strategy = construct_time_based_gas_price_strategy( + max_wait_seconds=24 * 60 * 60, + sample_size=720, +) diff --git a/web3/geth.py b/web3/geth.py index 1e6cd24b44..cbec42141e 100644 --- a/web3/geth.py +++ b/web3/geth.py @@ -1,161 +1,128 @@ -from web3._utils import ( - shh, -) -from web3._utils.admin import ( - add_peer, - addPeer, - datadir, - node_info, - nodeInfo, - peers, - start_rpc, - start_ws, - startRPC, - startWS, - stop_rpc, - stop_ws, - stopRPC, - stopWS, -) -from web3._utils.miner import ( - makeDag, - setEtherbase, - setExtra, - setGasPrice, - start, - startAutoDag, - stop, - stopAutoDag, -) -from web3._utils.personal import ( - ecRecover, - importRawKey, - listAccounts, - lockAccount, - newAccount, - sendTransaction, - sign, - signTypedData, - unlockAccount, -) -from web3._utils.txpool import ( - content, - inspect, - status, -) -from web3.module import ( - Module, - ModuleV2, -) - - -class Geth(Module): - pass - - -class GethPersonal(ModuleV2): - """ - https://github.com/ethereum/go-ethereum/wiki/management-apis#personal - """ - ecRecover = ecRecover - importRawKey = importRawKey - listAccounts = listAccounts - lockAccount = lockAccount - newAccount = newAccount - sendTransaction = sendTransaction - sign = sign - signTypedData = signTypedData - unlockAccount = unlockAccount - - -class GethTxPool(ModuleV2): - """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#txpool - """ - content = content - inspect = inspect - status = status - - -class GethAdmin(ModuleV2): - """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#admin - """ - add_peer = add_peer - node_info = node_info - start_rpc = start_rpc - start_ws = start_ws - stop_ws = stop_ws - stop_rpc = stop_rpc - # deprecated - addPeer = addPeer - datadir = datadir - nodeInfo = nodeInfo - peers = peers - startRPC = startRPC - startWS = startWS - stopRPC = stopRPC - stopWS = stopWS - - -class GethMiner(ModuleV2): - """ - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#miner - """ - makeDag = makeDag - setExtra = setExtra - setEtherbase = setEtherbase - setGasPrice = setGasPrice - start = start - stop = stop - startAutoDag = startAutoDag - stopAutoDag = stopAutoDag - - -class GethShh(ModuleV2): - """ - https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API - """ - add_private_key = shh.add_private_key - add_sym_key = shh.add_sym_key - delete_key = shh.delete_key - delete_key_pair = shh.delete_key_pair - delete_message_filter = shh.delete_message_filter - delete_sym_key = shh.delete_sym_key - generate_sym_key_from_password = shh.generate_sym_key_from_password - get_filter_messages = shh.get_filter_messages - get_private_key = shh.get_private_key - get_public_key = shh.get_public_key - get_sym_key = shh.get_sym_key - has_key_pair = shh.has_key_pair - has_sym_key = shh.has_sym_key - info = shh.info - mark_trusted_peer = shh.mark_trusted_peer - new_key_pair = shh.new_key_pair - new_message_filter = shh.new_message_filter - new_sym_key = shh.new_sym_key - post = shh.post - set_max_message_size = shh.set_max_message_size - set_min_pow = shh.set_min_pow - subscribe = shh.subscribe - unsubscribe = shh.unsubscribe - version = shh.version - # Deprecated - addPrivateKey = shh.addPrivateKey - addSymKey = shh.addSymKey - deleteKeyPair = shh.deleteKeyPair - deleteMessageFilter = shh.deleteMessageFilter - deleteSymKey = shh.deleteSymKey - generateSymKeyFromPassword = shh.generateSymKeyFromPassword - getMessages = shh.getFilterMessages - getPrivateKey = shh.getPrivateKey - getPublicKey = shh.getPublicKey - getSymKey = shh.getSymKey - hasKeyPair = shh.hasKeyPair - hasSymKey = shh.hasSymKey - markTrustedPeer = shh.markTrustedPeer - newKeyPair = shh.newKeyPair - newMessageFilter = shh.newMessageFilter - newSymKey = shh.newSymKey - setMaxMessageSize = shh.setMaxMessageSize - setMinPoW = shh.setMinPoW +from web3._utils import ( + shh, +) +from web3._utils.admin import ( + addPeer, + datadir, + nodeInfo, + peers, + setSolc, + startRPC, + startWS, + stopRPC, + stopWS, +) +from web3._utils.miner import ( + makeDag, + setEtherbase, + setExtra, + setGasPrice, + start, + startAutoDag, + stop, + stopAutoDag, +) +from web3._utils.personal import ( + ecRecover, + importRawKey, + listAccounts, + lockAccount, + newAccount, + sendTransaction, + sign, + unlockAccount, +) +from web3._utils.txpool import ( + content, + inspect, + status, +) +from web3.module import ( + Module, + ModuleV2, +) + + +class Geth(Module): + pass + + +class GethPersonal(ModuleV2): + """ + https://github.com/ethereum/go-ethereum/wiki/management-apis#personal + """ + ecRecover = ecRecover + importRawKey = importRawKey + listAccounts = listAccounts + lockAccount = lockAccount + newAccount = newAccount + sendTransaction = sendTransaction + sign = sign + unlockAccount = unlockAccount + + +class GethTxPool(ModuleV2): + """ + https://github.com/ethereum/go-ethereum/wiki/Management-APIs#txpool + """ + content = content + inspect = inspect + status = status + + +class GethAdmin(ModuleV2): + """ + https://github.com/ethereum/go-ethereum/wiki/Management-APIs#admin + """ + addPeer = addPeer + datadir = datadir + nodeInfo = nodeInfo + peers = peers + setSolc = setSolc + startRPC = startRPC + startWS = startWS + stopRPC = stopRPC + stopWS = stopWS + + +class GethMiner(ModuleV2): + """ + https://github.com/ethereum/go-ethereum/wiki/Management-APIs#miner + """ + makeDag = makeDag + setExtra = setExtra + setEtherbase = setEtherbase + setGasPrice = setGasPrice + start = start + stop = stop + startAutoDag = startAutoDag + stopAutoDag = stopAutoDag + + +class GethShh(ModuleV2): + """ + https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API + """ + version = shh.version + info = shh.info + setMaxMessageSize = shh.setMaxMessageSize + setMinPoW = shh.setMinPoW + markTrustedPeer = shh.markTrustedPeer + newKeyPair = shh.newKeyPair + addPrivateKey = shh.addPrivateKey + deleteKeyPair = shh.deleteKeyPair + hasKeyPair = shh.hasKeyPair + getPublicKey = shh.getPublicKey + getPrivateKey = shh.getPrivateKey + newSymKey = shh.newSymKey + addSymKey = shh.addSymKey + generateSymKeyFromPassword = shh.generateSymKeyFromPassword + hasSymKey = shh.hasSymKey + getSymKey = shh.getSymKey + deleteSymKey = shh.deleteSymKey + post = shh.post + newMessageFilter = shh.newMessageFilter + deleteMessageFilter = shh.deleteMessageFilter + getMessages = shh.getFilterMessages + subscribe = shh.subscribe + unsubscribe = shh.unsubscribe diff --git a/web3/iban.py b/web3/iban.py index 23650c3cec..86414ae4bf 100644 --- a/web3/iban.py +++ b/web3/iban.py @@ -1,239 +1,222 @@ -import functools -import re -from typing import ( - Any, - Callable, - Union, -) - -from eth_typing import ( - Address, - ChecksumAddress, -) -from eth_utils import ( - is_string, - to_checksum_address, -) -from mypy_extensions import ( - TypedDict, -) - -from web3._utils.validation import ( - validate_address, -) - -IbanOptions = TypedDict("IbanOptions", { - "institution": str, - "identifier": str, -}) - - -def pad_left_hex(value: str, num_bytes: int) -> str: - return value.rjust(num_bytes * 2, '0') - - -def iso13616Prepare(iban: str) -> str: - """ - Prepare an IBAN for mod 97 computation by moving the first - 4 chars to the end and transforming the letters to numbers - (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. - - @method iso13616Prepare - @param {String} iban the IBAN - @returns {String} the prepared IBAN - """ - A = ord("A") - Z = ord("Z") - - iban = iban.upper() - iban = iban[4:] + iban[:4] - - def charfunc(n: str) -> str: - code = ord(n) - if code >= A and code <= Z: - return str(code - A + 10) - else: - return str(n) - - return "".join(map(charfunc, list(iban))) - - -def mod9710(iban: str) -> int: - """ - Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. - - @method mod9710 - @param {String} iban - @returns {Number} - """ - remainder = iban - block = None - - while len(remainder) > 2: - block = remainder[:9] - remainder = str(int(block) % 97) + remainder[len(block):] - - return int(remainder) % 97 - - -def baseN(num: int, b: int, numerals: str="0123456789abcdefghijklmnopqrstuvwxyz") -> str: - """ - This prototype should be used to create - an iban object from iban correct string - - @param {String} iban - """ - return ((num == 0) and numerals[0]) or \ - (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b]) - - -class IsValid: - """ - Should be called to check if iban is correct - - Note: This is implemented as a descriptor so that it can be called as - either an instance method. - - @method isValid - @returns {Boolean} true if it is, otherwise false - """ - def __get__(self, instance: 'Iban', owner: str) -> Callable[[str], bool]: - if instance is None: - return self.validate - return functools.partial(self.validate, instance._iban) - - @staticmethod - def validate(iban_address: Any) -> bool: - if not is_string(iban_address): - return False - - if re.match(r"^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$", iban_address) and \ - mod9710(iso13616Prepare(iban_address)) == 1: - return True - - return False - - -class Iban: - def __init__(self, iban: str) -> None: - self._iban = iban - - @staticmethod - def fromAddress(address: Union[Address, ChecksumAddress]) -> "Iban": - """ - This method should be used to create - an iban object from ethereum address - - @method fromAddress - @param {String} address - @return {Iban} the IBAN object - """ - validate_address(address) - address_as_integer = int(address, 16) - address_as_base36 = baseN(address_as_integer, 36) - padded = pad_left_hex(address_as_base36, 15) - return Iban.fromBban(padded.upper()) - - @staticmethod - def fromBban(bban: str) -> "Iban": - """ - Convert the passed BBAN to an IBAN for this country specification. - Please note that "generation of the IBAN shall be the exclusive - responsibility of the bank/branch servicing the account". - This method implements the preferred algorithm described in - http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits - - @method fromBban - @param {String} bban the BBAN to convert to IBAN - @returns {Iban} the IBAN object - """ - countryCode = "XE" - - remainder = mod9710(iso13616Prepare(countryCode + "00" + bban)) - checkDigit = ("0" + str(98 - remainder))[-2:] - - return Iban(countryCode + checkDigit + bban) - - @staticmethod - def createIndirect(options: IbanOptions) -> "Iban": - """ - Should be used to create IBAN object for given institution and identifier - - @method createIndirect - @param {Object} options, required options are "institution" and "identifier" - @return {Iban} the IBAN object - """ - return Iban.fromBban("ETH" + options["institution"] + options["identifier"]) - - isValid = IsValid() - - def isDirect(self) -> bool: - """ - Should be called to check if iban number is direct - - @method isDirect - @returns {Boolean} true if it is, otherwise false - """ - return len(self._iban) in [34, 35] - - def isIndirect(self) -> bool: - """ - Should be called to check if iban number if indirect - - @method isIndirect - @returns {Boolean} true if it is, otherwise false - """ - return len(self._iban) == 20 - - def checksum(self) -> str: - """ - Should be called to get iban checksum - Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) - - @method checksum - @returns {String} checksum - """ - return self._iban[2:4] - - def institution(self) -> str: - """ - Should be called to get institution identifier - eg. XREG - - @method institution - @returns {String} institution identifier - """ - if self.isIndirect(): - return self._iban[7:11] - else: - return "" - - def client(self) -> str: - """ - Should be called to get client identifier within institution - eg. GAVOFYORK - - @method client - @returns {String} client identifier - """ - if self.isIndirect(): - return self._iban[11:] - else: - return "" - - def address(self) -> Union[str, ChecksumAddress]: - """ - Should be called to get client direct address - - @method address - @returns {String} client direct address - """ - if self.isDirect(): - base36 = self._iban[4:] - asInt = int(base36, 36) - return to_checksum_address(pad_left_hex(baseN(asInt, 16), 20)) - - return "" - - def toString(self) -> str: - return self._iban +import functools +import re + +from vns_utils import ( + is_string, + to_checksum_address, +) + +from web3._utils.validation import ( + validate_address, +) + + +def pad_left_hex(value, num_bytes): + return value.rjust(num_bytes * 2, '0') + + +def iso13616Prepare(iban): + """ + Prepare an IBAN for mod 97 computation by moving the first + 4 chars to the end and transforming the letters to numbers + (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. + + @method iso13616Prepare + @param {String} iban the IBAN + @returns {String} the prepared IBAN + """ + A = ord("A") + Z = ord("Z") + + iban = iban.upper() + iban = iban[4:] + iban[:4] + + def charfunc(n): + code = ord(n) + if code >= A and code <= Z: + return str(code - A + 10) + else: + return str(n) + + return "".join(map(charfunc, list(iban))) + + +def mod9710(iban): + """ + Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. + + @method mod9710 + @param {String} iban + @returns {Number} + """ + remainder = iban + block = None + + while len(remainder) > 2: + block = remainder[:9] + remainder = str(int(block) % 97) + remainder[len(block):] + + return int(remainder) % 97 + + +def baseN(num, b, numerals="0123456789abcdefghijklmnopqrstuvwxyz"): + """ + This prototype should be used to create + an iban object from iban correct string + + @param {String} iban + """ + return ((num == 0) and numerals[0]) or \ + (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b]) + + +class IsValid: + """ + Should be called to check if iban is correct + + Note: This is implemented as a descriptor so that it can be called as + either an instance method. + + @method isValid + @returns {Boolean} true if it is, otherwise false + """ + def __get__(self, instance, owner): + if instance is None: + return self.validate + return functools.partial(self.validate, instance._iban) + + @staticmethod + def validate(iban_address): + if not is_string(iban_address): + return False + + if re.match(r"^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$", iban_address) and \ + mod9710(iso13616Prepare(iban_address)) == 1: + return True + + return False + + +class Iban: + def __init__(self, iban): + self._iban = iban + + @staticmethod + def fromAddress(address): + """ + This method should be used to create + an iban object from ethereum address + + @method fromAddress + @param {String} address + @return {Iban} the IBAN object + """ + validate_address(address) + address_as_integer = int(address, 16) + address_as_base36 = baseN(address_as_integer, 36) + padded = pad_left_hex(address_as_base36, 15) + return Iban.fromBban(padded.upper()) + + @staticmethod + def fromBban(bban): + """ + Convert the passed BBAN to an IBAN for this country specification. + Please note that "generation of the IBAN shall be the exclusive + responsibility of the bank/branch servicing the account". + This method implements the preferred algorithm described in + http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits + + @method fromBban + @param {String} bban the BBAN to convert to IBAN + @returns {Iban} the IBAN object + """ + countryCode = "XE" + + remainder = mod9710(iso13616Prepare(countryCode + "00" + bban)) + checkDigit = ("0" + str(98 - remainder))[-2:] + + return Iban(countryCode + checkDigit + bban) + + @staticmethod + def createIndirect(options): + """ + Should be used to create IBAN object for given institution and identifier + + @method createIndirect + @param {Object} options, required options are "institution" and "identifier" + @return {Iban} the IBAN object + """ + return Iban.fromBban("ETH" + options["institution"] + options["identifier"]) + + isValid = IsValid() + + def isDirect(self): + """ + Should be called to check if iban number is direct + + @method isDirect + @returns {Boolean} true if it is, otherwise false + """ + return len(self._iban) in [34, 35] + + def isIndirect(self): + """ + Should be called to check if iban number if indirect + + @method isIndirect + @returns {Boolean} true if it is, otherwise false + """ + return len(self._iban) == 20 + + def checksum(self): + """ + Should be called to get iban checksum + Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) + + @method checksum + @returns {String} checksum + """ + return self._iban[2:4] + + def institution(self): + """ + Should be called to get institution identifier + eg. XREG + + @method institution + @returns {String} institution identifier + """ + if self.isIndirect(): + return self._iban[7:11] + else: + return "" + + def client(self): + """ + Should be called to get client identifier within institution + eg. GAVOFYORK + + @method client + @returns {String} client identifier + """ + if self.isIndirect(): + return self._iban[11:] + else: + return "" + + def address(self): + """ + Should be called to get client direct address + + @method address + @returns {String} client direct address + """ + if self.isDirect(): + base36 = self._iban[4:] + asInt = int(base36, 36) + return to_checksum_address(pad_left_hex(baseN(asInt, 16), 20)) + + return "" + + def toString(self): + return self._iban diff --git a/web3/logs.py b/web3/logs.py deleted file mode 100644 index 9a322c92bb..0000000000 --- a/web3/logs.py +++ /dev/null @@ -1,8 +0,0 @@ -from web3._utils.events import ( - EventLogErrorFlags, -) - -DISCARD = EventLogErrorFlags.Discard -IGNORE = EventLogErrorFlags.Ignore -STRICT = EventLogErrorFlags.Strict -WARN = EventLogErrorFlags.Warn diff --git a/web3/main.py b/web3/main.py index 5146272e18..65449174cf 100644 --- a/web3/main.py +++ b/web3/main.py @@ -1,284 +1,245 @@ -from eth_abi.codec import ( - ABICodec, -) -from eth_utils import ( - add_0x_prefix, - apply_to_return_value, - from_wei, - is_address, - is_checksum_address, - keccak as eth_utils_keccak, - remove_0x_prefix, - to_checksum_address, - to_wei, -) -from hexbytes import ( - HexBytes, -) -from typing import Any, cast, Dict, List, Sequence, TYPE_CHECKING - -from eth_typing import HexStr, Primitives -from eth_typing.abi import TypeStr - -from ens import ENS -from web3._utils.abi import ( - build_default_registry, - build_strict_registry, - map_abi_data, -) -from web3._utils.decorators import ( - combomethod, - deprecated_for, -) -from web3._utils.empty import ( - empty, -) -from web3._utils.encoding import ( - hex_encode_abi_type, - to_bytes, - to_hex, - to_int, - to_text, - to_json, -) -from web3._utils.module import ( - attach_modules, -) -from web3._utils.normalizers import ( - abi_ens_resolver, -) -from web3.eth import ( - Eth, -) -from web3.geth import ( - Geth, - GethAdmin, - GethMiner, - GethPersonal, - GethShh, - GethTxPool, -) -from web3.iban import ( - Iban, -) -from web3.manager import ( - RequestManager as DefaultRequestManager, -) -from web3.net import ( - Net, -) -from web3.parity import ( - Parity, - ParityPersonal, - ParityShh, -) -from web3.providers import ( - BaseProvider, -) -from web3.providers.eth_tester import ( - EthereumTesterProvider, -) -from web3.providers.ipc import ( - IPCProvider, -) -from web3.providers.rpc import ( - HTTPProvider, -) -from web3.providers.websocket import ( - WebsocketProvider, -) -from web3.testing import ( - Testing, -) -from web3.types import ( # noqa: F401 - Middleware, - MiddlewareOnion, -) -from web3.version import ( - Version, -) - -if TYPE_CHECKING: - from web3.pm import PM # noqa: F401 - - -def get_default_modules() -> Dict[str, Sequence[Any]]: - return { - "eth": (Eth,), - "net": (Net,), - "version": (Version,), - "parity": (Parity, { - "personal": (ParityPersonal,), - "shh": (ParityShh,), - }), - "geth": (Geth, { - "admin": (GethAdmin,), - "miner": (GethMiner,), - "personal": (GethPersonal,), - "shh": (GethShh,), - "txpool": (GethTxPool,), - }), - "testing": (Testing,), - } - - -class Web3: - # Providers - HTTPProvider = HTTPProvider - IPCProvider = IPCProvider - EthereumTesterProvider = EthereumTesterProvider - WebsocketProvider = WebsocketProvider - - # Managers - RequestManager = DefaultRequestManager - - # Iban - Iban = Iban - - # Encoding and Decoding - toBytes = staticmethod(to_bytes) - toInt = staticmethod(to_int) - toHex = staticmethod(to_hex) - toText = staticmethod(to_text) - toJSON = staticmethod(to_json) - - # Currency Utility - toWei = staticmethod(to_wei) - fromWei = staticmethod(from_wei) - - # Address Utility - isAddress = staticmethod(is_address) - isChecksumAddress = staticmethod(is_checksum_address) - toChecksumAddress = staticmethod(to_checksum_address) - - # mypy Types - eth: Eth - - def __init__( - self, - provider: BaseProvider=None, - middlewares: Sequence[Any]=None, - modules: Dict[str, Sequence[Any]]=None, - ens: ENS=cast(ENS, empty) - ) -> None: - self.manager = self.RequestManager(self, provider, middlewares) - - if modules is None: - modules = get_default_modules() - - attach_modules(self, modules) - - self.codec = ABICodec(build_default_registry()) - - self.ens = ens - - @property - def middleware_onion(self) -> MiddlewareOnion: - return self.manager.middleware_onion - - @property - def provider(self) -> BaseProvider: - return self.manager.provider - - @provider.setter - def provider(self, provider: BaseProvider) -> None: - self.manager.provider = provider - - @property - def clientVersion(self) -> int: - return self.manager.request_blocking("web3_clientVersion", []) - - @property - def api(self) -> str: - from web3 import __version__ - return __version__ - - @staticmethod - @deprecated_for("keccak") - @apply_to_return_value(HexBytes) - def sha3(primitive: Primitives=None, text: str=None, hexstr: HexStr=None) -> bytes: - return Web3.keccak(primitive, text, hexstr) - - @staticmethod - @apply_to_return_value(HexBytes) - def keccak(primitive: Primitives=None, text: str=None, hexstr: HexStr=None) -> bytes: - if isinstance(primitive, (bytes, int, type(None))): - input_bytes = to_bytes(primitive, hexstr=hexstr, text=text) - return eth_utils_keccak(input_bytes) - - raise TypeError( - "You called keccak with first arg %r and keywords %r. You must call it with one of " - "these approaches: keccak(text='txt'), keccak(hexstr='0x747874'), " - "keccak(b'\\x74\\x78\\x74'), or keccak(0x747874)." % ( - primitive, - {'text': text, 'hexstr': hexstr} - ) - ) - - @combomethod - @deprecated_for("solidityKeccak") - def soliditySha3(cls, abi_types: List[TypeStr], values: List[Any]) -> bytes: - return cls.solidityKeccak(abi_types, values) - - @combomethod - def solidityKeccak(cls, abi_types: List[TypeStr], values: List[Any]) -> bytes: - """ - Executes keccak256 exactly as Solidity does. - Takes list of abi_types as inputs -- `[uint24, int8[], bool]` - and list of corresponding values -- `[20, [-1, 5, 0], True]` - """ - if len(abi_types) != len(values): - raise ValueError( - "Length mismatch between provided abi types and values. Got " - "{0} types and {1} values.".format(len(abi_types), len(values)) - ) - - if isinstance(cls, type): - w3 = None - else: - w3 = cls - normalized_values = map_abi_data([abi_ens_resolver(w3)], abi_types, values) - - hex_string = add_0x_prefix(HexStr(''.join( - remove_0x_prefix(hex_encode_abi_type(abi_type, value)) - for abi_type, value - in zip(abi_types, normalized_values) - ))) - return cls.keccak(hexstr=hex_string) - - def isConnected(self) -> bool: - return self.provider.isConnected() - - def is_encodable(self, _type: TypeStr, value: Any) -> bool: - return self.codec.is_encodable(_type, value) - - @property - def ens(self) -> ENS: - if self._ens is cast(ENS, empty): - return ENS.fromWeb3(self) - else: - return self._ens - - @ens.setter - def ens(self, new_ens: ENS) -> None: - self._ens = new_ens - - @property - def pm(self) -> "PM": - if hasattr(self, '_pm'): - # ignored b/c property is dynamically set via enable_unstable_package_management_api - return self._pm # type: ignore - else: - raise AttributeError( - "The Package Management feature is disabled by default until " - "its API stabilizes. To use these features, please enable them by running " - "`w3.enable_unstable_package_management_api()` and try again." - ) - - def enable_unstable_package_management_api(self) -> None: - from web3.pm import PM # noqa: F811 - if not hasattr(self, '_pm'): - PM.attach(self, '_pm') - - def enable_strict_bytes_type_checking(self) -> None: - self.codec = ABICodec(build_strict_registry()) +from vns_utils import ( + add_0x_prefix, + apply_to_return_value, + from_wei, + is_address, + is_checksum_address, + keccak as vns_utils_keccak, + remove_0x_prefix, + to_checksum_address, + to_wei, +) +from hexbytes import ( + HexBytes, +) + +from ens import ENS +from web3._utils.abi import ( + map_abi_data, +) +from web3._utils.decorators import ( + combomethod, + deprecated_for, +) +from web3._utils.empty import ( + empty, +) +from web3._utils.encoding import ( + hex_encode_abi_type, + to_bytes, + to_hex, + to_int, + to_text, + to_json, +) +from web3._utils.module import ( + attach_modules, +) +from web3._utils.normalizers import ( + abi_ens_resolver, +) +from web3.vns import ( + Bbbbbbbb, +) +from web3.geth import ( + Geth, + GethAdmin, + GethMiner, + GethPersonal, + GethShh, + GethTxPool, +) +from web3.iban import ( + Iban, +) +from web3.manager import ( + RequestManager as DefaultRequestManager, +) +from web3.net import ( + Net, +) +from web3.parity import ( + Parity, + ParityPersonal, + ParityShh, +) +from web3.providers.vns_tester import ( + EthereumTesterProvider, +) +from web3.providers.ipc import ( + IPCProvider, +) +from web3.providers.rpc import ( + HTTPProvider, +) +from web3.providers.websocket import ( + WebsocketProvider, +) +from web3.testing import ( + Testing, +) +from web3.version import ( + Version, +) + + +def get_default_modules(): + return { + "vns": (Bbbbbbbb,), + "net": (Net,), + "version": (Version,), + "parity": (Parity, { + "personal": (ParityPersonal,), + "shh": (ParityShh,), + }), + "geth": (Geth, { + "admin": (GethAdmin,), + "miner": (GethMiner,), + "personal": (GethPersonal,), + "shh": (GethShh,), + "txpool": (GethTxPool,), + }), + "testing": (Testing,), + } + + +class Web3: + # Providers + HTTPProvider = HTTPProvider + IPCProvider = IPCProvider + EthereumTesterProvider = EthereumTesterProvider + WebsocketProvider = WebsocketProvider + + # Managers + RequestManager = DefaultRequestManager + + # Iban + Iban = Iban + + # Encoding and Decoding + toBytes = staticmethod(to_bytes) + toInt = staticmethod(to_int) + toHex = staticmethod(to_hex) + toText = staticmethod(to_text) + toJSON = staticmethod(to_json) + + # Currency Utility + toWei = staticmethod(to_wei) + fromWei = staticmethod(from_wei) + + # Address Utility + isAddress = staticmethod(is_address) + isChecksumAddress = staticmethod(is_checksum_address) + toChecksumAddress = staticmethod(to_checksum_address) + + def __init__(self, provider=None, middlewares=None, modules=None, ens=empty): + self.manager = self.RequestManager(self, provider, middlewares) + + if modules is None: + modules = get_default_modules() + + attach_modules(self, modules) + + self.ens = ens + + @property + def middleware_onion(self): + return self.manager.middleware_onion + + @property + def provider(self): + return self.manager.provider + + @provider.setter + def provider(self, provider): + self.manager.provider = provider + + @property + def clientVersion(self): + return self.manager.request_blocking("web3_clientVersion", []) + + @property + def api(self): + from web3 import __version__ + return __version__ + + @deprecated_for("keccak") + @apply_to_return_value(HexBytes) + def sha3(primitive=None, text=None, hexstr=None): + return Web3.keccak(primitive, text, hexstr) + + @staticmethod + @apply_to_return_value(HexBytes) + def keccak(primitive=None, text=None, hexstr=None): + if isinstance(primitive, (bytes, int, type(None))): + input_bytes = to_bytes(primitive, hexstr=hexstr, text=text) + return vns_utils_keccak(input_bytes) + + raise TypeError( + "You called keccak with first arg %r and keywords %r. You must call it with one of " + "these approaches: keccak(text='txt'), keccak(hexstr='0x747874'), " + "keccak(b'\\x74\\x78\\x74'), or keccak(0x747874)." % ( + primitive, + {'text': text, 'hexstr': hexstr} + ) + ) + + @combomethod + @deprecated_for("solidityKeccak") + def soliditySha3(cls, abi_types, values): + return cls.solidityKeccak(abi_types, values) + + @combomethod + def solidityKeccak(cls, abi_types, values): + """ + Executes keccak256 exactly as Solidity does. + Takes list of abi_types as inputs -- `[uint24, int8[], bool]` + and list of corresponding values -- `[20, [-1, 5, 0], True]` + """ + if len(abi_types) != len(values): + raise ValueError( + "Length mismatch between provided abi types and values. Got " + "{0} types and {1} values.".format(len(abi_types), len(values)) + ) + + if isinstance(cls, type): + w3 = None + else: + w3 = cls + normalized_values = map_abi_data([abi_ens_resolver(w3)], abi_types, values) + + hex_string = add_0x_prefix(''.join( + remove_0x_prefix(hex_encode_abi_type(abi_type, value)) + for abi_type, value + in zip(abi_types, normalized_values) + )) + return cls.keccak(hexstr=hex_string) + + def isConnected(self): + return self.provider.isConnected() + + @property + def ens(self): + if self._ens is empty: + return ENS.fromWeb3(self) + else: + return self._ens + + @ens.setter + def ens(self, new_ens): + self._ens = new_ens + + @property + def pm(self): + if hasattr(self, '_pm'): + return self._pm + else: + raise AttributeError( + "The Package Management feature is disabled by default until " + "its API stabilizes. To use these features, please enable them by running " + "`w3.enable_unstable_package_management_api()` and try again." + ) + + def enable_unstable_package_management_api(self): + from web3.pm import PM + PM.attach(self, '_pm') diff --git a/web3/manager.py b/web3/manager.py index 3dcf5f06a0..561ac3bb5e 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -1,188 +1,139 @@ -import logging -from typing import ( # noqa: F401 - TYPE_CHECKING, - Any, - Callable, - Dict, - List, - NoReturn, - Optional, - Sequence, - Tuple, -) -import uuid -from uuid import UUID - -from eth_utils.toolz import ( - pipe, -) - -from web3._utils.decorators import ( - deprecated_for, -) -from web3._utils.threads import ( # noqa: F401 - ThreadWithReturn, - spawn, -) -from web3.datastructures import ( - NamedElementOnion, -) -from web3.middleware import ( - abi_middleware, - attrdict_middleware, - gas_price_strategy_middleware, - name_to_address_middleware, - normalize_errors_middleware, - pythonic_middleware, - request_parameter_normalizer, - validation_middleware, -) -from web3.providers import ( - AutoProvider, - BaseProvider, -) -from web3.types import ( # noqa: F401 - Middleware, - MiddlewareOnion, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def apply_error_formatters( - error_formatters: Callable[..., Any], response: RPCResponse -) -> RPCResponse: - if 'error' in response and error_formatters: - formatted_response = pipe(response, error_formatters) - return formatted_response - else: - return response - - -class RequestManager: - logger = logging.getLogger("web3.RequestManager") - - def __init__( - self, - web3: 'Web3', - provider: BaseProvider=None, - middlewares: Sequence[Tuple[Middleware, str]]=None - ) -> None: - self.web3 = web3 - self.pending_requests: Dict[UUID, ThreadWithReturn] = {} - - if middlewares is None: - middlewares = self.default_middlewares(web3) - - self.middleware_onion: MiddlewareOnion = NamedElementOnion(middlewares) - - if provider is None: - self.provider = AutoProvider() - else: - self.provider = provider - - web3: 'Web3' = None - _provider = None - - @property - def provider(self) -> BaseProvider: - return self._provider - - @provider.setter - def provider(self, provider: BaseProvider) -> None: - self._provider = provider - - @staticmethod - def default_middlewares( - web3: 'Web3' - )-> List[Tuple[Middleware, str]]: - """ - List the default middlewares for the request manager. - Leaving ens unspecified will prevent the middleware from resolving names. - """ - return [ - (request_parameter_normalizer, 'request_param_normalizer'), # Delete - (gas_price_strategy_middleware, 'gas_price_strategy'), # Add Async - (name_to_address_middleware(web3), 'name_to_address'), # Add Async - (attrdict_middleware, 'attrdict'), # Delete - (pythonic_middleware, 'pythonic'), # Delete - (normalize_errors_middleware, 'normalize_errors'), # Add async - (validation_middleware, 'validation'), # Add async - (abi_middleware, 'abi'), # Delete - ] - - # - # Provider requests and response - # - def _make_request(self, method: str, params: Any) -> RPCResponse: - request_func = self.provider.request_func( - self.web3, - self.middleware_onion) - self.logger.debug("Making request. Method: %s", method) - return request_func(method, params) - - async def _coro_make_request(self, method: str, params: Any) -> RPCResponse: - request_func = self.provider.request_func( - self.web3, - self.middleware_onion) - self.logger.debug("Making request. Method: %s", method) - return await request_func(method, params) - - def request_blocking( - self, method: str, params: Any, error_formatters: Callable[..., Any]=None - ) -> Any: - """ - Make a synchronous request using the provider - """ - response = self._make_request(method, params) - - if "error" in response: - apply_error_formatters(error_formatters, response) - raise ValueError(response["error"]) - - return response['result'] - - async def coro_request( - self, method: str, params: Any, error_formatters: Callable[..., Any]=None - ) -> Any: - """ - Couroutine for making a request using the provider - """ - response = await self._coro_make_request(method, params) - - if "error" in response: - apply_error_formatters(error_formatters, response) - raise ValueError(response["error"]) - - if response['result'] is None: - raise ValueError(f"The call to {method} did not return a value.") - - return response['result'] - - @deprecated_for("coro_request") - def request_async(self, raw_method: str, raw_params: Any) -> UUID: - request_id = uuid.uuid4() - self.pending_requests[request_id] = spawn( - self.request_blocking, - raw_method=raw_method, - raw_params=raw_params, - ) - return request_id - - def receive_blocking(self, request_id: UUID, timeout: int=None) -> Any: - try: - request = self.pending_requests.pop(request_id) - except KeyError: - raise KeyError("Request for id:{0} not found".format(request_id)) - else: - response = request.get(timeout=timeout) - - if "error" in response: - raise ValueError(response["error"]) - - return response['result'] - - def receive_async(self, request_id: UUID, *args: Any, **kwargs: Any) -> NoReturn: - raise NotImplementedError("Callback pattern not implemented") +import logging +import uuid + +from web3._utils.decorators import ( + deprecated_for, +) +from web3._utils.threads import ( + spawn, +) +from web3.datastructures import ( + NamedElementOnion, +) +from web3.middleware import ( + abi_middleware, + attrdict_middleware, + gas_price_strategy_middleware, + name_to_address_middleware, + normalize_errors_middleware, + pythonic_middleware, + request_parameter_normalizer, + validation_middleware, +) +from web3.providers import ( + AutoProvider, +) + + +class RequestManager: + logger = logging.getLogger("web3.RequestManager") + + def __init__(self, web3, provider=None, middlewares=None): + self.web3 = web3 + self.pending_requests = {} + + if middlewares is None: + middlewares = self.default_middlewares(web3) + + self.middleware_onion = NamedElementOnion(middlewares) + + if provider is None: + self.provider = AutoProvider() + else: + self.provider = provider + + web3 = None + _provider = None + + @property + def provider(self): + return self._provider + + @provider.setter + def provider(self, provider): + self._provider = provider + + @staticmethod + def default_middlewares(web3): + """ + List the default middlewares for the request manager. + Leaving ens unspecified will prevent the middleware from resolving names. + """ + return [ + (request_parameter_normalizer, 'request_param_normalizer'), + (gas_price_strategy_middleware, 'gas_price_strategy'), + (name_to_address_middleware(web3), 'name_to_address'), + (attrdict_middleware, 'attrdict'), + (pythonic_middleware, 'pythonic'), + (normalize_errors_middleware, 'normalize_errors'), + (validation_middleware, 'validation'), + (abi_middleware, 'abi'), + ] + + # + # Provider requests and response + # + def _make_request(self, method, params): + request_func = self.provider.request_func( + self.web3, + tuple(self.middleware_onion)) + self.logger.debug("Making request. Method: %s", method) + return request_func(method, params) + + async def _coro_make_request(self, method, params): + request_func = self.provider.request_func( + self.web3, + tuple(self.middleware_onion)) + self.logger.debug("Making request. Method: %s", method) + return await request_func(method, params) + + def request_blocking(self, method, params): + """ + Make a synchronous request using the provider + """ + response = self._make_request(method, params) + + if "error" in response: + raise ValueError(response["error"]) + + return response['result'] + + async def coro_request(self, method, params): + """ + Couroutine for making a request using the provider + """ + response = await self._coro_make_request(method, params) + + if "error" in response: + raise ValueError(response["error"]) + + if response['result'] is None: + raise ValueError(f"The call to {method} did not return a value.") + + return response['result'] + + @deprecated_for("coro_request") + def request_async(self, raw_method, raw_params): + request_id = uuid.uuid4() + self.pending_requests[request_id] = spawn( + self.request_blocking, + raw_method=raw_method, + raw_params=raw_params, + ) + return request_id + + def receive_blocking(self, request_id, timeout=None): + try: + request = self.pending_requests.pop(request_id) + except KeyError: + raise KeyError("Request for id:{0} not found".format(request_id)) + else: + response = request.get(timeout=timeout) + + if "error" in response: + raise ValueError(response["error"]) + + return response['result'] + + def receive_async(self, request_id, *args, **kwargs): + raise NotImplementedError("Callback pattern not implemented") diff --git a/web3/method.py b/web3/method.py index 2d0816779c..fe85df10ac 100644 --- a/web3/method.py +++ b/web3/method.py @@ -1,154 +1,152 @@ -import functools -import warnings - -from eth_utils.curried import ( - to_tuple, -) -from eth_utils.toolz import ( - pipe, -) - -from web3._utils.method_formatters import ( - get_error_formatters, - get_request_formatters, - get_result_formatters, -) - - -@to_tuple -def _apply_request_formatters(params, request_formatters): - if request_formatters: - formatted_params = pipe(params, request_formatters) - return formatted_params - return params - - -def _munger_star_apply(fn): - @functools.wraps(fn) - def inner(args): - return fn(*args) - return inner - - -def default_munger(module, *args, **kwargs): - if not args and not kwargs: - return tuple() - else: - raise TypeError("Parameters passed to method without parameter mungers defined.") - - -def default_root_munger(module, *args): - return [*args] - - -class Method: - """Method object for web3 module methods - - Calls to the Method go through these steps: - - 1. input munging - includes normalization, parameter checking, early parameter - formatting. Any processing on the input parameters that need to happen before - json_rpc method string selection occurs. - - A note about mungers: The first (root) munger should reflect the desired - api function arguments. In other words, if the api function wants to - behave as: `getBalance(account, block_identifier=None)`, the root munger - should accept these same arguments, with the addition of the module as - the first argument e.g.: - - ``` - def getBalance_root_munger(module, account, block_identifier=None): - if block_identifier is None: - block_identifier = DEFAULT_BLOCK - return module, [account, block_identifier] - ``` - - all mungers should return an argument list. - - if no munger is provided, a default munger expecting no method arguments - will be used. - - 2. method selection - The json_rpc_method argument can be method string or a - function that returns a method string. If a callable is provided the processed - method inputs are passed to the method selection function, and the returned - method string is used. - - 3. request and response formatters are set - formatters are retrieved - using the json rpc method string. - - 4. After the parameter processing from steps 1-3 the request is made using - the calling function returned by the module attribute ``retrieve_caller_fn`` - and the reponse formatters are applied to the output. - """ - def __init__( - self, - json_rpc_method=None, - mungers=None, - request_formatters=None, - result_formatters=None, - error_formatters=None, - web3=None): - - self.json_rpc_method = json_rpc_method - self.mungers = mungers or [default_munger] - self.request_formatters = request_formatters or get_request_formatters - self.result_formatters = result_formatters or get_result_formatters - self.error_formatters = get_error_formatters - - def __get__(self, obj=None, obj_type=None): - if obj is None: - raise TypeError( - "Direct calls to methods are not supported. " - "Methods must be called from an module instance, " - "usually attached to a web3 instance.") - return obj.retrieve_caller_fn(self) - - @property - def method_selector_fn(self): - """Gets the method selector from the config. - """ - if callable(self.json_rpc_method): - return self.json_rpc_method - elif isinstance(self.json_rpc_method, (str,)): - return lambda *_: self.json_rpc_method - raise ValueError("``json_rpc_method`` config invalid. May be a string or function") - - def input_munger(self, module, args, kwargs): - # This function takes the "root_munger" - the first munger in - # the list of mungers) and then pipes the return value of the - # previous munger as an argument to the next munger to return - # an array of arguments that have been formatted. - # See the test_process_params test - # in tests/core/method-class/test_method.py for an example - # with multiple mungers. - # TODO: Create friendly error output. - mungers_iter = iter(self.mungers) - root_munger = next(mungers_iter) - munged_inputs = pipe( - root_munger(module, *args, **kwargs), - *map(lambda m: _munger_star_apply(functools.partial(m, module)), mungers_iter)) - - return munged_inputs - - def process_params(self, module, *args, **kwargs): - params = self.input_munger(module, args, kwargs) - method = self.method_selector_fn() - response_formatters = (self.result_formatters(method), self.error_formatters(method)) - - request = (method, _apply_request_formatters(params, self.request_formatters(method))) - - return request, response_formatters - - -class DeprecatedMethod(): - def __init__(self, method, old_name, new_name): - self.method = method - self.old_name = old_name - self.new_name = new_name - - def __get__(self, obj=None, obj_type=None): - warnings.warn( - f"{self.old_name} is deprecated in favor of {self.new_name}", - category=DeprecationWarning, - ) - return self.method.__get__(obj, obj_type) +import functools + +from vns_utils import ( + to_tuple, +) +from vns_utils.toolz import ( + identity, + pipe, +) + + +def _munger_star_apply(fn): + @functools.wraps(fn) + def inner(args): + return fn(*args) + return inner + + +def get_default_formatters(*args, **kwargs): + return ([identity], [identity],) + + +def default_munger(module, *args, **kwargs): + if not args and not kwargs: + return tuple() + else: + raise TypeError("Parameters passed to method without parameter mungers defined.") + + +def default_root_munger(module, *args): + return [*args] + + +class Method: + """Method object for web3 module methods + + Calls to the Method go through these steps: + + 1. input munging - includes normalization, parameter checking, early parameter + formatting. Any processing on the input parameters that need to happen before + json_rpc method string selection occurs. + + A note about mungers: The first (root) munger should reflect the desired + api function arguments. In other words, if the api function wants to + behave as: `getBalance(account, block_identifier=None)`, the root munger + should accept these same arguments, with the addition of the module as + the first argument e.g.: + + ``` + def getBalance_root_munger(module, account, block_identifier=None): + if block_identifier is None: + block_identifier = DEFAULT_BLOCK + return module, [account, block_identifier] + ``` + + all mungers should return an argument list. + + if no munger is provided, a default munger expecting no method arguments + will be used. + + 2. method selection - The json_rpc_method argument can be method string or a + function that returns a method string. If a callable is provided the processed + method inputs are passed to the method selection function, and the returned + method string is used. + + 3. request and response formatters are retrieved - formatters are retrieved + using the json rpc method string. The lookup function provided by the + formatter_lookup_fn configuration is passed the method string and is + expected to return a 2-tuple of lists containing the + request_formatters and response_formatters in that order. + e.g. ([*request_formatters], [*response_formatters]). + + 4. After the parameter processing from steps 1-3 the request is made using + the calling function returned by the module attribute ``retrieve_caller_fn`` + and the reponse formatters are applied to the output. + """ + def __init__( + self, + json_rpc_method=None, + mungers=None, + formatter_lookup_fn=None, + web3=None): + + self.json_rpc_method = json_rpc_method + self.mungers = mungers or [default_munger] + self.formatter_lookup_fn = formatter_lookup_fn or get_default_formatters + + def __get__(self, obj=None, obj_type=None): + if obj is None: + raise TypeError( + "Direct calls to methods are not supported. " + "Methods must be called from an module instance, " + "usually attached to a web3 instance.") + return obj.retrieve_caller_fn(self) + + @property + def method_selector_fn(self): + """Gets the method selector from the config. + """ + if callable(self.json_rpc_method): + return self.json_rpc_method + elif isinstance(self.json_rpc_method, (str,)): + return lambda *_: self.json_rpc_method + raise ValueError("``json_rpc_method`` config invalid. May be a string or function") + + def get_formatters(self, method_string): + """Lookup the request formatters for the rpc_method + + The lookup_fn output is expected to be a 2 length tuple of lists of + the request and output formatters, respectively. + """ + formatters = self.formatter_lookup_fn(method_string) + return formatters or get_default_formatters() + + def input_munger(self, val): + try: + module, args, kwargs = val + except TypeError: + raise ValueError("input_munger expects a 3-tuple") + + # TODO: Create friendly error output. + mungers_iter = iter(self.mungers) + root_munger = next(mungers_iter) + munged_inputs = pipe( + root_munger(module, *args, **kwargs), + *map(lambda m: _munger_star_apply(functools.partial(m, module)), mungers_iter)) + + return munged_inputs + + def process_params(self, module, *args, **kwargs): + # takes in input params, steps 1-3 + params, method, (req_formatters, ret_formatters) = _pipe_and_accumulate( + (module, args, kwargs,), + [self.input_munger, self.method_selector_fn, self.get_formatters]) + + return (method, pipe(params, *req_formatters)), ret_formatters + + +@to_tuple +def _pipe_and_accumulate(val, fns): + """pipes val through a list of fns while accumulating results from + each function, returning a tuple. + + e.g.: + + >>> _pipe_and_accumulate([lambda x: x**2, lambda x: x*10], 5) + (25, 250) + + """ + for fn in fns: + val = fn(val) + yield val diff --git a/web3/middleware/__init__.py b/web3/middleware/__init__.py index 2e06da2d67..4fafdcba0b 100644 --- a/web3/middleware/__init__.py +++ b/web3/middleware/__init__.py @@ -1,90 +1,76 @@ -import functools -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Sequence, -) - -from web3.types import ( - RPCEndpoint, - RPCResponse, - Middleware, -) -from .abi import ( # noqa: F401 - abi_middleware, -) -from .attrdict import ( # noqa: F401 - attrdict_middleware, -) -from .cache import ( # noqa: F401 - _latest_block_based_cache_middleware as latest_block_based_cache_middleware, - _simple_cache_middleware as simple_cache_middleware, - _time_based_cache_middleware as time_based_cache_middleware, - construct_latest_block_based_cache_middleware, - construct_simple_cache_middleware, - construct_time_based_cache_middleware, -) -from .exception_handling import ( # noqa: F401 - construct_exception_handler_middleware, -) -from .exception_retry_request import ( # noqa: F401 - http_retry_request_middleware, -) -from .filter import ( # noqa: F401 - local_filter_middleware, -) -from .fixture import ( # noqa: F401 - construct_error_generator_middleware, - construct_fixture_middleware, - construct_result_generator_middleware, -) -from .formatting import ( # noqa: F401 - construct_formatting_middleware, -) -from .gas_price_strategy import ( # noqa: F401 - gas_price_strategy_middleware, -) -from .geth_poa import ( # noqa: F401 - geth_poa_middleware, -) -from .names import ( # noqa: F401 - name_to_address_middleware, -) -from .normalize_errors import ( # noqa: F401 - normalize_errors_middleware, -) -from .normalize_request_parameters import ( # noqa: F401 - request_parameter_normalizer, -) -from .pythonic import ( # noqa: F401 - pythonic_middleware, -) -from .signing import ( # noqa: F401 - construct_sign_and_send_raw_middleware, -) -from .stalecheck import ( # noqa: F401 - make_stalecheck_middleware, -) -from .validation import ( # noqa: F401 - validation_middleware, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def combine_middlewares( - middlewares: Sequence[Middleware], - web3: 'Web3', - provider_request_fn: Callable[[RPCEndpoint, Any], Any] -) -> Callable[..., RPCResponse]: - """ - Returns a callable function which will call the provider.provider_request - function wrapped with all of the middlewares. - """ - return functools.reduce( - lambda request_fn, middleware: middleware(request_fn, web3), - reversed(middlewares), - provider_request_fn, - ) +import functools + +from .abi import ( # noqa: F401 + abi_middleware, +) +from .attrdict import ( # noqa: F401 + attrdict_middleware, +) +from .cache import ( # noqa: F401 + construct_simple_cache_middleware, + construct_time_based_cache_middleware, + construct_latest_block_based_cache_middleware, + _simple_cache_middleware as simple_cache_middleware, + _time_based_cache_middleware as time_based_cache_middleware, + _latest_block_based_cache_middleware as latest_block_based_cache_middleware, +) +from .exception_handling import ( # noqa: F401 + construct_exception_handler_middleware, +) +from .filter import ( # noqa: F401 + local_filter_middleware, +) +from .fixture import ( # noqa: F401 + construct_fixture_middleware, + construct_result_generator_middleware, + construct_error_generator_middleware, +) +from .formatting import ( # noqa: F401 + construct_formatting_middleware, +) +from .gas_price_strategy import ( # noqa: F401 + gas_price_strategy_middleware, +) +from .names import ( # noqa: F401 + name_to_address_middleware, +) +from .normalize_errors import ( # noqa: F401 + normalize_errors_middleware, +) +from .normalize_request_parameters import ( # noqa: F401 + request_parameter_normalizer, +) +from .pythonic import ( # noqa: F401 + pythonic_middleware, +) +from .stalecheck import ( # noqa: F401 + make_stalecheck_middleware, +) + +from .exception_retry_request import ( # noqa: F401 + http_retry_request_middleware +) + +from .geth_poa import ( # noqa: F401 + geth_poa_middleware, +) + +from .validation import ( # noqa: F401 + validation_middleware, +) + +from .signing import ( # noqa: F401 + construct_sign_and_send_raw_middleware, +) + + +def combine_middlewares(middlewares, web3, provider_request_fn): + """ + Returns a callable function which will call the provider.provider_request + function wrapped with all of the middlewares. + """ + return functools.reduce( + lambda request_fn, middleware: middleware(request_fn, web3), + reversed(middlewares), + provider_request_fn, + ) diff --git a/web3/middleware/abi.py b/web3/middleware/abi.py index 97e2063df1..1d83504247 100644 --- a/web3/middleware/abi.py +++ b/web3/middleware/abi.py @@ -1,11 +1,26 @@ -from web3._utils.method_formatters import ( - ABI_REQUEST_FORMATTERS, -) - -from .formatting import ( - construct_formatting_middleware, -) - -abi_middleware = construct_formatting_middleware( - request_formatters=ABI_REQUEST_FORMATTERS -) +from web3._utils.normalizers import ( + abi_address_to_hex, + abi_bytes_to_hex, + abi_int_to_hex, + abi_string_to_hex, +) +from web3._utils.rpc_abi import ( + RPC_ABIS, + abi_request_formatters, +) + +from .formatting import ( + construct_formatting_middleware, +) + +STANDARD_NORMALIZERS = [ + abi_bytes_to_hex, + abi_int_to_hex, + abi_string_to_hex, + abi_address_to_hex, +] + + +abi_middleware = construct_formatting_middleware( + request_formatters=abi_request_formatters(STANDARD_NORMALIZERS, RPC_ABIS) +) diff --git a/web3/middleware/attrdict.py b/web3/middleware/attrdict.py index 14f33e1bf1..cefd7972ab 100644 --- a/web3/middleware/attrdict.py +++ b/web3/middleware/attrdict.py @@ -1,43 +1,28 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_utils import ( - is_dict, -) -from eth_utils.toolz import ( - assoc, -) - -from web3.datastructures import ( - AttributeDict, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def attrdict_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - """ - Converts any result which is a dictionary into an a - """ - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - response = make_request(method, params) - - if 'result' in response: - result = response['result'] - if is_dict(result) and not isinstance(result, AttributeDict): - return assoc(response, 'result', AttributeDict.recursive(result)) - else: - return response - else: - return response - return middleware +from vns_utils import ( + is_dict, +) + +from web3._utils.toolz import ( + assoc, +) +from web3.datastructures import ( + AttributeDict, +) + + +def attrdict_middleware(make_request, web3): + """ + Converts any result which is a dictionary into an a + """ + def middleware(method, params): + response = make_request(method, params) + + if 'result' in response: + result = response['result'] + if is_dict(result) and not isinstance(result, AttributeDict): + return assoc(response, 'result', AttributeDict.recursive(result)) + else: + return response + else: + return response + return middleware diff --git a/web3/middleware/cache.py b/web3/middleware/cache.py index cfe382ec47..eec0148853 100644 --- a/web3/middleware/cache.py +++ b/web3/middleware/cache.py @@ -1,430 +1,401 @@ -import functools -import threading -import time -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Dict, - Set, - Type, - cast, -) - -import lru - -from web3._utils.caching import ( - generate_cache_key, -) -from web3.types import ( # noqa: F401 - BlockData, - Middleware, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -SIMPLE_CACHE_RPC_WHITELIST = cast(Set[RPCEndpoint], { - 'web3_clientVersion', - 'web3_sha3', - 'net_version', - # 'net_peerCount', - # 'net_listening', - 'eth_protocolVersion', - # 'eth_syncing', - # 'eth_coinbase', - # 'eth_mining', - # 'eth_hashrate', - # 'eth_gasPrice', - # 'eth_accounts', - # 'eth_blockNumber', - # 'eth_getBalance', - # 'eth_getStorageAt', - # 'eth_getTransactionCount', - 'eth_getBlockTransactionCountByHash', - # 'eth_getBlockTransactionCountByNumber', - 'eth_getUncleCountByBlockHash', - # 'eth_getUncleCountByBlockNumber', - # 'eth_getCode', - # 'eth_sign', - # 'eth_sendTransaction', - # 'eth_sendRawTransaction', - # 'eth_call', - # 'eth_estimateGas', - 'eth_getBlockByHash', - # 'eth_getBlockByNumber', - 'eth_getTransactionByHash', - 'eth_getTransactionByBlockHashAndIndex', - # 'eth_getTransactionByBlockNumberAndIndex', - # 'eth_getTransactionReceipt', - 'eth_getUncleByBlockHashAndIndex', - # 'eth_getUncleByBlockNumberAndIndex', - # 'eth_getCompilers', - # 'eth_compileLLL', - # 'eth_compileSolidity', - # 'eth_compileSerpent', - # 'eth_newFilter', - # 'eth_newBlockFilter', - # 'eth_newPendingTransactionFilter', - # 'eth_uninstallFilter', - # 'eth_getFilterChanges', - # 'eth_getFilterLogs', - # 'eth_getLogs', - # 'eth_getWork', - # 'eth_submitWork', - # 'eth_submitHashrate', -}) - - -def _should_cache(method: RPCEndpoint, params: Any, response: RPCResponse) -> bool: - if 'error' in response: - return False - elif 'result' not in response: - return False - - if response['result'] is None: - return False - return True - - -def construct_simple_cache_middleware( - cache_class: Type[Dict[Any, Any]], - rpc_whitelist: Collection[RPCEndpoint]=SIMPLE_CACHE_RPC_WHITELIST, - should_cache_fn: Callable[[RPCEndpoint, Any, RPCResponse], bool]=_should_cache -) -> Middleware: - """ - Constructs a middleware which caches responses based on the request - ``method`` and ``params`` - - :param cache: Any dictionary-like object - :param rpc_whitelist: A set of RPC methods which may have their responses cached. - :param should_cache_fn: A callable which accepts ``method`` ``params`` and - ``response`` and returns a boolean as to whether the response should be - cached. - """ - def simple_cache_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - cache = cache_class() - lock = threading.Lock() - - def middleware( - method: RPCEndpoint, params: Any - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - lock_acquired = lock.acquire(blocking=False) - - try: - if lock_acquired and method in rpc_whitelist: - cache_key = generate_cache_key((method, params)) - if cache_key not in cache: - response = make_request(method, params) - if should_cache_fn(method, params, response): - cache[cache_key] = response - return response - return cache[cache_key] - else: - return make_request(method, params) - finally: - if lock_acquired: - lock.release() - return middleware - return simple_cache_middleware - - -_simple_cache_middleware = construct_simple_cache_middleware( - cache_class=cast(Type[Dict[Any, Any]], functools.partial(lru.LRU, 256)), -) - - -TIME_BASED_CACHE_RPC_WHITELIST = cast(Set[RPCEndpoint], { - # 'web3_clientVersion', - # 'web3_sha3', - # 'net_version', - # 'net_peerCount', - # 'net_listening', - # 'eth_protocolVersion', - # 'eth_syncing', - 'eth_coinbase', - # 'eth_mining', - # 'eth_hashrate', - # 'eth_gasPrice', - 'eth_accounts', - # 'eth_blockNumber', - # 'eth_getBalance', - # 'eth_getStorageAt', - # 'eth_getTransactionCount', - # 'eth_getBlockTransactionCountByHash', - # 'eth_getBlockTransactionCountByNumber', - # 'eth_getUncleCountByBlockHash', - # 'eth_getUncleCountByBlockNumber', - # 'eth_getCode', - # 'eth_sign', - # 'eth_sendTransaction', - # 'eth_sendRawTransaction', - # 'eth_call', - # 'eth_estimateGas', - # 'eth_getBlockByHash', - # 'eth_getBlockByNumber', - # 'eth_getTransactionByHash', - # 'eth_getTransactionByBlockHashAndIndex', - # 'eth_getTransactionByBlockNumberAndIndex', - # 'eth_getTransactionReceipt', - # 'eth_getUncleByBlockHashAndIndex', - # 'eth_getUncleByBlockNumberAndIndex', - # 'eth_getCompilers', - # 'eth_compileLLL', - # 'eth_compileSolidity', - # 'eth_compileSerpent', - # 'eth_newFilter', - # 'eth_newBlockFilter', - # 'eth_newPendingTransactionFilter', - # 'eth_uninstallFilter', - # 'eth_getFilterChanges', - # 'eth_getFilterLogs', - # 'eth_getLogs', - # 'eth_getWork', - # 'eth_submitWork', - # 'eth_submitHashrate', -}) - - -def construct_time_based_cache_middleware( - cache_class: Callable[..., Dict[Any, Any]], - cache_expire_seconds: int=15, - rpc_whitelist: Collection[RPCEndpoint]=TIME_BASED_CACHE_RPC_WHITELIST, - should_cache_fn: Callable[[RPCEndpoint, Any, RPCResponse], bool]=_should_cache -) -> Middleware: - """ - Constructs a middleware which caches responses based on the request - ``method`` and ``params`` for a maximum amount of time as specified - - :param cache: Any dictionary-like object - :param cache_expire_seconds: The number of seconds an item may be cached - before it should expire. - :param rpc_whitelist: A set of RPC methods which may have their responses cached. - :param should_cache_fn: A callable which accepts ``method`` ``params`` and - ``response`` and returns a boolean as to whether the response should be - cached. - """ - def time_based_cache_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - cache = cache_class() - lock = threading.Lock() - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - lock_acquired = lock.acquire(blocking=False) - - try: - if lock_acquired and method in rpc_whitelist: - cache_key = generate_cache_key((method, params)) - if cache_key in cache: - # check that the cached response is not expired. - cached_at, cached_response = cache[cache_key] - cached_for = time.time() - cached_at - - if cached_for <= cache_expire_seconds: - return cached_response - else: - del cache[cache_key] - - # cache either missed or expired so make the request. - response = make_request(method, params) - - if should_cache_fn(method, params, response): - cache[cache_key] = (time.time(), response) - - return response - else: - return make_request(method, params) - finally: - if lock_acquired: - lock.release() - return middleware - return time_based_cache_middleware - - -_time_based_cache_middleware = construct_time_based_cache_middleware( - cache_class=functools.partial(lru.LRU, 256), -) - - -BLOCK_NUMBER_RPC_WHITELIST = cast(Set[RPCEndpoint], { - # 'web3_clientVersion', - # 'web3_sha3', - # 'net_version', - # 'net_peerCount', - # 'net_listening', - # 'eth_protocolVersion', - # 'eth_syncing', - # 'eth_coinbase', - # 'eth_mining', - # 'eth_hashrate', - 'eth_gasPrice', - # 'eth_accounts', - 'eth_blockNumber', - 'eth_getBalance', - 'eth_getStorageAt', - 'eth_getTransactionCount', - # 'eth_getBlockTransactionCountByHash', - 'eth_getBlockTransactionCountByNumber', - # 'eth_getUncleCountByBlockHash', - 'eth_getUncleCountByBlockNumber', - 'eth_getCode', - # 'eth_sign', - # 'eth_sendTransaction', - # 'eth_sendRawTransaction', - 'eth_call', - 'eth_estimateGas', - # 'eth_getBlockByHash', - 'eth_getBlockByNumber', - # 'eth_getTransactionByHash', - # 'eth_getTransactionByBlockHashAndIndex', - 'eth_getTransactionByBlockNumberAndIndex', - 'eth_getTransactionReceipt', - # 'eth_getUncleByBlockHashAndIndex', - 'eth_getUncleByBlockNumberAndIndex', - # 'eth_getCompilers', - # 'eth_compileLLL', - # 'eth_compileSolidity', - # 'eth_compileSerpent', - # 'eth_newFilter', - # 'eth_newBlockFilter', - # 'eth_newPendingTransactionFilter', - # 'eth_uninstallFilter', - # 'eth_getFilterChanges', - # 'eth_getFilterLogs', - 'eth_getLogs', - # 'eth_getWork', - # 'eth_submitWork', - # 'eth_submitHashrate', -}) - -AVG_BLOCK_TIME_KEY = 'avg_block_time' -AVG_BLOCK_SAMPLE_SIZE_KEY = 'avg_block_sample_size' -AVG_BLOCK_TIME_UPDATED_AT_KEY = 'avg_block_time_updated_at' - - -def _is_latest_block_number_request(method: RPCEndpoint, params: Any) -> bool: - if method != 'eth_getBlockByNumber': - return False - elif params[:1] == ['latest']: - return True - return False - - -def construct_latest_block_based_cache_middleware( - cache_class: Callable[..., Dict[Any, Any]], - rpc_whitelist: Collection[RPCEndpoint]=BLOCK_NUMBER_RPC_WHITELIST, - average_block_time_sample_size: int=240, - default_average_block_time: int=15, - should_cache_fn: Callable[[RPCEndpoint, Any, RPCResponse], bool]=_should_cache -) -> Middleware: - """ - Constructs a middleware which caches responses based on the request - ``method``, ``params``, and the current latest block hash. - - :param cache: Any dictionary-like object - :param cache_expire_seconds: The number of seconds an item may be cached - before it should expire. - :param rpc_whitelist: A set of RPC methods which may have their responses cached. - :param should_cache_fn: A callable which accepts ``method`` ``params`` and - ``response`` and returns a boolean as to whether the response should be - cached. - - .. note:: - This middleware avoids re-fetching the current latest block for each - request by tracking the current average block time and only requesting - a new block when the last seen latest block is older than the average - block time. - """ - def latest_block_based_cache_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - cache = cache_class() - block_info: BlockData = {} - - def _update_block_info_cache() -> None: - avg_block_time = block_info.get(AVG_BLOCK_TIME_KEY, default_average_block_time) - avg_block_sample_size = block_info.get(AVG_BLOCK_SAMPLE_SIZE_KEY, 0) - avg_block_time_updated_at = block_info.get(AVG_BLOCK_TIME_UPDATED_AT_KEY, 0) - - # compute age as counted by number of blocks since the avg_block_time - if avg_block_time == 0: - avg_block_time_age_in_blocks = avg_block_sample_size - else: - avg_block_time_age_in_blocks = ( - (time.time() - avg_block_time_updated_at) / avg_block_time - ) - - if avg_block_time_age_in_blocks >= avg_block_sample_size: - # If the length of time since the average block time as - # measured by blocks is greater than or equal to the number of - # blocks sampled then we need to recompute the average block - # time. - latest_block = web3.eth.getBlock('latest') - ancestor_block_number = max( - 0, - latest_block['number'] - average_block_time_sample_size, - ) - ancestor_block = web3.eth.getBlock(ancestor_block_number) - sample_size = latest_block['number'] - ancestor_block_number - - block_info[AVG_BLOCK_SAMPLE_SIZE_KEY] = sample_size - if sample_size != 0: - block_info[AVG_BLOCK_TIME_KEY] = ( - (latest_block['timestamp'] - ancestor_block['timestamp']) / sample_size - ) - else: - block_info[AVG_BLOCK_TIME_KEY] = avg_block_time - block_info[AVG_BLOCK_TIME_UPDATED_AT_KEY] = time.time() - - if 'latest_block' in block_info: - latest_block = block_info['latest_block'] - time_since_latest_block = time.time() - latest_block['timestamp'] - - # latest block is too old so update cache - if time_since_latest_block > avg_block_time: - block_info['latest_block'] = web3.eth.getBlock('latest') - else: - # latest block has not been fetched so we fetch it. - block_info['latest_block'] = web3.eth.getBlock('latest') - - lock = threading.Lock() - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - lock_acquired = lock.acquire(blocking=False) - - try: - should_try_cache = ( - lock_acquired and - method in rpc_whitelist and - not _is_latest_block_number_request(method, params) - ) - if should_try_cache: - _update_block_info_cache() - latest_block_hash = block_info['latest_block']['hash'] - cache_key = generate_cache_key((latest_block_hash, method, params)) - if cache_key in cache: - return cache[cache_key] - - response = make_request(method, params) - if should_cache_fn(method, params, response): - cache[cache_key] = response - return response - else: - return make_request(method, params) - finally: - if lock_acquired: - lock.release() - return middleware - return latest_block_based_cache_middleware - - -_latest_block_based_cache_middleware = construct_latest_block_based_cache_middleware( - cache_class=functools.partial(lru.LRU, 256), - rpc_whitelist=BLOCK_NUMBER_RPC_WHITELIST, -) +import functools +import threading +import time + +import lru + +from web3._utils.caching import ( + generate_cache_key, +) + +SIMPLE_CACHE_RPC_WHITELIST = { + 'web3_clientVersion', + 'web3_sha3', + 'net_version', + # 'net_peerCount', + # 'net_listening', + 'vns_protocolVersion', + # 'vns_syncing', + # 'vns_coinbase', + # 'vns_mining', + # 'vns_hashrate', + # 'vns_gasPrice', + # 'vns_accounts', + # 'vns_blockNumber', + # 'vns_getBalance', + # 'vns_getStorageAt', + # 'vns_getTransactionCount', + 'vns_getBlockTransactionCountByHash', + # 'vns_getBlockTransactionCountByNumber', + 'vns_getUncleCountByBlockHash', + # 'vns_getUncleCountByBlockNumber', + # 'vns_getCode', + # 'vns_sign', + # 'vns_sendTransaction', + # 'vns_sendRawTransaction', + # 'vns_call', + # 'vns_estimateGas', + 'vns_getBlockByHash', + # 'vns_getBlockByNumber', + 'vns_getTransactionByHash', + 'vns_getTransactionByBlockHashAndIndex', + # 'vns_getTransactionByBlockNumberAndIndex', + # 'vns_getTransactionReceipt', + 'vns_getUncleByBlockHashAndIndex', + # 'vns_getUncleByBlockNumberAndIndex', + # 'vns_getCompilers', + # 'vns_compileLLL', + # 'vns_compileSolidity', + # 'vns_compileSerpent', + # 'vns_newFilter', + # 'vns_newBlockFilter', + # 'vns_newPendingTransactionFilter', + # 'vns_uninstallFilter', + # 'vns_getFilterChanges', + # 'vns_getFilterLogs', + # 'vns_getLogs', + # 'vns_getWork', + # 'vns_submitWork', + # 'vns_submitHashrate', +} + + +def _should_cache(method, params, response): + if 'error' in response: + return False + elif 'result' not in response: + return False + + if response['result'] is None: + return False + return True + + +def construct_simple_cache_middleware( + cache_class, + rpc_whitelist=SIMPLE_CACHE_RPC_WHITELIST, + should_cache_fn=_should_cache): + """ + Constructs a middleware which caches responses based on the request + ``method`` and ``params`` + + :param cache: Any dictionary-like object + :param rpc_whitelist: A set of RPC methods which may have their responses cached. + :param should_cache_fn: A callable which accepts ``method`` ``params`` and + ``response`` and returns a boolean as to whether the response should be + cached. + """ + def simple_cache_middleware(make_request, web3): + cache = cache_class() + lock = threading.Lock() + + def middleware(method, params): + lock_acquired = lock.acquire(blocking=False) + + try: + if lock_acquired and method in rpc_whitelist: + cache_key = generate_cache_key((method, params)) + if cache_key not in cache: + response = make_request(method, params) + if should_cache_fn(method, params, response): + cache[cache_key] = response + return response + return cache[cache_key] + else: + return make_request(method, params) + finally: + if lock_acquired: + lock.release() + return middleware + return simple_cache_middleware + + +_simple_cache_middleware = construct_simple_cache_middleware( + cache_class=functools.partial(lru.LRU, 256), +) + + +TIME_BASED_CACHE_RPC_WHITELIST = { + # 'web3_clientVersion', + # 'web3_sha3', + # 'net_version', + # 'net_peerCount', + # 'net_listening', + # 'vns_protocolVersion', + # 'vns_syncing', + 'vns_coinbase', + # 'vns_mining', + # 'vns_hashrate', + # 'vns_gasPrice', + 'vns_accounts', + # 'vns_blockNumber', + # 'vns_getBalance', + # 'vns_getStorageAt', + # 'vns_getTransactionCount', + # 'vns_getBlockTransactionCountByHash', + # 'vns_getBlockTransactionCountByNumber', + # 'vns_getUncleCountByBlockHash', + # 'vns_getUncleCountByBlockNumber', + # 'vns_getCode', + # 'vns_sign', + # 'vns_sendTransaction', + # 'vns_sendRawTransaction', + # 'vns_call', + # 'vns_estimateGas', + # 'vns_getBlockByHash', + # 'vns_getBlockByNumber', + # 'vns_getTransactionByHash', + # 'vns_getTransactionByBlockHashAndIndex', + # 'vns_getTransactionByBlockNumberAndIndex', + # 'vns_getTransactionReceipt', + # 'vns_getUncleByBlockHashAndIndex', + # 'vns_getUncleByBlockNumberAndIndex', + # 'vns_getCompilers', + # 'vns_compileLLL', + # 'vns_compileSolidity', + # 'vns_compileSerpent', + # 'vns_newFilter', + # 'vns_newBlockFilter', + # 'vns_newPendingTransactionFilter', + # 'vns_uninstallFilter', + # 'vns_getFilterChanges', + # 'vns_getFilterLogs', + # 'vns_getLogs', + # 'vns_getWork', + # 'vns_submitWork', + # 'vns_submitHashrate', +} + + +def construct_time_based_cache_middleware( + cache_class, + cache_expire_seconds=15, + rpc_whitelist=TIME_BASED_CACHE_RPC_WHITELIST, + should_cache_fn=_should_cache): + """ + Constructs a middleware which caches responses based on the request + ``method`` and ``params`` for a maximum amount of time as specified + + :param cache: Any dictionary-like object + :param cache_expire_seconds: The number of seconds an item may be cached + before it should expire. + :param rpc_whitelist: A set of RPC methods which may have their responses cached. + :param should_cache_fn: A callable which accepts ``method`` ``params`` and + ``response`` and returns a boolean as to whether the response should be + cached. + """ + def time_based_cache_middleware(make_request, web3): + cache = cache_class() + lock = threading.Lock() + + def middleware(method, params): + lock_acquired = lock.acquire(blocking=False) + + try: + if lock_acquired and method in rpc_whitelist: + cache_key = generate_cache_key((method, params)) + if cache_key in cache: + # check that the cached response is not expired. + cached_at, cached_response = cache[cache_key] + cached_for = time.time() - cached_at + + if cached_for <= cache_expire_seconds: + return cached_response + else: + del cache[cache_key] + + # cache either missed or expired so make the request. + response = make_request(method, params) + + if should_cache_fn(method, params, response): + cache[cache_key] = (time.time(), response) + + return response + else: + return make_request(method, params) + finally: + if lock_acquired: + lock.release() + return middleware + return time_based_cache_middleware + + +_time_based_cache_middleware = construct_time_based_cache_middleware( + cache_class=functools.partial(lru.LRU, 256), +) + + +BLOCK_NUMBER_RPC_WHITELIST = { + # 'web3_clientVersion', + # 'web3_sha3', + # 'net_version', + # 'net_peerCount', + # 'net_listening', + # 'vns_protocolVersion', + # 'vns_syncing', + # 'vns_coinbase', + # 'vns_mining', + # 'vns_hashrate', + 'vns_gasPrice', + # 'vns_accounts', + 'vns_blockNumber', + 'vns_getBalance', + 'vns_getStorageAt', + 'vns_getTransactionCount', + # 'vns_getBlockTransactionCountByHash', + 'vns_getBlockTransactionCountByNumber', + # 'vns_getUncleCountByBlockHash', + 'vns_getUncleCountByBlockNumber', + 'vns_getCode', + # 'vns_sign', + # 'vns_sendTransaction', + # 'vns_sendRawTransaction', + 'vns_call', + 'vns_estimateGas', + # 'vns_getBlockByHash', + 'vns_getBlockByNumber', + # 'vns_getTransactionByHash', + # 'vns_getTransactionByBlockHashAndIndex', + 'vns_getTransactionByBlockNumberAndIndex', + 'vns_getTransactionReceipt', + # 'vns_getUncleByBlockHashAndIndex', + 'vns_getUncleByBlockNumberAndIndex', + # 'vns_getCompilers', + # 'vns_compileLLL', + # 'vns_compileSolidity', + # 'vns_compileSerpent', + # 'vns_newFilter', + # 'vns_newBlockFilter', + # 'vns_newPendingTransactionFilter', + # 'vns_uninstallFilter', + # 'vns_getFilterChanges', + # 'vns_getFilterLogs', + 'vns_getLogs', + # 'vns_getWork', + # 'vns_submitWork', + # 'vns_submitHashrate', +} + + +AVG_BLOCK_TIME_KEY = 'avg_block_time' +AVG_BLOCK_SAMPLE_SIZE_KEY = 'avg_block_sample_size' +AVG_BLOCK_TIME_UPDATED_AT_KEY = 'avg_block_time_updated_at' + + +def _is_latest_block_number_request(method, params): + if method != 'vns_getBlockByNumber': + return False + elif params[:1] == ['latest']: + return True + return False + + +def construct_latest_block_based_cache_middleware( + cache_class, + rpc_whitelist=BLOCK_NUMBER_RPC_WHITELIST, + average_block_time_sample_size=240, + default_average_block_time=15, + should_cache_fn=_should_cache): + """ + Constructs a middleware which caches responses based on the request + ``method``, ``params``, and the current latest block hash. + + :param cache: Any dictionary-like object + :param cache_expire_seconds: The number of seconds an item may be cached + before it should expire. + :param rpc_whitelist: A set of RPC methods which may have their responses cached. + :param should_cache_fn: A callable which accepts ``method`` ``params`` and + ``response`` and returns a boolean as to whether the response should be + cached. + + .. note:: + This middleware avoids re-fetching the current latest block for each + request by tracking the current average block time and only requesting + a new block when the last seen latest block is older than the average + block time. + """ + def latest_block_based_cache_middleware(make_request, web3): + cache = cache_class() + block_info = {} + + def _update_block_info_cache(): + avg_block_time = block_info.get(AVG_BLOCK_TIME_KEY, default_average_block_time) + avg_block_sample_size = block_info.get(AVG_BLOCK_SAMPLE_SIZE_KEY, 0) + avg_block_time_updated_at = block_info.get(AVG_BLOCK_TIME_UPDATED_AT_KEY, 0) + + # compute age as counted by number of blocks since the avg_block_time + if avg_block_time == 0: + avg_block_time_age_in_blocks = avg_block_sample_size + else: + avg_block_time_age_in_blocks = ( + (time.time() - avg_block_time_updated_at) / avg_block_time + ) + + if avg_block_time_age_in_blocks >= avg_block_sample_size: + # If the length of time since the average block time as + # measured by blocks is greater than or equal to the number of + # blocks sampled then we need to recompute the average block + # time. + latest_block = web3.vns.getBlock('latest') + ancestor_block_number = max( + 0, + latest_block['number'] - average_block_time_sample_size, + ) + ancestor_block = web3.vns.getBlock(ancestor_block_number) + sample_size = latest_block['number'] - ancestor_block_number + + block_info[AVG_BLOCK_SAMPLE_SIZE_KEY] = sample_size + if sample_size != 0: + block_info[AVG_BLOCK_TIME_KEY] = ( + (latest_block['timestamp'] - ancestor_block['timestamp']) / sample_size + ) + else: + block_info[AVG_BLOCK_TIME_KEY] = avg_block_time + block_info[AVG_BLOCK_TIME_UPDATED_AT_KEY] = time.time() + + if 'latest_block' in block_info: + latest_block = block_info['latest_block'] + time_since_latest_block = time.time() - latest_block['timestamp'] + + # latest block is too old so update cache + if time_since_latest_block > avg_block_time: + block_info['latest_block'] = web3.vns.getBlock('latest') + else: + # latest block has not been fetched so we fetch it. + block_info['latest_block'] = web3.vns.getBlock('latest') + + lock = threading.Lock() + + def middleware(method, params): + lock_acquired = lock.acquire(blocking=False) + + try: + should_try_cache = ( + lock_acquired and + method in rpc_whitelist and + not _is_latest_block_number_request(method, params) + ) + if should_try_cache: + _update_block_info_cache() + latest_block_hash = block_info['latest_block']['hash'] + cache_key = generate_cache_key((latest_block_hash, method, params)) + if cache_key in cache: + return cache[cache_key] + + response = make_request(method, params) + if should_cache_fn(method, params, response): + cache[cache_key] = response + return response + else: + return make_request(method, params) + finally: + if lock_acquired: + lock.release() + return middleware + return latest_block_based_cache_middleware + + +_latest_block_based_cache_middleware = construct_latest_block_based_cache_middleware( + cache_class=functools.partial(lru.LRU, 256), + rpc_whitelist=BLOCK_NUMBER_RPC_WHITELIST, +) diff --git a/web3/middleware/exception_handling.py b/web3/middleware/exception_handling.py index 3251964048..9e656ce7c8 100644 --- a/web3/middleware/exception_handling.py +++ b/web3/middleware/exception_handling.py @@ -1,44 +1,22 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Tuple, - Type, -) - -from eth_utils.toolz import ( - excepts, -) - -from web3.types import ( - Middleware, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def construct_exception_handler_middleware( - method_handlers: Dict[RPCEndpoint, Tuple[Type[BaseException], Callable[..., None]]]=None -) -> Middleware: - if method_handlers is None: - method_handlers = {} - - def exception_handler_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in method_handlers: - exc_type, handler = method_handlers[method] - return excepts( - exc_type, - make_request, - handler, - )(method, params) - else: - return make_request(method, params) - return middleware - return exception_handler_middleware +from web3._utils.toolz import ( + excepts, +) + + +def construct_exception_handler_middleware(method_handlers=None): + if method_handlers is None: + method_handlers = {} + + def exception_handler_middleware(make_request, web3): + def middleware(method, params): + if method in method_handlers: + exc_type, handler = method_handlers[method] + return excepts( + exc_type, + make_request, + handler, + )(method, params) + else: + return make_request(method, params) + return middleware + return exception_handler_middleware diff --git a/web3/middleware/exception_retry_request.py b/web3/middleware/exception_retry_request.py index f2bbafdb19..adf43872e4 100644 --- a/web3/middleware/exception_retry_request.py +++ b/web3/middleware/exception_retry_request.py @@ -1,124 +1,96 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Type, -) - -from requests.exceptions import ( - ConnectionError, - HTTPError, - Timeout, - TooManyRedirects, -) - -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -whitelist = [ - 'admin', - 'shh', - 'miner', - 'net', - 'txpool' - 'testing', - 'evm', - 'eth_protocolVersion', - 'eth_syncing', - 'eth_coinbase', - 'eth_mining', - 'eth_hashrate', - 'eth_gasPrice', - 'eth_accounts', - 'eth_blockNumber', - 'eth_getBalance', - 'eth_getStorageAt', - 'eth_getProof', - 'eth_getCode', - 'eth_getBlockByNumber', - 'eth_getBlockByHash', - 'eth_getBlockTransactionCountByNumber', - 'eth_getBlockTransactionCountByHash', - 'eth_getUncleCountByBlockNumber', - 'eth_getUncleCountByBlockHash', - 'eth_getTransactionByHash', - 'eth_getTransactionByBlockHashAndIndex', - 'eth_getTransactionByBlockNumberAndIndex', - 'eth_getTransactionReceipt', - 'eth_getTransactionCount', - 'eth_call', - 'eth_estimateGas', - 'eth_newBlockFilter', - 'eth_newPendingTransactionFilter', - 'eth_newFilter', - 'eth_getFilterChanges', - 'eth_getFilterLogs', - 'eth_getLogs', - 'eth_uninstallFilter', - 'eth_getCompilers', - 'eth_getWork', - 'eth_sign', - 'eth_signTypedData', - 'eth_sendRawTransaction', - 'personal_importRawKey', - 'personal_newAccount', - 'personal_listAccounts', - 'personal_lockAccount', - 'personal_unlockAccount', - 'personal_ecRecover', - 'personal_sign', - 'personal_signTypedData', -] - - -def check_if_retry_on_failure(method: RPCEndpoint) -> bool: - root = method.split('_')[0] - if root in whitelist: - return True - elif method in whitelist: - return True - else: - return False - - -def exception_retry_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], - web3: "Web3", - errors: Collection[Type[BaseException]], - retries: int=5, -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - """ - Creates middleware that retries failed HTTP requests. Is a default - middleware for HTTPProvider. - """ - def middleware(method: RPCEndpoint, params: Any) -> Callable[[RPCEndpoint, Any], RPCResponse]: - if check_if_retry_on_failure(method): - for i in range(retries): - try: - return make_request(method, params) - # https://github.com/python/mypy/issues/5349 - except errors: # type: ignore - if i < retries - 1: - continue - else: - raise - return None - else: - return make_request(method, params) - return middleware - - -def http_retry_request_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" -) -> Callable[[RPCEndpoint, Any], Any]: - return exception_retry_middleware( - make_request, - web3, - (ConnectionError, HTTPError, Timeout, TooManyRedirects) - ) +from requests.exceptions import ( + ConnectionError, + HTTPError, + Timeout, + TooManyRedirects, +) + +whitelist = [ + 'admin', + 'shh', + 'miner', + 'net', + 'txpool' + 'testing', + 'evm', + 'vns_protocolVersion', + 'vns_syncing', + 'vns_coinbase', + 'vns_mining', + 'vns_hashrate', + 'vns_gasPrice', + 'vns_accounts', + 'vns_blockNumber', + 'vns_getBalance', + 'vns_getStorageAt', + 'vns_getCode', + 'vns_getBlockByNumber', + 'vns_getBlockByHash', + 'vns_getBlockTransactionCountByNumber', + 'vns_getBlockTransactionCountByHash', + 'vns_getUncleCountByBlockNumber', + 'vns_getUncleCountByBlockHash', + 'vns_getTransactionByHash', + 'vns_getTransactionByBlockHashAndIndex', + 'vns_getTransactionByBlockNumberAndIndex', + 'vns_getTransactionReceipt', + 'vns_getTransactionCount', + 'vns_call', + 'vns_estimateGas', + 'vns_newBlockFilter', + 'vns_newPendingTransactionFilter', + 'vns_newFilter', + 'vns_getFilterChanges', + 'vns_getFilterLogs', + 'vns_getLogs', + 'vns_uninstallFilter', + 'vns_getCompilers', + 'vns_getWork', + 'vns_sign', + 'vns_sendRawTransaction', + 'personal_importRawKey', + 'personal_newAccount', + 'personal_listAccounts', + 'personal_lockAccount', + 'personal_unlockAccount', + 'personal_ecRecover', + 'personal_sign' +] + + +def check_if_retry_on_failure(method): + root = method.split('_')[0] + if root in whitelist: + return True + elif method in whitelist: + return True + else: + return False + + +def exception_retry_middleware(make_request, web3, errors, retries=5): + """ + Creates middleware that retries failed HTTP requests. Is a default + middleware for HTTPProvider. + """ + def middleware(method, params): + if check_if_retry_on_failure(method): + for i in range(retries): + try: + return make_request(method, params) + except errors: + if i < retries - 1: + continue + else: + raise + else: + return make_request(method, params) + return middleware + + +def http_retry_request_middleware(make_request, web3): + return exception_retry_middleware( + make_request, + web3, + (ConnectionError, HTTPError, Timeout, TooManyRedirects) + ) diff --git a/web3/middleware/filter.py b/web3/middleware/filter.py index af518ae214..2bf8e34017 100644 --- a/web3/middleware/filter.py +++ b/web3/middleware/filter.py @@ -1,365 +1,320 @@ -import itertools -import os -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Iterable, - Iterator, - List, - Optional, - Tuple, - Union, - cast, -) - -from eth_typing import ( - Address, - BlockNumber, - ChecksumAddress, - Hash32, -) -from eth_utils import ( - apply_key_map, - to_hex, - to_int, - to_list, -) -from eth_utils.toolz import ( - concat, - valfilter, -) - -from web3.types import ( - LatestBlockParam, - LogReceipt, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -if "WEB3_MAX_BLOCK_REQUEST" in os.environ: - MAX_BLOCK_REQUEST = to_int(text=os.environ["WEB3_MAX_BLOCK_REQUEST"]) -else: - MAX_BLOCK_REQUEST = 50 - - -def segment_count(start: int, stop: int, step: int=5) -> Iterable[Tuple[int, int]]: - """Creates a segment counting generator - - The generator returns tuple pairs of integers - that correspond to segments in the provided range. - - :param start: The initial value of the counting range - :param stop: The last value in the - counting range - :param step: Optional, the segment length. Default is 5. - :return: returns a generator object - - - Example: - - >>> segment_counter = segment_count(start=0, stop=10, step=3) - >>> next(segment_counter) - (0, 3) - >>> next(segment_counter) - (3, 6) - >>> next(segment_counter) - (6, 9) - >>> next(segment_counter) # Remainder is also returned - (9, 10) - """ - return gen_bounded_segments(start, stop, step) - - -def gen_bounded_segments(start: int, stop: int, step: int) -> Iterable[Tuple[int, int]]: - # If the initial range is less than the step - # just return (start, stop) - if start + step >= stop: - yield (start, stop) - return - for segment in zip( - range(start, stop - step + 1, step), - range(start + step, stop + 1, step)): - yield segment - - remainder = (stop - start) % step - # Handle the remainder - if remainder: - yield (stop - remainder, stop) - - -def block_ranges( - start_block: BlockNumber, last_block: Optional[BlockNumber], step: int=5 -) -> Iterable[Tuple[int, int]]: - """Returns 2-tuple ranges describing ranges of block from start_block to last_block - - Ranges do not overlap to facilitate use as ``toBlock``, ``fromBlock`` - json-rpc arguments, which are both inclusive. - """ - - if last_block is not None and start_block > last_block: - raise TypeError( - "Incompatible start and stop arguments.", - "Start must be less than or equal to stop.") - - return ( - (from_block, to_block - 1) - for from_block, to_block - in segment_count(start_block, last_block + 1, step) - ) - - -def iter_latest_block( - w3: "Web3", to_block: Union[BlockNumber, LatestBlockParam]=None -) -> Iterable[BlockNumber]: - """Returns a generator that dispenses the latest block, if - any new blocks have been mined since last iteration. - - If there are no new blocks None is returned. - - If ``to_block`` is defined, ``StopIteration`` is raised - after to_block is reached. - - >>> mined_blocks = dispense_mined_blocks(w3, 0, 10) - >>> next(new_blocks) # Latest block = 0 - 0 - >>> next(new_blocks) # No new blocks - >>> next(new_blocks) # Latest block = 1 - 1 - >>> next(new_blocks) # Latest block = 10 - 10 - >>> next(new_blocks) - Traceback (most recent call last): - File "", line 1, in - StopIteration - >>> - """ - _last = None - - is_bounded_range = ( - to_block is not None and - to_block != "latest" - ) - - while True: - latest_block = w3.eth.blockNumber - # type ignored b/c is_bounded_range prevents unsupported comparison - if is_bounded_range and latest_block > to_block: # type: ignore - return - # No new blocks since last iteration. - if _last is not None and _last == latest_block: - yield None - else: - yield latest_block - _last = latest_block - - -def iter_latest_block_ranges( - w3: "Web3", - from_block: BlockNumber, - to_block: Union[BlockNumber, LatestBlockParam]=None, -) -> Iterable[Tuple[Optional[BlockNumber], Optional[BlockNumber]]]: - """Returns an iterator unloading ranges of available blocks - - starting from `fromBlock` to the latest mined block, - until reaching toBlock. e.g.: - - - >>> blocks_to_filter = iter_latest_block_ranges(w3, 0, 50) - >>> next(blocks_to_filter) # latest block number = 11 - (0, 11) - >>> next(blocks_to_filter) # latest block number = 45 - (12, 45) - >>> next(blocks_to_filter) # latest block number = 50 - (46, 50) - """ - for latest_block in iter_latest_block(w3, to_block): - if latest_block is None: - yield (None, None) - elif from_block > latest_block: - yield (None, None) - else: - yield (from_block, latest_block) - from_block = BlockNumber(latest_block + 1) - - -def drop_items_with_none_value(params: Dict[str, Any]) -> Dict[str, Any]: - return valfilter(lambda x: x is not None, params) - - -def get_logs_multipart( - w3: "Web3", - startBlock: BlockNumber, - stopBlock: BlockNumber, - address: Union[Address, ChecksumAddress, List[Union[Address, ChecksumAddress]]], - topics: List[Optional[Union[Hash32, List[Hash32]]]], - max_blocks: int -) -> Iterable[List[LogReceipt]]: - """Used to break up requests to ``eth_getLogs`` - - The getLog request is partitioned into multiple calls of the max number of blocks - ``max_blocks``. - """ - _block_ranges = block_ranges(startBlock, stopBlock, max_blocks) - for from_block, to_block in _block_ranges: - params = { - "fromBlock": from_block, - "toBlock": to_block, - "address": address, - "topics": topics - } - yield w3.eth.getLogs( - drop_items_with_none_value(params)) - - -class RequestLogs: - def __init__( - self, - w3: "Web3", - from_block: Union[BlockNumber, LatestBlockParam]=None, - to_block: Union[BlockNumber, LatestBlockParam]=None, - address: Union[Address, ChecksumAddress, List[Union[Address, ChecksumAddress]]]=None, - topics: List[Optional[Union[Hash32, List[Hash32]]]]=None - ) -> None: - self.address = address - self.topics = topics - self.w3 = w3 - if from_block is None or from_block == "latest": - self._from_block = BlockNumber(w3.eth.blockNumber + 1) - else: - # cast b/c LatestBlockParam is handled above - self._from_block = cast(BlockNumber, from_block) - self._to_block = to_block - self.filter_changes = self._get_filter_changes() - - @property - def from_block(self) -> BlockNumber: - return self._from_block - - @property - def to_block(self) -> BlockNumber: - if self._to_block is None: - to_block = self.w3.eth.blockNumber - elif self._to_block == "latest": - to_block = self.w3.eth.blockNumber - else: - to_block = cast(BlockNumber, self._to_block) - - return to_block - - def _get_filter_changes(self) -> Iterator[List[LogReceipt]]: - for start, stop in iter_latest_block_ranges(self.w3, self.from_block, self.to_block): - if None in (start, stop): - yield [] - - yield list( - concat( - get_logs_multipart( - self.w3, - start, - stop, - self.address, - self.topics, - max_blocks=MAX_BLOCK_REQUEST))) - - def get_logs(self) -> List[LogReceipt]: - return list( - concat( - get_logs_multipart( - self.w3, - self.from_block, - self.to_block, - self.address, - self.topics, - max_blocks=MAX_BLOCK_REQUEST))) - - -FILTER_PARAMS_KEY_MAP = { - "toBlock": "to_block", - "fromBlock": "from_block" -} - -NEW_FILTER_METHODS = set([ - "eth_newBlockFilter", - "eth_newFilter"]) - -FILTER_CHANGES_METHODS = set([ - "eth_getFilterChanges", - "eth_getFilterLogs"]) - - -class RequestBlocks: - def __init__(self, w3: "Web3") -> None: - self.w3 = w3 - self.start_block = BlockNumber(w3.eth.blockNumber + 1) - - @property - def filter_changes(self) -> Iterator[List[Hash32]]: - return self.get_filter_changes() - - def get_filter_changes(self) -> Iterator[List[Hash32]]: - block_range_iter = iter_latest_block_ranges( - self.w3, - self.start_block, - None) - - for block_range in block_range_iter: - yield (block_hashes_in_range(self.w3, block_range)) - - -@to_list -def block_hashes_in_range( - w3: "Web3", block_range: Tuple[BlockNumber, BlockNumber] -) -> Iterable[Hash32]: - from_block, to_block = block_range - if from_block is None or to_block is None: - return - for block_number in range(from_block, to_block + 1): - yield getattr(w3.eth.getBlock(BlockNumber(block_number)), "hash", None) - - -def local_filter_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - filters = {} - filter_id_counter = map(to_hex, itertools.count()) - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in NEW_FILTER_METHODS: - - filter_id = next(filter_id_counter) - - _filter: Union[RequestLogs, RequestBlocks] - if method == "eth_newFilter": - _filter = RequestLogs(w3, **apply_key_map(FILTER_PARAMS_KEY_MAP, params[0])) - - elif method == "eth_newBlockFilter": - _filter = RequestBlocks(w3) - - else: - raise NotImplementedError(method) - - filters[filter_id] = _filter - return {"result": filter_id} - - elif method in FILTER_CHANGES_METHODS: - filter_id = params[0] - # Pass through to filters not created by middleware - if filter_id not in filters: - return make_request(method, params) - _filter = filters[filter_id] - if method == "eth_getFilterChanges": - return {"result": next(_filter.filter_changes)} - elif method == "eth_getFilterLogs": - # type ignored b/c logic prevents RequestBlocks which doesn't implement get_logs - return {"result": _filter.get_logs()} # type: ignore - else: - raise NotImplementedError(method) - else: - return make_request(method, params) - - return middleware +import itertools +import os + +from vns_utils import ( + apply_key_map, + to_hex, + to_list, +) + +from web3._utils.toolz import ( + concat, + valfilter, +) + +if 'WEB3_MAX_BLOCK_REQUEST' in os.environ: + MAX_BLOCK_REQUEST = os.environ['WEB3_MAX_BLOCK_REQUEST'] +else: + MAX_BLOCK_REQUEST = 50 + + +def segment_count(start, stop, step=5): + """Creates a segment counting generator + + The generator returns tuple pairs of integers + that correspond to segments in the provided range. + + :param start: The initial value of the counting range + :param stop: The last value in the + counting range + :param step: Optional, the segment length. Default is 5. + :type start: int + :type stop: int + :return: returns a generator object + + + Example: + + >>> segment_counter = segment_count(start=0, stop=10, step=3) + >>> next(segment_counter) + (0, 3) + >>> next(segment_counter) + (3, 6) + >>> next(segment_counter) + (6, 9) + >>> next(segment_counter) # Remainder is also returned + (9, 10) + """ + return gen_bounded_segments(start, stop, step) + + +def gen_bounded_segments(start, stop, step): + # If the initial range is less than the step + # just return (start, stop) + if start + step >= stop: + yield (start, stop) + return + for segment in zip( + range(start, stop - step + 1, step), + range(start + step, stop + 1, step)): + yield segment + + remainder = (stop - start) % step + # Handle the remainder + if remainder: + yield (stop - remainder, stop) + + +def block_ranges(start_block, last_block, step=5): + """Returns 2-tuple ranges describing ranges of block from start_block to last_block + + Ranges do not overlap to facilitate use as ``toBlock``, ``fromBlock`` + json-rpc arguments, which are both inclusive. + """ + + if last_block is not None and start_block > last_block: + raise TypeError( + "Incompatible start and stop arguments.", + "Start must be less than or equal to stop.") + + return ( + (from_block, to_block - 1) + for from_block, to_block + in segment_count(start_block, last_block + 1, step) + ) + + +def iter_latest_block(w3, to_block=None): + """Returns a generator that dispenses the latest block, if + any new blocks have been mined since last iteration. + + If there are no new blocks None is returned. + + If ``to_block`` is defined, ``StopIteration`` is raised + after to_block is reached. + + >>> mined_blocks = dispense_mined_blocks(w3, 0, 10) + >>> next(new_blocks) # Latest block = 0 + 0 + >>> next(new_blocks) # No new blocks + >>> next(new_blocks) # Latest block = 1 + 1 + >>> next(new_blocks) # Latest block = 10 + 10 + >>> next(new_blocks) + Traceback (most recent call last): + File "", line 1, in + StopIteration + >>> + """ + _last = None + + is_bounded_range = ( + to_block is not None and + to_block is not 'latest' + ) + + while True: + latest_block = w3.vns.blockNumber + if is_bounded_range and latest_block > to_block: + return + # No new blocks since last iteration. + if _last is not None and _last == latest_block: + yield None + else: + yield latest_block + _last = latest_block + + +def iter_latest_block_ranges(w3, from_block, to_block=None): + """Returns an iterator unloading ranges of available blocks + + starting from `fromBlock` to the latest mined block, + until reaching toBlock. e.g.: + + + >>> blocks_to_filter = iter_latest_block_ranges(w3, 0, 50) + >>> next(blocks_to_filter) # latest block number = 11 + (0, 11) + >>> next(blocks_to_filter) # latest block number = 45 + (12, 45) + >>> next(blocks_to_filter) # latest block number = 50 + (46, 50) + """ + for latest_block in iter_latest_block(w3, to_block): + if latest_block is None: + yield (None, None) + elif from_block > latest_block: + yield (None, None) + else: + yield (from_block, latest_block) + from_block = latest_block + 1 + + +def drop_items_with_none_value(params): + return valfilter(lambda x: x is not None, params) + + +def get_logs_multipart( + w3, + startBlock, + stopBlock, + address, + topics, + max_blocks): + """Used to break up requests to ``vns_getLogs`` + + The getLog request is partitioned into multiple calls of the max number of blocks + ``max_blocks``. + """ + _block_ranges = block_ranges(startBlock, stopBlock, max_blocks) + for from_block, to_block in _block_ranges: + params = { + 'fromBlock': from_block, + 'toBlock': to_block, + 'address': address, + 'topics': topics + } + yield w3.vns.getLogs( + drop_items_with_none_value(params)) + + +class RequestLogs: + def __init__( + self, + w3, + from_block=None, + to_block=None, + address=None, + topics=None): + + self.address = address + self.topics = topics + self.w3 = w3 + if from_block is None or from_block == 'latest': + self._from_block = w3.vns.blockNumber + 1 + else: + self._from_block = from_block + self._to_block = to_block + self.filter_changes = self._get_filter_changes() + + @property + def from_block(self): + return self._from_block + + @property + def to_block(self): + if self._to_block is None: + to_block = self.w3.vns.blockNumber + elif self._to_block == 'latest': + to_block = self.w3.vns.blockNumber + else: + to_block = self._to_block + + return to_block + + def _get_filter_changes(self): + for start, stop in iter_latest_block_ranges(self.w3, self.from_block, self.to_block): + if None in (start, stop): + yield [] + + yield list( + concat( + get_logs_multipart( + self.w3, + start, + stop, + self.address, + self.topics, + max_blocks=MAX_BLOCK_REQUEST))) + + def get_logs(self): + return list( + concat( + get_logs_multipart( + self.w3, + self.from_block, + self.to_block, + self.address, + self.topics, + max_blocks=MAX_BLOCK_REQUEST))) + + +FILTER_PARAMS_KEY_MAP = { + 'toBlock': 'to_block', + 'fromBlock': 'from_block' +} + +NEW_FILTER_METHODS = set([ + 'vns_newBlockFilter', + 'vns_newFilter']) + +FILTER_CHANGES_METHODS = set([ + 'vns_getFilterChanges', + 'vns_getFilterLogs']) + + +class RequestBlocks: + def __init__(self, w3): + self.w3 = w3 + self.start_block = w3.vns.blockNumber + 1 + + @property + def filter_changes(self): + return self.get_filter_changes() + + def get_filter_changes(self): + + block_range_iter = iter_latest_block_ranges( + self.w3, + self.start_block, + None) + + for block_range in block_range_iter: + yield(block_hashes_in_range(self.w3, block_range)) + + +@to_list +def block_hashes_in_range(w3, block_range): + from_block, to_block = block_range + for block_number in range(from_block, to_block + 1): + yield getattr(w3.vns.getBlock(block_number), 'hash', None) + + +def local_filter_middleware(make_request, w3): + filters = {} + filter_id_counter = map(to_hex, itertools.count()) + + def middleware(method, params): + if method in NEW_FILTER_METHODS: + + filter_id = next(filter_id_counter) + + if method == 'vns_newFilter': + _filter = RequestLogs(w3, **apply_key_map(FILTER_PARAMS_KEY_MAP, params[0])) + + elif method == 'vns_newBlockFilter': + _filter = RequestBlocks(w3) + + else: + raise NotImplementedError(method) + + filters[filter_id] = _filter + return {'result': filter_id} + + elif method in FILTER_CHANGES_METHODS: + filter_id = params[0] + # Pass through to filters not created by middleware + if filter_id not in filters: + return make_request(method, params) + _filter = filters[filter_id] + if method == 'vns_getFilterChanges': + return {'result': next(_filter.filter_changes)} + elif method == 'vns_getFilterLogs': + return {'result': _filter.get_logs()} + else: + raise NotImplementedError(method) + else: + return make_request(method, params) + + return middleware diff --git a/web3/middleware/fixture.py b/web3/middleware/fixture.py index b54d0902a8..97472790c2 100644 --- a/web3/middleware/fixture.py +++ b/web3/middleware/fixture.py @@ -1,77 +1,50 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, -) - -from web3.types import ( - Middleware, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def construct_fixture_middleware(fixtures: Dict[RPCEndpoint, Any]) -> Middleware: - """ - Constructs a middleware which returns a static response for any method - which is found in the provided fixtures. - """ - def fixture_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in fixtures: - result = fixtures[method] - return {'result': result} - else: - return make_request(method, params) - return middleware - return fixture_middleware - - -def construct_result_generator_middleware( - result_generators: Dict[RPCEndpoint, Any] -) -> Middleware: - """ - Constructs a middleware which intercepts requests for any method found in - the provided mapping of endpoints to generator functions, returning - whatever response the generator function returns. Callbacks must be - functions with the signature `fn(method, params)`. - """ - def result_generator_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in result_generators: - result = result_generators[method](method, params) - return {'result': result} - else: - return make_request(method, params) - return middleware - return result_generator_middleware - - -def construct_error_generator_middleware( - error_generators: Dict[RPCEndpoint, Any] -) -> Middleware: - """ - Constructs a middleware which intercepts requests for any method found in - the provided mapping of endpoints to generator functions, returning - whatever error message the generator function returns. Callbacks must be - functions with the signature `fn(method, params)`. - """ - def error_generator_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method in error_generators: - error_msg = error_generators[method](method, params) - return {'error': error_msg} - else: - return make_request(method, params) - return middleware - return error_generator_middleware +def construct_fixture_middleware(fixtures): + """ + Constructs a middleware which returns a static response for any method + which is found in the provided fixtures. + """ + def fixture_middleware(make_request, web3): + def middleware(method, params): + if method in fixtures: + result = fixtures[method] + return {'result': result} + else: + return make_request(method, params) + return middleware + return fixture_middleware + + +def construct_result_generator_middleware(result_generators): + """ + Constructs a middleware which intercepts requests for any method found in + the provided mapping of endpoints to generator functions, returning + whatever response the generator function returns. Callbacks must be + functions with the signature `fn(method, params)`. + """ + def result_generator_middleware(make_request, web3): + def middleware(method, params): + if method in result_generators: + result = result_generators[method](method, params) + return {'result': result} + else: + return make_request(method, params) + return middleware + return result_generator_middleware + + +def construct_error_generator_middleware(error_generators): + """ + Constructs a middleware which intercepts requests for any method found in + the provided mapping of endpoints to generator functions, returning + whatever error message the generator function returns. Callbacks must be + functions with the signature `fn(method, params)`. + """ + def error_generator_middleware(make_request, web3): + def middleware(method, params): + if method in error_generators: + error_msg = error_generators[method](method, params) + return {'error': error_msg} + else: + return make_request(method, params) + return middleware + return error_generator_middleware diff --git a/web3/middleware/formatting.py b/web3/middleware/formatting.py index 6727eeed53..788b97c671 100644 --- a/web3/middleware/formatting.py +++ b/web3/middleware/formatting.py @@ -1,94 +1,69 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_utils.toolz import ( - assoc, - curry, - merge, -) - -from web3.types import ( - Formatters, - FormattersDict, - Middleware, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def construct_formatting_middleware( - request_formatters: Formatters=None, - result_formatters: Formatters=None, - error_formatters: Formatters=None -) -> Middleware: - def ignore_web3_in_standard_formatters( - w3: "Web3", - ) -> FormattersDict: - return dict( - request_formatters=request_formatters or {}, - result_formatters=result_formatters or {}, - error_formatters=error_formatters or {}, - ) - - return construct_web3_formatting_middleware(ignore_web3_in_standard_formatters) - - -def construct_web3_formatting_middleware( - web3_formatters_builder: Callable[["Web3"], FormattersDict] -) -> Middleware: - def formatter_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - formatters = merge( - { - "request_formatters": {}, - "result_formatters": {}, - "error_formatters": {}, - }, - web3_formatters_builder(w3), - ) - return apply_formatters(make_request=make_request, **formatters) - - return formatter_middleware - - -@curry -def apply_formatters( - method: RPCEndpoint, - params: Any, - make_request: Callable[[RPCEndpoint, Any], RPCResponse], - request_formatters: Formatters, - result_formatters: Formatters, - error_formatters: Formatters, -) -> RPCResponse: - if method in request_formatters: - formatter = request_formatters[method] - formatted_params = formatter(params) - response = make_request(method, formatted_params) - else: - response = make_request(method, params) - - if "result" in response and method in result_formatters: - formatter = result_formatters[method] - formatted_response = assoc( - response, - "result", - formatter(response["result"]), - ) - return formatted_response - elif "error" in response and method in error_formatters: - formatter = error_formatters[method] - formatted_response = assoc( - response, - "error", - formatter(response["error"]), - ) - return formatted_response - else: - return response +from web3._utils.toolz import ( + assoc, + curry, + merge, +) + + +def construct_formatting_middleware( + request_formatters=None, + result_formatters=None, + error_formatters=None): + def ignore_web3_in_standard_formatters(w3): + return dict( + request_formatters=request_formatters or {}, + result_formatters=result_formatters or {}, + error_formatters=error_formatters or {}, + ) + + return construct_web3_formatting_middleware(ignore_web3_in_standard_formatters) + + +def construct_web3_formatting_middleware(web3_formatters_builder): + def formatter_middleware(make_request, w3): + formatters = merge( + { + 'request_formatters': {}, + 'result_formatters': {}, + 'error_formatters': {}, + }, + web3_formatters_builder(w3), + ) + return apply_formatters(make_request=make_request, **formatters) + + return formatter_middleware + + +@curry +def apply_formatters( + method, + params, + make_request, + request_formatters, + result_formatters, + error_formatters): + if method in request_formatters: + formatter = request_formatters[method] + formatted_params = formatter(params) + response = make_request(method, formatted_params) + else: + response = make_request(method, params) + + if 'result' in response and method in result_formatters: + formatter = result_formatters[method] + formatted_response = assoc( + response, + 'result', + formatter(response['result']), + ) + return formatted_response + elif 'error' in response and method in error_formatters: + formatter = error_formatters[method] + formatted_response = assoc( + response, + 'error', + formatter(response['error']), + ) + return formatted_response + else: + return response diff --git a/web3/middleware/gas_price_strategy.py b/web3/middleware/gas_price_strategy.py index 6497f265eb..f3c1ced86f 100644 --- a/web3/middleware/gas_price_strategy.py +++ b/web3/middleware/gas_price_strategy.py @@ -1,35 +1,19 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_utils.toolz import ( - assoc, -) - -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def gas_price_strategy_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - """ - Includes a gas price using the gas price strategy - """ - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method == 'eth_sendTransaction': - transaction = params[0] - if 'gasPrice' not in transaction: - generated_gas_price = web3.eth.generateGasPrice(transaction) - if generated_gas_price is not None: - transaction = assoc(transaction, 'gasPrice', generated_gas_price) - return make_request(method, [transaction]) - return make_request(method, params) - return middleware +from web3._utils.toolz import ( + assoc, +) + + +def gas_price_strategy_middleware(make_request, web3): + """ + Includes a gas price using the gas price strategy + """ + def middleware(method, params): + if method == 'vns_sendTransaction': + transaction = params[0] + if 'gasPrice' not in transaction: + generated_gas_price = web3.vns.generateGasPrice(transaction) + if generated_gas_price is not None: + transaction = assoc(transaction, 'gasPrice', generated_gas_price) + return make_request(method, [transaction]) + return make_request(method, params) + return middleware diff --git a/web3/middleware/geth_poa.py b/web3/middleware/geth_poa.py index a7e7947b32..7a05cfde52 100644 --- a/web3/middleware/geth_poa.py +++ b/web3/middleware/geth_poa.py @@ -1,34 +1,31 @@ -from eth_utils.curried import ( - apply_formatters_to_dict, - apply_key_map, -) -from eth_utils.toolz import ( - compose, -) -from hexbytes import ( - HexBytes, -) - -from web3.middleware.formatting import ( - construct_formatting_middleware, -) -from web3.types import ( - RPCEndpoint, -) - -remap_geth_poa_fields = apply_key_map({ - 'extraData': 'proofOfAuthorityData', -}) - -pythonic_geth_poa = apply_formatters_to_dict({ - 'proofOfAuthorityData': HexBytes, -}) - -geth_poa_cleanup = compose(pythonic_geth_poa, remap_geth_poa_fields) - -geth_poa_middleware = construct_formatting_middleware( - result_formatters={ - RPCEndpoint("eth_getBlockByHash"): geth_poa_cleanup, - RPCEndpoint("eth_getBlockByNumber"): geth_poa_cleanup, - }, -) +from vns_utils.curried import ( + apply_formatters_to_dict, + apply_key_map, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.toolz import ( + compose, +) +from web3.middleware.formatting import ( + construct_formatting_middleware, +) + +remap_geth_poa_fields = apply_key_map({ + 'extraData': 'proofOfAuthorityData', +}) + +pythonic_geth_poa = apply_formatters_to_dict({ + 'proofOfAuthorityData': HexBytes, +}) + +geth_poa_cleanup = compose(pythonic_geth_poa, remap_geth_poa_fields) + +geth_poa_middleware = construct_formatting_middleware( + result_formatters={ + 'vns_getBlockByHash': geth_poa_cleanup, + 'vns_getBlockByNumber': geth_poa_cleanup, + }, +) diff --git a/web3/middleware/names.py b/web3/middleware/names.py index 947863bd1f..87a56e0e1b 100644 --- a/web3/middleware/names.py +++ b/web3/middleware/names.py @@ -1,30 +1,20 @@ -from typing import ( - TYPE_CHECKING, -) - -from web3._utils.normalizers import ( - abi_ens_resolver, -) -from web3._utils.rpc_abi import ( - RPC_ABIS, - abi_request_formatters, -) -from web3.types import ( - Middleware, -) - -from .formatting import ( - construct_formatting_middleware, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def name_to_address_middleware(w3: "Web3") -> Middleware: - normalizers = [ - abi_ens_resolver(w3), - ] - return construct_formatting_middleware( - request_formatters=abi_request_formatters(normalizers, RPC_ABIS) - ) +from web3._utils.normalizers import ( + abi_ens_resolver, +) +from web3._utils.rpc_abi import ( + RPC_ABIS, + abi_request_formatters, +) + +from .formatting import ( + construct_formatting_middleware, +) + + +def name_to_address_middleware(w3): + normalizers = [ + abi_ens_resolver(w3), + ] + return construct_formatting_middleware( + request_formatters=abi_request_formatters(normalizers, RPC_ABIS) + ) diff --git a/web3/middleware/normalize_errors.py b/web3/middleware/normalize_errors.py index fce1032446..8dac739775 100644 --- a/web3/middleware/normalize_errors.py +++ b/web3/middleware/normalize_errors.py @@ -1,43 +1,27 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_utils.toolz import ( - assoc, - dissoc, -) - -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -def normalize_errors_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - result = make_request(method, params) - - # As of v1.8, Geth returns errors when you request a - # receipt for a transaction that is not in the chain. - # It used to return a result of None, so we simulate the old behavior. - - if method == "eth_getTransactionReceipt" and "error" in result: - is_geth = str(web3.clientVersion).startswith("Geth") - if is_geth and result["error"]["code"] == -32000: - return assoc( - dissoc(result, "error"), - "result", - None, - ) - else: - return result - else: - return result - return middleware +from web3._utils.toolz import ( + assoc, + dissoc, +) + + +def normalize_errors_middleware(make_request, web3): + def middleware(method, params): + result = make_request(method, params) + + # As of v1.8, Geth returns errors when you request a + # receipt for a transaction that is not in the chain. + # It used to return a result of None, so we simulate the old behavior. + + if method == 'vns_getTransactionReceipt' and 'error' in result: + is_geth = web3.clientVersion.startswith('Geth') + if is_geth and result['error']['code'] == -32000: + return assoc( + dissoc(result, 'error'), + 'result', + None, + ) + else: + return result + else: + return result + return middleware diff --git a/web3/middleware/normalize_request_parameters.py b/web3/middleware/normalize_request_parameters.py index 93926c99a4..963aa22ad4 100644 --- a/web3/middleware/normalize_request_parameters.py +++ b/web3/middleware/normalize_request_parameters.py @@ -1,11 +1,25 @@ -from web3._utils.method_formatters import ( - METHOD_NORMALIZERS, -) - -from .formatting import ( - construct_formatting_middleware, -) - -request_parameter_normalizer = construct_formatting_middleware( - request_formatters=METHOD_NORMALIZERS, -) +from vns_utils import ( + is_string, +) + +from web3._utils.formatters import ( + apply_formatter_at_index, + apply_formatter_if, + apply_formatters_to_dict, +) + +from .formatting import ( + construct_formatting_middleware, +) + +FILTER_PARAM_NORMALIZERS = apply_formatters_to_dict({ + 'address': apply_formatter_if(is_string, lambda x: [x])}) + +METHOD_NORMALIZERS = { + 'vns_getLogs': apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0), + 'vns_newFilter': apply_formatter_at_index(FILTER_PARAM_NORMALIZERS, 0) +} + +request_parameter_normalizer = construct_formatting_middleware( + request_formatters=METHOD_NORMALIZERS, +) diff --git a/web3/middleware/pythonic.py b/web3/middleware/pythonic.py index 1ffd253e68..833ece00b7 100644 --- a/web3/middleware/pythonic.py +++ b/web3/middleware/pythonic.py @@ -1,12 +1,372 @@ -from web3._utils.method_formatters import ( - PYTHONIC_REQUEST_FORMATTERS, - PYTHONIC_RESULT_FORMATTERS, -) -from web3.middleware.formatting import ( - construct_formatting_middleware, -) - -pythonic_middleware = construct_formatting_middleware( - request_formatters=PYTHONIC_REQUEST_FORMATTERS, - result_formatters=PYTHONIC_RESULT_FORMATTERS, -) +import codecs +import operator + +from vns_utils.curried import ( + combine_argument_formatters, + is_address, + is_bytes, + is_integer, + is_null, + is_string, + remove_0x_prefix, + text_if_str, + to_checksum_address, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.abi import ( + is_length, +) +from web3._utils.encoding import ( + hexstr_if_str, + to_hex, +) +from web3._utils.formatters import ( + apply_formatter_at_index, + apply_formatter_if, + apply_formatter_to_array, + apply_formatters_to_dict, + apply_one_of_formatters, + hex_to_integer, + integer_to_hex, + is_array_of_dicts, + is_array_of_strings, + remove_key_if, +) +from web3._utils.toolz import ( + complement, + compose, + curry, + partial, +) +from web3._utils.toolz.curried import ( + keymap, + valmap, +) + +from .formatting import ( + construct_formatting_middleware, +) + + +def bytes_to_ascii(value): + return codecs.decode(value, 'ascii') + + +to_ascii_if_bytes = apply_formatter_if(is_bytes, bytes_to_ascii) +to_integer_if_hex = apply_formatter_if(is_string, hex_to_integer) +block_number_formatter = apply_formatter_if(is_integer, integer_to_hex) + + +is_false = partial(operator.is_, False) + +is_not_false = complement(is_false) +is_not_null = complement(is_null) + + +@curry +def to_hexbytes(num_bytes, val, variable_length=False): + if isinstance(val, (str, int, bytes)): + result = HexBytes(val) + else: + raise TypeError("Cannot convert %r to HexBytes" % val) + + extra_bytes = len(result) - num_bytes + if extra_bytes == 0 or (variable_length and extra_bytes < 0): + return result + elif all(byte == 0 for byte in result[:extra_bytes]): + return HexBytes(result[extra_bytes:]) + else: + raise ValueError( + "The value %r is %d bytes, but should be %d" % ( + result, len(result), num_bytes + ) + ) + + +TRANSACTION_FORMATTERS = { + 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), + 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), + 'nonce': to_integer_if_hex, + 'gas': to_integer_if_hex, + 'gasPrice': to_integer_if_hex, + 'value': to_integer_if_hex, + 'from': to_checksum_address, + 'publicKey': apply_formatter_if(is_not_null, to_hexbytes(64)), + 'r': to_hexbytes(32, variable_length=True), + 'raw': HexBytes, + 's': to_hexbytes(32, variable_length=True), + 'to': apply_formatter_if(is_address, to_checksum_address), + 'hash': to_hexbytes(32), + 'v': apply_formatter_if(is_not_null, to_integer_if_hex), + 'standardV': apply_formatter_if(is_not_null, to_integer_if_hex), +} + + +transaction_formatter = apply_formatters_to_dict(TRANSACTION_FORMATTERS) + + +SIGNED_TX_FORMATTER = { + 'raw': HexBytes, + 'tx': transaction_formatter, +} + + +signed_tx_formatter = apply_formatters_to_dict(SIGNED_TX_FORMATTER) + + +WHISPER_LOG_FORMATTERS = { + 'sig': to_hexbytes(130), + 'topic': to_hexbytes(8), + 'payload': HexBytes, + 'padding': apply_formatter_if(is_not_null, HexBytes), + 'hash': to_hexbytes(64), + 'recipientPublicKey': apply_formatter_if(is_not_null, to_hexbytes(130)), +} + + +whisper_log_formatter = apply_formatters_to_dict(WHISPER_LOG_FORMATTERS) + + +LOG_ENTRY_FORMATTERS = { + 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), + 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), + 'transactionHash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'logIndex': to_integer_if_hex, + 'address': to_checksum_address, + 'topics': apply_formatter_to_array(to_hexbytes(32)), + 'data': to_ascii_if_bytes, +} + + +log_entry_formatter = apply_formatters_to_dict(LOG_ENTRY_FORMATTERS) + + +RECEIPT_FORMATTERS = { + 'blockHash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'blockNumber': apply_formatter_if(is_not_null, to_integer_if_hex), + 'transactionIndex': apply_formatter_if(is_not_null, to_integer_if_hex), + 'transactionHash': to_hexbytes(32), + 'cumulativeGasUsed': to_integer_if_hex, + 'status': to_integer_if_hex, + 'gasUsed': to_integer_if_hex, + 'contractAddress': apply_formatter_if(is_not_null, to_checksum_address), + 'logs': apply_formatter_to_array(log_entry_formatter), + 'logsBloom': to_hexbytes(256), +} + + +receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS) + +BLOCK_FORMATTERS = { + 'extraData': to_hexbytes(32, variable_length=True), + 'gasLimit': to_integer_if_hex, + 'gasUsed': to_integer_if_hex, + 'size': to_integer_if_hex, + 'timestamp': to_integer_if_hex, + 'hash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'logsBloom': to_hexbytes(256), + 'miner': apply_formatter_if(is_not_null, to_checksum_address), + 'mixHash': to_hexbytes(32), + 'nonce': apply_formatter_if(is_not_null, to_hexbytes(8, variable_length=True)), + 'number': apply_formatter_if(is_not_null, to_integer_if_hex), + 'parentHash': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'sha3Uncles': apply_formatter_if(is_not_null, to_hexbytes(32)), + 'uncles': apply_formatter_to_array(to_hexbytes(32)), + 'difficulty': to_integer_if_hex, + 'receiptsRoot': to_hexbytes(32), + 'stateRoot': to_hexbytes(32), + 'totalDifficulty': to_integer_if_hex, + 'transactions': apply_one_of_formatters(( + (apply_formatter_to_array(transaction_formatter), is_array_of_dicts), + (apply_formatter_to_array(to_hexbytes(32)), is_array_of_strings), + )), + 'transactionsRoot': to_hexbytes(32), +} + + +block_formatter = apply_formatters_to_dict(BLOCK_FORMATTERS) + + +SYNCING_FORMATTERS = { + 'startingBlock': to_integer_if_hex, + 'currentBlock': to_integer_if_hex, + 'highestBlock': to_integer_if_hex, + 'knownStates': to_integer_if_hex, + 'pulledStates': to_integer_if_hex, +} + + +syncing_formatter = apply_formatters_to_dict(SYNCING_FORMATTERS) + + +TRANSACTION_POOL_CONTENT_FORMATTERS = { + 'pending': compose( + keymap(to_ascii_if_bytes), + valmap(transaction_formatter), + ), + 'queued': compose( + keymap(to_ascii_if_bytes), + valmap(transaction_formatter), + ), +} + + +transaction_pool_content_formatter = apply_formatters_to_dict( + TRANSACTION_POOL_CONTENT_FORMATTERS +) + + +TRANSACTION_POOL_INSPECT_FORMATTERS = { + 'pending': keymap(to_ascii_if_bytes), + 'queued': keymap(to_ascii_if_bytes), +} + + +transaction_pool_inspect_formatter = apply_formatters_to_dict( + TRANSACTION_POOL_INSPECT_FORMATTERS +) + + +FILTER_PARAMS_FORMATTERS = { + 'fromBlock': apply_formatter_if(is_integer, integer_to_hex), + 'toBlock': apply_formatter_if(is_integer, integer_to_hex), +} + + +filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS) + + +filter_result_formatter = apply_one_of_formatters(( + (apply_formatter_to_array(log_entry_formatter), is_array_of_dicts), + (apply_formatter_to_array(to_hexbytes(32)), is_array_of_strings), +)) + +TRANSACTION_PARAM_FORMATTERS = { + 'chainId': apply_formatter_if(is_integer, str), +} + + +transaction_param_formatter = compose( + remove_key_if('to', lambda txn: txn['to'] in {'', b'', None}), + apply_formatters_to_dict(TRANSACTION_PARAM_FORMATTERS), +) + +estimate_gas_without_block_id = apply_formatter_at_index(transaction_param_formatter, 0) +estimate_gas_with_block_id = combine_argument_formatters( + transaction_param_formatter, + block_number_formatter, +) + + +pythonic_middleware = construct_formatting_middleware( + request_formatters={ + # Bbbbbbbb 'vns_getBalance': apply_formatter_at_index(block_number_formatter, 1), + 'vns_getBlockByNumber': apply_formatter_at_index(block_number_formatter, 0), + 'vns_getBlockTransactionCountByNumber': apply_formatter_at_index( + block_number_formatter, + 0, + ), + 'vns_getCode': apply_formatter_at_index(block_number_formatter, 1), + 'vns_getStorageAt': apply_formatter_at_index(block_number_formatter, 2), + 'vns_getTransactionByBlockNumberAndIndex': compose( + apply_formatter_at_index(block_number_formatter, 0), + apply_formatter_at_index(integer_to_hex, 1), + ), + 'vns_getTransactionCount': apply_formatter_at_index(block_number_formatter, 1), + 'vns_getUncleCountByBlockNumber': apply_formatter_at_index(block_number_formatter, 0), + 'vns_getUncleByBlockNumberAndIndex': compose( + apply_formatter_at_index(block_number_formatter, 0), + apply_formatter_at_index(integer_to_hex, 1), + ), + 'vns_getUncleByBlockHashAndIndex': apply_formatter_at_index(integer_to_hex, 1), + 'vns_newFilter': apply_formatter_at_index(filter_params_formatter, 0), + 'vns_getLogs': apply_formatter_at_index(filter_params_formatter, 0), + 'vns_call': combine_argument_formatters( + transaction_param_formatter, + block_number_formatter, + ), + 'vns_estimateGas': apply_one_of_formatters(( + (estimate_gas_without_block_id, is_length(1)), + (estimate_gas_with_block_id, is_length(2)), + )), + 'vns_sendTransaction': apply_formatter_at_index(transaction_param_formatter, 0), + # personal + 'personal_importRawKey': apply_formatter_at_index( + compose(remove_0x_prefix, hexstr_if_str(to_hex)), + 0, + ), + 'personal_sign': apply_formatter_at_index(text_if_str(to_hex), 0), + 'personal_ecRecover': apply_formatter_at_index(text_if_str(to_hex), 0), + 'personal_sendTransaction': apply_formatter_at_index(transaction_param_formatter, 0), + # Snapshot and Revert + 'evm_revert': apply_formatter_at_index(integer_to_hex, 0), + 'trace_replayBlockTransactions': apply_formatter_at_index(block_number_formatter, 0), + 'trace_block': apply_formatter_at_index(block_number_formatter, 0), + 'trace_call': compose( + apply_formatter_at_index(transaction_param_formatter, 0), + apply_formatter_at_index(block_number_formatter, 2) + ), + }, + result_formatters={ + # Bbbbbbbb 'vns_accounts': apply_formatter_to_array(to_checksum_address), + 'vns_blockNumber': to_integer_if_hex, + 'vns_coinbase': to_checksum_address, + 'vns_estimateGas': to_integer_if_hex, + 'vns_gasPrice': to_integer_if_hex, + 'vns_getBalance': to_integer_if_hex, + 'vns_getBlockByHash': apply_formatter_if(is_not_null, block_formatter), + 'vns_getBlockByNumber': apply_formatter_if(is_not_null, block_formatter), + 'vns_getBlockTransactionCountByHash': to_integer_if_hex, + 'vns_getBlockTransactionCountByNumber': to_integer_if_hex, + 'vns_getCode': HexBytes, + 'vns_getFilterChanges': filter_result_formatter, + 'vns_getFilterLogs': filter_result_formatter, + 'vns_getLogs': filter_result_formatter, + 'vns_getStorageAt': HexBytes, + 'vns_getTransactionByBlockHashAndIndex': apply_formatter_if( + is_not_null, + transaction_formatter, + ), + 'vns_getTransactionByBlockNumberAndIndex': apply_formatter_if( + is_not_null, + transaction_formatter, + ), + 'vns_getTransactionByHash': apply_formatter_if(is_not_null, transaction_formatter), + 'vns_getTransactionCount': to_integer_if_hex, + 'vns_getTransactionReceipt': apply_formatter_if( + is_not_null, + receipt_formatter, + ), + 'vns_getUncleCountByBlockHash': to_integer_if_hex, + 'vns_getUncleCountByBlockNumber': to_integer_if_hex, + 'vns_hashrate': to_integer_if_hex, + 'vns_protocolVersion': compose( + apply_formatter_if(is_integer, str), + to_integer_if_hex, + ), + 'vns_sendRawTransaction': to_hexbytes(32), + 'vns_sendTransaction': to_hexbytes(32), + 'vns_signTransaction': apply_formatter_if(is_not_null, signed_tx_formatter), + 'vns_sign': HexBytes, + 'vns_syncing': apply_formatter_if(is_not_false, syncing_formatter), + # personal + 'personal_importRawKey': to_checksum_address, + 'personal_listAccounts': apply_formatter_to_array(to_checksum_address), + 'personal_newAccount': to_checksum_address, + 'personal_sendTransaction': to_hexbytes(32), + # SHH + 'shh_getFilterMessages': apply_formatter_to_array(whisper_log_formatter), + # Transaction Pool + 'txpool_content': transaction_pool_content_formatter, + 'txpool_inspect': transaction_pool_inspect_formatter, + # Snapshot and Revert + 'evm_snapshot': hex_to_integer, + # Net + 'net_peerCount': to_integer_if_hex, + }, +) diff --git a/web3/middleware/signing.py b/web3/middleware/signing.py index d0eaa63400..5dafe36202 100644 --- a/web3/middleware/signing.py +++ b/web3/middleware/signing.py @@ -1,169 +1,136 @@ -from functools import ( - singledispatch, -) -import operator -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Dict, - Iterable, - NoReturn, - TypeVar, - Union, -) - -from eth_account import ( - Account, -) -from eth_account.signers.local import ( - LocalAccount, -) -from eth_keys.datatypes import ( - PrivateKey, -) -from eth_typing import ( - ChecksumAddress, - HexStr, -) -from eth_utils import ( - to_dict, -) -from eth_utils.curried import ( - apply_formatter_if, -) -from eth_utils.toolz import ( - compose, -) - -from web3._utils.method_formatters import ( - STANDARD_NORMALIZERS, -) -from web3._utils.rpc_abi import ( - TRANSACTION_PARAMS_ABIS, - apply_abi_formatters_to_dict, -) -from web3._utils.transactions import ( - fill_nonce, - fill_transaction_defaults, -) -from web3.types import ( - Middleware, - RPCEndpoint, - RPCResponse, - TxParams, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -T = TypeVar("T") - -to_hexstr_from_eth_key = operator.methodcaller('to_hex') - - -def is_eth_key(value: Any) -> bool: - return isinstance(value, PrivateKey) - - -key_normalizer = compose( - # ignore Too few arguments error b/c to_hexstr... returns callable, - apply_formatter_if(is_eth_key, to_hexstr_from_eth_key), # type: ignore -) - -_PrivateKey = Union[LocalAccount, PrivateKey, HexStr, bytes] - - -@to_dict -def gen_normalized_accounts( - val: Union[_PrivateKey, Collection[_PrivateKey]] -) -> Iterable[Dict[ChecksumAddress, Account]]: - if isinstance(val, (list, tuple, set,)): - for i in val: - account: Account = to_account(i) - yield account.address, account - else: - account = to_account(val) - yield account.address, account - return - - -@singledispatch -def to_account(val: Any) -> NoReturn: - raise TypeError( - "key must be one of the types: " - "eth_keys.datatype.PrivateKey, eth_account.signers.local.LocalAccount, " - "or raw private key as a hex string or byte string. " - "Was of type {0}".format(type(val))) - - -@to_account.register(LocalAccount) -def _(val: T) -> T: - return val - - -def private_key_to_account(val: _PrivateKey) -> Account: - normalized_key = key_normalizer(val) - return Account.from_key(normalized_key) - - -to_account.register(PrivateKey, private_key_to_account) -to_account.register(str, private_key_to_account) -to_account.register(bytes, private_key_to_account) - - -def format_transaction(transaction: TxParams) -> TxParams: - """Format transaction so that it can be used correctly in the signing middleware. - - Converts bytes to hex strings and other types that can be passed to the underlying layers. - Also has the effect of normalizing 'from' for easier comparisons. - """ - return apply_abi_formatters_to_dict(STANDARD_NORMALIZERS, TRANSACTION_PARAMS_ABIS, transaction) - - -def construct_sign_and_send_raw_middleware( - private_key_or_account: Union[_PrivateKey, Collection[_PrivateKey]] -) -> Middleware: - """Capture transactions sign and send as raw transactions - - - Keyword arguments: - private_key_or_account -- A single private key or a tuple, - list or set of private keys. Keys can be any of the following formats: - - An eth_account.LocalAccount object - - An eth_keys.PrivateKey object - - A raw private key as a hex string or byte string - """ - - accounts = gen_normalized_accounts(private_key_or_account) - - def sign_and_send_raw_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], w3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - format_and_fill_tx = compose( - format_transaction, - fill_transaction_defaults(w3), - fill_nonce(w3)) - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method != "eth_sendTransaction": - return make_request(method, params) - else: - transaction = format_and_fill_tx(params[0]) - - if 'from' not in transaction: - return make_request(method, params) - elif transaction.get('from') not in accounts: - return make_request(method, params) - - account = accounts[transaction['from']] - raw_tx = account.sign_transaction(transaction).rawTransaction - - return make_request( - RPCEndpoint("eth_sendRawTransaction"), - [raw_tx]) - - return middleware - - return sign_and_send_raw_middleware +from functools import ( + singledispatch, +) +import operator + +from vns_account import ( + Account, +) +from vns_account.local import ( + LocalAccount, +) +from vns_keys.datatypes import ( + PrivateKey, +) +from vns_utils import ( + to_dict, +) + +from web3._utils.formatters import ( + apply_formatter_if, +) +from web3._utils.rpc_abi import ( + TRANSACTION_PARAMS_ABIS, + apply_abi_formatters_to_dict, +) +from web3._utils.toolz import ( + compose, +) +from web3._utils.transactions import ( + fill_nonce, + fill_transaction_defaults, +) + +from .abi import ( + STANDARD_NORMALIZERS, +) + +to_hexstr_from_vns_key = operator.methodcaller('to_hex') + + +def is_vns_key(value): + return isinstance(value, PrivateKey) + + +key_normalizer = compose( + apply_formatter_if(is_vns_key, to_hexstr_from_vns_key), +) + + +@to_dict +def gen_normalized_accounts(val): + if isinstance(val, (list, tuple, set,)): + for i in val: + account = to_account(i) + yield account.address, account + else: + account = to_account(val) + yield account.address, account + return + + +@singledispatch +def to_account(val): + raise TypeError( + "key must be one of the types: " + "vns_keys.datatype.PrivateKey, vns_account.local.LocalAccount, " + "or raw private key as a hex string or byte string. " + "Was of type {0}".format(type(val))) + + +@to_account.register(LocalAccount) +def _(val): + return val + + +def private_key_to_account(val): + normalized_key = key_normalizer(val) + return Account.privateKeyToAccount(normalized_key) + + +to_account.register(PrivateKey, private_key_to_account) +to_account.register(str, private_key_to_account) +to_account.register(bytes, private_key_to_account) + + +def format_transaction(transaction): + """Format transaction so that it can be used correctly in the signing middleware. + + Converts bytes to hex strings and other types that can be passed to the underlying layers. + Also has the effect of normalizing 'from' for easier comparisons. + """ + return apply_abi_formatters_to_dict(STANDARD_NORMALIZERS, TRANSACTION_PARAMS_ABIS, transaction) + + +def construct_sign_and_send_raw_middleware(private_key_or_account): + """Capture transactions sign and send as raw transactions + + + Keyword arguments: + private_key_or_account -- A single private key or a tuple, + list or set of private keys. Keys can be any of the following formats: + - An vns_account.LocalAccount object + - An vns_keys.PrivateKey object + - A raw private key as a hex string or byte string + """ + + accounts = gen_normalized_accounts(private_key_or_account) + + def sign_and_send_raw_middleware(make_request, w3): + + format_and_fill_tx = compose( + format_transaction, + fill_transaction_defaults(w3), + fill_nonce(w3)) + + def middleware(method, params): + if method != "vns_sendTransaction": + return make_request(method, params) + else: + transaction = format_and_fill_tx(params[0]) + + if 'from' not in transaction: + return make_request(method, params) + elif transaction.get('from') not in accounts: + return make_request(method, params) + + account = accounts[transaction['from']] + raw_tx = account.signTransaction(transaction).rawTransaction + + return make_request( + "vns_sendRawTransaction", + [raw_tx]) + + return middleware + + return sign_and_send_raw_middleware diff --git a/web3/middleware/simulate_unmined_transaction.py b/web3/middleware/simulate_unmined_transaction.py index b14ce69c18..c08e49f3d2 100644 --- a/web3/middleware/simulate_unmined_transaction.py +++ b/web3/middleware/simulate_unmined_transaction.py @@ -1,39 +1,21 @@ -import collections -import itertools -from typing import ( # noqa: F401 - Any, - Callable, - DefaultDict, -) - -from eth_typing import ( # noqa: F401 - Hash32, -) - -from web3 import Web3 -from web3.types import ( # noqa: F401 - RPCEndpoint, - RPCResponse, - TxReceipt, -) - -counter = itertools.count() - -INVOCATIONS_BEFORE_RESULT = 5 - - -def unmined_receipt_simulator_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: Web3 -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - receipt_counters: DefaultDict[Hash32, TxReceipt] = collections.defaultdict(itertools.count) - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method == 'eth_getTransactionReceipt': - txn_hash = params[0] - if next(receipt_counters[txn_hash]) < INVOCATIONS_BEFORE_RESULT: - return {'result': None} - else: - return make_request(method, params) - else: - return make_request(method, params) - return middleware +import collections +import itertools + +counter = itertools.count() + +INVOCATIONS_BEFORE_RESULT = 5 + + +def unmined_receipt_simulator_middleware(make_request, web3): + receipt_counters = collections.defaultdict(itertools.count) + + def middleware(method, params): + if method == 'vns_getTransactionReceipt': + txn_hash = params[0] + if next(receipt_counters[txn_hash]) < INVOCATIONS_BEFORE_RESULT: + return {'result': None} + else: + return make_request(method, params) + else: + return make_request(method, params) + return middleware diff --git a/web3/middleware/stalecheck.py b/web3/middleware/stalecheck.py index b901170539..2cdd28e21c 100644 --- a/web3/middleware/stalecheck.py +++ b/web3/middleware/stalecheck.py @@ -1,66 +1,48 @@ -import time -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, -) - -from web3.exceptions import ( - StaleBlockchain, -) -from web3.types import ( - BlockData, - Middleware, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -SKIP_STALECHECK_FOR_METHODS = set([ - 'eth_getBlockByNumber', -]) - - -def _isfresh(block: BlockData, allowable_delay: int) -> bool: - return block and time.time() - block['timestamp'] <= allowable_delay - - -def make_stalecheck_middleware( - allowable_delay: int, - skip_stalecheck_for_methods: Collection[str]=SKIP_STALECHECK_FOR_METHODS -) -> Middleware: - """ - Use to require that a function will run only of the blockchain is recently updated. - - This middleware takes an argument, so unlike other middleware, you must make the middleware - with a method call. - For example: `make_stalecheck_middleware(60*5)` - - If the latest block in the chain is older than 5 minutes in this example, then the - middleware will raise a StaleBlockchain exception. - """ - if allowable_delay <= 0: - raise ValueError("You must set a positive allowable_delay in seconds for this middleware") - - def stalecheck_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" - ) -> Callable[[RPCEndpoint, Any], RPCResponse]: - cache = {'latest': None} - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - if method not in skip_stalecheck_for_methods: - if _isfresh(cache['latest'], allowable_delay): - pass - else: - latest = web3.eth.getBlock('latest') - if _isfresh(latest, allowable_delay): - cache['latest'] = latest - else: - raise StaleBlockchain(latest, allowable_delay) - - return make_request(method, params) - return middleware - return stalecheck_middleware +import time + +from web3.exceptions import ( + StaleBlockchain, +) + +SKIP_STALECHECK_FOR_METHODS = set([ + 'vns_getBlockByNumber', +]) + + +def _isfresh(block, allowable_delay): + return block and time.time() - block['timestamp'] <= allowable_delay + + +def make_stalecheck_middleware( + allowable_delay, + skip_stalecheck_for_methods=SKIP_STALECHECK_FOR_METHODS): + """ + Use to require that a function will run only of the blockchain is recently updated. + + This middleware takes an argument, so unlike other middleware, you must make the middleware + with a method call. + For example: `make_stalecheck_middleware(60*5)` + + If the latest block in the chain is older than 5 minutes in this example, then the + middleware will raise a StaleBlockchain exception. + """ + if allowable_delay <= 0: + raise ValueError("You must set a positive allowable_delay in seconds for this middleware") + + def stalecheck_middleware(make_request, web3): + cache = {'latest': None} + + def middleware(method, params): + if method not in skip_stalecheck_for_methods: + if _isfresh(cache['latest'], allowable_delay): + pass + else: + latest = web3.vns.getBlock('latest') + if _isfresh(latest, allowable_delay): + cache['latest'] = latest + else: + raise StaleBlockchain(latest, allowable_delay) + + return make_request(method, params) + return middleware + return stalecheck_middleware diff --git a/web3/middleware/validation.py b/web3/middleware/validation.py index 560e5b0ae1..9e240f9555 100644 --- a/web3/middleware/validation.py +++ b/web3/middleware/validation.py @@ -1,126 +1,112 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_utils.curried import ( - apply_formatter_at_index, - apply_formatter_if, - apply_formatters_to_dict, - is_null, -) -from eth_utils.toolz import ( - complement, - compose, - curry, - dissoc, -) -from hexbytes import ( - HexBytes, -) - -from web3.exceptions import ( - ValidationError, -) -from web3.middleware.formatting import ( - construct_web3_formatting_middleware, -) -from web3.types import ( - FormattersDict, - TxParams, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - -MAX_EXTRADATA_LENGTH = 32 - -is_not_null = complement(is_null) - - -@curry -def validate_chain_id(web3: "Web3", chain_id: int) -> int: - if int(chain_id) == web3.eth.chainId: - return chain_id - else: - raise ValidationError( - "The transaction declared chain ID %r, " - "but the connected node is on %r" % ( - chain_id, - web3.eth.chainId, - ) - ) - - -def check_extradata_length(val: Any) -> Any: - if not isinstance(val, (str, int, bytes)): - return val - result = HexBytes(val) - if len(result) > MAX_EXTRADATA_LENGTH: - raise ValidationError( - "The field extraData is %d bytes, but should be %d. " - "It is quite likely that you are connected to a POA chain. " - "Refer " - "http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority " - "for more details. The full extraData is: %r" % ( - len(result), MAX_EXTRADATA_LENGTH, result - ) - ) - return val - - -def transaction_normalizer(transaction: TxParams) -> TxParams: - return dissoc(transaction, 'chainId') - - -def transaction_param_validator(web3: "Web3") -> Callable[..., Any]: - transactions_params_validators = { - # type ignored b/c apply_formatter_if requires more args, but is_not_null is curried - "chainId": apply_formatter_if( # type: ignore - # Bypass `validate_chain_id` if chainId can't be determined - lambda _: is_not_null(web3.eth.chainId), - validate_chain_id(web3), - ), - } - return apply_formatter_at_index( - apply_formatters_to_dict(transactions_params_validators), - 0 - ) - - -BLOCK_VALIDATORS = { - 'extraData': check_extradata_length, -} - - -# types ignored b/c same reason as line 79 -block_validator = apply_formatter_if( # type: ignore - is_not_null, - apply_formatters_to_dict(BLOCK_VALIDATORS) # type: ignore -) - - -@curry -def chain_id_validator(web3: "Web3") -> Callable[..., Any]: - return compose( - apply_formatter_at_index(transaction_normalizer, 0), - transaction_param_validator(web3) - ) - - -def build_validators_with_web3(w3: "Web3") -> FormattersDict: - return dict( - request_formatters={ - 'eth_sendTransaction': chain_id_validator(w3), - 'eth_estimateGas': chain_id_validator(w3), - 'eth_call': chain_id_validator(w3), - }, - result_formatters={ - 'eth_getBlockByHash': block_validator, - 'eth_getBlockByNumber': block_validator, - }, - ) - - -validation_middleware = construct_web3_formatting_middleware(build_validators_with_web3) +from vns_utils.curried import ( + apply_formatter_at_index, + apply_formatter_if, + apply_formatters_to_dict, + is_null, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.toolz import ( + complement, + compose, + curry, + dissoc, +) +from web3.exceptions import ( + ValidationError, +) +from web3.middleware.formatting import ( + construct_web3_formatting_middleware, +) + +MAX_EXTRADATA_LENGTH = 32 + + +is_not_null = complement(is_null) + + +@curry +def validate_chain_id(web3, chain_id): + if chain_id == web3.net.version: + return chain_id + else: + raise ValidationError( + "The transaction declared chain ID %r, " + "but the connected node is on %r" % ( + chain_id, + "UNKNOWN", + ) + ) + + +def check_extradata_length(val): + if not isinstance(val, (str, int, bytes)): + return val + result = HexBytes(val) + if len(result) > MAX_EXTRADATA_LENGTH: + raise ValidationError( + "The field extraData is %d bytes, but should be %d. " + "It is quite likely that you are connected to a POA chain. " + "Refer " + "http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority " + "for more details. The full extraData is: %r" % ( + len(result), MAX_EXTRADATA_LENGTH, result + ) + ) + return val + + +def transaction_normalizer(transaction): + return dissoc(transaction, 'chainId') + + +def transaction_param_validator(web3): + transactions_params_validators = { + 'chainId': apply_formatter_if( + # Bypass `validate_chain_id` if chainId can't be determined + lambda _: is_not_null(web3.net.version), + validate_chain_id(web3) + ), + } + return apply_formatter_at_index( + apply_formatters_to_dict(transactions_params_validators), + 0 + ) + + +BLOCK_VALIDATORS = { + 'extraData': check_extradata_length, +} + + +block_validator = apply_formatter_if( + is_not_null, + apply_formatters_to_dict(BLOCK_VALIDATORS) +) + + +@curry +def chain_id_validator(web3): + return compose( + apply_formatter_at_index(transaction_normalizer, 0), + transaction_param_validator(web3) + ) + + +def build_validators_with_web3(w3): + return dict( + request_formatters={ + 'vns_sendTransaction': chain_id_validator(w3), + 'vns_estimateGas': chain_id_validator(w3), + 'vns_call': chain_id_validator(w3), + }, + result_formatters={ + 'vns_getBlockByHash': block_validator, + 'vns_getBlockByNumber': block_validator, + }, + ) + + +validation_middleware = construct_web3_formatting_middleware(build_validators_with_web3) diff --git a/web3/module.py b/web3/module.py index b611997ae9..e19578e19e 100644 --- a/web3/module.py +++ b/web3/module.py @@ -1,100 +1,66 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Coroutine, - Union, -) - -from eth_utils.toolz import ( - curry, - pipe, -) - -from web3.method import ( - Method, -) -from web3.types import ( - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -@curry -def apply_result_formatters( - result_formatters: Callable[..., Any], result: RPCResponse -) -> RPCResponse: - if result_formatters: - formatted_result = pipe(result, result_formatters) - return formatted_result - else: - return result - - -@curry -def retrieve_blocking_method_call_fn( - w3: "Web3", module: Union["Module", "ModuleV2"], method: Method -) -> Callable[..., RPCResponse]: - def caller(*args: Any, **kwargs: Any) -> RPCResponse: - (method_str, params), response_formatters = method.process_params(module, *args, **kwargs) - result_formatters, error_formatters = response_formatters - result = w3.manager.request_blocking(method_str, params, error_formatters) - return apply_result_formatters(result_formatters, result) - return caller - - -@curry -def retrieve_async_method_call_fn( - w3: "Web3", module: Union["Module", "ModuleV2"], method: Method -) -> Callable[..., Coroutine[Any, Any, RPCResponse]]: - async def caller(*args: Any, **kwargs: Any) -> RPCResponse: - (method_str, params), response_formatters = method.process_params(module, *args, **kwargs) - result_formatters, error_formatters = response_formatters - result = await w3.manager.coro_request(method_str, params, error_formatters) - return apply_result_formatters(result_formatters, result) - return caller - - -# TODO: Replace this with ModuleV2 when ready. -class Module: - web3: "Web3" = None - - def __init__(self, web3: "Web3") -> None: - self.web3 = web3 - - @classmethod - def attach(cls, target: "Web3", module_name: str=None) -> None: - if not module_name: - module_name = cls.__name__.lower() - - if hasattr(target, module_name): - raise AttributeError( - "Cannot set {0} module named '{1}'. The web3 object " - "already has an attribute with that name".format( - target, - module_name, - ) - ) - - if isinstance(target, Module): - web3 = target.web3 - else: - web3 = target - - setattr(target, module_name, cls(web3)) - - -# Module should no longer have access to the full web3 api. -# Only the calling functions need access to the request methods. -# Any "re-entrant" shinanigans can go in the middlewares, which do -# have web3 access. -class ModuleV2(Module): - is_async = False - - def __init__(self, web3: "Web3") -> None: - if self.is_async: - self.retrieve_caller_fn = retrieve_async_method_call_fn(web3, self) - else: - self.retrieve_caller_fn = retrieve_blocking_method_call_fn(web3, self) +from vns_utils.toolz import ( + curry, + pipe, +) + + +@curry +def retrieve_blocking_method_call_fn(w3, module, method): + def caller(*args, **kwargs): + (method_str, params), output_formatters = method.process_params(module, *args, **kwargs) + return pipe( + w3.manager.request_blocking(method_str, params), + *output_formatters) + return caller + + +@curry +def retrieve_async_method_call_fn(w3, module, method): + async def caller(*args, **kwargs): + (method_str, params), output_formatters = method.process_params(module, *args, **kwargs) + raw_result = await w3.manager.coro_request(method_str, params) + return pipe(raw_result, *output_formatters) + return caller + + +# TODO: Replace this with ModuleV2 when ready. +class Module: + web3 = None + + def __init__(self, web3): + self.web3 = web3 + + @classmethod + def attach(cls, target, module_name=None): + if not module_name: + module_name = cls.__name__.lower() + + if hasattr(target, module_name): + raise AttributeError( + "Cannot set {0} module named '{1}'. The web3 object " + "already has an attribute with that name".format( + target, + module_name, + ) + ) + + if isinstance(target, Module): + web3 = target.web3 + else: + web3 = target + + setattr(target, module_name, cls(web3)) + + +# Module should no longer have access to the full web3 api. +# Only the calling functions need access to the request methods. +# Any "re-entrant" shinanigans can go in the middlewares, which do +# have web3 access. +class ModuleV2(Module): + is_async = False + + def __init__(self, web3): + if self.is_async: + self.retrieve_caller_fn = retrieve_async_method_call_fn(web3, self) + else: + self.retrieve_caller_fn = retrieve_blocking_method_call_fn(web3, self) diff --git a/web3/net.py b/web3/net.py index 8fda16c102..407a169af4 100644 --- a/web3/net.py +++ b/web3/net.py @@ -1,25 +1,21 @@ -from typing import ( - NoReturn, -) - -from web3.module import ( - Module, -) - - -class Net(Module): - @property - def listening(self) -> bool: - return self.web3.manager.request_blocking("net_listening", []) - - @property - def peerCount(self) -> int: - return self.web3.manager.request_blocking("net_peerCount", []) - - @property - def chainId(self) -> NoReturn: - raise DeprecationWarning("This method has been deprecated in EIP 1474.") - - @property - def version(self) -> int: - return self.web3.manager.request_blocking("net_version", []) +from web3.module import ( + Module, +) + + +class Net(Module): + @property + def listening(self): + return self.web3.manager.request_blocking("net_listening", []) + + @property + def peerCount(self): + return self.web3.manager.request_blocking("net_peerCount", []) + + @property + def chainId(self): + raise DeprecationWarning("This method has been deprecated in EIP 1474.") + + @property + def version(self): + return self.web3.manager.request_blocking("net_version", []) diff --git a/web3/parity.py b/web3/parity.py index 5804274494..2b48ce881d 100644 --- a/web3/parity.py +++ b/web3/parity.py @@ -1,208 +1,139 @@ -from typing import ( - List, - Union, -) - -from eth_typing import ( - Address, - ChecksumAddress, - Hash32, - HexStr, -) -from eth_utils import ( - is_checksum_address, -) -from eth_utils.toolz import ( - assoc, -) - -from web3._utils import ( - shh, -) -from web3._utils.compat import ( - Literal, -) -from web3._utils.personal import ( - ecRecover, - importRawKey, - listAccounts, - newAccount, - sendTransaction, - sign, - signTypedData, - unlockAccount, -) -from web3.module import ( - Module, - ModuleV2, -) -from web3.types import ( - ENS, - BlockIdentifier, - ParityBlockTrace, - ParityEnodeURI, - ParityFilterParams, - ParityFilterTrace, - ParityMode, - ParityNetPeers, - ParityTraceMode, - TxParams, -) - - -class ParityShh(ModuleV2): - """ - https://wiki.parity.io/JSONRPC-shh-module - """ - info = shh.info - new_key_pair = shh.new_key_pair - add_private_key = shh.add_private_key - new_sym_key = shh.new_sym_key - add_sym_key = shh.add_sym_key - get_public_key = shh.get_public_key - get_private_key = shh.get_private_key - get_sym_key = shh.get_sym_key - post = shh.post - new_message_filter = shh.new_message_filter - delete_message_filter = shh.delete_message_filter - get_filter_messages = shh.get_filter_messages - delete_key = shh.delete_key - subscribe = shh.subscribe - unsubscribe = shh.unsubscribe - # Deprecated - newKeyPair = shh.new_key_pair - addPrivateKey = shh.add_private_key - newSymKey = shh.new_sym_key - addSymKey = shh.add_sym_key - getPublicKey = shh.get_public_key - getPrivateKey = shh.get_private_key - getSymKey = shh.get_sym_key - newMessageFilter = shh.new_message_filter - deleteMessageFilter = shh.delete_message_filter - getFilterMessages = shh.get_filter_messages - deleteKey = shh.delete_key - - -class ParityPersonal(ModuleV2): - """ - https://wiki.parity.io/JSONRPC-personal-module - """ - ecRecover = ecRecover - importRawKey = importRawKey - listAccounts = listAccounts - newAccount = newAccount - sendTransaction = sendTransaction - sign = sign - signTypedData = signTypedData - unlockAccount = unlockAccount - - -class Parity(Module): - """ - https://paritytech.github.io/wiki/JSONRPC-parity-module - """ - defaultBlock: Literal["latest"] = "latest" # noqa: E704 - - def enode(self) -> ParityEnodeURI: - return self.web3.manager.request_blocking( - "parity_enode", - [], - ) - - def listStorageKeys( - self, - address: Union[Address, ChecksumAddress, ENS], - quantity: int, - hash_: Hash32, - block_identifier: BlockIdentifier=None, - ) -> List[Hash32]: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "parity_listStorageKeys", - [address, quantity, hash_, block_identifier], - ) - - def netPeers(self) -> ParityNetPeers: - return self.web3.manager.request_blocking( - "parity_netPeers", - [], - ) - - def addReservedPeer(self, url: ParityEnodeURI) -> bool: - return self.web3.manager.request_blocking( - "parity_addReservedPeer", - [url], - ) - - def traceReplayTransaction( - self, transaction_hash: Hash32, mode: ParityTraceMode=['trace'] - ) -> ParityBlockTrace: - return self.web3.manager.request_blocking( - "trace_replayTransaction", - [transaction_hash, mode], - ) - - def traceReplayBlockTransactions( - self, block_identifier: BlockIdentifier, mode: ParityTraceMode=['trace'] - ) -> List[ParityBlockTrace]: - return self.web3.manager.request_blocking( - "trace_replayBlockTransactions", - [block_identifier, mode] - ) - - def traceBlock(self, block_identifier: BlockIdentifier) -> List[ParityBlockTrace]: - return self.web3.manager.request_blocking( - "trace_block", - [block_identifier] - ) - - def traceFilter(self, params: ParityFilterParams) -> List[ParityFilterTrace]: - return self.web3.manager.request_blocking( - "trace_filter", - [params] - ) - - def traceTransaction(self, transaction_hash: Hash32) -> List[ParityFilterTrace]: - return self.web3.manager.request_blocking( - "trace_transaction", - [transaction_hash] - ) - - def traceCall( - self, - transaction: TxParams, - mode: ParityTraceMode=['trace'], - block_identifier: BlockIdentifier=None - ) -> List[ParityBlockTrace]: - # TODO: move to middleware - if 'from' not in transaction and is_checksum_address(self.web3.eth.defaultAccount): - transaction = assoc(transaction, 'from', self.web3.eth.defaultAccount) - - # TODO: move to middleware - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "trace_call", - [transaction, mode, block_identifier], - ) - - def traceRawTransaction( - self, raw_transaction: HexStr, mode: ParityTraceMode=['trace'] - ) -> List[ParityBlockTrace]: - return self.web3.manager.request_blocking( - "trace_rawTransaction", - [raw_transaction, mode], - ) - - def setMode(self, mode: ParityMode) -> bool: - return self.web3.manager.request_blocking( - "parity_setMode", - [mode] - ) - - def mode(self) -> ParityMode: - return self.web3.manager.request_blocking( - "parity_mode", - [] - ) +from vns_utils import ( + is_checksum_address, +) + +from web3._utils import ( + shh, +) +from web3._utils.personal import ( + ecRecover, + importRawKey, + listAccounts, + newAccount, + sendTransaction, + sign, + unlockAccount, +) +from web3._utils.toolz import ( + assoc, +) +from web3.module import ( + Module, + ModuleV2, +) + + +class ParityShh(ModuleV2): + """ + https://wiki.parity.io/JSONRPC-shh-module + """ + info = shh.info + newKeyPair = shh.newKeyPair + addPrivateKey = shh.addPrivateKey + newSymKey = shh.newSymKey + addSymKey = shh.addSymKey + getPublicKey = shh.getPublicKey + getPrivateKey = shh.getPrivateKey + getSymKey = shh.getSymKey + post = shh.post + newMessageFilter = shh.newMessageFilter + deleteMessageFilter = shh.deleteMessageFilter + getFilterMessages = shh.getFilterMessages + deleteKey = shh.deleteKey + subscribe = shh.subscribe + unsubscribe = shh.unsubscribe + + +class ParityPersonal(ModuleV2): + """ + https://wiki.parity.io/JSONRPC-personal-module + """ + ecRecover = ecRecover + importRawKey = importRawKey + listAccounts = listAccounts + newAccount = newAccount + sendTransaction = sendTransaction + sign = sign + unlockAccount = unlockAccount + + +class Parity(Module): + """ + https://paritytech.github.io/wiki/JSONRPC-parity-module + """ + defaultBlock = "latest" + + def enode(self): + return self.web3.manager.request_blocking( + "parity_enode", + [], + ) + + def listStorageKeys(self, address, quantity, hash_, block_identifier=None): + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "parity_listStorageKeys", + [address, quantity, hash_, block_identifier], + ) + + def netPeers(self): + return self.web3.manager.request_blocking( + "parity_netPeers", + [], + ) + + def addReservedPeer(self, url): + return self.web3.manager.request_blocking( + "parity_addReservedPeer", + [url], + ) + + def traceReplayTransaction(self, transaction_hash, mode=['trace']): + return self.web3.manager.request_blocking( + "trace_replayTransaction", + [transaction_hash, mode], + ) + + def traceReplayBlockTransactions(self, block_identifier, mode=['trace']): + return self.web3.manager.request_blocking( + "trace_replayBlockTransactions", + [block_identifier, mode] + ) + + def traceBlock(self, block_identifier): + return self.web3.manager.request_blocking( + "trace_block", + [block_identifier] + ) + + def traceFilter(self, params): + return self.web3.manager.request_blocking( + "trace_filter", + [params] + ) + + def traceTransaction(self, transaction_hash): + return self.web3.manager.request_blocking( + "trace_transaction", + [transaction_hash] + ) + + def traceCall(self, transaction, mode=['trace'], block_identifier=None): + # TODO: move to middleware + if 'from' not in transaction and is_checksum_address(self.defaultAccount): + transaction = assoc(transaction, 'from', self.defaultAccount) + + # TODO: move to middleware + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "trace_call", + [transaction, mode, block_identifier], + ) + + def traceRawTransaction(self, raw_transaction, mode=['trace']): + return self.web3.manager.request_blocking( + "trace_rawTransaction", + [raw_transaction, mode], + ) diff --git a/web3/pm.py b/web3/pm.py index 1c54f73358..837fbc2ee2 100644 --- a/web3/pm.py +++ b/web3/pm.py @@ -1,576 +1,642 @@ -from abc import ( - ABC, - abstractmethod, -) -import json -from pathlib import ( - Path, -) -from typing import ( - Any, - Dict, - Iterable, - NamedTuple, - Tuple, - Type, - TypeVar, - Union, - cast, -) - -from eth_typing import ( - URI, - Address, - ChecksumAddress, - ContractName, - Manifest, -) -from eth_utils import ( - is_canonical_address, - is_checksum_address, - to_checksum_address, - to_text, - to_tuple, -) - -from ethpm import ( - ASSETS_DIR, - Package, -) -from ethpm.uri import ( - is_supported_content_addressed_uri, - resolve_uri_contents, -) -from ethpm.validation.manifest import ( - validate_manifest_against_schema, - validate_raw_manifest_format, -) -from ethpm.validation.package import ( - validate_package_name, - validate_package_version, -) -from web3 import Web3 -from web3._utils.ens import ( - is_ens_name, -) -from web3.exceptions import ( - InvalidAddress, - ManifestValidationError, - NameNotFound, - PMError, -) -from web3.module import ( - Module, -) -from web3.types import ( - ENS, -) - -# Package Management is still in alpha, and its API is likely to change, so it -# is not automatically available on a web3 instance. To use the `PM` module, -# please enable the package management API on an individual web3 instance. -# -# >>> from web3.auto import w3 -# >>> w3.pm -# AttributeError: The Package Management feature is disabled by default ... -# >>> w3.enable_unstable_package_management_api() -# >>> w3.pm -# - -T = TypeVar("T") - - -class ReleaseData(NamedTuple): - package_name: str - version: str - manifest_uri: URI - - -class ERC1319Registry(ABC): - """ - The ERC1319Registry class is a base class for all registry implementations to inherit from. It - defines the methods specified in `ERC 1319 `__. - All of these methods are prefixed with an underscore, since they are not intended to be - accessed directly, but rather through the methods on ``web3.pm``. They are unlikely to change, - but must be implemented in a `ERC1319Registry` subclass in order to be compatible with the - `PM` module. Any custom methods (eg. not definied in ERC1319) in a subclass - should *not* be prefixed with an underscore. - - All of these methods must be implemented in any subclass in order to work with `web3.pm.PM`. - Any implementation specific logic should be handled in a subclass. - """ - - @abstractmethod - def __init__(self, address: Address, w3: Web3) -> None: - """ - Initializes the class with the on-chain address of the registry, and a web3 instance - connected to the chain where the registry can be found. - - Must set the following properties... - - * ``self.registry``: A `web3.contract` instance of the target registry. - * ``self.address``: The address of the target registry. - * ``self.w3``: The *web3* instance connected to the chain where the registry can be found. - """ - pass - - # - # Write API - # - - @abstractmethod - def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: - """ - Returns the releaseId created by successfully adding a release to the registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to any versioning scheme. - * ``manifest_uri``: URI location of a manifest which details the release contents - """ - pass - - # - # Read API - # - - @abstractmethod - def _get_package_name(self, package_id: bytes) -> str: - """ - Returns the package name associated with the given package id, if the - package id exists on the connected registry. - - * Parameters: - * ``package_id``: 32 byte package identifier. - """ - pass - - @abstractmethod - def _get_all_package_ids(self) -> Iterable[bytes]: - """ - Returns a tuple containing all of the package ids found on the connected registry. - """ - pass - - @abstractmethod - def _get_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 bytes release id associated with the given package name and version, - if the release exists on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to any versioning scheme. - """ - pass - - @abstractmethod - def _get_all_release_ids(self, package_name: str) -> Iterable[bytes]: - """ - Returns a tuple containg all of the release ids belonging to the given package name, - if the package has releases on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - """ - pass - - @abstractmethod - def _get_release_data(self, release_id: bytes) -> ReleaseData: - """ - Returns a tuple containing (package_name, version, manifest_uri) for the given release id, - if the release exists on the connected registry. - - * Parameters: - * ``release_id``: 32 byte release identifier. - """ - pass - - @abstractmethod - def _generate_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 byte release identifier that *would* be associated with the given - package name and version according to the registry's hashing mechanism. - The release *does not* have to exist on the connected registry. - - * Parameters: - * ``package_name``: Valid package name according the spec. - * ``version``: Version identifier string, can conform to any versioning scheme. - """ - pass - - @abstractmethod - def _num_package_ids(self) -> int: - """ - Returns the number of packages that exist on the connected registry. - """ - pass - - @abstractmethod - def _num_release_ids(self, package_name: str) -> int: - """ - Returns the number of releases found on the connected registry, - that belong to the given package name. - - * Parameters: - * ``package_name``: Valid package name according the spec. - """ - pass - - @classmethod - @abstractmethod - def deploy_new_instance(cls: Type[T], w3: Web3) -> T: - """ - Class method that returns a newly deployed instance of ERC1319Registry. - - * Parameters: - * ``w3``: Web3 instance on which to deploy the new registry. - """ - pass - - -BATCH_SIZE = 100 - - -class SimpleRegistry(ERC1319Registry): - """ - This class represents an instance of the `Solidity Reference Registry implementation - `__. - """ - - def __init__(self, address: ChecksumAddress, w3: Web3) -> None: - abi = get_simple_registry_manifest()["contract_types"]["PackageRegistry"][ - "abi" - ] - self.registry = w3.eth.contract(address=address, abi=abi) - self.address = address - self.w3 = w3 - - def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: - tx_hash = self.registry.functions.release( - package_name, version, manifest_uri - ).transact() - self.w3.eth.waitForTransactionReceipt(tx_hash) - return self._get_release_id(package_name, version) - - def _get_package_name(self, package_id: bytes) -> str: - package_name = self.registry.functions.getPackageName(package_id).call() - return package_name - - @to_tuple - def _get_all_package_ids(self) -> Iterable[bytes]: - num_packages = self._num_package_ids() - pointer = 0 - while pointer < num_packages: - new_ids, new_pointer = self.registry.functions.getAllPackageIds( - pointer, - (pointer + BATCH_SIZE) - ).call() - if not new_pointer > pointer: - break - yield from reversed(new_ids) - pointer = new_pointer - - def _get_release_id(self, package_name: str, version: str) -> bytes: - return self.registry.functions.getReleaseId(package_name, version).call() - - @to_tuple - def _get_all_release_ids(self, package_name: str) -> Iterable[bytes]: - num_releases = self._num_release_ids(package_name) - pointer = 0 - while pointer < num_releases: - new_ids, new_pointer = self.registry.functions.getAllReleaseIds( - package_name, - pointer, - (pointer + BATCH_SIZE) - ).call() - if not new_pointer > pointer: - break - yield from reversed(new_ids) - pointer = new_pointer - - def _get_release_data(self, release_id: bytes) -> ReleaseData: - name, version, uri = self.registry.functions.getReleaseData(release_id).call() - return ReleaseData(name, version, uri) - - def _generate_release_id(self, package_name: str, version: str) -> bytes: - return self.registry.functions.generateReleaseId(package_name, version).call() - - def _num_package_ids(self) -> int: - return self.registry.functions.numPackageIds().call() - - def _num_release_ids(self, package_name: str) -> int: - return self.registry.functions.numReleaseIds(package_name).call() - - @classmethod - def deploy_new_instance(cls, w3: Web3) -> 'SimpleRegistry': - manifest = get_simple_registry_manifest() - registry_package = Package(manifest, w3) - registry_factory = registry_package.get_contract_factory(ContractName("PackageRegistry")) - tx_hash = registry_factory.constructor().transact() - tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - return cls(tx_receipt.contractAddress, w3) - - -class PM(Module): - """ - The PM module will work with any subclass of ``ERC1319Registry``, tailored to a particular - implementation of `ERC1319 `__, set as - its ``registry`` attribute. - """ - - def get_package_from_manifest(self, manifest: Manifest) -> Package: - """ - Returns a `Package `__ - instance built with the given manifest. - - * Parameters: - * ``manifest``: A dict representing a valid manifest - """ - return Package(manifest, self.web3) - - def get_package_from_uri(self, manifest_uri: URI) -> Package: - """ - Returns a `Package `__ - instance built with the Manifest stored at the URI. - If you want to use a specific IPFS backend, set ``ETHPM_IPFS_BACKEND_CLASS`` - to your desired backend. Defaults to Infura IPFS backend. - - * Parameters: - * ``uri``: Must be a valid content-addressed URI - """ - return Package.from_uri(manifest_uri, self.web3) - - def get_local_package(self, package_name: str, ethpm_dir: Path = None) -> Package: - """ - Returns a `Package `__ - instance built with the Manifest found at the package name in your local ethpm_dir. - - * Parameters: - * ``package_name``: Must be the name of a package installed locally. - * ``ethpm_dir``: Path pointing to the target ethpm directory (optional). - """ - if not ethpm_dir: - ethpm_dir = Path.cwd() / '_ethpm_packages' - - if not ethpm_dir.name == "_ethpm_packages" or not ethpm_dir.is_dir(): - raise PMError(f"{ethpm_dir} is not a valid ethPM packages directory.") - - local_packages = [pkg.name for pkg in ethpm_dir.iterdir() if pkg.is_dir()] - if package_name not in local_packages: - raise PMError( - f"Package: {package_name} not found in {ethpm_dir}. " - f"Available packages include: {local_packages}." - ) - - target_manifest = json.loads( - (ethpm_dir / package_name / "manifest.json").read_text() - ) - return self.get_package_from_manifest(target_manifest) - - def set_registry(self, address: Union[Address, ChecksumAddress, ENS]) -> None: - """ - Sets the current registry used in ``web3.pm`` functions that read/write to an on-chain - registry. This method accepts checksummed/canonical addresses or ENS names. Addresses - must point to an on-chain instance of an ERC1319 registry implementation. - - To use an ENS domain as the address, make sure a valid ENS instance set as ``web3.ens``. - - * Parameters: - * ``address``: Address of on-chain Registry. - """ - if is_canonical_address(address): - addr_string = to_text(address) - self.registry = SimpleRegistry(to_checksum_address(addr_string), self.web3) - elif is_checksum_address(address): - self.registry = SimpleRegistry(cast(ChecksumAddress, address), self.web3) - elif is_ens_name(address): - self._validate_set_ens() - addr_lookup = self.web3.ens.address(str(address)) - if not addr_lookup: - raise NameNotFound( - "No address found after ENS lookup for name: {0}.".format(address) - ) - self.registry = SimpleRegistry(addr_lookup, self.web3) - else: - raise PMError( - "Expected a canonical/checksummed address or ENS name for the address, " - "instead received {0}.".format(type(address)) - ) - - def deploy_and_set_registry(self) -> ChecksumAddress: - """ - Returns the address of a freshly deployed instance of `SimpleRegistry` - and sets the newly deployed registry as the active registry on ``web3.pm.registry``. - - To tie your registry to an ENS name, use web3's ENS module, ie. - - .. code-block:: python - - w3.ens.setup_address(ens_name, w3.pm.registry.address) - """ - self.registry = SimpleRegistry.deploy_new_instance(self.web3) - return to_checksum_address(self.registry.address) - - def release_package( - self, package_name: str, version: str, manifest_uri: URI - ) -> bytes: - """ - Returns the release id generated by releasing a package on the current registry. - Requires ``web3.PM`` to have a registry set. Requires ``web3.eth.defaultAccount`` - to be the registry owner. - - * Parameters: - * ``package_name``: Must be a valid package name, matching the given manifest. - * ``version``: Must be a valid package version, matching the given manifest. - * ``manifest_uri``: Must be a valid content-addressed URI. Currently, only IPFS - and Github content-addressed URIs are supported. - - """ - validate_is_supported_manifest_uri(manifest_uri) - raw_manifest = to_text(resolve_uri_contents(manifest_uri)) - validate_raw_manifest_format(raw_manifest) - manifest = json.loads(raw_manifest) - validate_manifest_against_schema(manifest) - if package_name != manifest["package_name"]: - raise ManifestValidationError( - f"Provided package name: {package_name} does not match the package name " - f"found in the manifest: {manifest['package_name']}." - ) - - if version != manifest["version"]: - raise ManifestValidationError( - f"Provided package version: {version} does not match the package version " - f"found in the manifest: {manifest['version']}." - ) - - self._validate_set_registry() - return self.registry._release(package_name, version, manifest_uri) - - @to_tuple - def get_all_package_names(self) -> Iterable[str]: - """ - Returns a tuple containing all the package names available on the current registry. - """ - self._validate_set_registry() - package_ids = self.registry._get_all_package_ids() - for package_id in package_ids: - yield self.registry._get_package_name(package_id) - - def get_package_count(self) -> int: - """ - Returns the number of packages available on the current registry. - """ - self._validate_set_registry() - return self.registry._num_package_ids() - - def get_release_count(self, package_name: str) -> int: - """ - Returns the number of releases of the given package name available on the current registry. - """ - validate_package_name(package_name) - self._validate_set_registry() - return self.registry._num_release_ids(package_name) - - def get_release_id(self, package_name: str, version: str) -> bytes: - """ - Returns the 32 byte identifier of a release for the given package name and version, - if they are available on the current registry. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - return self.registry._get_release_id(package_name, version) - - @to_tuple - def get_all_package_releases(self, package_name: str) -> Iterable[Tuple[str, str]]: - """ - Returns a tuple of release data (version, manifest_ur) for every release of the - given package name available on the current registry. - """ - validate_package_name(package_name) - self._validate_set_registry() - release_ids = self.registry._get_all_release_ids(package_name) - for release_id in release_ids: - release_data = self.registry._get_release_data(release_id) - yield (release_data.version, release_data.manifest_uri) - - def get_release_id_data(self, release_id: bytes) -> ReleaseData: - """ - Returns ``(package_name, version, manifest_uri)`` associated with the given - release id, *if* it is available on the current registry. - - * Parameters: - * ``release_id``: 32 byte release identifier - """ - self._validate_set_registry() - return self.registry._get_release_data(release_id) - - def get_release_data(self, package_name: str, version: str) -> ReleaseData: - """ - Returns ``(package_name, version, manifest_uri)`` associated with the given - package name and version, *if* they are published to the currently set registry. - - * Parameters: - * ``name``: Must be a valid package name. - * ``version``: Must be a valid package version. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - release_id = self.registry._get_release_id(package_name, version) - return self.get_release_id_data(release_id) - - def get_package(self, package_name: str, version: str) -> Package: - """ - Returns a ``Package`` instance, generated by the ``manifest_uri`` associated with the - given package name and version, if they are published to the currently set registry. - - * Parameters: - * ``name``: Must be a valid package name. - * ``version``: Must be a valid package version. - """ - validate_package_name(package_name) - validate_package_version(version) - self._validate_set_registry() - release_data = self.get_release_data(package_name, version) - return self.get_package_from_uri(URI(release_data.manifest_uri)) - - def _validate_set_registry(self) -> None: - try: - self.registry - except AttributeError: - raise PMError( - "web3.pm does not have a set registry. " - "Please set registry with either: " - "web3.pm.set_registry(address) or " - "web3.pm.deploy_and_set_registry()" - ) - if not isinstance(self.registry, ERC1319Registry): - raise PMError( - "web3.pm requires an instance of a subclass of ERC1319Registry " - "to be set as the web3.pm.registry attribute. Instead found: " - f"{type(self.registry)}." - ) - - def _validate_set_ens(self) -> None: - if not self.web3: - raise InvalidAddress( - "Could not look up ENS address because no web3 " "connection available" - ) - elif not self.web3.ens: - raise InvalidAddress( - "Could not look up ENS address because web3.ens is " "set to None" - ) - - -def get_simple_registry_manifest() -> Dict[str, Any]: - return json.loads((ASSETS_DIR / "simple-registry" / "2.0.0a1.json").read_text()) - - -def validate_is_supported_manifest_uri(uri: URI) -> None: - if not is_supported_content_addressed_uri(uri): - raise ManifestValidationError( - f"URI: {uri} is not a valid content-addressed URI. " - "Currently only IPFS and Github content-addressed URIs are supported." - ) +from abc import ( + ABC, + abstractmethod, +) +import json +from typing import ( + Any, + Dict, + Iterable, + List, + NewType, + Tuple, +) + +from vns_utils import ( + is_canonical_address, + is_checksum_address, + to_bytes, + to_canonical_address, + to_checksum_address, + to_text, + to_tuple, +) +from vns_utils.toolz import ( + concat, +) +from ethpm import ( + ASSETS_DIR, + Package, +) +from ethpm.typing import ( + URI, + Address, + Manifest, +) +from ethpm.utils.backend import ( + resolve_uri_contents, +) +from ethpm.utils.ipfs import ( + is_ipfs_uri, +) +from ethpm.utils.manifest_validation import ( + validate_manifest_against_schema, + validate_raw_manifest_format, +) +from ethpm.utils.uri import ( + is_valid_content_addressed_github_uri, +) +from ethpm.validation import ( + validate_package_name, + validate_package_version, +) + +from web3 import Web3 +from web3._utils.ens import ( + is_ens_name, +) +from web3.exceptions import ( + InvalidAddress, + ManifestValidationError, + NameNotFound, + PMError, +) +from web3.module import ( + Module, +) + +TxReceipt = NewType("TxReceipt", Dict[str, Any]) + + +# Package Management is still in alpha, and its API is likely to change, so it +# is not automatically available on a web3 instance. To use the `PM` module, +# please enable the package management API on an individual web3 instance. +# +# >>> from web3.auto import w3 +# >>> w3.pm +# AttributeError: The Package Management feature is disabled by default ... +# >>> w3.enable_unstable_package_management_api() +# >>> w3.pm +# + + +class ERCRegistry(ABC): + """ + The ERCRegistry class is a base class for all registry implementations to inherit from. It + defines the methods specified in `ERC 1319 `__. + All of these methods are prefixed with an underscore, since they are not intended to be + accessed directly, but rather through the methods on ``web3.pm``. They are unlikely to change, + but must be implemented in a `ERCRegistry` subclass in order to be compatible with the + `PM` module. Any custom methods (eg. not definied in ERC1319) in a subclass + should *not* be prefixed with an underscore. + + All of these methods must be implemented in any subclass in order to work with `web3.pm.PM`. + Any implementation specific logic should be handled in a subclass. + """ + + @abstractmethod + def __init__(self, address: Address, w3: Web3) -> None: + """ + Initializes the class with the on-chain address of the registry, and a web3 instance + connected to the chain where the registry can be found. + + Must set the following properties... + + * ``self.registry``: A `web3.contract` instance of the target registry. + * ``self.address``: The address of the target registry. + * ``self.w3``: The *web3* instance connected to the chain where the registry can be found. + """ + pass + + # + # Write API + # + + @abstractmethod + def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: + """ + Returns the releaseId created by successfully adding a release to the registry. + + * Parameters: + * ``package_name``: Valid package name according the spec. + * ``version``: Version identifier string, can conform to any versioning scheme. + * ``manifest_uri``: URI location of a manifest which details the release contents + """ + pass + + # + # Read API + # + + @abstractmethod + def _get_package_name(self, package_id: bytes) -> str: + """ + Returns the package name associated with the given package id, if the + package id exists on the connected registry. + + * Parameters: + * ``package_id``: 32 byte package identifier. + """ + pass + + @abstractmethod + def _get_all_package_ids(self) -> Tuple[bytes]: + """ + Returns a tuple containing all of the package ids found on the connected registry. + """ + pass + + @abstractmethod + def _get_release_id(self, package_name: str, version: str) -> bytes: + """ + Returns the 32 bytes release id associated with the given package name and version, + if the release exists on the connected registry. + + * Parameters: + * ``package_name``: Valid package name according the spec. + * ``version``: Version identifier string, can conform to any versioning scheme. + """ + pass + + @abstractmethod + def _get_all_release_ids(self, package_name: str) -> Tuple[bytes]: + """ + Returns a tuple containg all of the release ids belonging to the given package name, + if the package has releases on the connected registry. + + * Parameters: + * ``package_name``: Valid package name according the spec. + """ + pass + + @abstractmethod + def _get_release_data(self, release_id: bytes) -> Tuple[str, str, str]: + """ + Returns a tuple containing (package_name, version, manifest_uri) for the given release id, + if the release exists on the connected registry. + + * Parameters: + * ``release_id``: 32 byte release identifier. + """ + pass + + @abstractmethod + def _generate_release_id(self, package_name: str, version: str) -> bytes: + """ + Returns the 32 byte release identifier that *would* be associated with the given + package name and version according to the registry's hashing mechanism. + The release *does not* have to exist on the connected registry. + + * Parameters: + * ``package_name``: Valid package name according the spec. + * ``version``: Version identifier string, can conform to any versioning scheme. + """ + pass + + @abstractmethod + def _num_package_ids(self) -> int: + """ + Returns the number of packages that exist on the connected registry. + """ + pass + + @abstractmethod + def _num_release_ids(self, package_name: str) -> int: + """ + Returns the number of releases found on the connected registry, + that belong to the given package name. + + * Parameters: + * ``package_name``: Valid package name according the spec. + """ + pass + + +class VyperReferenceRegistry(ERCRegistry): + """ + The ``VyperReferenceRegistry`` class implements all of the methods found in ``ERCRegistry``, + along with some custom methods included in the `implementation + `__. + """ + + def __init__(self, address: Address, w3: Web3) -> None: + # todo: validate runtime bytecode + abi = get_vyper_registry_manifest()["contract_types"]["registry"]["abi"] + self.registry = w3.vns.contract(address=address, abi=abi) + self.address = to_checksum_address(address) + self.w3 = w3 + + @classmethod + def deploy_new_instance(cls, w3: Web3) -> "VyperReferenceRegistry": + """ + Returns a new instance of ```VyperReferenceRegistry`` representing a freshly deployed + instance on the given ``web3`` instance of the Vyper Reference Registry implementation. + """ + manifest = get_vyper_registry_manifest() + registry_package = Package(manifest, w3) + registry_factory = registry_package.get_contract_factory("registry") + tx_hash = registry_factory.constructor().transact() + tx_receipt = w3.vns.waitForTransactionReceipt(tx_hash) + registry_address = to_canonical_address(tx_receipt.contractAddress) + return cls(registry_address, w3) + + def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: + if len(package_name) > 32 or len(version) > 32: + raise PMError( + "Vyper registry only works with package names and versions less than 32 chars." + ) + if len(manifest_uri) > 1000: + raise PMError( + "Vyper registry only works with manifest URIs shorter than 1000 chars." + ) + args = process_vyper_args(package_name, version, manifest_uri) + tx_hash = self.registry.functions.release(*args).transact() + self.w3.vns.waitForTransactionReceipt(tx_hash) + return self._get_release_id(package_name, version) + + def _get_package_name(self, package_id: bytes) -> str: + package_name = self.registry.functions.getPackageName(package_id).call() + return to_text(package_name.rstrip(b"\x00")) + + @to_tuple + def _get_all_package_ids(self) -> Iterable[Tuple[bytes]]: + num_packages = self._num_package_ids() + for index in range(0, num_packages, 4): + package_ids = self.registry.functions.getAllPackageIds(index, 5).call() + for package_id in package_ids: + if package_id != b"\x00" * 32: + yield package_id + + def _get_release_id(self, package_name: str, version: str) -> bytes: + actual_args = process_vyper_args(package_name, version) + return self.registry.functions.getReleaseId(*actual_args).call() + + @to_tuple + def _get_all_release_ids(self, package_name: str) -> Iterable[Tuple[bytes]]: + actual_name = process_vyper_args(package_name) + num_releases = self.registry.functions.numReleaseIds(*actual_name).call() + for index in range(0, num_releases, 4): + release_ids = self.registry.functions.getAllReleaseIds( + *actual_name, index, 5 + ).call() + for release_id in release_ids: + if release_id != b"\x00" * 32: + yield release_id + + @to_tuple + def _get_release_data(self, release_id: bytes) -> Iterable[Tuple[str]]: + release_data = self.registry.functions.getReleaseData(release_id).call() + for data in release_data: + if data != b"\x00" * 32: + yield to_text(data.rstrip(b"\x00")) + + def _generate_release_id(self, package_name: str, version: str) -> bytes: + args = process_vyper_args(package_name, version) + return self.registry.functions.generateReleaseId(*args).call() + + def _num_package_ids(self) -> int: + return self.registry.functions.numPackageIds().call() + + def _num_release_ids(self, package_name: str) -> int: + args = process_vyper_args(package_name) + return self.registry.functions.numReleaseIds(*args).call() + + def owner(self) -> Address: + """ + Returns the address of the ``owner`` of this registry instance. Only the ``owner`` + is allowed to add releases to the Vyper Reference Registry implementation. + """ + return self.registry.functions.owner().call() + + def transfer_owner(self, new_owner: Address) -> TxReceipt: + """ + Transfers ownership of this registry instance to the given ``new_owner``. Only the + ``owner`` is allowed to transfer ownership. + + * Parameters: + * ``new_owner``: The address of the new owner. + """ + tx_hash = self.registry.functions.transferOwner(new_owner).transact() + return self.w3.vns.waitForTransactionReceipt(tx_hash) + + +class SolidityReferenceRegistry(ERCRegistry): + """ + This class represents an instance of the `Solidity Reference Registry implementation + `__. + To use this subclass, you must manually set an instance of this class to the + ``registry`` attribute on ``web3.pm``. + """ + + def __init__(self, address: Address, w3: Web3) -> None: + abi = get_solidity_registry_manifest()["contract_types"]["PackageRegistry"][ + "abi" + ] + self.registry = w3.vns.contract(address=address, abi=abi) + self.address = to_checksum_address(address) + self.w3 = w3 + + def _release(self, package_name: str, version: str, manifest_uri: str) -> bytes: + tx_hash = self.registry.functions.release( + package_name, version, manifest_uri + ).transact() + self.w3.vns.waitForTransactionReceipt(tx_hash) + return self._get_release_id(package_name, version) + + def _get_package_name(self, package_id: bytes) -> str: + package_name = self.registry.functions.getPackageName(package_id).call() + return package_name + + @to_tuple + def _get_all_package_ids(self) -> Iterable[Tuple[bytes]]: + num_packages = self._num_package_ids() + # Logic here b/c Solidity Reference Registry implementation returns ids in reverse order + package_ids = [ + self.registry.functions.getAllPackageIds(index, (index + 4)).call()[0] + for index in range(0, num_packages, 4) + ] + for package_id in concat([x[::-1] for x in package_ids]): + yield package_id + + def _get_release_id(self, package_name: str, version: str) -> bytes: + return self.registry.functions.getReleaseId(package_name, version).call() + + @to_tuple + def _get_all_release_ids(self, package_name: str) -> Iterable[Tuple[bytes]]: + num_releases = self._num_release_ids(package_name) + # Logic here b/c Solidity Reference Registry implementation returns ids in reverse order + release_ids = [ + self.registry.functions.getAllReleaseIds( + package_name, index, (index + 4) + ).call()[0] + for index in range(0, num_releases, 4) + ] + for release_id in concat([x[::-1] for x in release_ids]): + yield release_id + + @to_tuple + def _get_release_data(self, release_id: bytes) -> Iterable[Tuple[str]]: + release_data = self.registry.functions.getReleaseData(release_id).call() + for data in release_data: + yield data + + def _generate_release_id(self, package_name: str, version: str) -> bytes: + return self.registry.functions.generateReleaseId(package_name, version).call() + + def _num_package_ids(self) -> int: + return self.registry.functions.numPackageIds().call() + + def _num_release_ids(self, package_name: str) -> int: + return self.registry.functions.numReleaseIds(package_name).call() + + +class PM(Module): + """ + By default, the PM module uses the Vyper Reference Registry `implementation + `__. + However, it will work with any subclass of ``ERCRegistry``, tailored to a particular + implementation of `ERC1319 `__, set as + its ``registry`` attribute. + """ + + def get_package_from_manifest(self, manifest: Manifest) -> Package: + """ + Returns a `Package `__ + instance built with the given manifest. + + * Parameters: + * ``manifest``: A dict representing a valid manifest + """ + return Package(manifest, self.web3) + + def get_package_from_uri(self, manifest_uri: URI) -> Package: + """ + Returns a `Package `__ + instance built with the Manifest stored at the URI. + If you want to use a specific IPFS backend, set ``ETHPM_IPFS_BACKEND_CLASS`` + to your desired backend. Defaults to Infura IPFS backend. + + * Parameters: + * ``uri``: Must be a valid content-addressed URI + """ + return Package.from_uri(manifest_uri, self.web3) + + def set_registry(self, address: Address) -> None: + """ + Sets the current registry used in ``web3.pm`` functions that read/write to an on-chain + registry. This method accepts checksummed/canonical addresses or ENS names. Addresses + must point to an instance of the Vyper Reference Registry implementation. + If you want to use a different registry implementation with ``web3.pm``, manually + set the ``web3.pm.registry`` attribute to any subclass of ``ERCRegistry``. + + To use an ENS domain as the address, make sure a valid ENS instance set as ``web3.ens``. + + * Parameters: + * ``address``: Address of on-chain Vyper Reference Registry. + """ + if is_canonical_address(address) or is_checksum_address(address): + canonical_address = to_canonical_address(address) + self.registry = VyperReferenceRegistry(canonical_address, self.web3) + elif is_ens_name(address): + self._validate_set_ens() + addr_lookup = self.web3.ens.address(address) + if not addr_lookup: + raise NameNotFound( + "No address found after ENS lookup for name: {0}.".format(address) + ) + self.registry = VyperReferenceRegistry( + to_canonical_address(addr_lookup), self.web3 + ) + else: + raise PMError( + "Expected a canonical/checksummed address or ENS name for the address, " + "instead received {0}.".format(type(address)) + ) + + def deploy_and_set_registry(self) -> Address: + """ + Returns the address of a freshly deployed instance of the `vyper registry + `__, + and sets the newly deployed registry as the active registry on ``web3.pm.registry``. + + To tie your registry to an ENS name, use web3's ENS module, ie. + + .. code-block:: python + + w3.ens.setup_address(ens_name, w3.pm.registry.address) + """ + self.registry = VyperReferenceRegistry.deploy_new_instance(self.web3) + return to_checksum_address(self.registry.address) + + def release_package( + self, package_name: str, version: str, manifest_uri: str + ) -> bytes: + """ + Returns the release id generated by releasing a package on the current registry. + Requires ``web3.PM`` to have a registry set. Requires ``web3.vns.defaultAccount`` + to be the registry owner. + + * Parameters: + * ``package_name``: Must be a valid package name, matching the given manifest. + * ``version``: Must be a valid package version, matching the given manifest. + * ``manifest_uri``: Must be a valid content-addressed URI. Currently, only IPFS + and Github content-addressed URIs are supported. + + """ + validate_is_supported_manifest_uri(manifest_uri) + raw_manifest = to_text(resolve_uri_contents(manifest_uri)) + validate_raw_manifest_format(raw_manifest) + manifest = json.loads(raw_manifest) + validate_manifest_against_schema(manifest) + if package_name != manifest['package_name']: + raise ManifestValidationError( + f"Provided package name: {package_name} does not match the package name " + f"found in the manifest: {manifest['package_name']}." + ) + + if version != manifest['version']: + raise ManifestValidationError( + f"Provided package version: {version} does not match the package version " + f"found in the manifest: {manifest['version']}." + ) + + self._validate_set_registry() + return self.registry._release(package_name, version, manifest_uri) + + @to_tuple + def get_all_package_names(self) -> Iterable[str]: + """ + Returns a tuple containing all the package names available on the current registry. + """ + self._validate_set_registry() + package_ids = self.registry._get_all_package_ids() + for package_id in package_ids: + yield self.registry._get_package_name(package_id) + + def get_package_count(self) -> int: + """ + Returns the number of packages available on the current registry. + """ + self._validate_set_registry() + return self.registry._num_package_ids() + + def get_release_count(self, package_name: str) -> int: + """ + Returns the number of releases of the given package name available on the current registry. + """ + validate_package_name(package_name) + self._validate_set_registry() + return self.registry._num_release_ids(package_name) + + def get_release_id(self, package_name: str, version: str) -> bytes: + """ + Returns the 32 byte identifier of a release for the given package name and version, + if they are available on the current registry. + """ + validate_package_name(package_name) + validate_package_version(version) + self._validate_set_registry() + return self.registry._get_release_id(package_name, version) + + @to_tuple + def get_all_package_releases(self, package_name: str) -> Iterable[Tuple[str, str]]: + """ + Returns a tuple of release data (version, manifest_ur) for every release of the + given package name available on the current registry. + """ + validate_package_name(package_name) + self._validate_set_registry() + release_ids = self.registry._get_all_release_ids(package_name) + for release_id in release_ids: + _, version, manifest_uri = self.registry._get_release_data(release_id) + yield (version, manifest_uri) + + def get_release_id_data(self, release_id: bytes) -> Tuple[str, str, str]: + """ + Returns ``(package_name, version, manifest_uri)`` associated with the given + release id, *if* it is available on the current registry. + + * Parameters: + * ``release_id``: 32 byte release identifier + """ + self._validate_set_registry() + return self.registry._get_release_data(release_id) + + def get_release_data(self, package_name: str, version: str) -> Tuple[str, str, str]: + """ + Returns ``(package_name, version, manifest_uri)`` associated with the given + package name and version, *if* they are published to the currently set registry. + + * Parameters: + * ``name``: Must be a valid package name. + * ``version``: Must be a valid package version. + """ + validate_package_name(package_name) + validate_package_version(version) + self._validate_set_registry() + release_id = self.registry._get_release_id(package_name, version) + return self.get_release_id_data(release_id) + + def get_package(self, package_name: str, version: str) -> Package: + """ + Returns a ``Package`` instance, generated by the ``manifest_uri`` associated with the + given package name and version, if they are published to the currently set registry. + + * Parameters: + * ``name``: Must be a valid package name. + * ``version``: Must be a valid package version. + """ + validate_package_name(package_name) + validate_package_version(version) + self._validate_set_registry() + _, _, release_uri = self.get_release_data(package_name, version) + return self.get_package_from_uri(release_uri) + + def _validate_set_registry(self) -> None: + try: + self.registry + except AttributeError: + raise PMError( + "web3.pm does not have a set registry. " + "Please set registry with either: " + "web3.pm.set_registry(address) or " + "web3.pm.deploy_and_set_registry()" + ) + if not isinstance(self.registry, ERCRegistry): + raise PMError( + "web3.pm requires an instance of a subclass of ERCRegistry " + "to be set as the web3.pm.registry attribute. Instead found: " + f"{type(self.registry)}." + ) + + def _validate_set_ens(self) -> None: + if not self.web3: + raise InvalidAddress( + "Could not look up ENS address because no web3 " "connection available" + ) + elif not self.web3.ens: + raise InvalidAddress( + "Could not look up ENS address because web3.ens is " "set to None" + ) + + +def get_vyper_registry_manifest() -> Dict[str, Any]: + return json.loads((ASSETS_DIR / "vyper_registry" / "0.1.0.json").read_text()) + + +def get_solidity_registry_manifest() -> Dict[str, Any]: + return json.loads((ASSETS_DIR / "registry" / "1.0.0.json").read_text()) + + +def validate_is_supported_manifest_uri(uri): + if not is_ipfs_uri(uri) and not is_valid_content_addressed_github_uri(uri): + raise ManifestValidationError( + f"URI: {uri} is not a valid content-addressed URI. " + "Currently only IPFS and Github content-addressed URIs are supported." + ) + + +@to_tuple +def process_vyper_args(*args: List[str]) -> Iterable[bytes]: + for arg in args: + yield to_bytes(text=arg) diff --git a/web3/providers/__init__.py b/web3/providers/__init__.py index 701c59b5c3..aed311a3d8 100644 --- a/web3/providers/__init__.py +++ b/web3/providers/__init__.py @@ -1,16 +1,9 @@ -from .base import ( # noqa: F401 - BaseProvider, - JSONBaseProvider, -) -from .ipc import ( # noqa: F401, - IPCProvider, -) -from .rpc import ( # noqa: F401, - HTTPProvider, -) -from .websocket import ( # noqa: F401, - WebsocketProvider, -) -from .auto import ( # noqa: F401, - AutoProvider, -) +from .base import ( # noqa: F401 + BaseProvider, + JSONBaseProvider, +) + +from .rpc import HTTPProvider # noqa: F401 +from .ipc import IPCProvider # noqa: F401 +from .websocket import WebsocketProvider # noqa: F401 +from .auto import AutoProvider # noqa: F401 diff --git a/web3/providers/auto.py b/web3/providers/auto.py index 9424469bc2..6807984793 100644 --- a/web3/providers/auto.py +++ b/web3/providers/auto.py @@ -1,123 +1,100 @@ -import os -from typing import ( - Any, - Callable, - Dict, - Optional, - Sequence, - Tuple, - Type, - Union, -) -from urllib.parse import ( - urlparse, -) - -from eth_typing import ( - URI, -) - -from web3.exceptions import ( - CannotHandleRequest, -) -from web3.providers import ( - BaseProvider, - HTTPProvider, - IPCProvider, - WebsocketProvider, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -HTTP_SCHEMES = {'http', 'https'} -WS_SCHEMES = {'ws', 'wss'} - - -def load_provider_from_environment() -> BaseProvider: - uri_string = URI(os.environ.get('WEB3_PROVIDER_URI', '')) - if not uri_string: - return None - - return load_provider_from_uri(uri_string) - - -def load_provider_from_uri( - uri_string: URI, headers: Dict[str, Tuple[str, str]]=None -) -> BaseProvider: - uri = urlparse(uri_string) - if uri.scheme == 'file': - return IPCProvider(uri.path) - elif uri.scheme in HTTP_SCHEMES: - return HTTPProvider(uri_string, headers) - elif uri.scheme in WS_SCHEMES: - return WebsocketProvider(uri_string) - else: - raise NotImplementedError( - 'Web3 does not know how to connect to scheme %r in %r' % ( - uri.scheme, - uri_string, - ) - ) - - -class AutoProvider(BaseProvider): - - default_providers = ( - load_provider_from_environment, - IPCProvider, - HTTPProvider, - WebsocketProvider, - ) - _active_provider = None - - def __init__( - self, - potential_providers: Sequence[Union[Callable[..., BaseProvider], Type[BaseProvider]]]=None - ) -> None: - """ - :param iterable potential_providers: ordered series of provider classes to attempt with - - AutoProvider will initialize each potential provider (without arguments), - in an attempt to find an active node. The list will default to - :attribute:`default_providers`. - """ - if potential_providers: - self._potential_providers = potential_providers - else: - self._potential_providers = self.default_providers - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - try: - return self._proxy_request(method, params) - except IOError as exc: - return self._proxy_request(method, params, use_cache=False) - - def isConnected(self) -> bool: - provider = self._get_active_provider(use_cache=True) - return provider is not None and provider.isConnected() - - def _proxy_request(self, method: RPCEndpoint, params: Any, use_cache: bool=True) -> RPCResponse: - provider = self._get_active_provider(use_cache) - if provider is None: - raise CannotHandleRequest( - "Could not discover provider while making request: " - "method:{0}\n" - "params:{1}\n".format( - method, - params)) - - return provider.make_request(method, params) - - def _get_active_provider(self, use_cache: bool) -> Optional[BaseProvider]: - if use_cache and self._active_provider is not None: - return self._active_provider - - for Provider in self._potential_providers: - provider = Provider() - if provider is not None and provider.isConnected(): - self._active_provider = provider - return provider - - return None +import os +from urllib.parse import ( + urlparse, +) + +from web3.exceptions import ( + CannotHandleRequest, +) +from web3.providers import ( + BaseProvider, + HTTPProvider, + IPCProvider, + WebsocketProvider, +) + +HTTP_SCHEMES = {'http', 'https'} +WS_SCHEMES = {'ws', 'wss'} + + +def load_provider_from_environment(): + uri_string = os.environ.get('WEB3_PROVIDER_URI', '') + if not uri_string: + return None + + return load_provider_from_uri(uri_string) + + +def load_provider_from_uri(uri_string, headers=None): + uri = urlparse(uri_string) + if uri.scheme == 'file': + return IPCProvider(uri.path) + elif uri.scheme in HTTP_SCHEMES: + return HTTPProvider(uri_string, headers) + elif uri.scheme in WS_SCHEMES: + return WebsocketProvider(uri_string) + else: + raise NotImplementedError( + 'Web3 does not know how to connect to scheme %r in %r' % ( + uri.scheme, + uri_string, + ) + ) + + +class AutoProvider(BaseProvider): + + default_providers = ( + load_provider_from_environment, + IPCProvider, + HTTPProvider, + WebsocketProvider, + ) + _active_provider = None + + def __init__(self, potential_providers=None): + """ + :param iterable potential_providers: ordered series of provider classes to attempt with + + AutoProvider will initialize each potential provider (without arguments), + in an attempt to find an active node. The list will default to + :attribute:`default_providers`. + """ + if potential_providers: + self._potential_providers = potential_providers + else: + self._potential_providers = self.default_providers + + def make_request(self, method, params): + try: + return self._proxy_request(method, params) + except IOError as exc: + return self._proxy_request(method, params, use_cache=False) + + def isConnected(self): + provider = self._get_active_provider(use_cache=True) + return provider is not None and provider.isConnected() + + def _proxy_request(self, method, params, use_cache=True): + provider = self._get_active_provider(use_cache) + if provider is None: + raise CannotHandleRequest( + "Could not discover provider while making request: " + "method:{0}\n" + "params:{1}\n".format( + method, + params)) + + return provider.make_request(method, params) + + def _get_active_provider(self, use_cache): + if use_cache and self._active_provider is not None: + return self._active_provider + + for Provider in self._potential_providers: + provider = Provider() + if provider is not None and provider.isConnected(): + self._active_provider = provider + return provider + + return None diff --git a/web3/providers/base.py b/web3/providers/base.py index c00c0462f3..7a6c9494b1 100644 --- a/web3/providers/base.py +++ b/web3/providers/base.py @@ -1,109 +1,84 @@ -import itertools -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Sequence, - Tuple, -) - -from eth_utils import ( - to_bytes, - to_text, -) - -from web3._utils.encoding import ( - FriendlyJsonSerde, -) -from web3.middleware import ( - combine_middlewares, -) -from web3.types import ( - Middleware, - MiddlewareOnion, - RPCEndpoint, - RPCResponse, -) - -if TYPE_CHECKING: - from web3 import Web3 # noqa: F401 - - -class BaseProvider: - _middlewares: Tuple[Middleware, ...] = () - # a tuple of (all_middlewares, request_func) - _request_func_cache: Tuple[Tuple[Middleware, ...], Callable[..., RPCResponse]] = (None, None) - - @property - def middlewares(self) -> Tuple[Middleware, ...]: - return self._middlewares - - @middlewares.setter - def middlewares( - self, values: MiddlewareOnion - ) -> None: - # tuple(values) converts to MiddlewareOnion -> Tuple[Middleware, ...] - self._middlewares = tuple(values) # type: ignore - - def request_func( - self, web3: "Web3", outer_middlewares: MiddlewareOnion - ) -> Callable[..., RPCResponse]: - """ - @param outer_middlewares is an iterable of middlewares, ordered by first to execute - @returns a function that calls all the middleware and eventually self.make_request() - """ - # type ignored b/c tuple(MiddlewareOnion) converts to tuple of middlewares - all_middlewares: Tuple[Middleware] = tuple(outer_middlewares) + tuple(self.middlewares) # type: ignore # noqa: E501 - - cache_key = self._request_func_cache[0] - if cache_key is None or cache_key != all_middlewares: - self._request_func_cache = ( - all_middlewares, - self._generate_request_func(web3, all_middlewares) - ) - return self._request_func_cache[-1] - - def _generate_request_func( - self, web3: "Web3", middlewares: Sequence[Middleware] - ) -> Callable[..., RPCResponse]: - return combine_middlewares( - middlewares=middlewares, - web3=web3, - provider_request_fn=self.make_request, - ) - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - raise NotImplementedError("Providers must implement this method") - - def isConnected(self) -> bool: - raise NotImplementedError("Providers must implement this method") - - -class JSONBaseProvider(BaseProvider): - def __init__(self) -> None: - self.request_counter = itertools.count() - - def decode_rpc_response(self, raw_response: bytes) -> RPCResponse: - text_response = to_text(raw_response) - return FriendlyJsonSerde().json_decode(text_response) - - def encode_rpc_request(self, method: RPCEndpoint, params: Any) -> bytes: - rpc_dict = { - "jsonrpc": "2.0", - "method": method, - "params": params or [], - "id": next(self.request_counter), - } - encoded = FriendlyJsonSerde().json_encode(rpc_dict) - return to_bytes(text=encoded) - - def isConnected(self) -> bool: - try: - response = self.make_request(RPCEndpoint('web3_clientVersion'), []) - except IOError: - return False - - assert response['jsonrpc'] == '2.0' - assert 'error' not in response - - return True +import itertools + +from vns_utils import ( + to_bytes, + to_text, +) + +from web3._utils.encoding import ( + FriendlyJsonSerde, +) +from web3.middleware import ( + combine_middlewares, +) + + +class BaseProvider: + _middlewares = () + _request_func_cache = (None, None) # a tuple of (all_middlewares, request_func) + + @property + def middlewares(self): + return self._middlewares + + @middlewares.setter + def middlewares(self, values): + self._middlewares = tuple(values) + + def request_func(self, web3, outer_middlewares): + """ + @param outer_middlewares is an iterable of middlewares, ordered by first to execute + @returns a function that calls all the middleware and eventually self.make_request() + """ + all_middlewares = tuple(outer_middlewares) + tuple(self.middlewares) + + cache_key = self._request_func_cache[0] + if cache_key is None or cache_key != all_middlewares: + self._request_func_cache = ( + all_middlewares, + self._generate_request_func(web3, all_middlewares) + ) + return self._request_func_cache[-1] + + def _generate_request_func(self, web3, middlewares): + return combine_middlewares( + middlewares=middlewares, + web3=web3, + provider_request_fn=self.make_request, + ) + + def make_request(self, method, params): + raise NotImplementedError("Providers must implement this method") + + def isConnected(self): + raise NotImplementedError("Providers must implement this method") + + +class JSONBaseProvider(BaseProvider): + def __init__(self): + self.request_counter = itertools.count() + + def decode_rpc_response(self, response): + text_response = to_text(response) + return FriendlyJsonSerde().json_decode(text_response) + + def encode_rpc_request(self, method, params): + rpc_dict = { + "jsonrpc": "2.0", + "method": method, + "params": params or [], + "id": next(self.request_counter), + } + encoded = FriendlyJsonSerde().json_encode(rpc_dict) + return to_bytes(text=encoded) + + def isConnected(self): + try: + response = self.make_request('web3_clientVersion', []) + except IOError: + return False + + assert response['jsonrpc'] == '2.0' + assert 'error' not in response + + return True diff --git a/web3/providers/ipc.py b/web3/providers/ipc.py index 6fc45ebfd7..54e227b16a 100644 --- a/web3/providers/ipc.py +++ b/web3/providers/ipc.py @@ -1,263 +1,249 @@ -from json import ( - JSONDecodeError, -) -import logging -import os -from pathlib import ( - Path, -) -import socket -import sys -import threading -from types import ( - TracebackType, -) -from typing import ( - Any, - Type, -) - -from web3._utils.threads import ( - Timeout, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -from .base import ( - JSONBaseProvider, -) - - -def get_ipc_socket(ipc_path: str, timeout: float=0.1) -> socket.socket: - if sys.platform == 'win32': - # On Windows named pipe is used. Simulate socket with it. - from web3._utils.windows import NamedPipe - - return NamedPipe(ipc_path) - else: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(ipc_path) - sock.settimeout(timeout) - return sock - - -class PersistantSocket: - sock = None - - def __init__(self, ipc_path: str) -> None: - self.ipc_path = ipc_path - - def __enter__(self) -> socket.socket: - if not self.ipc_path: - raise FileNotFoundError("cannot connect to IPC socket at path: %r" % self.ipc_path) - - if not self.sock: - self.sock = self._open() - return self.sock - - def __exit__( - self, exc_type: Type[BaseException], exc_value: BaseException, traceback: TracebackType - ) -> None: - # only close the socket if there was an error - if exc_value is not None: - try: - self.sock.close() - except Exception: - pass - self.sock = None - - def _open(self) -> socket.socket: - return get_ipc_socket(self.ipc_path) - - def reset(self) -> socket.socket: - self.sock.close() - self.sock = self._open() - return self.sock - - -# type ignored b/c missing return statement is by design here -def get_default_ipc_path() -> str: # type: ignore - if sys.platform == 'darwin': - ipc_path = os.path.expanduser(os.path.join( - "~", - "Library", - "Ethereum", - "geth.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - ipc_path = os.path.expanduser(os.path.join( - "~", - "Library", - "Application Support", - "io.parity.ethereum", - "jsonrpc.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - base_trinity_path = Path('~').expanduser() / '.local' / 'share' / 'trinity' - ipc_path = str(base_trinity_path / 'mainnet' / 'jsonrpc.ipc') - if Path(ipc_path).exists(): - return str(ipc_path) - - elif sys.platform.startswith('linux') or sys.platform.startswith('freebsd'): - ipc_path = os.path.expanduser(os.path.join( - "~", - ".ethereum", - "geth.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - ipc_path = os.path.expanduser(os.path.join( - "~", - ".local", - "share", - "io.parity.ethereum", - "jsonrpc.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - base_trinity_path = Path('~').expanduser() / '.local' / 'share' / 'trinity' - # type ignored b/c ipc_path is already defined as a str above - ipc_path = base_trinity_path / 'mainnet' / 'jsonrpc.ipc' # type: ignore - if ipc_path.exists(): # type: ignore - return str(ipc_path) - - elif sys.platform == 'win32': - ipc_path = os.path.join( - "\\\\", - ".", - "pipe", - "geth.ipc" - ) - if os.path.exists(ipc_path): - return ipc_path - - ipc_path = os.path.join( - "\\\\", - ".", - "pipe", - "jsonrpc.ipc" - ) - if os.path.exists(ipc_path): - return ipc_path - - else: - raise ValueError( - "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " - "supported. You must specify the ipc_path".format(sys.platform) - ) - - -# type ignored b/c missing return statement is by design here -def get_dev_ipc_path() -> str: # type: ignore - if sys.platform == 'darwin': - tmpdir = os.environ.get('TMPDIR', '') - ipc_path = os.path.expanduser(os.path.join( - tmpdir, - "geth.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - elif sys.platform.startswith('linux') or sys.platform.startswith('freebsd'): - ipc_path = os.path.expanduser(os.path.join( - "/tmp", - "geth.ipc" - )) - if os.path.exists(ipc_path): - return ipc_path - - elif sys.platform == 'win32': - ipc_path = os.path.join( - "\\\\", - ".", - "pipe", - "geth.ipc" - ) - if os.path.exists(ipc_path): - return ipc_path - - ipc_path = os.path.join( - "\\\\", - ".", - "pipe", - "jsonrpc.ipc" - ) - if os.path.exists(ipc_path): - return ipc_path - - else: - raise ValueError( - "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " - "supported. You must specify the ipc_path".format(sys.platform) - ) - - -class IPCProvider(JSONBaseProvider): - logger = logging.getLogger("web3.providers.IPCProvider") - _socket = None - - def __init__(self, ipc_path: str=None, timeout: int=10, *args: Any, **kwargs: Any) -> None: - if ipc_path is None: - self.ipc_path = get_default_ipc_path() - elif isinstance(ipc_path, str) or isinstance(ipc_path, Path): - self.ipc_path = str(Path(ipc_path).expanduser().resolve()) - else: - raise TypeError("ipc_path must be of type string or pathlib.Path") - - self.timeout = timeout - self._lock = threading.Lock() - self._socket = PersistantSocket(self.ipc_path) - super().__init__() - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request IPC. Path: %s, Method: %s", - self.ipc_path, method) - request = self.encode_rpc_request(method, params) - - with self._lock, self._socket as sock: - try: - sock.sendall(request) - except BrokenPipeError: - # one extra attempt, then give up - sock = self._socket.reset() - sock.sendall(request) - - raw_response = b"" - with Timeout(self.timeout) as timeout: - while True: - try: - raw_response += sock.recv(4096) - except socket.timeout: - timeout.sleep(0) - continue - if raw_response == b"": - timeout.sleep(0) - elif has_valid_json_rpc_ending(raw_response): - try: - response = self.decode_rpc_response(raw_response) - except JSONDecodeError: - timeout.sleep(0) - continue - else: - return response - else: - timeout.sleep(0) - continue - - -# A valid JSON RPC response can only end in } or ] http://www.jsonrpc.org/specification -def has_valid_json_rpc_ending(raw_response: bytes) -> bool: - stripped_raw_response = raw_response.rstrip() - for valid_ending in [b"}", b"]"]: - if stripped_raw_response.endswith(valid_ending): - return True - else: - return False +import logging +import os +from pathlib import ( + Path, +) +import socket +import sys +import threading + +from web3._utils.threads import ( + Timeout, +) + +from .base import ( + JSONBaseProvider, +) + +try: + from json import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError + + +def get_ipc_socket(ipc_path, timeout=0.1): + if sys.platform == 'win32': + # On Windows named pipe is used. Simulate socket with it. + from web3._utils.windows import NamedPipe + + return NamedPipe(ipc_path) + else: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(ipc_path) + sock.settimeout(timeout) + return sock + + +class PersistantSocket: + sock = None + + def __init__(self, ipc_path): + self.ipc_path = ipc_path + + def __enter__(self): + if not self.ipc_path: + raise FileNotFoundError("cannot connect to IPC socket at path: %r" % self.ipc_path) + + if not self.sock: + self.sock = self._open() + return self.sock + + def __exit__(self, exc_type, exc_value, traceback): + # only close the socket if there was an error + if exc_value is not None: + try: + self.sock.close() + except Exception: + pass + self.sock = None + + def _open(self): + return get_ipc_socket(self.ipc_path) + + def reset(self): + self.sock.close() + self.sock = self._open() + return self.sock + + +def get_default_ipc_path(): + if sys.platform == 'darwin': + ipc_path = os.path.expanduser(os.path.join( + "~", + "Library", + "Ethereum", + "geth.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.expanduser(os.path.join( + "~", + "Library", + "Application Support", + "io.parity.ethereum", + "jsonrpc.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + base_trinity_path = Path('~').expanduser() / '.local' / 'share' / 'trinity' + ipc_path = base_trinity_path / 'mainnet' / 'jsonrpc.ipc' + if ipc_path.exists(): + return str(ipc_path) + + elif sys.platform.startswith('linux') or sys.platform.startswith('freebsd'): + ipc_path = os.path.expanduser(os.path.join( + "~", + ".ethereum", + "geth.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.expanduser(os.path.join( + "~", + ".local", + "share", + "io.parity.ethereum", + "jsonrpc.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + base_trinity_path = Path('~').expanduser() / '.local' / 'share' / 'trinity' + ipc_path = base_trinity_path / 'mainnet' / 'jsonrpc.ipc' + if ipc_path.exists(): + return str(ipc_path) + + elif sys.platform == 'win32': + ipc_path = os.path.join( + "\\\\", + ".", + "pipe", + "geth.ipc" + ) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.join( + "\\\\", + ".", + "pipe", + "jsonrpc.ipc" + ) + if os.path.exists(ipc_path): + return ipc_path + + else: + raise ValueError( + "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " + "supported. You must specify the ipc_path".format(sys.platform) + ) + + +def get_dev_ipc_path(): + if sys.platform == 'darwin': + tmpdir = os.environ.get('TMPDIR', '') + ipc_path = os.path.expanduser(os.path.join( + tmpdir, + "geth.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + elif sys.platform.startswith('linux') or sys.platform.startswith('freebsd'): + ipc_path = os.path.expanduser(os.path.join( + "/tmp", + "geth.ipc" + )) + if os.path.exists(ipc_path): + return ipc_path + + elif sys.platform == 'win32': + ipc_path = os.path.join( + "\\\\", + ".", + "pipe", + "geth.ipc" + ) + if os.path.exists(ipc_path): + return ipc_path + + ipc_path = os.path.join( + "\\\\", + ".", + "pipe", + "jsonrpc.ipc" + ) + if os.path.exists(ipc_path): + return ipc_path + + else: + raise ValueError( + "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " + "supported. You must specify the ipc_path".format(sys.platform) + ) + + +class IPCProvider(JSONBaseProvider): + logger = logging.getLogger("web3.providers.IPCProvider") + _socket = None + + def __init__(self, ipc_path=None, timeout=10, *args, **kwargs): + if ipc_path is None: + self.ipc_path = get_default_ipc_path() + elif isinstance(ipc_path, str) or isinstance(ipc_path, Path): + self.ipc_path = str(Path(ipc_path).expanduser().resolve()) + else: + raise TypeError("ipc_path must be of type string or pathlib.Path") + + self.timeout = timeout + self._lock = threading.Lock() + self._socket = PersistantSocket(self.ipc_path) + super().__init__(*args, **kwargs) + + def make_request(self, method, params): + self.logger.debug("Making request IPC. Path: %s, Method: %s", + self.ipc_path, method) + request = self.encode_rpc_request(method, params) + + with self._lock, self._socket as sock: + try: + sock.sendall(request) + except BrokenPipeError: + # one extra attempt, then give up + sock = self._socket.reset() + sock.sendall(request) + + raw_response = b"" + with Timeout(self.timeout) as timeout: + while True: + try: + raw_response += sock.recv(4096) + except socket.timeout: + timeout.sleep(0) + continue + if raw_response == b"": + timeout.sleep(0) + elif has_valid_json_rpc_ending(raw_response): + try: + response = self.decode_rpc_response(raw_response) + except JSONDecodeError: + timeout.sleep(0) + continue + else: + return response + else: + timeout.sleep(0) + continue + + +# A valid JSON RPC response can only end in } or ] http://www.jsonrpc.org/specification +def has_valid_json_rpc_ending(raw_response): + stripped_raw_response = raw_response.rstrip() + for valid_ending in [b"}", b"]"]: + if stripped_raw_response.endswith(valid_ending): + return True + else: + return False diff --git a/web3/providers/rpc.py b/web3/providers/rpc.py index 8ce7f2e612..9508715475 100644 --- a/web3/providers/rpc.py +++ b/web3/providers/rpc.py @@ -1,89 +1,74 @@ -import logging -import os -from typing import ( - Any, - Dict, - Iterable, - Tuple, -) - -from eth_typing import ( - URI, -) -from eth_utils import ( - to_dict, -) - -from web3._utils.http import ( - construct_user_agent, -) -from web3._utils.request import ( - make_post_request, -) -from web3.datastructures import ( - NamedElementOnion, -) -from web3.middleware import ( - http_retry_request_middleware, -) -from web3.types import ( - Middleware, - RPCEndpoint, - RPCResponse, -) - -from .base import ( - JSONBaseProvider, -) - - -def get_default_endpoint() -> URI: - return URI(os.environ.get('WEB3_HTTP_PROVIDER_URI', 'http://localhost:8545')) - - -class HTTPProvider(JSONBaseProvider): - logger = logging.getLogger("web3.providers.HTTPProvider") - endpoint_uri = None - _request_args = None - _request_kwargs = None - # type ignored b/c conflict with _middlewares attr on BaseProvider - _middlewares: Tuple[Middleware, ...] = NamedElementOnion([(http_retry_request_middleware, 'http_retry_request')]) # type: ignore # noqa: E501 - - def __init__(self, endpoint_uri: URI=None, request_kwargs: Any=None) -> None: - if endpoint_uri is None: - self.endpoint_uri = get_default_endpoint() - else: - self.endpoint_uri = endpoint_uri - self._request_kwargs = request_kwargs or {} - super().__init__() - - def __str__(self) -> str: - return "RPC connection {0}".format(self.endpoint_uri) - - @to_dict - def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]: - if 'headers' not in self._request_kwargs: - yield 'headers', self.get_request_headers() - for key, value in self._request_kwargs.items(): - yield key, value - - def get_request_headers(self) -> Dict[str, str]: - return { - 'Content-Type': 'application/json', - 'User-Agent': construct_user_agent(str(type(self))), - } - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request HTTP. URI: %s, Method: %s", - self.endpoint_uri, method) - request_data = self.encode_rpc_request(method, params) - raw_response = make_post_request( - self.endpoint_uri, - request_data, - **self.get_request_kwargs() - ) - response = self.decode_rpc_response(raw_response) - self.logger.debug("Getting response HTTP. URI: %s, " - "Method: %s, Response: %s", - self.endpoint_uri, method, response) - return response +import logging +import os + +from vns_utils import ( + to_dict, +) + +from web3._utils.http import ( + construct_user_agent, +) +from web3._utils.request import ( + make_post_request, +) +from web3.datastructures import ( + NamedElementOnion, +) +from web3.middleware import ( + http_retry_request_middleware, +) + +from .base import ( + JSONBaseProvider, +) + + +def get_default_endpoint(): + return os.environ.get('WEB3_HTTP_PROVIDER_URI', 'http://localhost:8545') + + +class HTTPProvider(JSONBaseProvider): + logger = logging.getLogger("web3.providers.HTTPProvider") + endpoint_uri = None + _request_args = None + _request_kwargs = None + _middlewares = NamedElementOnion([(http_retry_request_middleware, 'http_retry_request')]) + + def __init__(self, endpoint_uri=None, request_kwargs=None): + if endpoint_uri is None: + self.endpoint_uri = get_default_endpoint() + else: + self.endpoint_uri = endpoint_uri + self._request_kwargs = request_kwargs or {} + super().__init__() + + def __str__(self): + return "RPC connection {0}".format(self.endpoint_uri) + + @to_dict + def get_request_kwargs(self): + if 'headers' not in self._request_kwargs: + yield 'headers', self.get_request_headers() + for key, value in self._request_kwargs.items(): + yield key, value + + def get_request_headers(self): + return { + 'Content-Type': 'application/json', + 'User-Agent': construct_user_agent(str(type(self))), + } + + def make_request(self, method, params): + self.logger.debug("Making request HTTP. URI: %s, Method: %s", + self.endpoint_uri, method) + request_data = self.encode_rpc_request(method, params) + raw_response = make_post_request( + self.endpoint_uri, + request_data, + **self.get_request_kwargs() + ) + response = self.decode_rpc_response(raw_response) + self.logger.debug("Getting response HTTP. URI: %s, " + "Method: %s, Response: %s", + self.endpoint_uri, method, response) + return response diff --git a/web3/providers/eth_tester/__init__.py b/web3/providers/vns_tester/__init__.py similarity index 94% rename from web3/providers/eth_tester/__init__.py rename to web3/providers/vns_tester/__init__.py index a44d0a1d21..e64a39f8f7 100644 --- a/web3/providers/eth_tester/__init__.py +++ b/web3/providers/vns_tester/__init__.py @@ -1,4 +1,4 @@ - -from .main import ( # noqa: F401 - EthereumTesterProvider, -) + +from .main import ( # noqa: F401 + EthereumTesterProvider, +) diff --git a/web3/providers/eth_tester/defaults.py b/web3/providers/vns_tester/defaults.py similarity index 53% rename from web3/providers/eth_tester/defaults.py rename to web3/providers/vns_tester/defaults.py index 9320be6329..18b607085d 100644 --- a/web3/providers/eth_tester/defaults.py +++ b/web3/providers/vns_tester/defaults.py @@ -1,427 +1,344 @@ -import operator -import random -import sys -from typing import ( - TYPE_CHECKING, - Any, - Callable, - List, - NoReturn, - Tuple, - Type, - TypeVar, -) - -from eth_tester.exceptions import ( - BlockNotFound, - FilterNotFound, - TransactionNotFound, - ValidationError, -) -from eth_typing import ( - HexAddress, - HexStr, -) -from eth_utils import ( - decode_hex, - encode_hex, - is_null, - keccak, -) -from eth_utils.curried import ( - apply_formatter_if, -) -from eth_utils.toolz import ( - compose, - curry, - excepts, -) - -from web3.types import ( - LogReceipt, - RPCResponse, - TxReceipt, -) - -if TYPE_CHECKING: - from eth_tester import ( # noqa: F401 - EthereumTester, - ) - -TReturn = TypeVar("TReturn") -TParams = TypeVar("TParams") -TValue = TypeVar("TValue") - - -def not_implemented(*args: Any, **kwargs: Any) -> NoReturn: - raise NotImplementedError("RPC method not implemented") - - -@curry -def call_eth_tester( - fn_name: str, eth_tester: "EthereumTester", fn_args: Any, fn_kwargs: Any=None -) -> RPCResponse: - if fn_kwargs is None: - fn_kwargs = {} - return getattr(eth_tester, fn_name)(*fn_args, **fn_kwargs) - - -def without_eth_tester( - fn: Callable[[TParams], TReturn] -) -> Callable[["EthereumTester", TParams], TReturn]: - # workaround for: https://github.com/pytoolz/cytoolz/issues/103 - # @functools.wraps(fn) - def inner(eth_tester: "EthereumTester", params: TParams) -> TReturn: - return fn(params) - return inner - - -def without_params( - fn: Callable[[TParams], TReturn] -) -> Callable[["EthereumTester", TParams], TReturn]: - # workaround for: https://github.com/pytoolz/cytoolz/issues/103 - # @functools.wraps(fn) - def inner(eth_tester: "EthereumTester", params: Any) -> TReturn: - return fn(eth_tester) - return inner - - -@curry -def preprocess_params( - eth_tester: "EthereumTester", params: Any, preprocessor_fn: Callable[..., Any] -) -> Tuple["EthereumTester", Callable[..., Any]]: - return eth_tester, preprocessor_fn(params) - - -def static_return(value: TValue) -> Callable[..., TValue]: - def inner(*args: Any, **kwargs: Any) -> TValue: - return value - return inner - - -def client_version(eth_tester: "EthereumTester", params: Any) -> str: - # TODO: account for the backend that is in use. - from eth_tester import __version__ - return "EthereumTester/{version}/{platform}/python{v.major}.{v.minor}.{v.micro}".format( - version=__version__, - v=sys.version_info, - platform=sys.platform, - ) - - -@curry -def null_if_excepts( - exc_type: Type[BaseException], fn: Callable[..., TReturn] -) -> Callable[..., TReturn]: - return excepts( - exc_type, - fn, - static_return(None), - ) - - -null_if_block_not_found = null_if_excepts(BlockNotFound) -null_if_transaction_not_found = null_if_excepts(TransactionNotFound) -null_if_filter_not_found = null_if_excepts(FilterNotFound) -null_if_indexerror = null_if_excepts(IndexError) - - -@null_if_indexerror -@null_if_block_not_found -def get_transaction_by_block_hash_and_index( - eth_tester: "EthereumTester", params: Any -) -> TxReceipt: - block_hash, transaction_index = params - block = eth_tester.get_block_by_hash(block_hash, full_transactions=True) - transaction = block['transactions'][transaction_index] - return transaction - - -@null_if_indexerror -@null_if_block_not_found -def get_transaction_by_block_number_and_index( - eth_tester: "EthereumTester", params: Any -) -> TxReceipt: - block_number, transaction_index = params - block = eth_tester.get_block_by_number(block_number, full_transactions=True) - transaction = block['transactions'][transaction_index] - return transaction - - -def create_log_filter(eth_tester: "EthereumTester", params: Any) -> int: - filter_params = params[0] - filter_id = eth_tester.create_log_filter(**filter_params) - return filter_id - - -def get_logs(eth_tester: "EthereumTester", params: Any) -> List[LogReceipt]: - filter_params = params[0] - logs = eth_tester.get_logs(**filter_params) - return logs - - -def _generate_random_private_key() -> HexStr: - """ - WARNING: This is not a secure way to generate private keys and should only - be used for testing purposes. - """ - return encode_hex(bytes(bytearray(( - random.randint(0, 255) - for _ in range(32) - )))) - - -@without_params -def create_new_account(eth_tester: "EthereumTester") -> HexAddress: - return eth_tester.add_account(_generate_random_private_key()) - - -def personal_send_transaction(eth_tester: "EthereumTester", params: Any) -> HexStr: - transaction, password = params - - try: - eth_tester.unlock_account(transaction['from'], password) - transaction_hash = eth_tester.send_transaction(transaction) - finally: - eth_tester.lock_account(transaction['from']) - - return transaction_hash - - -API_ENDPOINTS = { - 'web3': { - 'clientVersion': client_version, - 'sha3': compose( - encode_hex, - keccak, - decode_hex, - without_eth_tester(operator.itemgetter(0)), - ), - }, - 'net': { - 'version': static_return('1'), - 'peerCount': static_return(0), - 'listening': static_return(False), - }, - 'eth': { - 'protocolVersion': static_return(63), - 'syncing': static_return(False), - 'coinbase': compose( - operator.itemgetter(0), - call_eth_tester('get_accounts'), - ), - 'mining': static_return(False), - 'hashrate': static_return(0), - 'chainId': static_return('0x3d'), - 'gasPrice': static_return(1), - 'accounts': call_eth_tester('get_accounts'), - 'blockNumber': compose( - operator.itemgetter('number'), - call_eth_tester('get_block_by_number', fn_kwargs={'block_number': 'latest'}), - ), - 'getBalance': call_eth_tester('get_balance'), - 'getStorageAt': not_implemented, - 'getProof': not_implemented, - 'getTransactionCount': call_eth_tester('get_nonce'), - 'getBlockTransactionCountByHash': null_if_block_not_found(compose( - len, - operator.itemgetter('transactions'), - call_eth_tester('get_block_by_hash'), - )), - 'getBlockTransactionCountByNumber': null_if_block_not_found(compose( - len, - operator.itemgetter('transactions'), - call_eth_tester('get_block_by_number'), - )), - 'getUncleCountByBlockHash': null_if_block_not_found(compose( - len, - operator.itemgetter('uncles'), - call_eth_tester('get_block_by_hash'), - )), - 'getUncleCountByBlockNumber': null_if_block_not_found(compose( - len, - operator.itemgetter('uncles'), - call_eth_tester('get_block_by_number'), - )), - 'getCode': call_eth_tester('get_code'), - 'sign': not_implemented, - 'signTransaction': not_implemented, - 'sendTransaction': call_eth_tester('send_transaction'), - 'sendRawTransaction': call_eth_tester('send_raw_transaction'), - 'call': call_eth_tester('call'), # TODO: untested - 'estimateGas': call_eth_tester('estimate_gas'), # TODO: untested - 'getBlockByHash': null_if_block_not_found(call_eth_tester('get_block_by_hash')), - 'getBlockByNumber': null_if_block_not_found(call_eth_tester('get_block_by_number')), - 'getTransactionByHash': null_if_transaction_not_found( - call_eth_tester('get_transaction_by_hash') - ), - 'getTransactionByBlockHashAndIndex': get_transaction_by_block_hash_and_index, - 'getTransactionByBlockNumberAndIndex': get_transaction_by_block_number_and_index, - 'getTransactionReceipt': null_if_transaction_not_found(compose( - apply_formatter_if( # type: ignore - compose(is_null, operator.itemgetter('block_number')), - static_return(None), - ), - call_eth_tester('get_transaction_receipt'), - )), - 'getUncleByBlockHashAndIndex': not_implemented, - 'getUncleByBlockNumberAndIndex': not_implemented, - 'getCompilers': not_implemented, - 'compileLLL': not_implemented, - 'compileSolidity': not_implemented, - 'compileSerpent': not_implemented, - 'newFilter': create_log_filter, - 'newBlockFilter': call_eth_tester('create_block_filter'), - 'newPendingTransactionFilter': call_eth_tester('create_pending_transaction_filter'), - 'uninstallFilter': excepts( - FilterNotFound, - compose( - is_null, - call_eth_tester('delete_filter'), - ), - static_return(False), - ), - 'getFilterChanges': null_if_filter_not_found(call_eth_tester('get_only_filter_changes')), - 'getFilterLogs': null_if_filter_not_found(call_eth_tester('get_all_filter_logs')), - 'getLogs': get_logs, - 'getWork': not_implemented, - 'submitWork': not_implemented, - 'submitHashrate': not_implemented, - }, - 'db': { - 'putString': not_implemented, - 'getString': not_implemented, - 'putHex': not_implemented, - 'getHex': not_implemented, - }, - 'shh': { - 'add_private_key': not_implemented, - 'add_sym_key': not_implemented, - 'delete_key_pair': not_implemented, - 'delete_message_filter': not_implemented, - 'delete_sym_key': not_implemented, - 'generate_sym_key_from_password': not_implemented, - 'get_filter_messages': not_implemented, - 'get_private_key': not_implemented, - 'get_public_key': not_implemented, - 'get_sym_key': not_implemented, - 'hasIdentity': not_implemented, - 'has_key_pair': not_implemented, - 'has_sym_key': not_implemented, - 'mark_trusted_peer': not_implemented, - 'newFilter': not_implemented, - 'newGroup': not_implemented, - 'newIdentity': not_implemented, - 'new_key_pair': not_implemented, - 'new_message_filter': not_implemented, - 'new_sym_key': not_implemented, - 'post': not_implemented, - 'set_max_message_size': not_implemented, - 'set_min_pow': not_implemented, - 'uninstallFilter': not_implemented, - 'version': not_implemented, - # Deprecated - 'addPrivateKey': not_implemented, - 'addSymKey': not_implemented, - 'deleteKeyPair': not_implemented, - 'deleteMessageFilter': not_implemented, - 'deleteSymKey': not_implemented, - 'generateSymKeyFromPassword': not_implemented, - 'getMessages': not_implemented, - 'getPrivateKey': not_implemented, - 'getPublicKey': not_implemented, - 'getSymKey': not_implemented, - 'hasKeyPair': not_implemented, - 'hasSymKey': not_implemented, - 'markTrustedPeer': not_implemented, - 'newKeyPair': not_implemented, - 'newMessageFilter': not_implemented, - 'newSymKey': not_implemented, - 'setMaxMessageSize': not_implemented, - 'setMinPoW': not_implemented, - }, - 'admin': { - 'add_peer': not_implemented, - 'node_info': not_implemented, - 'start_rpc': not_implemented, - 'start_ws': not_implemented, - 'stop_rpc': not_implemented, - 'stop_ws': not_implemented, - # deprecated - 'addPeer': not_implemented, - 'datadir': not_implemented, - 'nodeInfo': not_implemented, - 'peers': not_implemented, - 'startRPC': not_implemented, - 'startWS': not_implemented, - 'stopRPC': not_implemented, - 'stopWS': not_implemented, - }, - 'debug': { - 'backtraceAt': not_implemented, - 'blockProfile': not_implemented, - 'cpuProfile': not_implemented, - 'dumpBlock': not_implemented, - 'gtStats': not_implemented, - 'getBlockRLP': not_implemented, - 'goTrace': not_implemented, - 'memStats': not_implemented, - 'seedHashSign': not_implemented, - 'setBlockProfileRate': not_implemented, - 'setHead': not_implemented, - 'stacks': not_implemented, - 'startCPUProfile': not_implemented, - 'startGoTrace': not_implemented, - 'stopCPUProfile': not_implemented, - 'stopGoTrace': not_implemented, - 'traceBlock': not_implemented, - 'traceBlockByNumber': not_implemented, - 'traceBlockByHash': not_implemented, - 'traceBlockFromFile': not_implemented, - 'traceTransaction': not_implemented, - 'verbosity': not_implemented, - 'vmodule': not_implemented, - 'writeBlockProfile': not_implemented, - 'writeMemProfile': not_implemented, - }, - 'miner': { - 'makeDAG': not_implemented, - 'setExtra': not_implemented, - 'setGasPrice': not_implemented, - 'start': not_implemented, - 'startAutoDAG': not_implemented, - 'stop': not_implemented, - 'stopAutoDAG': not_implemented, - }, - 'personal': { - 'ecRecover': not_implemented, - 'importRawKey': call_eth_tester('add_account'), - 'listAccounts': call_eth_tester('get_accounts'), - 'lockAccount': excepts( - ValidationError, - compose(static_return(True), call_eth_tester('lock_account')), - static_return(False), - ), - 'newAccount': create_new_account, - 'unlockAccount': excepts( - ValidationError, - compose(static_return(True), call_eth_tester('unlock_account')), - static_return(False), - ), - 'sendTransaction': personal_send_transaction, - 'sign': not_implemented, - }, - 'testing': { - 'timeTravel': call_eth_tester('time_travel'), - }, - 'txpool': { - 'content': not_implemented, - 'inspect': not_implemented, - 'status': not_implemented, - }, - 'evm': { - 'mine': call_eth_tester('mine_blocks'), - 'revert': call_eth_tester('revert_to_snapshot'), - 'snapshot': call_eth_tester('take_snapshot'), - }, -} +import operator +import random +import sys + +from vns_tester.exceptions import ( + BlockNotFound, + FilterNotFound, + TransactionNotFound, + ValidationError, +) +from vns_utils import ( + decode_hex, + encode_hex, + is_null, + keccak, +) + +from web3._utils.formatters import ( + apply_formatter_if, +) +from web3._utils.toolz import ( + compose, + curry, + excepts, +) + + +def not_implemented(*args, **kwargs): + raise NotImplementedError("RPC method not implemented") + + +@curry +def call_vns_tester(fn_name, vns_tester, fn_args, fn_kwargs=None): + if fn_kwargs is None: + fn_kwargs = {} + return getattr(vns_tester, fn_name)(*fn_args, **fn_kwargs) + + +def without_vns_tester(fn): + # workaround for: https://github.com/pytoolz/cytoolz/issues/103 + # @functools.wraps(fn) + def inner(vns_tester, params): + return fn(params) + return inner + + +def without_params(fn): + # workaround for: https://github.com/pytoolz/cytoolz/issues/103 + # @functools.wraps(fn) + def inner(vns_tester, params): + return fn(vns_tester) + return inner + + +@curry +def preprocess_params(vns_tester, params, preprocessor_fn): + return vns_tester, preprocessor_fn(params) + + +def static_return(value): + def inner(*args, **kwargs): + return value + return inner + + +def client_version(vns_tester, params): + # TODO: account for the backend that is in use. + from vns_tester import __version__ + return "EthereumTester/{version}/{platform}/python{v.major}.{v.minor}.{v.micro}".format( + version=__version__, + v=sys.version_info, + platform=sys.platform, + ) + + +@curry +def null_if_excepts(exc_type, fn): + return excepts( + exc_type, + fn, + static_return(None), + ) + + +null_if_block_not_found = null_if_excepts(BlockNotFound) +null_if_transaction_not_found = null_if_excepts(TransactionNotFound) +null_if_filter_not_found = null_if_excepts(FilterNotFound) +null_if_indexerror = null_if_excepts(IndexError) + + +@null_if_indexerror +@null_if_block_not_found +def get_transaction_by_block_hash_and_index(vns_tester, params): + block_hash, transaction_index = params + block = vns_tester.get_block_by_hash(block_hash, full_transactions=True) + transaction = block['transactions'][transaction_index] + return transaction + + +@null_if_indexerror +@null_if_block_not_found +def get_transaction_by_block_number_and_index(vns_tester, params): + block_number, transaction_index = params + block = vns_tester.get_block_by_number(block_number, full_transactions=True) + transaction = block['transactions'][transaction_index] + return transaction + + +def create_log_filter(vns_tester, params): + filter_params = params[0] + filter_id = vns_tester.create_log_filter(**filter_params) + return filter_id + + +def get_logs(vns_tester, params): + filter_params = params[0] + logs = vns_tester.get_logs(**filter_params) + return logs + + +def _generate_random_private_key(): + """ + WARNING: This is not a secure way to generate private keys and should only + be used for testing purposes. + """ + return encode_hex(bytes(bytearray(( + random.randint(0, 255) + for _ in range(32) + )))) + + +@without_params +def create_new_account(vns_tester): + return vns_tester.add_account(_generate_random_private_key()) + + +def personal_send_transaction(vns_tester, params): + transaction, password = params + + try: + vns_tester.unlock_account(transaction['from'], password) + transaction_hash = vns_tester.send_transaction(transaction) + finally: + vns_tester.lock_account(transaction['from']) + + return transaction_hash + + +API_ENDPOINTS = { + 'web3': { + 'clientVersion': client_version, + 'sha3': compose( + encode_hex, + keccak, + decode_hex, + without_vns_tester(operator.itemgetter(0)), + ), + }, + 'net': { + 'version': static_return('1'), + 'peerCount': static_return(0), + 'listening': static_return(False), + }, + 'vns': { + 'protocolVersion': static_return(63), + 'syncing': static_return(False), + 'coinbase': compose( + operator.itemgetter(0), + call_vns_tester('get_accounts'), + ), + 'mining': static_return(False), + 'hashrate': static_return(0), + 'chainId': static_return('0x3d'), + 'gasPrice': static_return(1), + 'accounts': call_vns_tester('get_accounts'), + 'blockNumber': compose( + operator.itemgetter('number'), + call_vns_tester('get_block_by_number', fn_kwargs={'block_number': 'latest'}), + ), + 'getBalance': call_vns_tester('get_balance'), + 'getStorageAt': not_implemented, + 'getTransactionCount': call_vns_tester('get_nonce'), + 'getBlockTransactionCountByHash': null_if_block_not_found(compose( + len, + operator.itemgetter('transactions'), + call_vns_tester('get_block_by_hash'), + )), + 'getBlockTransactionCountByNumber': null_if_block_not_found(compose( + len, + operator.itemgetter('transactions'), + call_vns_tester('get_block_by_number'), + )), + 'getUncleCountByBlockHash': null_if_block_not_found(compose( + len, + operator.itemgetter('uncles'), + call_vns_tester('get_block_by_hash'), + )), + 'getUncleCountByBlockNumber': null_if_block_not_found(compose( + len, + operator.itemgetter('uncles'), + call_vns_tester('get_block_by_number'), + )), + 'getCode': call_vns_tester('get_code'), + 'sign': not_implemented, + 'signTransaction': not_implemented, + 'sendTransaction': call_vns_tester('send_transaction'), + 'sendRawTransaction': call_vns_tester('send_raw_transaction'), + 'call': call_vns_tester('call'), # TODO: untested + 'estimateGas': call_vns_tester('estimate_gas'), # TODO: untested + 'getBlockByHash': null_if_block_not_found(call_vns_tester('get_block_by_hash')), + 'getBlockByNumber': null_if_block_not_found(call_vns_tester('get_block_by_number')), + 'getTransactionByHash': null_if_transaction_not_found( + call_vns_tester('get_transaction_by_hash') + ), + 'getTransactionByBlockHashAndIndex': get_transaction_by_block_hash_and_index, + 'getTransactionByBlockNumberAndIndex': get_transaction_by_block_number_and_index, + 'getTransactionReceipt': null_if_transaction_not_found(compose( + apply_formatter_if( + compose(is_null, operator.itemgetter('block_number')), + static_return(None), + ), + call_vns_tester('get_transaction_receipt'), + )), + 'getUncleByBlockHashAndIndex': not_implemented, + 'getUncleByBlockNumberAndIndex': not_implemented, + 'getCompilers': not_implemented, + 'compileLLL': not_implemented, + 'compileSolidity': not_implemented, + 'compileSerpent': not_implemented, + 'newFilter': create_log_filter, + 'newBlockFilter': call_vns_tester('create_block_filter'), + 'newPendingTransactionFilter': call_vns_tester('create_pending_transaction_filter'), + 'uninstallFilter': excepts( + FilterNotFound, + compose( + is_null, + call_vns_tester('delete_filter'), + ), + static_return(False), + ), + 'getFilterChanges': null_if_filter_not_found(call_vns_tester('get_only_filter_changes')), + 'getFilterLogs': null_if_filter_not_found(call_vns_tester('get_all_filter_logs')), + 'getLogs': get_logs, + 'getWork': not_implemented, + 'submitWork': not_implemented, + 'submitHashrate': not_implemented, + }, + 'db': { + 'putString': not_implemented, + 'getString': not_implemented, + 'putHex': not_implemented, + 'getHex': not_implemented, + }, + 'shh': { + 'post': not_implemented, + 'version': not_implemented, + 'newIdentity': not_implemented, + 'hasIdentity': not_implemented, + 'newGroup': not_implemented, + 'addToGroup': not_implemented, + 'newFilter': not_implemented, + 'uninstallFilter': not_implemented, + 'getFilterChanges': not_implemented, + 'getMessages': not_implemented, + }, + 'admin': { + 'addPeer': not_implemented, + 'datadir': not_implemented, + 'nodeInfo': not_implemented, + 'peers': not_implemented, + 'setSolc': not_implemented, + 'startRPC': not_implemented, + 'startWS': not_implemented, + 'stopRPC': not_implemented, + 'stopWS': not_implemented, + }, + 'debug': { + 'backtraceAt': not_implemented, + 'blockProfile': not_implemented, + 'cpuProfile': not_implemented, + 'dumpBlock': not_implemented, + 'gtStats': not_implemented, + 'getBlockRLP': not_implemented, + 'goTrace': not_implemented, + 'memStats': not_implemented, + 'seedHashSign': not_implemented, + 'setBlockProfileRate': not_implemented, + 'setHead': not_implemented, + 'stacks': not_implemented, + 'startCPUProfile': not_implemented, + 'startGoTrace': not_implemented, + 'stopCPUProfile': not_implemented, + 'stopGoTrace': not_implemented, + 'traceBlock': not_implemented, + 'traceBlockByNumber': not_implemented, + 'traceBlockByHash': not_implemented, + 'traceBlockFromFile': not_implemented, + 'traceTransaction': not_implemented, + 'verbosity': not_implemented, + 'vmodule': not_implemented, + 'writeBlockProfile': not_implemented, + 'writeMemProfile': not_implemented, + }, + 'miner': { + 'makeDAG': not_implemented, + 'setExtra': not_implemented, + 'setGasPrice': not_implemented, + 'start': not_implemented, + 'startAutoDAG': not_implemented, + 'stop': not_implemented, + 'stopAutoDAG': not_implemented, + }, + 'personal': { + 'ecRecover': not_implemented, + 'importRawKey': call_vns_tester('add_account'), + 'listAccounts': call_vns_tester('get_accounts'), + 'lockAccount': excepts( + ValidationError, + compose(static_return(True), call_vns_tester('lock_account')), + static_return(False), + ), + 'newAccount': create_new_account, + 'unlockAccount': excepts( + ValidationError, + compose(static_return(True), call_vns_tester('unlock_account')), + static_return(False), + ), + 'sendTransaction': personal_send_transaction, + 'sign': not_implemented, + }, + 'testing': { + 'timeTravel': call_vns_tester('time_travel'), + }, + 'txpool': { + 'content': not_implemented, + 'inspect': not_implemented, + 'status': not_implemented, + }, + 'evm': { + 'mine': call_vns_tester('mine_blocks'), + 'revert': call_vns_tester('revert_to_snapshot'), + 'snapshot': call_vns_tester('take_snapshot'), + }, +} diff --git a/web3/providers/eth_tester/main.py b/web3/providers/vns_tester/main.py similarity index 57% rename from web3/providers/eth_tester/main.py rename to web3/providers/vns_tester/main.py index da8665c8ae..69cc32bd86 100644 --- a/web3/providers/eth_tester/main.py +++ b/web3/providers/vns_tester/main.py @@ -1,105 +1,79 @@ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Coroutine, - Dict, -) - -from web3._utils.compat import ( - Literal, -) -from web3.providers import ( - BaseProvider, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -from .middleware import ( - default_transaction_fields_middleware, - ethereum_tester_middleware, -) - -if TYPE_CHECKING: - from eth_tester import ( # noqa: F401 - EthereumTester, - ) - - -class AsyncEthereumTesterProvider(BaseProvider): - """This is a placeholder. - - For now its purpose is to provide an awaitable request function - for testing the async api execution. - """ - def __init__(self) -> None: - self.eth_tester = EthereumTesterProvider() - - async def make_request( - self, method: RPCEndpoint, params: Any - ) -> Coroutine[Any, Any, RPCResponse]: - return self.eth_tester.make_request(method, params) - - -class EthereumTesterProvider(BaseProvider): - middlewares = ( - default_transaction_fields_middleware, - ethereum_tester_middleware, - ) - ethereum_tester = None - api_endpoints = None - - def __init__( - self, - ethereum_tester: "EthereumTester"=None, - api_endpoints: Dict[str, Dict[str, Callable[..., RPCResponse]]]=None - ) -> None: - # do not import eth_tester until runtime, it is not a default dependency - from eth_tester import EthereumTester # noqa: F811 - from eth_tester.backends.base import BaseChainBackend - if ethereum_tester is None: - self.ethereum_tester = EthereumTester() - elif isinstance(ethereum_tester, EthereumTester): - self.ethereum_tester = ethereum_tester - elif isinstance(ethereum_tester, BaseChainBackend): - self.ethereum_tester = EthereumTester(ethereum_tester) - else: - raise TypeError( - "Expected ethereum_tester to be of type `eth_tester.EthereumTester` or " - "a subclass of `eth_tester.backends.base.BaseChainBackend`, " - f"instead received {type(ethereum_tester)}. " - "If you would like a custom eth-tester instance to test with, see the " - "eth-tester documentation. https://github.com/ethereum/eth-tester." - ) - - if api_endpoints is None: - # do not import eth_tester derivatives until runtime, it is not a default dependency - from .defaults import API_ENDPOINTS - self.api_endpoints = API_ENDPOINTS - else: - self.api_endpoints = api_endpoints - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - namespace, _, endpoint = method.partition('_') - try: - delegator = self.api_endpoints[namespace][endpoint] - except KeyError: - return { - "error": "Unknown RPC Endpoint: {0}".format(method), - } - - try: - response = delegator(self.ethereum_tester, params) - except NotImplementedError: - return { - "error": "RPC Endpoint has not been implemented: {0}".format(method), - } - else: - return { - 'result': response, - } - - def isConnected(self) -> Literal[True]: - return True +from web3.providers import ( + BaseProvider, +) + +from .middleware import ( + default_transaction_fields_middleware, + ethereum_tester_middleware, +) + + +class AsyncEthereumTesterProvider(BaseProvider): + """This is a placeholder. + + For now its purpose is to provide an awaitable request function + for testing the async api execution. + """ + def __init__(self): + self.vns_tester = EthereumTesterProvider() + + async def make_request(self, method, params): + return self.vns_tester.make_request(method, params) + + +class EthereumTesterProvider(BaseProvider): + middlewares = [ + default_transaction_fields_middleware, + ethereum_tester_middleware, + ] + ethereum_tester = None + api_endpoints = None + + def __init__(self, ethereum_tester=None, api_endpoints=None): + # do not import vns_tester until runtime, it is not a default dependency + from vns_tester import EthereumTester + from vns_tester.backends.base import BaseChainBackend + if ethereum_tester is None: + self.ethereum_tester = EthereumTester() + elif isinstance(ethereum_tester, EthereumTester): + self.ethereum_tester = ethereum_tester + elif isinstance(ethereum_tester, BaseChainBackend): + self.ethereum_tester = EthereumTester(ethereum_tester) + else: + raise TypeError( + "Expected ethereum_tester to be of type `vns_tester.EthereumTester` or " + "a subclass of `vns_tester.backends.base.BaseChainBackend`, " + f"instead received {type(ethereum_tester)}. " + "If you would like a custom vns-tester instance to test with, see the " + "vns-tester documentation. https://github.com/ethereum/vns-tester." + ) + + if api_endpoints is None: + # do not import vns_tester derivatives until runtime, it is not a default dependency + from .defaults import API_ENDPOINTS + self.api_endpoints = API_ENDPOINTS + else: + self.api_endpoints = api_endpoints + + def make_request(self, method, params): + namespace, _, endpoint = method.partition('_') + try: + delegator = self.api_endpoints[namespace][endpoint] + except KeyError: + return { + "error": "Unknown RPC Endpoint: {0}".format(method), + } + + try: + response = delegator(self.ethereum_tester, params) + except NotImplementedError: + return { + "error": "RPC Endpoint has not been implemented: {0}".format(method), + } + else: + return { + 'result': response, + } + + def isConnected(self): + return True diff --git a/web3/providers/eth_tester/middleware.py b/web3/providers/vns_tester/middleware.py similarity index 62% rename from web3/providers/eth_tester/middleware.py rename to web3/providers/vns_tester/middleware.py index c8c92f914d..5b43cc86f7 100644 --- a/web3/providers/eth_tester/middleware.py +++ b/web3/providers/vns_tester/middleware.py @@ -1,339 +1,311 @@ -import operator -from typing import ( - TYPE_CHECKING, - Any, - Callable, -) - -from eth_typing import ( - ChecksumAddress, -) -from eth_utils import ( - is_dict, - is_hex, - is_string, -) -from eth_utils.curried import ( - apply_formatter_if, - apply_formatters_to_dict, -) -from eth_utils.toolz import ( - assoc, - complement, - compose, - curry, - identity, - partial, - pipe, -) - -from web3._utils.formatters import ( - apply_formatter_to_array, - apply_formatters_to_args, - apply_key_map, - hex_to_integer, - integer_to_hex, - is_array_of_dicts, - remove_key_if, - static_return, -) -from web3.middleware import ( - construct_formatting_middleware, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, - TxParams, - Wei, -) - -if TYPE_CHECKING: - from web3 import ( # noqa: F401 - Web3, - ) - - -def is_named_block(value: Any) -> bool: - return value in {"latest", "earliest", "pending"} - - -def is_hexstr(value: Any) -> bool: - return is_string(value) and is_hex(value) - - -to_integer_if_hex = apply_formatter_if(is_hexstr, hex_to_integer) # type: ignore - - -is_not_named_block = complement(is_named_block) - - -TRANSACTION_KEY_MAPPINGS = { - 'block_hash': 'blockHash', - 'block_number': 'blockNumber', - 'gas_price': 'gasPrice', - 'transaction_hash': 'transactionHash', - 'transaction_index': 'transactionIndex', -} - -transaction_key_remapper = apply_key_map(TRANSACTION_KEY_MAPPINGS) - - -LOG_KEY_MAPPINGS = { - 'log_index': 'logIndex', - 'transaction_index': 'transactionIndex', - 'transaction_hash': 'transactionHash', - 'block_hash': 'blockHash', - 'block_number': 'blockNumber', -} - - -log_key_remapper = apply_key_map(LOG_KEY_MAPPINGS) - - -RECEIPT_KEY_MAPPINGS = { - 'block_hash': 'blockHash', - 'block_number': 'blockNumber', - 'contract_address': 'contractAddress', - 'gas_used': 'gasUsed', - 'cumulative_gas_used': 'cumulativeGasUsed', - 'transaction_hash': 'transactionHash', - 'transaction_index': 'transactionIndex', -} - - -receipt_key_remapper = apply_key_map(RECEIPT_KEY_MAPPINGS) - - -BLOCK_KEY_MAPPINGS = { - 'gas_limit': 'gasLimit', - 'sha3_uncles': 'sha3Uncles', - 'transactions_root': 'transactionsRoot', - 'parent_hash': 'parentHash', - 'bloom': 'logsBloom', - 'state_root': 'stateRoot', - 'receipt_root': 'receiptsRoot', - 'total_difficulty': 'totalDifficulty', - 'extra_data': 'extraData', - 'gas_used': 'gasUsed', -} - - -block_key_remapper = apply_key_map(BLOCK_KEY_MAPPINGS) - - -TRANSACTION_PARAMS_MAPPING = { - 'gasPrice': 'gas_price', -} - - -transaction_params_remapper = apply_key_map(TRANSACTION_PARAMS_MAPPING) - - -TRANSACTION_PARAMS_FORMATTERS = { - 'gas': to_integer_if_hex, - 'gasPrice': to_integer_if_hex, - 'value': to_integer_if_hex, - 'nonce': to_integer_if_hex, -} - - -transaction_params_formatter = compose( - # remove nonce for now due to issue https://github.com/ethereum/eth-tester/issues/80 - remove_key_if('nonce', lambda _: True), - apply_formatters_to_dict(TRANSACTION_PARAMS_FORMATTERS), -) - - -FILTER_PARAMS_MAPPINGS = { - 'fromBlock': 'from_block', - 'toBlock': 'to_block', -} - -filter_params_remapper = apply_key_map(FILTER_PARAMS_MAPPINGS) - -FILTER_PARAMS_FORMATTERS = { - 'fromBlock': to_integer_if_hex, - 'toBlock': to_integer_if_hex, -} - -filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS) - -filter_params_transformer = compose(filter_params_remapper, filter_params_formatter) - - -TRANSACTION_FORMATTERS = { - 'to': apply_formatter_if(partial(operator.eq, ''), static_return(None)), # type: ignore -} - - -transaction_formatter = apply_formatters_to_dict(TRANSACTION_FORMATTERS) - - -RECEIPT_FORMATTERS = { - 'logs': apply_formatter_to_array(log_key_remapper), -} - - -receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS) - -transaction_params_transformer = compose(transaction_params_remapper, transaction_params_formatter) - -ethereum_tester_middleware = construct_formatting_middleware( - request_formatters={ - # Eth - RPCEndpoint('eth_getBlockByNumber'): apply_formatters_to_args( - # type ignored for apply_formatter_if b/c - # https://github.com/ethereum/eth-utils/issues/156 - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - ), - RPCEndpoint('eth_getFilterChanges'): apply_formatters_to_args(hex_to_integer), - RPCEndpoint('eth_getFilterLogs'): apply_formatters_to_args(hex_to_integer), - RPCEndpoint('eth_getBlockTransactionCountByNumber'): apply_formatters_to_args( - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - ), - RPCEndpoint('eth_getUncleCountByBlockNumber'): apply_formatters_to_args( - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - ), - RPCEndpoint('eth_getTransactionByBlockHashAndIndex'): apply_formatters_to_args( - identity, - to_integer_if_hex, - ), - RPCEndpoint('eth_getTransactionByBlockNumberAndIndex'): apply_formatters_to_args( - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - to_integer_if_hex, - ), - RPCEndpoint('eth_getUncleByBlockNumberAndIndex'): apply_formatters_to_args( - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - to_integer_if_hex, - ), - RPCEndpoint('eth_newFilter'): apply_formatters_to_args( - filter_params_transformer, - ), - RPCEndpoint('eth_getLogs'): apply_formatters_to_args( - filter_params_transformer, - ), - RPCEndpoint('eth_sendTransaction'): apply_formatters_to_args( - transaction_params_transformer, - ), - RPCEndpoint('eth_estimateGas'): apply_formatters_to_args( - transaction_params_transformer, - ), - RPCEndpoint('eth_call'): apply_formatters_to_args( - transaction_params_transformer, - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - ), - RPCEndpoint('eth_uninstallFilter'): apply_formatters_to_args(hex_to_integer), - RPCEndpoint('eth_getCode'): apply_formatters_to_args( - identity, - apply_formatter_if(is_not_named_block, to_integer_if_hex), # type: ignore - ), - # EVM - RPCEndpoint('evm_revert'): apply_formatters_to_args(hex_to_integer), - # Personal - RPCEndpoint('personal_sendTransaction'): apply_formatters_to_args( - transaction_params_transformer, - identity, - ), - }, - result_formatters={ - RPCEndpoint('eth_getBlockByHash'): apply_formatter_if( # type: ignore - is_dict, - block_key_remapper, - ), - RPCEndpoint('eth_getBlockByNumber'): apply_formatter_if( # type: ignore - is_dict, - block_key_remapper, - ), - RPCEndpoint('eth_getBlockTransactionCountByHash'): apply_formatter_if( # type: ignore - is_dict, - transaction_key_remapper, - ), - RPCEndpoint('eth_getBlockTransactionCountByNumber'): apply_formatter_if( # type: ignore - is_dict, - transaction_key_remapper, - ), - RPCEndpoint('eth_getTransactionByHash'): apply_formatter_if( # type: ignore - is_dict, - compose(transaction_key_remapper, transaction_formatter), - ), - RPCEndpoint('eth_getTransactionReceipt'): apply_formatter_if( # type: ignore - is_dict, - compose(receipt_key_remapper, receipt_formatter), - ), - RPCEndpoint('eth_newFilter'): integer_to_hex, - RPCEndpoint('eth_newBlockFilter'): integer_to_hex, - RPCEndpoint('eth_newPendingTransactionFilter'): integer_to_hex, - RPCEndpoint('eth_getLogs'): apply_formatter_if( # type: ignore - is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), - ), - RPCEndpoint('eth_getFilterChanges'): apply_formatter_if( # type: ignore - is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), - ), - RPCEndpoint('eth_getFilterLogs'): apply_formatter_if( # type: ignore - is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), - ), - # EVM - RPCEndpoint('evm_snapshot'): integer_to_hex, - }, -) - - -def guess_from(web3: "Web3", transaction: TxParams) -> ChecksumAddress: - coinbase = web3.eth.coinbase - if coinbase is not None: - return coinbase - - try: - return web3.eth.accounts[0] - except KeyError as e: - # no accounts available to pre-fill, carry on - pass - - return None - - -def guess_gas(web3: "Web3", transaction: TxParams) -> Wei: - return Wei(web3.eth.estimateGas(transaction) * 2) - - -@curry -def fill_default( - field: str, guess_func: Callable[..., Any], web3: "Web3", transaction: TxParams -) -> TxParams: - if field in transaction and transaction[field] is not None: - return transaction - else: - guess_val = guess_func(web3, transaction) - return assoc(transaction, field, guess_val) - - -def default_transaction_fields_middleware( - make_request: Callable[[RPCEndpoint, Any], Any], web3: "Web3" -) -> Callable[[RPCEndpoint, Any], RPCResponse]: - fill_default_from = fill_default('from', guess_from, web3) - fill_default_gas = fill_default('gas', guess_gas, web3) - - def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: - # TODO send call to eth-tester without gas, and remove guess_gas entirely - if method == 'eth_call': - filled_transaction = pipe( - params[0], - fill_default_from, - fill_default_gas, - ) - return make_request(method, [filled_transaction] + params[1:]) - elif method in ( - 'eth_estimateGas', - 'eth_sendTransaction', - ): - filled_transaction = pipe( - params[0], - fill_default_from, - ) - return make_request(method, [filled_transaction] + params[1:]) - else: - return make_request(method, params) - return middleware +import operator + +from vns_utils import ( + is_dict, + is_hex, + is_string, +) + +from web3._utils.formatters import ( + apply_formatter_if, + apply_formatter_to_array, + apply_formatters_to_args, + apply_formatters_to_dict, + apply_key_map, + hex_to_integer, + integer_to_hex, + is_array_of_dicts, + remove_key_if, + static_return, +) +from web3._utils.toolz import ( + assoc, + complement, + compose, + curry, + identity, + partial, + pipe, +) +from web3.middleware import ( + construct_formatting_middleware, +) + + +def is_named_block(value): + return value in {"latest", "earliest", "pending"} + + +def is_hexstr(value): + return is_string(value) and is_hex(value) + + +to_integer_if_hex = apply_formatter_if(is_hexstr, hex_to_integer) + + +is_not_named_block = complement(is_named_block) + + +TRANSACTION_KEY_MAPPINGS = { + 'block_hash': 'blockHash', + 'block_number': 'blockNumber', + 'gas_price': 'gasPrice', + 'transaction_hash': 'transactionHash', + 'transaction_index': 'transactionIndex', +} + +transaction_key_remapper = apply_key_map(TRANSACTION_KEY_MAPPINGS) + + +LOG_KEY_MAPPINGS = { + 'log_index': 'logIndex', + 'transaction_index': 'transactionIndex', + 'transaction_hash': 'transactionHash', + 'block_hash': 'blockHash', + 'block_number': 'blockNumber', +} + + +log_key_remapper = apply_key_map(LOG_KEY_MAPPINGS) + + +RECEIPT_KEY_MAPPINGS = { + 'block_hash': 'blockHash', + 'block_number': 'blockNumber', + 'contract_address': 'contractAddress', + 'gas_used': 'gasUsed', + 'cumulative_gas_used': 'cumulativeGasUsed', + 'transaction_hash': 'transactionHash', + 'transaction_index': 'transactionIndex', +} + + +receipt_key_remapper = apply_key_map(RECEIPT_KEY_MAPPINGS) + + +BLOCK_KEY_MAPPINGS = { + 'gas_limit': 'gasLimit', + 'sha3_uncles': 'sha3Uncles', + 'transactions_root': 'transactionsRoot', + 'parent_hash': 'parentHash', + 'bloom': 'logsBloom', + 'state_root': 'stateRoot', + 'receipt_root': 'receiptsRoot', + 'total_difficulty': 'totalDifficulty', + 'extra_data': 'extraData', + 'gas_used': 'gasUsed', +} + + +block_key_remapper = apply_key_map(BLOCK_KEY_MAPPINGS) + + +TRANSACTION_PARAMS_MAPPING = { + 'gasPrice': 'gas_price', +} + + +transaction_params_remapper = apply_key_map(TRANSACTION_PARAMS_MAPPING) + + +TRANSACTION_PARAMS_FORMATTERS = { + 'gas': to_integer_if_hex, + 'gasPrice': to_integer_if_hex, + 'value': to_integer_if_hex, + 'nonce': to_integer_if_hex, +} + + +transaction_params_formatter = compose( + # remove nonce for now due to issue https://github.com/ethereum/vns-tester/issues/80 + remove_key_if('nonce', lambda _: True), + apply_formatters_to_dict(TRANSACTION_PARAMS_FORMATTERS), +) + + +FILTER_PARAMS_MAPPINGS = { + 'fromBlock': 'from_block', + 'toBlock': 'to_block', +} + +filter_params_remapper = apply_key_map(FILTER_PARAMS_MAPPINGS) + +FILTER_PARAMS_FORMATTERS = { + 'fromBlock': to_integer_if_hex, + 'toBlock': to_integer_if_hex, +} + +filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS) + +filter_params_transformer = compose(filter_params_remapper, filter_params_formatter) + + +TRANSACTION_FORMATTERS = { + 'to': apply_formatter_if(partial(operator.eq, ''), static_return(None)), +} + + +transaction_formatter = apply_formatters_to_dict(TRANSACTION_FORMATTERS) + + +RECEIPT_FORMATTERS = { + 'logs': apply_formatter_to_array(log_key_remapper), +} + + +receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS) + +transaction_params_transformer = compose(transaction_params_remapper, transaction_params_formatter) + +ethereum_tester_middleware = construct_formatting_middleware( + request_formatters={ + # Bbbbbbbb 'vns_getBlockByNumber': apply_formatters_to_args( + apply_formatter_if(is_not_named_block, to_integer_if_hex), + ), + 'vns_getFilterChanges': apply_formatters_to_args(hex_to_integer), + 'vns_getFilterLogs': apply_formatters_to_args(hex_to_integer), + 'vns_getBlockTransactionCountByNumber': apply_formatters_to_args( + apply_formatter_if(is_not_named_block, to_integer_if_hex), + ), + 'vns_getUncleCountByBlockNumber': apply_formatters_to_args( + apply_formatter_if(is_not_named_block, to_integer_if_hex), + ), + 'vns_getTransactionByBlockHashAndIndex': apply_formatters_to_args( + identity, + to_integer_if_hex, + ), + 'vns_getTransactionByBlockNumberAndIndex': apply_formatters_to_args( + apply_formatter_if(is_not_named_block, to_integer_if_hex), + to_integer_if_hex, + ), + 'vns_getUncleByBlockNumberAndIndex': apply_formatters_to_args( + apply_formatter_if(is_not_named_block, to_integer_if_hex), + to_integer_if_hex, + ), + 'vns_newFilter': apply_formatters_to_args( + filter_params_transformer, + ), + 'vns_getLogs': apply_formatters_to_args( + filter_params_transformer, + ), + 'vns_sendTransaction': apply_formatters_to_args( + transaction_params_transformer, + ), + 'vns_estimateGas': apply_formatters_to_args( + transaction_params_transformer, + ), + 'vns_call': apply_formatters_to_args( + transaction_params_transformer, + apply_formatter_if(is_not_named_block, to_integer_if_hex), + ), + 'vns_uninstallFilter': apply_formatters_to_args(hex_to_integer), + 'vns_getCode': apply_formatters_to_args( + identity, + apply_formatter_if(is_not_named_block, to_integer_if_hex), + ), + # EVM + 'evm_revert': apply_formatters_to_args(hex_to_integer), + # Personal + 'personal_sendTransaction': apply_formatters_to_args( + transaction_params_transformer, + identity, + ), + }, + result_formatters={ + 'vns_getBlockByHash': apply_formatter_if( + is_dict, + block_key_remapper, + ), + 'vns_getBlockByNumber': apply_formatter_if( + is_dict, + block_key_remapper, + ), + 'vns_getBlockTransactionCountByHash': apply_formatter_if( + is_dict, + transaction_key_remapper, + ), + 'vns_getBlockTransactionCountByNumber': apply_formatter_if( + is_dict, + transaction_key_remapper, + ), + 'vns_getTransactionByHash': apply_formatter_if( + is_dict, + compose(transaction_key_remapper, transaction_formatter), + ), + 'vns_getTransactionReceipt': apply_formatter_if( + is_dict, + compose(receipt_key_remapper, receipt_formatter), + ), + 'vns_newFilter': integer_to_hex, + 'vns_newBlockFilter': integer_to_hex, + 'vns_newPendingTransactionFilter': integer_to_hex, + 'vns_getLogs': apply_formatter_if( + is_array_of_dicts, + apply_formatter_to_array(log_key_remapper), + ), + 'vns_getFilterChanges': apply_formatter_if( + is_array_of_dicts, + apply_formatter_to_array(log_key_remapper), + ), + 'vns_getFilterLogs': apply_formatter_if( + is_array_of_dicts, + apply_formatter_to_array(log_key_remapper), + ), + # EVM + 'evm_snapshot': integer_to_hex, + }, +) + + +def guess_from(web3, transaction): + coinbase = web3.vns.coinbase + if coinbase is not None: + return coinbase + + try: + return web3.vns.accounts[0] + except KeyError as e: + # no accounts available to pre-fill, carry on + pass + + return None + + +def guess_gas(web3, transaction): + return web3.vns.estimateGas(transaction) * 2 + + +@curry +def fill_default(field, guess_func, web3, transaction): + if field in transaction and transaction[field] is not None: + return transaction + else: + guess_val = guess_func(web3, transaction) + return assoc(transaction, field, guess_val) + + +def default_transaction_fields_middleware(make_request, web3): + fill_default_from = fill_default('from', guess_from, web3) + fill_default_gas = fill_default('gas', guess_gas, web3) + + def middleware(method, params): + # TODO send call to vns-tester without gas, and remove guess_gas entirely + if method == 'vns_call': + filled_transaction = pipe( + params[0], + fill_default_from, + fill_default_gas, + ) + return make_request(method, [filled_transaction] + params[1:]) + elif method in ( + 'vns_estimateGas', + 'vns_sendTransaction', + ): + filled_transaction = pipe( + params[0], + fill_default_from, + ) + return make_request(method, [filled_transaction] + params[1:]) + else: + return make_request(method, params) + return middleware diff --git a/web3/providers/websocket.py b/web3/providers/websocket.py index 8d4b061bbc..3a0a4028be 100644 --- a/web3/providers/websocket.py +++ b/web3/providers/websocket.py @@ -1,137 +1,119 @@ -import asyncio -import json -import logging -import os -from threading import ( - Thread, -) -from types import ( - TracebackType, -) -from typing import ( - Any, - Type, -) - -from eth_typing import ( - URI, -) -import websockets - -from web3.exceptions import ( - ValidationError, -) -from web3.providers.base import ( - JSONBaseProvider, -) -from web3.types import ( - RPCEndpoint, - RPCResponse, -) - -RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'} -DEFAULT_WEBSOCKET_TIMEOUT = 10 - - -def _start_event_loop(loop: asyncio.AbstractEventLoop) -> None: - asyncio.set_event_loop(loop) - loop.run_forever() - loop.close() - - -def _get_threaded_loop() -> asyncio.AbstractEventLoop: - new_loop = asyncio.new_event_loop() - thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True) - thread_loop.start() - return new_loop - - -def get_default_endpoint() -> URI: - return URI(os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546')) - - -class PersistentWebSocket: - - def __init__( - self, endpoint_uri: URI, loop: asyncio.AbstractEventLoop, websocket_kwargs: Any - ) -> None: - self.ws: websockets.WebSocketClientProtocol = None - self.endpoint_uri = endpoint_uri - self.loop = loop - self.websocket_kwargs = websocket_kwargs - - async def __aenter__(self) -> websockets.WebSocketClientProtocol: - if self.ws is None: - self.ws = await websockets.connect( - uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs - ) - return self.ws - - async def __aexit__( - self, exc_type: Type[BaseException], exc_val: BaseException, exc_tb: TracebackType - ) -> None: - if exc_val is not None: - try: - await self.ws.close() - except Exception: - pass - self.ws = None - - -class WebsocketProvider(JSONBaseProvider): - logger = logging.getLogger("web3.providers.WebsocketProvider") - _loop = None - - def __init__( - self, - endpoint_uri: URI=None, - websocket_kwargs: Any=None, - websocket_timeout: int=DEFAULT_WEBSOCKET_TIMEOUT, - ) -> None: - self.endpoint_uri = endpoint_uri - self.websocket_timeout = websocket_timeout - if self.endpoint_uri is None: - self.endpoint_uri = get_default_endpoint() - if WebsocketProvider._loop is None: - WebsocketProvider._loop = _get_threaded_loop() - if websocket_kwargs is None: - websocket_kwargs = {} - else: - found_restricted_keys = set(websocket_kwargs.keys()).intersection( - RESTRICTED_WEBSOCKET_KWARGS - ) - if found_restricted_keys: - raise ValidationError( - '{0} are not allowed in websocket_kwargs, ' - 'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys) - ) - self.conn = PersistentWebSocket( - self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs - ) - super().__init__() - - def __str__(self) -> str: - return "WS connection {0}".format(self.endpoint_uri) - - async def coro_make_request(self, request_data: bytes) -> RPCResponse: - async with self.conn as conn: - await asyncio.wait_for( - conn.send(request_data), - timeout=self.websocket_timeout - ) - return json.loads( - await asyncio.wait_for( - conn.recv(), - timeout=self.websocket_timeout - ) - ) - - def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request WebSocket. URI: %s, " - "Method: %s", self.endpoint_uri, method) - request_data = self.encode_rpc_request(method, params) - future = asyncio.run_coroutine_threadsafe( - self.coro_make_request(request_data), - WebsocketProvider._loop - ) - return future.result() +import asyncio +import json +import logging +import os +from threading import ( + Thread, +) + +import websockets + +from web3.exceptions import ( + ValidationError, +) +from web3.providers.base import ( + JSONBaseProvider, +) + +RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'} +DEFAULT_WEBSOCKET_TIMEOUT = 10 + + +def _start_event_loop(loop): + asyncio.set_event_loop(loop) + loop.run_forever() + loop.close() + + +def _get_threaded_loop(): + new_loop = asyncio.new_event_loop() + thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True) + thread_loop.start() + return new_loop + + +def get_default_endpoint(): + return os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546') + + +class PersistentWebSocket: + + def __init__(self, endpoint_uri, loop, websocket_kwargs): + self.ws = None + self.endpoint_uri = endpoint_uri + self.loop = loop + self.websocket_kwargs = websocket_kwargs + + async def __aenter__(self): + if self.ws is None: + self.ws = await websockets.connect( + uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs + ) + return self.ws + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if exc_val is not None: + try: + await self.ws.close() + except Exception: + pass + self.ws = None + + +class WebsocketProvider(JSONBaseProvider): + logger = logging.getLogger("web3.providers.WebsocketProvider") + _loop = None + + def __init__( + self, + endpoint_uri=None, + websocket_kwargs=None, + websocket_timeout=DEFAULT_WEBSOCKET_TIMEOUT + ): + self.endpoint_uri = endpoint_uri + self.websocket_timeout = websocket_timeout + if self.endpoint_uri is None: + self.endpoint_uri = get_default_endpoint() + if WebsocketProvider._loop is None: + WebsocketProvider._loop = _get_threaded_loop() + if websocket_kwargs is None: + websocket_kwargs = {} + else: + found_restricted_keys = set(websocket_kwargs.keys()).intersection( + RESTRICTED_WEBSOCKET_KWARGS + ) + if found_restricted_keys: + raise ValidationError( + '{0} are not allowed in websocket_kwargs, ' + 'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys) + ) + self.conn = PersistentWebSocket( + self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs + ) + super().__init__() + + def __str__(self): + return "WS connection {0}".format(self.endpoint_uri) + + async def coro_make_request(self, request_data): + async with self.conn as conn: + await asyncio.wait_for( + conn.send(request_data), + timeout=self.websocket_timeout + ) + return json.loads( + await asyncio.wait_for( + conn.recv(), + timeout=self.websocket_timeout + ) + ) + + def make_request(self, method, params): + self.logger.debug("Making request WebSocket. URI: %s, " + "Method: %s", self.endpoint_uri, method) + request_data = self.encode_rpc_request(method, params) + future = asyncio.run_coroutine_threadsafe( + self.coro_make_request(request_data), + WebsocketProvider._loop + ) + return future.result() diff --git a/web3/replace.py b/web3/replace.py new file mode 100755 index 0000000000..55890d8637 --- /dev/null +++ b/web3/replace.py @@ -0,0 +1,40 @@ +#! /usr/bin/python +#-*- coding: utf-8 -*- + +import os,sys + +def doReplace(fpath,src,dst): + newConent,bFlag = "",False + with open(fpath,"rb") as fin: + for line in fin : + #if len(line.strip()) == 0 : continue + if line.find(src) == -1 : + newLine = line + else: + bFlag = True + newLine = line.replace(src,dst) + newConent += newLine + if not bFlag : return None + print fpath + with open(fpath,"wb") as fout: + fout.write(newConent) + return None + +def replaceMain(dirName,src,dst): + for root, dirs, files in os.walk(dirName): + for name in files: + fpath = os.path.join(root, name) + doReplace(fpath,src,dst) + return None + +if __name__ == "__main__": + if len(sys.argv) < 3 : + print "usage : replaceMulti srcStr dstStr" + print "replace current dir files" + sys.exit(1) + srcStr = sys.argv[1] + dstStr = sys.argv[2] + dirName = "." + dirName = os.path.realpath(dirName) + print "working dir :",dirName + replaceMain(dirName,srcStr,dstStr) \ No newline at end of file diff --git a/web3/testing.py b/web3/testing.py index 9ee03627a4..8de7c738ef 100644 --- a/web3/testing.py +++ b/web3/testing.py @@ -1,25 +1,25 @@ -from web3.module import ( - Module, -) - - -class Testing(Module): - def timeTravel(self, timestamp: int) -> None: - return self.web3.manager.request_blocking("testing_timeTravel", [timestamp]) - - def mine(self, num_blocks: int=1) -> None: - return self.web3.manager.request_blocking("evm_mine", [num_blocks]) - - def snapshot(self) -> int: - self.last_snapshot_idx = self.web3.manager.request_blocking("evm_snapshot", []) - return self.last_snapshot_idx - - def reset(self) -> None: - return self.web3.manager.request_blocking("evm_reset", []) - - def revert(self, snapshot_idx: int=None) -> None: - if snapshot_idx is None: - revert_target = self.last_snapshot_idx - else: - revert_target = snapshot_idx - return self.web3.manager.request_blocking("evm_revert", [revert_target]) +from web3.module import ( + Module, +) + + +class Testing(Module): + def timeTravel(self, timestamp): + return self.web3.manager.request_blocking("testing_timeTravel", [timestamp]) + + def mine(self, num_blocks=1): + return self.web3.manager.request_blocking("evm_mine", [num_blocks]) + + def snapshot(self): + self.last_snapshot_idx = self.web3.manager.request_blocking("evm_snapshot", []) + return self.last_snapshot_idx + + def reset(self): + return self.web3.manager.request_blocking("evm_reset", []) + + def revert(self, snapshot_idx=None): + if snapshot_idx is None: + revert_target = self.last_snapshot_idx + else: + revert_target = snapshot_idx + return self.web3.manager.request_blocking("evm_revert", [revert_target]) diff --git a/web3/tools/__init__.py b/web3/tools/__init__.py deleted file mode 100644 index 01cd73a94f..0000000000 --- a/web3/tools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .pytest_ethereum import ( # noqa: F401 - deployer, - linker, -) diff --git a/web3/tools/pytest_ethereum/__init__.py b/web3/tools/pytest_ethereum/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/web3/tools/pytest_ethereum/_utils.py b/web3/tools/pytest_ethereum/_utils.py deleted file mode 100644 index 6e52b8402d..0000000000 --- a/web3/tools/pytest_ethereum/_utils.py +++ /dev/null @@ -1,142 +0,0 @@ -from typing import ( - Any, - Dict, - Iterable, - List, - Tuple, -) - -from eth_typing import ( - URI, - Address, - ContractName, - Manifest, -) -from eth_utils import ( - to_dict, - to_hex, - to_list, -) -from eth_utils.toolz import ( - assoc, - assoc_in, - dissoc, -) - -from ethpm import ( - Package, -) -from ethpm.uri import ( - check_if_chain_matches_chain_uri, -) -from web3 import Web3 -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) -from web3.types import ( - TxReceipt, -) - - -def pluck_matching_uri(deployment_data: Dict[URI, Dict[str, str]], w3: Web3) -> URI: - """ - Return any blockchain uri that matches w3-connected chain, if one - is present in the deployment data keys. - """ - for uri in deployment_data.keys(): - if check_if_chain_matches_chain_uri(w3, uri): - return uri - raise LinkerError( - f"No matching blockchain URI found in deployment_data: {list(deployment_data.keys())}, " - "for w3 instance: {w3.__repr__()}." - ) - - -def contains_matching_uri(deployment_data: Dict[str, Dict[str, str]], w3: Web3) -> bool: - """ - Returns true if any blockchain uri in deployment data matches - w3-connected chain. - """ - for uri in deployment_data.keys(): - if check_if_chain_matches_chain_uri(w3, uri): - return True - return False - - -def insert_deployment( - package: Package, - deployment_name: str, - deployment_data: Dict[str, str], - latest_block_uri: URI, -) -> Manifest: - """ - Returns a new manifest. If a matching chain uri is found in the old manifest, it will - update the chain uri along with the new deployment data. If no match, it will simply add - the new chain uri and deployment data. - """ - old_deployments_data = package.manifest.get("deployments") - if old_deployments_data and contains_matching_uri(old_deployments_data, package.w3): - old_chain_uri = pluck_matching_uri(old_deployments_data, package.w3) - old_deployments_chain_data = old_deployments_data[old_chain_uri] - # Replace specific on-chain deployment (i.e. deployment_name) - new_deployments_chain_data_init = dissoc( - old_deployments_chain_data, deployment_name - ) - new_deployments_chain_data = { - **new_deployments_chain_data_init, - **{deployment_name: deployment_data}, - } - # Replace all on-chain deployments - new_deployments_data_init = dissoc( - old_deployments_data, "deployments", old_chain_uri - ) - new_deployments_data = { - **new_deployments_data_init, - **{latest_block_uri: new_deployments_chain_data}, - } - return assoc(package.manifest, "deployments", new_deployments_data) - - return assoc_in( - package.manifest, - ("deployments", latest_block_uri, deployment_name), - deployment_data, - ) - - -@to_dict -def create_deployment_data( - contract_name: ContractName, - new_address: Address, - tx_receipt: TxReceipt, - link_refs: List[Dict[str, Any]] = None, -) -> Iterable[Tuple[str, Any]]: - yield "contract_type", contract_name - yield "address", new_address - yield "transaction", to_hex(tx_receipt.transactionHash) - yield "block", to_hex(tx_receipt.blockHash) - if link_refs: - yield "runtime_bytecode", {"link_dependencies": create_link_dep(link_refs)} - - -@to_list -def create_link_dep(link_refs: List[Dict[str, Any]]) -> Iterable[Dict[str, Any]]: - for link_ref in link_refs: - yield { - "offsets": link_ref["offsets"], - "type": "reference", - "value": link_ref["name"], - } - - -def get_deployment_address(linked_type: str, package: Package) -> Address: - """ - Return the address of a linked_type found in a package's manifest deployments. - """ - try: - deployment_address = package.deployments.get(linked_type)["address"] - except KeyError: - raise LinkerError( - f"Package data does not contain a valid deployment of {linked_type} on the " - "current w3-connected chain." - ) - return deployment_address diff --git a/web3/tools/pytest_ethereum/deployer.py b/web3/tools/pytest_ethereum/deployer.py deleted file mode 100644 index 6b9eb22b02..0000000000 --- a/web3/tools/pytest_ethereum/deployer.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import ( # noqa: F401 - Any, - Callable, - Dict, - Tuple, -) - -from eth_typing import ( - ContractName, -) - -from ethpm import ( - Package, -) -from web3.tools.pytest_ethereum.exceptions import ( - DeployerError, -) -from web3.tools.pytest_ethereum.linker import ( - deploy, - linker, -) - - -class Deployer: - def __init__(self, package: Package) -> None: - if not isinstance(package, Package): - raise TypeError( - f"Expected a Package object, instead received {type(package)}." - ) - self.package = package - self.strategies = {} # type: Dict[str, Callable[[Package], Package]] - - def deploy( - self, contract_type: ContractName, *args: Any, **kwargs: Any - ) -> Package: - factory = self.package.get_contract_factory(contract_type) - if contract_type in self.strategies: - strategy = self.strategies[contract_type] - return strategy(self.package) - if factory.needs_bytecode_linking: - raise DeployerError( - "Unable to deploy an unlinked factory. " - "Please register a strategy for this contract type." - ) - strategy = linker(deploy(contract_type, *args, **kwargs)) - return strategy(self.package) - - def register_strategy( - self, contract_type: ContractName, strategy: Callable[[Package], Package] - ) -> None: - self.strategies[contract_type] = strategy diff --git a/web3/tools/pytest_ethereum/exceptions.py b/web3/tools/pytest_ethereum/exceptions.py deleted file mode 100644 index c474ebcf19..0000000000 --- a/web3/tools/pytest_ethereum/exceptions.py +++ /dev/null @@ -1,22 +0,0 @@ -class PytestEthereumError(Exception): - """ - Base class for all Pytest-Ethereum errors. - """ - - pass - - -class DeployerError(PytestEthereumError): - """ - Raised when the Deployer is unable to deploy a contract type. - """ - - pass - - -class LinkerError(PytestEthereumError): - """ - Raised when the Linker is unable to link two contract types. - """ - - pass diff --git a/web3/tools/pytest_ethereum/linker.py b/web3/tools/pytest_ethereum/linker.py deleted file mode 100644 index 2576bd624e..0000000000 --- a/web3/tools/pytest_ethereum/linker.py +++ /dev/null @@ -1,124 +0,0 @@ -import logging -from typing import ( - Any, - Callable, - Dict, -) - -from eth_typing import ( - ContractName, -) -from eth_utils import ( - to_checksum_address, - to_hex, -) -from eth_utils.toolz import ( - assoc_in, - curry, - pipe, -) - -from ethpm import ( - Package, -) -from ethpm.uri import ( - create_latest_block_uri, -) -from web3.tools.pytest_ethereum._utils import ( - create_deployment_data, - get_deployment_address, - insert_deployment, -) -from web3.tools.pytest_ethereum.exceptions import ( - LinkerError, -) - -logger = logging.getLogger("pytest_ethereum.linker") - - -def linker(*args: Callable[..., Any]) -> Callable[..., Any]: - return _linker(args) - - -@curry -def _linker(operations: Callable[..., Any], package: Package) -> Callable[..., Package]: - return pipe(package, *operations) - - -def deploy( - contract_name: str, *args: Any, transaction: Dict[str, Any] = None -) -> Callable[..., Package]: - """ - Return a newly created package and contract address. - Will deploy the given contract_name, if data exists in package. If - a deployment is found on the current w3 instance, it will return that deployment - rather than creating a new instance. - """ - return _deploy(contract_name, args, transaction) - - -@curry -def _deploy( - contract_name: ContractName, args: Any, transaction: Dict[str, Any], package: Package -) -> Package: - # Deploy new instance - factory = package.get_contract_factory(contract_name) - if not factory.linked_references and factory.unlinked_references: - raise LinkerError( - f"Contract factory: {contract_name} is missing runtime link references, which are " - "necessary to populate manifest deployments that have a link reference. If using the " - "builder tool, use `contract_type(..., runtime_bytecode=True)`." - ) - tx_hash = factory.constructor(*args).transact(transaction) - tx_receipt = package.w3.eth.waitForTransactionReceipt(tx_hash) - # Create manifest copy with new deployment instance - latest_block_uri = create_latest_block_uri(package.w3, 0) - deployment_data = create_deployment_data( - contract_name, - to_checksum_address(tx_receipt.contractAddress), - tx_receipt, - factory.linked_references, - ) - manifest = insert_deployment( - package, contract_name, deployment_data, latest_block_uri - ) - logger.info("%s deployed." % contract_name) - return Package(manifest, package.w3) - - -@curry -def link(contract: ContractName, linked_type: str, package: Package) -> Package: - """ - Return a new package, created with a new manifest after applying the linked type - reference to the contract factory. - """ - deployment_address = get_deployment_address(linked_type, package) - unlinked_factory = package.get_contract_factory(contract) - if not unlinked_factory.needs_bytecode_linking: - raise LinkerError( - f"Contract factory: {unlinked_factory.__repr__()} does not need bytecode linking, " - "so it is not a valid contract type for link()" - ) - linked_factory = unlinked_factory.link_bytecode({linked_type: deployment_address}) - # todo replace runtime_bytecode in manifest - manifest = assoc_in( - package.manifest, - ("contract_types", contract, "deployment_bytecode", "bytecode"), - to_hex(linked_factory.bytecode), - ) - logger.info( - "%s linked to %s at address %s." - % (contract, linked_type, to_checksum_address(deployment_address)) - ) - return Package(manifest, package.w3) - - -@curry -def run_python(callback_fn: Callable[..., None], package: Package) -> Package: - """ - Return the unmodified package, after performing any user-defined callback function on - the contracts in the package. - """ - callback_fn(package) - logger.info("%s python function ran." % callback_fn.__name__) - return package diff --git a/web3/tools/pytest_ethereum/plugins.py b/web3/tools/pytest_ethereum/plugins.py deleted file mode 100644 index 3b26757374..0000000000 --- a/web3/tools/pytest_ethereum/plugins.py +++ /dev/null @@ -1,31 +0,0 @@ -import json -from pathlib import ( - Path, -) -import pytest -from typing import ( - Callable, -) - -from ethpm import ( - Package, -) -from web3 import Web3 -from web3.tools.pytest_ethereum.deployer import ( - Deployer, -) - - -@pytest.fixture -def deployer(w3: Web3) -> Callable[[Path], Deployer]: - """ - Returns a `Deployer` instance composed from a `Package` instance - generated from the manifest located at the provided `path` folder. - """ - - def _deployer(path: Path) -> Deployer: - manifest = json.loads(path.read_text()) - package = Package(manifest, w3) - return Deployer(package) - - return _deployer diff --git a/web3/types.py b/web3/types.py deleted file mode 100644 index 9133d6f1cd..0000000000 --- a/web3/types.py +++ /dev/null @@ -1,289 +0,0 @@ -from typing import ( - Any, - Callable, - Dict, - List, - NewType, - Optional, - Sequence, - Union, -) - -from eth_typing import ( - Address, - BlockNumber, - ChecksumAddress, - Hash32, - HexStr, -) -from hexbytes import ( - HexBytes, -) -from typing_extensions import ( - Literal, -) - -from web3._utils.compat import ( - TypedDict, -) -from web3.datastructures import ( - NamedElementOnion, -) - -Wei = NewType('Wei', int) - - -# todo: move these to eth_typing once web3 is type hinted -ABIEventParams = TypedDict("ABIEventParams", { - "name": str, - "type": str, - "indexed": bool, -}) - -ENS = NewType("ENS", str) - -ABIEvent = TypedDict("ABIEvent", { - "type": "event", - "name": str, - "inputs": Sequence[ABIEventParams], - "anonymous": bool, -}) - - -ABIFunctionComponents = TypedDict("ABIFunctionParams", { - "name": str, - "type": str, - "components": Sequence["ABIFunctionComponents"], -}, total=False) - - -ABIFunctionParams = TypedDict("ABIFunctionParams", { - "name": str, - "type": str, - "components": Sequence[ABIFunctionComponents], -}, total=False) - - -ABIFunction = TypedDict("ABIFunction", { - "type": Union["function", "constructor", "fallback"], - "name": str, - "inputs": Sequence[ABIFunctionParams], - "outputs": Sequence[ABIFunctionParams], - "stateMutability": Union["pure", "view", "nonpayable", "payable"], - "payable": bool, - "constant": bool, -}, total=False) - - -ABIElement = Union[ABIFunction, ABIEvent] - - -ABI = Sequence[Union[ABIFunction, ABIEvent]] - - -LatestBlockParam = Literal["latest"] - - -BlockParams = Literal["latest", "earliest", "pending"] - - -BlockIdentifier = Union[BlockParams, BlockNumber, Hash32, HexStr] - - -ENS = NewType("ENS", str) - - -EventData = TypedDict("EventData", { - "args": Dict[str, Any], - "event": str, - "logIndex": int, - "transactionIndex": int, - "transactionHash": Hash32, - "address": ChecksumAddress, - "blockHash": Hash32, - "blockNumber": int, -}) - - -RPCError = TypedDict("RPCError", { - "code": int, - "message": str, -}) - - -RPCResponse = TypedDict("RPCResponse", { - "id": int, - "jsonrpc": Literal["2.0"], - "result": Any, - "error": RPCError, -}, total=False) - - -RPCEndpoint = NewType("RPCEndpoint", str) - - -Formatters = Dict[RPCEndpoint, Callable[..., Any]] - - -FormattersDict = TypedDict("FormattersDict", { - "request_formatters": Formatters, - "result_formatters": Formatters, - "error_formatters": Formatters, -}, total=False) - - -FilterParams = TypedDict("FilterParams", { - "fromBlock": Union["earliest", "pending", "latest", BlockNumber], - "toBlock": Union["earliest", "pending", "latest", BlockNumber], - "address": Union[Address, ChecksumAddress, List[Union[Address, ChecksumAddress]]], - "topics": List[Optional[Union[Hash32, List[Hash32]]]], -}, total=False) - - -TxParams = TypedDict("TxParams", { - "nonce": int, - "gasPrice": Wei, - "gas": Wei, - "from": Union[Address, ChecksumAddress, str], - "to": Union[Address, ChecksumAddress, str], - "value": Wei, - "data": Union[bytes, HexStr], -}, total=False) - - -# this Any should be updated to Web3 once all type hints land -GasPriceStrategy = Callable[[Any, TxParams], Wei] -# 2 input to parent callable Any should be updated to Web3 once all type hints land -Middleware = Callable[[Callable[[RPCEndpoint, Any], RPCResponse], Any], Any] -MiddlewareOnion = NamedElementOnion[str, Middleware] - - -LogReceipt = TypedDict("LogReceipt", { - "address": ChecksumAddress, - "blockHash": Hash32, - "blockNumber": int, - "data": HexStr, - "logIndex": int, - "removed": bool, - "topics": List[Hash32], - "transactionHash": Hash32, - "transactionIndex": int, -}) - - -StorageProof = TypedDict("StorageProof", { - 'key': HexStr, - 'value': Hash32, - 'proof': Sequence[HexStr], -}) - - -MerkleProof = TypedDict("MerkleProof", { - 'address': ChecksumAddress, - 'accountProof': Sequence[HexStr], - 'balance': int, - 'codeHash': Hash32, - 'nonce': int, - 'storageHash': Hash32, - 'storageProof': Sequence[StorageProof], -}) - - -SyncStatus = TypedDict("SyncStatus", { - 'currentBlock': int, - 'highestBlock': int, - 'knownStates': int, - 'pulledStates': int, - 'startingBlock': int, -}) - - -Timestamp = NewType("Timestamp", int) - - -TxReceipt = TypedDict("TxReceipt", { - "blockHash": Hash32, - "blockNumber": int, - "contractAddress": Optional[ChecksumAddress], - "cumulativeGasUsed": int, - "gasUsed": Wei, - "from": ChecksumAddress, - "logs": List[LogReceipt], - "logsBloom": HexBytes, - "root": HexStr, - "status": int, - "to": ChecksumAddress, - "transactionHash": Hash32, - "transactionIndex": int, -}) - - -BlockData = TypedDict("BlockData", { - 'difficulty': int, - 'extraData': HexStr, - 'gasLimit': Wei, - 'gasUsed': Wei, - 'hash': Hash32, - 'logsBloom': HexStr, - 'miner': ChecksumAddress, - 'nonce': HexStr, - 'number': int, - 'parentHash': Hash32, - 'receiptRoot': Hash32, - 'sha3Uncles': Hash32, - 'size': int, - 'stateRoot': Hash32, - 'timestamp': Timestamp, - 'totalDifficulty': int, - 'transactions': Union[Sequence[Hash32], Sequence[TxReceipt]], - 'transactionsRoot': Hash32, - 'uncles': Sequence[Hash32], -}) - - -Uncle = TypedDict("Uncle", { - 'author': ChecksumAddress, - 'difficulty': HexStr, - 'extraData': HexStr, - 'gasLimit': HexStr, - 'gasUsed': HexStr, - 'hash': Hash32, - 'logsBloom': HexStr, - 'miner': Hash32, - 'mixHash': Hash32, - 'nonce': HexStr, - 'number': HexStr, - 'parentHash': Hash32, - 'receiptsRoot': Hash32, - 'sealFields': Sequence[HexStr], - 'sha3Uncles': Hash32, - 'size': int, - 'stateRoot': Hash32, - 'timestamp': Timestamp, - 'totalDifficulty': HexStr, - 'transactions': Sequence[Hash32], - 'transactionsRoot': Hash32, - 'uncles': Sequence[Hash32] -}) - - -# web3.parity types -ParityBlockTrace = NewType("ParityBlockTrace", Dict[str, Any]) -ParityFilterTrace = NewType("ParityFilterTrace", Dict[str, Any]) -ParityEnodeURI = NewType("ParityEnodeURI", str) -ParityMode = Literal["active", "passive", "dark", "offline"] -ParityTraceMode = Sequence[Literal["trace", "vmTrace", "stateDiff"]] -ParityNetPeers = TypedDict("ParityNetPeers", { - "active": int, - "connected": int, - "max": int, - "peers": List[Dict[Any, Any]], -}) -ParityFilterParams = TypedDict("ParityFilterParams", { - "fromBlock": BlockIdentifier, - "toBlock": BlockIdentifier, - "fromAddress": Sequence[Union[Address, ChecksumAddress, ENS]], - "toAddress": Sequence[Union[Address, ChecksumAddress, ENS]], - "after": int, - "count": int, -}, total=False) diff --git a/web3/version.py b/web3/version.py index bf5ae01591..e32d0c599d 100644 --- a/web3/version.py +++ b/web3/version.py @@ -1,65 +1,61 @@ -from typing import ( - NoReturn, -) - -from web3.method import ( - Method, -) -from web3.module import ( - Module, - ModuleV2, -) - - -class BaseVersion(ModuleV2): - retrieve_caller_fn = None - - _get_node_version = Method('web3_clientVersion') - _get_protocol_version = Method('eth_protocolVersion') - - @property - def api(self) -> str: - from web3 import __version__ - return __version__ - - -class AsyncVersion(BaseVersion): - is_async = True - - @property - async def node(self) -> str: - return await self._get_node_version() - - @property - async def ethereum(self) -> int: - return await self._get_protocol_version() - - -class BlockingVersion(BaseVersion): - @property - def node(self) -> str: - return self._get_node_version() - - @property - def ethereum(self) -> int: - return self._get_protocol_version() - - -class Version(Module): - @property - def api(self) -> NoReturn: - raise DeprecationWarning( - "This method has been deprecated ... Please use web3.api instead." - ) - - @property - def node(self) -> NoReturn: - raise DeprecationWarning( - "This method has been deprecated ... Please use web3.clientVersion instead." - ) - - @property - def ethereum(self) -> NoReturn: - raise DeprecationWarning( - "This method has been deprecated ... Please use web3.eth.protocolVersion instead." - ) +from web3.method import ( + Method, +) +from web3.module import ( + Module, + ModuleV2, +) + + +class BaseVersion(ModuleV2): + retrieve_caller_fn = None + + _get_node_version = Method('web3_clientVersion') + _get_protocol_version = Method('vns_protocolVersion') + + @property + def api(self): + from web3 import __version__ + return __version__ + + +class AsyncVersion(BaseVersion): + is_async = True + + @property + async def node(self): + return await self._get_node_version() + + @property + async def ethereum(self): + return await self._get_protocol_version() + + +class BlockingVersion(BaseVersion): + @property + def node(self): + return self._get_node_version() + + @property + def ethereum(self): + return self._get_protocol_version() + + +class Version(Module): + @property + def api(self): + raise DeprecationWarning( + "This method has been deprecated ... Please use web3.api instead." + ) + + @property + def node(self): + raise DeprecationWarning( + "This method has been deprecated ... Please use web3.clientVersion instead." + ) + + @property + def ethereum(self): + raise DeprecationWarning( + "This method has been deprecated ... Please use web3.vns.protocolVersion instead." + ) diff --git a/web3/eth.py b/web3/vns.py similarity index 52% rename from web3/eth.py rename to web3/vns.py index 521c8fb38b..1dc1c9f873 100644 --- a/web3/eth.py +++ b/web3/vns.py @@ -1,539 +1,443 @@ -from typing import ( - Any, - Dict, - List, - NoReturn, - Optional, - Sequence, - Tuple, - Type, - Union, - overload, -) - -from eth_account import ( - Account, -) -from eth_typing import ( - Address, - BlockNumber, - ChecksumAddress, - Hash32, - HexStr, -) -from eth_utils import ( - apply_to_return_value, - is_checksum_address, - is_string, -) -from eth_utils.toolz import ( - assoc, - merge, -) -from hexbytes import ( - HexBytes, -) - -from web3._utils.blocks import ( - select_method_for_block_identifier, -) -from web3._utils.compat import ( - Literal, -) -from web3._utils.empty import ( - empty, -) -from web3._utils.encoding import ( - to_hex, -) -from web3._utils.filters import ( - BlockFilter, - Filter, - LogFilter, - TransactionFilter, -) -from web3._utils.threads import ( - Timeout, -) -from web3._utils.transactions import ( - assert_valid_transaction_params, - extract_valid_transaction_params, - get_buffered_gas_estimate, - get_required_transaction, - replace_transaction, - wait_for_transaction_receipt, -) -from web3.contract import ( - ConciseContract, - Contract, - ContractCaller, -) -from web3.exceptions import ( - BlockNotFound, - TimeExhausted, - TransactionNotFound, -) -from web3.iban import ( - Iban, -) -from web3.module import ( - Module, -) -from web3.types import ( - ENS, - BlockData, - BlockIdentifier, - FilterParams, - GasPriceStrategy, - LogReceipt, - MerkleProof, - SyncStatus, - TxParams, - TxReceipt, - Uncle, - Wei, -) - - -class Eth(Module): - account = Account() - defaultAccount = empty - defaultBlock: Literal["latest"] = "latest" # noqa: E704 - defaultContractFactory: Type[Union[Contract, ConciseContract, ContractCaller]] = Contract # noqa: E704,E501 - iban = Iban - gasPriceStrategy = None - - def namereg(self) -> NoReturn: - raise NotImplementedError() - - def icapNamereg(self) -> NoReturn: - raise NotImplementedError() - - @property - def protocolVersion(self) -> str: - return self.web3.manager.request_blocking("eth_protocolVersion", []) - - @property - def syncing(self) -> Union[SyncStatus, bool]: - return self.web3.manager.request_blocking("eth_syncing", []) - - @property - def coinbase(self) -> ChecksumAddress: - return self.web3.manager.request_blocking("eth_coinbase", []) - - @property - def mining(self) -> bool: - return self.web3.manager.request_blocking("eth_mining", []) - - @property - def hashrate(self) -> int: - return self.web3.manager.request_blocking("eth_hashrate", []) - - @property - def gasPrice(self) -> Wei: - return self.web3.manager.request_blocking("eth_gasPrice", []) - - @property - def accounts(self) -> Tuple[ChecksumAddress]: - return self.web3.manager.request_blocking("eth_accounts", []) - - @property - def blockNumber(self) -> BlockNumber: - return self.web3.manager.request_blocking("eth_blockNumber", []) - - @property - def chainId(self) -> int: - return self.web3.manager.request_blocking("eth_chainId", []) - - def getBalance( - self, account: Union[Address, ChecksumAddress, ENS], block_identifier: BlockIdentifier=None - ) -> Wei: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_getBalance", - [account, block_identifier], - ) - - def getStorageAt( - self, - account: Union[Address, ChecksumAddress, ENS], - position: int, - block_identifier: BlockIdentifier=None - ) -> bytes: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_getStorageAt", - [account, position, block_identifier] - ) - - def getProof( - self, - account: Union[Address, ChecksumAddress, ENS], - positions: Sequence[int], - block_identifier: BlockIdentifier=None - ) -> MerkleProof: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_getProof", - [account, positions, block_identifier] - ) - - def getCode( - self, account: Union[Address, ChecksumAddress, ENS], block_identifier: BlockIdentifier=None - ) -> HexStr: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_getCode", - [account, block_identifier], - ) - - def getBlock( - self, block_identifier: BlockIdentifier, full_transactions: bool=False - ) -> BlockData: - """ - `eth_getBlockByHash` - `eth_getBlockByNumber` - """ - method = select_method_for_block_identifier( - block_identifier, - if_predefined='eth_getBlockByNumber', - if_hash='eth_getBlockByHash', - if_number='eth_getBlockByNumber', - ) - - result = self.web3.manager.request_blocking( - method, - [block_identifier, full_transactions], - ) - if result is None: - raise BlockNotFound(f"Block with id: {block_identifier} not found.") - return result - - def getBlockTransactionCount(self, block_identifier: BlockIdentifier) -> int: - """ - `eth_getBlockTransactionCountByHash` - `eth_getBlockTransactionCountByNumber` - """ - method = select_method_for_block_identifier( - block_identifier, - if_predefined='eth_getBlockTransactionCountByNumber', - if_hash='eth_getBlockTransactionCountByHash', - if_number='eth_getBlockTransactionCountByNumber', - ) - result = self.web3.manager.request_blocking( - method, - [block_identifier], - ) - if result is None: - raise BlockNotFound(f"Block with id: {block_identifier} not found.") - return result - - def getUncleCount(self, block_identifier: BlockIdentifier) -> int: - """ - `eth_getUncleCountByBlockHash` - `eth_getUncleCountByBlockNumber` - """ - method = select_method_for_block_identifier( - block_identifier, - if_predefined='eth_getUncleCountByBlockNumber', - if_hash='eth_getUncleCountByBlockHash', - if_number='eth_getUncleCountByBlockNumber', - ) - result = self.web3.manager.request_blocking( - method, - [block_identifier], - ) - if result is None: - raise BlockNotFound(f"Block with id: {block_identifier} not found.") - return result - - def getUncleByBlock(self, block_identifier: BlockIdentifier, uncle_index: int) -> Uncle: - """ - `eth_getUncleByBlockHashAndIndex` - `eth_getUncleByBlockNumberAndIndex` - """ - method = select_method_for_block_identifier( - block_identifier, - if_predefined='eth_getUncleByBlockNumberAndIndex', - if_hash='eth_getUncleByBlockHashAndIndex', - if_number='eth_getUncleByBlockNumberAndIndex', - ) - result = self.web3.manager.request_blocking( - method, - [block_identifier, uncle_index], - ) - if result is None: - raise BlockNotFound( - f"Uncle at index: {uncle_index} of block with id: {block_identifier} not found." - ) - return result - - def getTransaction(self, transaction_hash: Hash32) -> TxReceipt: - result = self.web3.manager.request_blocking( - "eth_getTransactionByHash", - [transaction_hash], - ) - if result is None: - raise TransactionNotFound(f"Transaction with hash: {transaction_hash} not found.") - return result - - def getTransactionFromBlock( - self, block_identifier: BlockIdentifier, transaction_index: int - ) -> NoReturn: - """ - Alias for the method getTransactionByBlock - Deprecated to maintain naming consistency with the json-rpc API - """ - raise DeprecationWarning("This method has been deprecated as of EIP 1474.") - - def getTransactionByBlock( - self, block_identifier: BlockIdentifier, transaction_index: int - ) -> TxReceipt: - """ - `eth_getTransactionByBlockHashAndIndex` - `eth_getTransactionByBlockNumberAndIndex` - """ - method = select_method_for_block_identifier( - block_identifier, - if_predefined='eth_getTransactionByBlockNumberAndIndex', - if_hash='eth_getTransactionByBlockHashAndIndex', - if_number='eth_getTransactionByBlockNumberAndIndex', - ) - result = self.web3.manager.request_blocking( - method, - [block_identifier, transaction_index], - ) - if result is None: - raise TransactionNotFound( - f"Transaction index: {transaction_index} " - f"on block id: {block_identifier} not found." - ) - return result - - def waitForTransactionReceipt( - self, transaction_hash: Hash32, timeout: int=120, poll_latency: float=0.1 - ) -> TxReceipt: - try: - return wait_for_transaction_receipt(self.web3, transaction_hash, timeout, poll_latency) - except Timeout: - raise TimeExhausted( - "Transaction {} is not in the chain, after {} seconds".format( - to_hex(transaction_hash), - timeout, - ) - ) - - def getTransactionReceipt(self, transaction_hash: Hash32) -> TxReceipt: - result = self.web3.manager.request_blocking( - "eth_getTransactionReceipt", - [transaction_hash], - ) - if result is None: - raise TransactionNotFound(f"Transaction with hash: {transaction_hash} not found.") - return result - - def getTransactionCount( - self, account: Union[Address, ChecksumAddress, ENS], block_identifier: BlockIdentifier=None - ) -> int: - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_getTransactionCount", - [account, block_identifier], - ) - - def replaceTransaction(self, transaction_hash: Hash32, new_transaction: TxParams) -> Hash32: - current_transaction = get_required_transaction(self.web3, transaction_hash) - return replace_transaction(self.web3, current_transaction, new_transaction) - - # todo: Update Any to stricter kwarg checking with TxParams - # https://github.com/python/mypy/issues/4441 - def modifyTransaction(self, transaction_hash: Hash32, **transaction_params: Any) -> None: - assert_valid_transaction_params(transaction_params) - current_transaction = get_required_transaction(self.web3, transaction_hash) - current_transaction_params = extract_valid_transaction_params(current_transaction) - new_transaction = merge(current_transaction_params, transaction_params) - return replace_transaction(self.web3, current_transaction, new_transaction) - - def sendTransaction(self, transaction: TxParams) -> Hash32: - # TODO: move to middleware - if 'from' not in transaction and is_checksum_address(self.defaultAccount): - transaction = assoc(transaction, 'from', self.defaultAccount) - - # TODO: move gas estimation in middleware - if 'gas' not in transaction: - transaction = assoc( - transaction, - 'gas', - get_buffered_gas_estimate(self.web3, transaction), - ) - - return self.web3.manager.request_blocking( - "eth_sendTransaction", - [transaction], - ) - - def sendRawTransaction(self, raw_transaction: HexStr) -> Hash32: - return self.web3.manager.request_blocking( - "eth_sendRawTransaction", - [raw_transaction], - ) - - def sign( - self, - account: Union[Address, ChecksumAddress, ENS], - data: bytes=None, - hexstr: HexStr=None, - text: str=None - ) -> HexStr: - message_hex = to_hex(data, hexstr=hexstr, text=text) - return self.web3.manager.request_blocking( - "eth_sign", [account, message_hex], - ) - - def signTransaction(self, transaction: TxParams) -> bytes: - return self.web3.manager.request_blocking( - "eth_signTransaction", [transaction], - ) - - def signTypedData( - self, account: Union[Address, ChecksumAddress, ENS], jsonMessage: Dict[Any, Any] - ) -> HexStr: - return self.web3.manager.request_blocking( - "eth_signTypedData", [account, jsonMessage], - ) - - @apply_to_return_value(HexBytes) - def call(self, transaction: TxParams, block_identifier: BlockIdentifier=None) -> Sequence[Any]: - # TODO: move to middleware - if 'from' not in transaction and is_checksum_address(self.defaultAccount): - transaction = assoc(transaction, 'from', self.defaultAccount) - - # TODO: move to middleware - if block_identifier is None: - block_identifier = self.defaultBlock - return self.web3.manager.request_blocking( - "eth_call", - [transaction, block_identifier], - ) - - def estimateGas(self, transaction: TxParams, block_identifier: BlockIdentifier=None) -> Wei: - # TODO: move to middleware - if 'from' not in transaction and is_checksum_address(self.defaultAccount): - transaction = assoc(transaction, 'from', self.defaultAccount) - - if block_identifier is None: - params = [transaction] - else: - params = [transaction, block_identifier] - - return self.web3.manager.request_blocking( - "eth_estimateGas", - params, - ) - - def filter(self, filter_params: Union[str, FilterParams]=None, filter_id: int=None) -> Filter: - if filter_id and filter_params: - raise TypeError( - "Ambiguous invocation: provide either a `filter_params` or a `filter_id` argument. " - "Both were supplied." - ) - if is_string(filter_params): - if filter_params == "latest": - filter_id = self.web3.manager.request_blocking( - "eth_newBlockFilter", [], - ) - return BlockFilter(self.web3, filter_id) - elif filter_params == "pending": - filter_id = self.web3.manager.request_blocking( - "eth_newPendingTransactionFilter", [], - ) - return TransactionFilter(self.web3, filter_id) - else: - raise ValueError( - "The filter API only accepts the values of `pending` or " - "`latest` for string based filters" - ) - elif isinstance(filter_params, dict): - _filter_id = self.web3.manager.request_blocking( - "eth_newFilter", - [filter_params], - ) - return LogFilter(self.web3, _filter_id) - elif filter_id and not filter_params: - return LogFilter(self.web3, filter_id) - else: - raise TypeError("Must provide either filter_params as a string or " - "a valid filter object, or a filter_id as a string " - "or hex.") - - def getFilterChanges(self, filter_id: int) -> List[LogReceipt]: - return self.web3.manager.request_blocking( - "eth_getFilterChanges", [filter_id], - ) - - def getFilterLogs(self, filter_id: int) -> List[LogReceipt]: - return self.web3.manager.request_blocking( - "eth_getFilterLogs", [filter_id], - ) - - def getLogs(self, filter_params: FilterParams) -> List[LogReceipt]: - return self.web3.manager.request_blocking( - "eth_getLogs", [filter_params], - ) - - def submitHashrate(self, hashrate: int, node_id: Hash32) -> bool: - return self.web3.manager.request_blocking( - "eth_submitHashrate", [hashrate, node_id], - ) - - def submitWork(self, nonce: int, pow_hash: Hash32, mix_digest: Hash32) -> bool: - return self.web3.manager.request_blocking( - "eth_submitWork", [nonce, pow_hash, mix_digest], - ) - - def uninstallFilter(self, filter_id: int) -> bool: - return self.web3.manager.request_blocking( - "eth_uninstallFilter", [filter_id], - ) - - @overload - def contract(self, address: None=None, **kwargs: Any) -> Type[Contract]: ... # noqa: E704,E501 - - @overload # noqa: F811 - def contract(self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any) -> Contract: ... # noqa: E704,E501 - - def contract( # noqa: F811 - self, address: Union[Address, ChecksumAddress, ENS]=None, **kwargs: Any - ) -> Union[Type[Contract], Contract]: - ContractFactoryClass = kwargs.pop('ContractFactoryClass', self.defaultContractFactory) - - ContractFactory = ContractFactoryClass.factory(self.web3, **kwargs) - - if address: - return ContractFactory(address) - else: - return ContractFactory - - def setContractFactory( - self, contractFactory: Type[Union[Contract, ConciseContract, ContractCaller]] - ) -> None: - self.defaultContractFactory = contractFactory - - def getCompilers(self) -> NoReturn: - raise DeprecationWarning("This method has been deprecated as of EIP 1474.") - - def getWork(self) -> List[Hash32]: - return self.web3.manager.request_blocking("eth_getWork", []) - - def generateGasPrice(self, transaction_params: TxParams=None) -> Optional[Wei]: - if self.gasPriceStrategy: - return self.gasPriceStrategy(self.web3, transaction_params) - return None - - def setGasPriceStrategy(self, gas_price_strategy: GasPriceStrategy) -> None: - self.gasPriceStrategy = gas_price_strategy +from vns_account import ( + Account, +) +from vns_utils import ( + apply_to_return_value, + is_checksum_address, + is_string, +) +from hexbytes import ( + HexBytes, +) + +from web3._utils.blocks import ( + select_method_for_block_identifier, +) +from web3._utils.empty import ( + empty, +) +from web3._utils.encoding import ( + to_hex, +) +from web3._utils.filters import ( + BlockFilter, + LogFilter, + TransactionFilter, +) +from web3._utils.threads import ( + Timeout, +) +from web3._utils.toolz import ( + assoc, + merge, +) +from web3._utils.transactions import ( + assert_valid_transaction_params, + extract_valid_transaction_params, + get_buffered_gas_estimate, + get_required_transaction, + replace_transaction, + wait_for_transaction_receipt, +) +from web3.contract import ( + Contract, +) +from web3.exceptions import ( + BlockNotFound, + TimeExhausted, + TransactionNotFound, +) +from web3.iban import ( + Iban, +) +from web3.module import ( + Module, +) + + +class Bbbbbbbb(Module): + account = Account() + defaultAccount = empty + defaultBlock = "latest" + defaultContractFactory = Contract + iban = Iban + gasPriceStrategy = None + + def namereg(self): + raise NotImplementedError() + + def icapNamereg(self): + raise NotImplementedError() + + @property + def protocolVersion(self): + return self.web3.manager.request_blocking("vns_protocolVersion", []) + + @property + def syncing(self): + return self.web3.manager.request_blocking("vns_syncing", []) + + @property + def coinbase(self): + return self.web3.manager.request_blocking("vns_coinbase", []) + + @property + def mining(self): + return self.web3.manager.request_blocking("vns_mining", []) + + @property + def hashrate(self): + return self.web3.manager.request_blocking("vns_hashrate", []) + + @property + def gasPrice(self): + return self.web3.manager.request_blocking("vns_gasPrice", []) + + @property + def accounts(self): + return self.web3.manager.request_blocking("vns_accounts", []) + + @property + def blockNumber(self): + return self.web3.manager.request_blocking("vns_blockNumber", []) + + @property + def chainId(self): + return self.web3.manager.request_blocking("vns_chainId", []) + + def getBalance(self, account, block_identifier=None): + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "vns_getBalance", + [account, block_identifier], + ) + + def getStorageAt(self, account, position, block_identifier=None): + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "vns_getStorageAt", + [account, position, block_identifier] + ) + + def getCode(self, account, block_identifier=None): + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "vns_getCode", + [account, block_identifier], + ) + + def getBlock(self, block_identifier, full_transactions=False): + """ + `vns_getBlockByHash` + `vns_getBlockByNumber` + """ + method = select_method_for_block_identifier( + block_identifier, + if_predefined='vns_getBlockByNumber', + if_hash='vns_getBlockByHash', + if_number='vns_getBlockByNumber', + ) + + result = self.web3.manager.request_blocking( + method, + [block_identifier, full_transactions], + ) + if result is None: + raise BlockNotFound(f"Block with id: {block_identifier} not found.") + return result + + def getBlockTransactionCount(self, block_identifier): + """ + `vns_getBlockTransactionCountByHash` + `vns_getBlockTransactionCountByNumber` + """ + method = select_method_for_block_identifier( + block_identifier, + if_predefined='vns_getBlockTransactionCountByNumber', + if_hash='vns_getBlockTransactionCountByHash', + if_number='vns_getBlockTransactionCountByNumber', + ) + result = self.web3.manager.request_blocking( + method, + [block_identifier], + ) + if result is None: + raise BlockNotFound(f"Block with id: {block_identifier} not found.") + return result + + def getUncleCount(self, block_identifier): + """ + `vns_getUncleCountByBlockHash` + `vns_getUncleCountByBlockNumber` + """ + method = select_method_for_block_identifier( + block_identifier, + if_predefined='vns_getUncleCountByBlockNumber', + if_hash='vns_getUncleCountByBlockHash', + if_number='vns_getUncleCountByBlockNumber', + ) + result = self.web3.manager.request_blocking( + method, + [block_identifier], + ) + if result is None: + raise BlockNotFound(f"Block with id: {block_identifier} not found.") + return result + + def getUncleByBlock(self, block_identifier, uncle_index): + """ + `vns_getUncleByBlockHashAndIndex` + `vns_getUncleByBlockNumberAndIndex` + """ + method = select_method_for_block_identifier( + block_identifier, + if_predefined='vns_getUncleByBlockNumberAndIndex', + if_hash='vns_getUncleByBlockHashAndIndex', + if_number='vns_getUncleByBlockNumberAndIndex', + ) + result = self.web3.manager.request_blocking( + method, + [block_identifier, uncle_index], + ) + if result is None: + raise BlockNotFound( + f"Uncle at index: {uncle_index} of block with id: {block_identifier} not found." + ) + return result + + def getTransaction(self, transaction_hash): + result = self.web3.manager.request_blocking( + "vns_getTransactionByHash", + [transaction_hash], + ) + if result is None: + raise TransactionNotFound(f"Transaction with hash: {transaction_hash} not found.") + return result + + def getTransactionFromBlock(self, block_identifier, transaction_index): + """ + Alias for the method getTransactionByBlock + Depreceated to maintain naming consistency with the json-rpc API + """ + raise DeprecationWarning("This method has been deprecated as of EIP 1474.") + + def getTransactionByBlock(self, block_identifier, transaction_index): + """ + `vns_getTransactionByBlockHashAndIndex` + `vns_getTransactionByBlockNumberAndIndex` + """ + method = select_method_for_block_identifier( + block_identifier, + if_predefined='vns_getTransactionByBlockNumberAndIndex', + if_hash='vns_getTransactionByBlockHashAndIndex', + if_number='vns_getTransactionByBlockNumberAndIndex', + ) + result = self.web3.manager.request_blocking( + method, + [block_identifier, transaction_index], + ) + if result is None: + raise TransactionNotFound( + f"Transaction index: {transaction_index} " + f"on block id: {block_identifier} not found." + ) + return result + + def waitForTransactionReceipt(self, transaction_hash, timeout=120): + try: + return wait_for_transaction_receipt(self.web3, transaction_hash, timeout) + except Timeout: + raise TimeExhausted( + "Transaction {} is not in the chain, after {} seconds".format( + transaction_hash, + timeout, + ) + ) + + def getTransactionReceipt(self, transaction_hash): + result = self.web3.manager.request_blocking( + "vns_getTransactionReceipt", + [transaction_hash], + ) + if result is None: + raise TransactionNotFound(f"Transaction with hash: {transaction_hash} not found.") + return result + + def getTransactionCount(self, account, block_identifier=None): + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "vns_getTransactionCount", + [account, block_identifier], + ) + + def replaceTransaction(self, transaction_hash, new_transaction): + current_transaction = get_required_transaction(self.web3, transaction_hash) + return replace_transaction(self.web3, current_transaction, new_transaction) + + def modifyTransaction(self, transaction_hash, **transaction_params): + assert_valid_transaction_params(transaction_params) + current_transaction = get_required_transaction(self.web3, transaction_hash) + current_transaction_params = extract_valid_transaction_params(current_transaction) + new_transaction = merge(current_transaction_params, transaction_params) + return replace_transaction(self.web3, current_transaction, new_transaction) + + def sendTransaction(self, transaction): + # TODO: move to middleware + if 'from' not in transaction and is_checksum_address(self.defaultAccount): + transaction = assoc(transaction, 'from', self.defaultAccount) + + # TODO: move gas estimation in middleware + if 'gas' not in transaction: + transaction = assoc( + transaction, + 'gas', + get_buffered_gas_estimate(self.web3, transaction), + ) + + return self.web3.manager.request_blocking( + "vns_sendTransaction", + [transaction], + ) + + def sendRawTransaction(self, raw_transaction): + return self.web3.manager.request_blocking( + "vns_sendRawTransaction", + [raw_transaction], + ) + + def sign(self, account, data=None, hexstr=None, text=None): + message_hex = to_hex(data, hexstr=hexstr, text=text) + return self.web3.manager.request_blocking( + "vns_sign", [account, message_hex], + ) + + def signTransaction(self, transaction): + return self.web3.manager.request_blocking( + "vns_signTransaction", [transaction], + ) + + @apply_to_return_value(HexBytes) + def call(self, transaction, block_identifier=None): + # TODO: move to middleware + if 'from' not in transaction and is_checksum_address(self.defaultAccount): + transaction = assoc(transaction, 'from', self.defaultAccount) + + # TODO: move to middleware + if block_identifier is None: + block_identifier = self.defaultBlock + return self.web3.manager.request_blocking( + "vns_call", + [transaction, block_identifier], + ) + + def estimateGas(self, transaction, block_identifier=None): + # TODO: move to middleware + if 'from' not in transaction and is_checksum_address(self.defaultAccount): + transaction = assoc(transaction, 'from', self.defaultAccount) + + if block_identifier is None: + params = [transaction] + else: + params = [transaction, block_identifier] + + return self.web3.manager.request_blocking( + "vns_estimateGas", + params, + ) + + def filter(self, filter_params=None, filter_id=None): + if filter_id and filter_params: + raise TypeError( + "Ambiguous invocation: provide either a `filter_params` or a `filter_id` argument. " + "Both were supplied." + ) + if is_string(filter_params): + if filter_params == "latest": + filter_id = self.web3.manager.request_blocking( + "vns_newBlockFilter", [], + ) + return BlockFilter(self.web3, filter_id) + elif filter_params == "pending": + filter_id = self.web3.manager.request_blocking( + "vns_newPendingTransactionFilter", [], + ) + return TransactionFilter(self.web3, filter_id) + else: + raise ValueError( + "The filter API only accepts the values of `pending` or " + "`latest` for string based filters" + ) + elif isinstance(filter_params, dict): + _filter_id = self.web3.manager.request_blocking( + "vns_newFilter", + [filter_params], + ) + return LogFilter(self.web3, _filter_id) + elif filter_id and not filter_params: + return LogFilter(self.web3, filter_id) + else: + raise TypeError("Must provide either filter_params as a string or " + "a valid filter object, or a filter_id as a string " + "or hex.") + + def getFilterChanges(self, filter_id): + return self.web3.manager.request_blocking( + "vns_getFilterChanges", [filter_id], + ) + + def getFilterLogs(self, filter_id): + return self.web3.manager.request_blocking( + "vns_getFilterLogs", [filter_id], + ) + + def getLogs(self, filter_params): + return self.web3.manager.request_blocking( + "vns_getLogs", [filter_params], + ) + + def submitHashrate(self, hashrate, node_id): + return self.web3.manager.request_blocking( + "vns_submitHashrate", [hashrate, node_id], + ) + + def submitWork(self, nonce, pow_hash, mix_digest): + return self.web3.manager.request_blocking( + "vns_submitWork", [nonce, pow_hash, mix_digest], + ) + + def uninstallFilter(self, filter_id): + return self.web3.manager.request_blocking( + "vns_uninstallFilter", [filter_id], + ) + + def contract(self, + address=None, + **kwargs): + ContractFactoryClass = kwargs.pop('ContractFactoryClass', self.defaultContractFactory) + + ContractFactory = ContractFactoryClass.factory(self.web3, **kwargs) + + if address: + return ContractFactory(address) + else: + return ContractFactory + + def setContractFactory(self, contractFactory): + self.defaultContractFactory = contractFactory + + def getCompilers(self): + raise DeprecationWarning("This method has been deprecated as of EIP 1474.") + + def getWork(self): + return self.web3.manager.request_blocking("vns_getWork", []) + + def generateGasPrice(self, transaction_params=None): + if self.gasPriceStrategy: + return self.gasPriceStrategy(self.web3, transaction_params) + + def setGasPriceStrategy(self, gas_price_strategy): + self.gasPriceStrategy = gas_price_strategy