diff --git a/conftest.py b/conftest.py index ba98c6459a..69373ff634 100644 --- a/conftest.py +++ b/conftest.py @@ -103,7 +103,7 @@ def web3(): @pytest.fixture -def web3_strict_types(): +def w3_strict_abi(): w3 = Web3(EthereumTesterProvider()) w3.enable_strict_bytes_type_checking() return w3 diff --git a/docs/abi_types.rst b/docs/abi_types.rst index 03576709ac..4d7172c8a9 100644 --- a/docs/abi_types.rst +++ b/docs/abi_types.rst @@ -28,6 +28,10 @@ All addresses must be supplied in one of three ways: 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 diff --git a/docs/contracts.rst b/docs/contracts.rst index 1efc48cac8..e94a1fc051 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -545,12 +545,12 @@ The following values will raise an error by default: - String is not valid hex However, you may want to be stricter with acceptable values for bytes types. -For this you can use the ``enable_strict_bytes_type_checking`` method, +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 that is less than (or greater than) the specified byte size + - 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 @@ -586,13 +586,75 @@ invoked will enforce a stricter set of rules on which values are accepted. - Needs to have exactly 4 bytes -For example, the following contract code will generate the abi below and some bytecode: +Taking the following contract code as an example: .. testsetup:: 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:: @@ -614,72 +676,12 @@ For example, the following contract code will generate the abi below and some by ... # } ... # } -.. doctest:: - >>> 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() + >>> # abi = "..." >>> # bytecode = "6080..." +.. doctest:: + >>> ArraysContract = w3.eth.contract(abi=abi, bytecode=bytecode) >>> tx_hash = ArraysContract.constructor([b'b']).transact() diff --git a/docs/examples.rst b/docs/examples.rst index 3cc2fde727..dde92106ca 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -202,19 +202,19 @@ 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: @@ -229,45 +229,45 @@ The following example demonstrates a few things: 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() diff --git a/docs/overview.rst b/docs/overview.rst index fb6673bc0e..67b80d14c8 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -341,6 +341,38 @@ Cryptographic Hashing >>> 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 ------- diff --git a/tests/core/contracts/conftest.py b/tests/core/contracts/conftest.py index 36617113f1..717906880b 100644 --- a/tests/core/contracts/conftest.py +++ b/tests/core/contracts/conftest.py @@ -215,11 +215,11 @@ def WithConstructorArgumentsContract(web3, @pytest.fixture() -def WithConstructorArgumentsContractStrict(web3_strict_types, +def WithConstructorArgumentsContractStrict(w3_strict_abi, WITH_CONSTRUCTOR_ARGUMENTS_CODE, WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, WITH_CONSTRUCTOR_ARGUMENTS_ABI): - return web3_strict_types.eth.contract( + return w3_strict_abi.eth.contract( abi=WITH_CONSTRUCTOR_ARGUMENTS_ABI, bytecode=WITH_CONSTRUCTOR_ARGUMENTS_CODE, bytecode_runtime=WITH_CONSTRUCTOR_ARGUMENTS_RUNTIME, @@ -402,27 +402,27 @@ def EMITTER(EMITTER_CODE, @pytest.fixture() -def StrictEmitter(web3_strict_types, EMITTER): - web3 = web3_strict_types - return web3.eth.contract(**EMITTER) +def StrictEmitter(w3_strict_abi, EMITTER): + w3 = w3_strict_abi + return w3.eth.contract(**EMITTER) @pytest.fixture() -def strict_emitter(web3_strict_types, +def strict_emitter(w3_strict_abi, StrictEmitter, wait_for_transaction, wait_for_block, address_conversion_func): - web3 = web3_strict_types + w3 = w3_strict_abi - wait_for_block(web3) + wait_for_block(w3) deploy_txn_hash = StrictEmitter.constructor().transact( - {'from': web3.eth.coinbase, 'gas': 1000000} + {'from': w3.eth.coinbase, 'gas': 1000000} ) - deploy_receipt = wait_for_transaction(web3, deploy_txn_hash) + deploy_receipt = wait_for_transaction(w3, deploy_txn_hash) contract_address = address_conversion_func(deploy_receipt['contractAddress']) - bytecode = web3.eth.getCode(contract_address) + bytecode = w3.eth.getCode(contract_address) assert bytecode == StrictEmitter.bytecode_runtime emitter_contract = StrictEmitter(address=contract_address) assert emitter_contract.address == contract_address @@ -625,8 +625,8 @@ def ArraysContract(web3, ARRAYS_CONTRACT): @pytest.fixture() -def StrictArraysContract(web3_strict_types, ARRAYS_CONTRACT): - return web3_strict_types.eth.contract(**ARRAYS_CONTRACT) +def StrictArraysContract(w3_strict_abi, ARRAYS_CONTRACT): + return w3_strict_abi.eth.contract(**ARRAYS_CONTRACT) CONTRACT_PAYABLE_TESTER_SOURCE = """ diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index f17ff8851e..221a272577 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -75,7 +75,7 @@ def arrays_contract(web3, ArraysContract, address_conversion_func): @pytest.fixture() -def strict_arrays_contract(web3_strict_types, StrictArraysContract, address_conversion_func): +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 @@ -83,7 +83,7 @@ def strict_arrays_contract(web3_strict_types, StrictArraysContract, address_conv ] byte_arr = [b'\xff', b'\xff', b'\xff', b'\xff'] return deploy( - web3_strict_types, + w3_strict_abi, StrictArraysContract, address_conversion_func, args=[bytes32_array, byte_arr] diff --git a/tests/core/contracts/test_contract_constructor_encoding.py b/tests/core/contracts/test_contract_constructor_encoding.py index 8ed8f264e5..3711dfaf9a 100644 --- a/tests/core/contracts/test_contract_constructor_encoding.py +++ b/tests/core/contracts/test_contract_constructor_encoding.py @@ -64,7 +64,7 @@ def test_contract_constructor_encoding_encoding(web3, WithConstructorArgumentsCo 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" + 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 @@ -76,6 +76,28 @@ def test_contract_constructor_encoding_encoding_warning(web3, WithConstructorArg 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', ( @@ -85,8 +107,8 @@ def test_contract_constructor_encoding_encoding_warning(web3, WithConstructorArg '61626364', ), ) -def test_contract_constructor_encoding_encoding_strict( - web3_strict_types, +def test_contract_constructor_encoding_encoding_strict_errors( + w3_strict_abi, WithConstructorArgumentsContractStrict, bytes_arg): with pytest.raises( diff --git a/tests/core/contracts/test_contract_deployment.py b/tests/core/contracts/test_contract_deployment.py index dbc62f2ab7..da7767c64c 100644 --- a/tests/core/contracts/test_contract_deployment.py +++ b/tests/core/contracts/test_contract_deployment.py @@ -4,9 +4,6 @@ from eth_utils import ( decode_hex, ) -from web3.exceptions import ( - ValidationError, -) def test_contract_deployment_no_constructor(web3, MathContract, @@ -62,25 +59,25 @@ def test_contract_deployment_with_constructor_with_arguments(web3, '0x0000000000000000000000000000000000000000000000000000000000000000' ) ) -def test_contract_deployment_with_constructor_with_arguments_strict(web3_strict_types, +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() + 1234, constructor_arg + ).transact() - txn_receipt = web3_strict_types.eth.waitForTransactionReceipt(deploy_txn) + 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 = web3_strict_types.eth.getCode(contract_address) + 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(web3_strict_types, +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( @@ -89,6 +86,7 @@ def test_contract_deployment_with_constructor_with_arguments_strict_error(web3_s ): WithConstructorArgumentsContractStrict.constructor(1234, 'abcd').transact() + def test_contract_deployment_with_constructor_with_address_argument(web3, WithConstructorAddressArgumentsContract, # noqa: E501 WITH_CONSTRUCTOR_ADDRESS_RUNTIME): # noqa: E501 diff --git a/tests/core/contracts/test_contract_method_abi_encoding.py b/tests/core/contracts/test_contract_method_abi_encoding.py index 1e2772e95d..c5e4540e93 100644 --- a/tests/core/contracts/test_contract_method_abi_encoding.py +++ b/tests/core/contracts/test_contract_method_abi_encoding.py @@ -12,50 +12,50 @@ @pytest.mark.parametrize( - 'abi,method,arguments,data,expected', + 'abi,arguments,data,expected', ( - (ABI_A, 'a', [], None, '0x0dbe671f'), - (ABI_A, 'a', [], '0x12345678', '0x12345678'), - ( + 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, - 'a', [0], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000000', + id='ABI_B, valid int args, no data' ), - ( + pytest.param( ABI_B, - 'a', [1], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + id='ABI_B, valid int args, no data' ), - ( + pytest.param( ABI_C, - 'a', [1], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + id='ABI_B, valid int args, no data' ), - ( + pytest.param( ABI_C, - 'a', [b'a'], None, '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', + id='ABI_C, valid byte args, no data' ), - ( + pytest.param( ABI_C, - 'a', ['0x61'], None, '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000', + id='ABI_C, valid hex args, no data' ), ), ) -def test_contract_abi_encoding(web3, abi, method, arguments, data, expected): +def test_contract_abi_encoding(web3, abi, arguments, data, expected): contract = web3.eth.contract(abi=abi) - actual = contract.encodeABI(method, arguments, data=data) + actual = contract.encodeABI('a', arguments, data=data) assert actual == expected @@ -71,100 +71,68 @@ def test_contract_abi_encoding_warning(web3): assert actual == '0x9f3fab586100000000000000000000000000000000000000000000000000000000000000' # noqa: E501 -@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.eth.contract(abi=abi) - actual = contract.encodeABI(method, kwargs=kwargs) - assert actual == expected +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( - 'abi,method,arguments,data', - ( - ( - ABI_C, - 'a', - [b'a'], - None, - ), - ( - ABI_C, - 'a', - ['0x61'], - None, - ), - ( - ABI_C, - 'a', - ['61'], - None, - ), - ), -) -def test_contract_abi_encoding_strict_with_error(web3_strict_types, abi, method, arguments, data): - contract = web3_strict_types.eth.contract(abi=abi) +@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(method, arguments, data=data) + contract.encodeABI('a', arguments, data=None) @pytest.mark.parametrize( - 'abi,method,arguments,data,expected', + 'abi,arguments,data,expected', ( - (ABI_A, 'a', [], None, '0x0dbe671f'), - (ABI_A, 'a', [], '0x12345678', '0x12345678'), - ( + 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, - 'a', [0], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000000', + id='ABI_B, valid int args, no data' ), - ( + pytest.param( ABI_B, - 'a', [1], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + id='ABI_B, valid int args, no data' ), - ( + pytest.param( ABI_C, - 'a', [1], None, '0xf0fdf8340000000000000000000000000000000000000000000000000000000000000001', + id='ABI_C, valid int args, no data' ), - ( + pytest.param( ABI_C, - 'a', [b'00000000000000000000000000000000'], None, '0x9f3fab583030303030303030303030303030303030303030303030303030303030303030', + id='ABI_C, valid bytestring args, no data' ), - ( + pytest.param( ABI_C, - 'a', ['0x0000000000000000000000000000000000000000000000000000000000000000'], None, '0x9f3fab580000000000000000000000000000000000000000000000000000000000000000', + id='ABI_C, valid hexstring args, no data' ), ), ) -def test_contract_abi_encoding_strict(web3_strict_types, abi, method, arguments, data, expected): - contract = web3_strict_types.eth.contract(abi=abi) - actual = contract.encodeABI(method, arguments, data=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 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 d0e2325f6c..353157aa43 100644 --- a/tests/core/contracts/test_contract_method_to_argument_matching.py +++ b/tests/core/contracts/test_contract_method_to_argument_matching.py @@ -162,8 +162,8 @@ def test_error_when_duplicate_match(web3): @pytest.mark.parametrize('arguments', (['0xf00b47'], [b''], [''], ['00' * 16])) -def test_strict_errors_if_type_is_wrong(web3_strict_types, arguments): - Contract = web3_strict_types.eth.contract(abi=MULTIPLE_FUNCTIONS) +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) @@ -178,8 +178,8 @@ def test_strict_errors_if_type_is_wrong(web3_strict_types, arguments): ([[(-1, True), (2, False)]], ['(int256,bool)[]']), ) ) -def test_strict_finds_function_with_matching_args(web3_strict_types, arguments, expected_types): - Contract = web3_strict_types.eth.contract(abi=MULTIPLE_FUNCTIONS) +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' diff --git a/tests/core/contracts/test_extracting_event_data.py b/tests/core/contracts/test_extracting_event_data.py index a82eeeec57..fd1a8de286 100644 --- a/tests/core/contracts/test_extracting_event_data.py +++ b/tests/core/contracts/test_extracting_event_data.py @@ -42,33 +42,6 @@ def emitter(web3, Emitter, wait_for_transaction, wait_for_block, address_convers return _emitter -@pytest.fixture() -def StrictEmitter(web3_strict_types, EMITTER): - return web3_strict_types.eth.contract(**EMITTER) - - -@pytest.fixture() -def strict_emitter( - web3_strict_types, - StrictEmitter, - wait_for_transaction, - wait_for_block, - address_conversion_func): - - wait_for_block(web3_strict_types) - deploy_txn_hash = StrictEmitter.constructor().transact( - {'from': web3_strict_types.eth.coinbase, 'gas': 1000000} - ) - deploy_receipt = web3_strict_types.eth.waitForTransactionReceipt(deploy_txn_hash) - contract_address = address_conversion_func(deploy_receipt['contractAddress']) - - bytecode = web3_strict_types.eth.getCode(contract_address) - assert bytecode == StrictEmitter.bytecode_runtime - _emitter = StrictEmitter(address=contract_address) - assert _emitter.address == contract_address - return _emitter - - @pytest.fixture() def EventContract(web3, EVENT_CONTRACT): return web3.eth.contract(**EVENT_CONTRACT) @@ -215,11 +188,9 @@ def test_event_data_extraction(web3, @pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args', + 'call_args,expected_args', ( ( - 'logListArgs', - 'LogListArgs', [[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 @@ -227,8 +198,6 @@ def test_event_data_extraction(web3, } ), ( - 'logListArgs', - 'LogListArgs', [[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 @@ -241,17 +210,16 @@ def test_event_data_extraction_bytes(web3, wait_for_transaction, emitter_log_topics, emitter_event_ids, - contract_fn, - event_name, call_args, expected_args): - emitter_fn = emitter.functions[contract_fn] + 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) @@ -268,40 +236,21 @@ def test_event_data_extraction_bytes(web3, assert event_data['event'] == event_name -@pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args', - ( - ( - 'logListArgs', - 'LogListArgs', - [['13'], ['54']], - { - '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'] - } - ), - ) -) def test_event_data_extraction_bytes_with_warning(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] + 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_fn(*call_args).transact() + 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) @@ -309,6 +258,10 @@ def test_event_data_extraction_bytes_with_warning(web3, 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'] @@ -319,33 +272,20 @@ def test_event_data_extraction_bytes_with_warning(web3, @pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_error', + 'call_args', ( ( - 'logListArgs', - 'LogListArgs', [[b'1312'], [b'4354']], - ValidationError, ), ( - 'logListArgs', - 'LogListArgs', [[b'1'], [b'5']], - ValidationError, ), ) ) -def test_event_data_extraction_bytes_strict_with_errors(web3_strict_types, - strict_emitter, - wait_for_transaction, - emitter_log_topics, - emitter_event_ids, - contract_fn, - event_name, - call_args, - expected_error): - emitter_fn = strict_emitter.functions[contract_fn] - with pytest.raises(expected_error): +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() @@ -385,14 +325,14 @@ def test_dynamic_length_argument_extraction(web3, assert event_data['event'] == 'LogDynamicArgs' -def test_argument_extraction_strict_bytes_types(web3_strict_types, +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(web3_strict_types, txn_hash) + txn_receipt = wait_for_transaction(w3_strict_abi, txn_hash) assert len(txn_receipt['logs']) == 1 log_entry = txn_receipt['logs'][0] @@ -403,12 +343,12 @@ def test_argument_extraction_strict_bytes_types(web3_strict_types, event_topic = emitter_log_topics.LogListArgs assert event_topic in log_entry['topics'] - encoded_arg_0 = web3_strict_types.codec.encode_abi(['bytes2'], arg_0) + 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 = web3_strict_types.keccak(padded_arg_0) + arg_0_topic = w3_strict_abi.keccak(padded_arg_0) assert arg_0_topic in log_entry['topics'] - event_data = get_event_data(web3_strict_types.codec, event_abi, log_entry) + event_data = get_event_data(w3_strict_abi.codec, event_abi, log_entry) expected_args = { "arg0": arg_0_topic, @@ -664,47 +604,18 @@ def test_event_rich_log( assert empty_rich_log == tuple() -@pytest.mark.parametrize( - 'contract_fn,event_name,call_args,expected_args,process_receipt', - ( - ( - 'logListArgs', - 'LogListArgs', - [[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'] - }, - True - ), - ( - 'logListArgs', - 'LogListArgs', - [[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'] - }, - False - ), - ) -) +@pytest.mark.parametrize('process_receipt', (True, False)) def test_event_rich_log_with_byte_args( web3, emitter, emitter_event_ids, wait_for_transaction, - contract_fn, - event_name, - call_args, - process_receipt, - expected_args): + process_receipt): - emitter_fn = emitter.functions[contract_fn] - txn_hash = emitter_fn(*call_args).transact() + txn_hash = emitter.functions.logListArgs([b'13'], [b'54']).transact() txn_receipt = wait_for_transaction(web3, txn_hash) - event_instance = emitter.events[event_name]() + event_instance = emitter.events.LogListArgs() if process_receipt: processed_logs = event_instance.processReceipt(txn_receipt) @@ -715,6 +626,10 @@ def test_event_rich_log_with_byte_args( 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: @@ -723,7 +638,7 @@ def test_event_rich_log_with_byte_args( 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 + assert rich_log['event'] == 'LogListArgs' def test_receipt_processing_with_discard_flag( diff --git a/tests/core/filtering/test_utils_functions.py b/tests/core/filtering/test_utils_functions.py index a72601e20a..142808fdef 100644 --- a/tests/core/filtering/test_utils_functions.py +++ b/tests/core/filtering/test_utils_functions.py @@ -3,6 +3,10 @@ ) import pytest +from eth_abi.exceptions import ( + ValueOutOfBounds, +) + from web3._utils.filters import ( match_fn, ) @@ -12,69 +16,114 @@ "data,expected,match_data_and_abi", ( ( - (-12345, 000, 111, Decimal(2) + Decimal(1) / Decimal(10)), - False, - ( - ("int", (-12345,)), - ("uint32", (444,)), - ("int", (565,)), - ("ufixed256x4", (Decimal(1.66660),)) + pytest.param( + (-12345, 000, 111, Decimal(2) + Decimal(1) / Decimal(10)), + False, + ( + ("int", (-12345,)), + ("uint32", (444,)), + ("int", (565,)), + ("ufixed256x4", (Decimal(1.66660),)) + ), + id='tuple with incorrect numerical return values' + ) + ), + ( + pytest.param( + (-12345, 000, 111, Decimal(2) + Decimal(1) / Decimal(10)), + True, + ( + ("int", (-12345,)), + ("uint32", None), + ("int", None), + ("ufixed256x4", None) + ), + id='tuple with correct numerical return values' ) ), ( - (-12345, 000, 111, Decimal(2) + Decimal(1) / Decimal(10)), - True, - ( - ("int", (-12345,)), - ("uint32", None), - ("int", None), - ("ufixed256x4", None) + pytest.param( + ("aye", "bee", "sea", b"\xde\xee"), + False, + ( + ("string", ("eee",)), + ("string", ("aye",)), + ("string", ("sea",)), + ("bytes", (b"\x00",)) + ), + id='tuple with incorrect string and bytes return values' ) ), ( - ("aye", "bee", "sea", b"\xde\xee"), - False, - ( - ("string", ("eee",)), - ("string", ("aye",)), - ("string", ("sea",)), - ("bytes", (b"\x00",)) + pytest.param( + ("aye", "bee", "sea", "0x1234"), + True, + ( + ("string", ("aye",)), + ("string", ("bee",)), + ("string", ("sea",)), + ("bytes", (b"\x124",)) + ), + id='tuple with valid string and hex values' ) ), ( - ("aye", "bee", "sea", b"\xde\xee"), - True, - ( - ("string", ("aye",)), - ("string", ("bee",)), - ("string", ("sea",)), - ("bytes", (b"\xde\xee",)) + pytest.param( + ("aye", "bee", "sea", b"\xde\xee"), + True, + ( + ("string", ("aye",)), + ("string", ("bee",)), + ("string", ("sea",)), + ("bytes", (b"\xde\xee",)) + ), + id="tuple with valid string and bytes values" ) ), ( - ("aye", "bee", "sea", b"\xde\xee"), - True, - ( - ("string", None), - ("string", None), - ("string", None), - ("bytes", None) + pytest.param( + ("aye", "bee", "sea", b"\xde\xee"), + True, + ( + ("string", None), + ("string", None), + ("string", None), + ("bytes", None) + ), + id="tuple with valid string and bytes values" ) ), ( - (("aye", "bee"), ("sea", "dee")), - True, - ( - ("string[]", (("aye", "bee"),)), - ("string[]", (("sea", "dee"),)), + pytest.param( + ((b"aye", b"bee"), ("sea", "dee")), + True, + ( + ("bytes3[]", ((b"aye", b"bee"),)), + ("string[]", (("sea", "dee"),)), + ), + id="lists with valud string and bytes values" ) ), ( - (["eee", "eff"], ["gee", "eich"]), - False, - ( - ("string[]", (("aye", "bee"),)), - ("string[]", (("sea", "dee"),)), + pytest.param( + (("aye", "bee"), ("sea", "dee")), + True, + ( + ("string[]", (("aye", "bee"),)), + ("string[]", (("sea", "dee"),)), + ), + id="lists with valid string values" + ) + ), + ( + pytest.param( + (["eee", "eff"], ["gee", "eich"]), + False, + ( + ("string[]", (("aye", "bee"),)), + ("string[]", (("sea", "dee"),)), + ), + id="lists with valid string values, incorrect return values" ) ), ) @@ -85,6 +134,96 @@ def test_match_fn_with_various_data_types(web3, data, expected, match_data_and_a assert match_fn(web3, match_data_and_abi, encoded_data) == expected +@pytest.mark.parametrize( + "data,expected,match_data_and_abi", + ( + ( + pytest.param( + ((b"1234",)), + True, + ( + ("bytes4", (b"1234",)), + ), + id="tuple with valid bytes value" + ) + ), + ( + pytest.param( + (("0x12343434",)), + True, + ( + ("bytes4", (b"\x12444",)), + ), + id="tuple with valid hex string" + ) + ), + ( + pytest.param( + ((b"1234",)), + False, + ( + ("bytes4", (b"5678",)), + ), + id="tuple with invalid return value" + ) + ), + ( + pytest.param( + (("0x1212", b"34"),), + True, + ( + ("bytes2[]", ((b"\x12\x12", b"34"),)), + ), + id="list with valid hexstring and byte values" + ) + ), + ( + pytest.param( + (("0x1212", b"34"),), + False, + ( + ("bytes2[]", ((b"12", b"34"),)), + ), + id="list with incorrect hexstring and byte return values" + ) + ), + ( + pytest.param( + (["aye", "bee"], ["sea", "dee"]), + True, + ( + ("string[]", (("aye", "bee"),)), + ("string[]", (("sea", "dee"),)), + ), + id="list with valid string values" + ) + ), + ) +) +def test_match_fn_with_various_data_types_strict(w3_strict_abi, + data, + expected, + match_data_and_abi): + abi_types, match_data = zip(*match_data_and_abi) + encoded_data = w3_strict_abi.codec.encode_abi(abi_types, data) + assert match_fn(w3_strict_abi, match_data_and_abi, encoded_data) == expected + + +@pytest.mark.parametrize( + "data,abi_type", + ( + pytest.param((b"124",), ("bytes4",), id="tuple with invalid bytes input"), + pytest.param(("0x123434",), ("bytes4",), id="tuple with hex input - too small"), + pytest.param(("0x1234343232",), ("bytes4",), id="tuple with hex input - too big"), + ) +) +def test_encode_abi_with_wrong_types_strict(w3_strict_abi, + data, + abi_type): + with pytest.raises(ValueOutOfBounds): + w3_strict_abi.codec.encode_abi(abi_type, data) + + def test_wrong_type_match_data(web3): data = ("hello", "goodbye") match_data_and_abi = ( diff --git a/tests/core/shh-module/test_shh_post.py b/tests/core/shh-module/test_shh_post.py index 44ad769bc1..ff69eb0c63 100644 --- a/tests/core/shh-module/test_shh_post.py +++ b/tests/core/shh-module/test_shh_post.py @@ -1,24 +1,10 @@ -# def test_shh_post(web3, skip_if_testrpc): -# skip_if_testrpc(web3) -# assert 0 -# receiver_pub = web3.shh.getPublicKey(web3.shh.newKeyPair()) -# try: -# response = web3.shh.post({ -# "topic": "0x12345678", -# "powTarget": 2.5, -# "powTime": 2, -# "payload": web3.toHex(text="testing shh on web3.py"), -# "pubKey": receiver_pub, -# }) -# except ValueError: -# # with pytest.raises(ValueError): -# response = web3.shh.post({ -# "topic": "0x12345678", -# "powTarget": 2.5, -# "powTime": 2, -# "payload": web3.toHex(text="testing shh on web3.py"), -# "pubKey": receiver_pub, -# }) -# assert False -# else: -# assert response is False +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/utilities/test_abi_is_encodable.py b/tests/core/utilities/test_abi_is_encodable.py index 3a78389b00..958c8e631a 100644 --- a/tests/core/utilities/test_abi_is_encodable.py +++ b/tests/core/utilities/test_abi_is_encodable.py @@ -1,10 +1,5 @@ import pytest -from web3 import ( - EthereumTesterProvider, - Web3, -) - @pytest.mark.parametrize( 'value,_type,expected', @@ -42,7 +37,6 @@ # Special bytes behavior ('0x12', 'bytes2', True), # with or without 0x 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 @@ -70,28 +64,26 @@ ((b'\x80', 0), '(string,int128)', False), ), ) -def test_is_encodable(value, _type, expected): - w3 = Web3(EthereumTesterProvider()) - actual = w3.is_encodable(_type, value) +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), - ('0123', 'bytes2', True), - - ('12', 'bytes', True), + ('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(value, _type, expected): - w3 = Web3(EthereumTesterProvider()) +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 = w3.is_encodable(_type, value) + actual = web3.is_encodable(_type, value) assert actual is expected @@ -108,11 +100,11 @@ def test_is_encodable_warnings(value, _type, expected): ('0x1', 'bytes2', False), # no odd length # Special bytes behavior - ('12', 'bytes', False), # has to have 0x if string + ('12', 'bytes', False), # no hex strings without leading 0x ('0x12', 'bytes', True), - ('1', 'bytes', False), - ('0x1', 'bytes', False), - ('0x0x0x0x', 'bytes', False), + ('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), @@ -120,9 +112,6 @@ def test_is_encodable_warnings(value, _type, expected): (b'\x80', 'string', False), # bytes that cannot be decoded with utf-8 are invalid ), ) -def test_is_encodable_strict(value, _type, expected): - w3 = Web3(EthereumTesterProvider()) - w3.enable_strict_bytes_type_checking() - - actual = w3.is_encodable(_type, value) +def test_is_encodable_strict(w3_strict_abi, value, _type, expected): + actual = w3_strict_abi.is_encodable(_type, value) assert actual is expected diff --git a/tests/core/utilities/test_construct_event_data_set.py b/tests/core/utilities/test_construct_event_data_set.py index f6f341d453..6cbc7d73fd 100644 --- a/tests/core/utilities/test_construct_event_data_set.py +++ b/tests/core/utilities/test_construct_event_data_set.py @@ -41,30 +41,25 @@ def hex_and_pad(i): @pytest.mark.parametrize( - 'event_abi,arguments,expected', + '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], @@ -72,7 +67,6 @@ def hex_and_pad(i): ], ), ( - EVENT_1_ABI, {'arg0': [1, 3], 'arg3': [2, 4]}, [ [hex_and_pad(1), hex_and_pad(2), None], @@ -83,36 +77,31 @@ def hex_and_pad(i): ), ) ) -def test_construct_event_data_set(web3, event_abi, arguments, expected): - actual = construct_event_data_set(event_abi, web3.codec, arguments) +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( - 'event_abi,arguments,expected', + '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], @@ -120,7 +109,6 @@ def test_construct_event_data_set(web3, event_abi, arguments, expected): ], ), ( - EVENT_1_ABI, {'arg0': [1, 3], 'arg3': [2, 4]}, [ [hex_and_pad(1), hex_and_pad(2), None], @@ -131,39 +119,34 @@ def test_construct_event_data_set(web3, event_abi, arguments, expected): ), ) ) -def test_construct_event_data_set_strict(web3_strict_types, event_abi, arguments, expected): - actual = construct_event_data_set(event_abi, web3_strict_types.codec, arguments) +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( - 'event_abi,arguments,expected_error', + 'arguments,expected_error', ( ( - EVENT_2_ABI, {'arg0': '131414'}, EncodingTypeError, ), ( - EVENT_2_ABI, {'arg0': b'131414'}, ValueOutOfBounds, ), ( - EVENT_2_ABI, {'arg0': b'13'}, ValueOutOfBounds, ), ( - EVENT_2_ABI, {'arg0': b'12'}, ValueOutOfBounds, ), ) ) -def test_construct_event_data_set_strict_with_errors(web3_strict_types, - event_abi, +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_abi, web3_strict_types.codec, arguments) + construct_event_data_set(EVENT_2_ABI, w3_strict_abi.codec, arguments) diff --git a/tests/core/utilities/test_construct_event_topics.py b/tests/core/utilities/test_construct_event_topics.py index 5190904d26..0931ba2cda 100644 --- a/tests/core/utilities/test_construct_event_topics.py +++ b/tests/core/utilities/test_construct_event_topics.py @@ -41,46 +41,39 @@ def hex_and_pad(i): @pytest.mark.parametrize( - 'event_abi,arguments,expected', + '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, @@ -90,52 +83,45 @@ def hex_and_pad(i): ), ) ) -def test_construct_event_topics(web3, event_abi, arguments, expected): - actual = construct_event_topic_set(event_abi, web3.codec, arguments) +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( - 'event_abi,arguments,expected', + '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, @@ -145,44 +131,34 @@ def test_construct_event_topics(web3, event_abi, arguments, expected): ), ) ) -def test_construct_event_topics_strict(web3_strict_types, event_abi, arguments, expected): - actual = construct_event_topic_set(event_abi, web3_strict_types.codec, arguments) +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( - 'event_abi,arguments,expected,error', + 'arguments,error', ( ( - EVENT_2_ABI, {'arg0': [b'123412']}, - [EVENT_2_TOPIC], ValueOutOfBounds, ), ( - EVENT_2_ABI, {'arg1': [b'']}, - [EVENT_2_TOPIC], ValueOutOfBounds, ), ( - EVENT_2_ABI, {'arg0': [b''], 'arg1': [b'']}, - [EVENT_2_TOPIC], ValueOutOfBounds, ), ( - EVENT_2_ABI, {'arg0': ['']}, - [EVENT_2_TOPIC], EncodingTypeError, ), ) ) -def test_construct_event_topics_strict_errors(web3_strict_types, - event_abi, +def test_construct_event_topics_strict_errors(w3_strict_abi, arguments, - expected, error): with pytest.raises(error): - construct_event_topic_set(event_abi, web3_strict_types.codec, arguments) + construct_event_topic_set(EVENT_2_ABI, w3_strict_abi.codec, arguments) diff --git a/tests/core/utilities/test_event_filter_builder.py b/tests/core/utilities/test_event_filter_builder.py index 6222e8f2cd..08ac40339e 100644 --- a/tests/core/utilities/test_event_filter_builder.py +++ b/tests/core/utilities/test_event_filter_builder.py @@ -1,5 +1,8 @@ import pytest +from eth_abi.exceptions import ( + ValueOutOfBounds, +) from hypothesis import ( given, strategies as st, @@ -50,3 +53,31 @@ 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 diff --git a/tests/core/utilities/test_validation.py b/tests/core/utilities/test_validation.py index ab8850a464..0dfe5359ee 100644 --- a/tests/core/utilities/test_validation.py +++ b/tests/core/utilities/test_validation.py @@ -135,8 +135,6 @@ def test_validation(param, validation, expected): ('bytes', "0x5402", None), ('bytes', "5402", TypeError), ('bytes', b'T\x02', None), - ('bytes2', b'T\x02', None), - ('bytes3', b'T\x02', None), ) ) def test_validate_abi_value(abi_type, value, expected): diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index b66586e590..92a181ee51 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -152,16 +152,37 @@ def validate_value(cls, value): super().validate_value(value) -class AcceptsHexStrMixin: - def validate(self): - super().validate() +class AcceptsHexStrEncoder(encoding.BaseEncoder): + def __init__(self, subencoder): + self.subencoder = subencoder + + @property + def is_dynamic(self): + return self.subencoder.is_dynamic + + @classmethod + def from_type_str(cls, abi_type, registry): + subencoder_cls = cls.get_subencoder_class() + subencoder = subencoder_cls.from_type_str(abi_type, registry) + return cls(subencoder) - if self.is_strict is None: - raise ValueError("`is_strict` may not be none") + @classmethod + def get_subencoder_class(cls): + if cls.subencoder_cls is None: + raise AttributeError(f'No subencoder class is set. {cls.__name__}') + return cls.subencoder_cls @combomethod def validate_value(self, value): - original_value = value + normalized_value = self.validate_and_normalize(value) + return self.subencoder.validate_value(normalized_value) + + def encode(self, value): + normalized_value = self.validate_and_normalize(value) + return self.subencoder.encode(normalized_value) + + def validate_and_normalize(self, value): + raw_value = value if is_text(value): try: value = decode_hex(value) @@ -171,33 +192,32 @@ def validate_value(self, value): msg=f'{value} is an invalid hex string', ) else: - self.check_for_0x(original_value) - - super().validate_value(value) - - def check_for_0x(self, original_value): - if original_value[:2] != '0x': - if self.is_strict: - self.invalidate_value( - original_value, - msg='hex string must be prefixed with 0x' - ) - elif original_value[:2] != '0x': - warnings.warn( - 'in v6 it will be invalid to pass a hex string without the "0x" prefix', - category=DeprecationWarning - ) + 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(AcceptsHexStrMixin, encoding.BytesEncoder): +class BytesEncoder(AcceptsHexStrEncoder): + subencoder_cls = encoding.BytesEncoder is_strict = False -class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder): +class ByteStringEncoder(AcceptsHexStrEncoder): + subencoder_cls = encoding.ByteStringEncoder is_strict = False -class StrictByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder): +class StrictByteStringEncoder(AcceptsHexStrEncoder): + subencoder_cls = encoding.ByteStringEncoder is_strict = True @@ -231,26 +251,26 @@ def validate(self): raise ValueError("Value byte size exceeds data size") def encode(self, value): - self.validate_value(value) - return self.encode_fn(value) + normalized_value = self.validate_value(value) + return self.encode_fn(normalized_value) def validate_value(self, value): if not is_bytes(value) and not is_text(value): self.invalidate_value(value) - original_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 an invalid hex string', + msg=f'{value} is not a valid hex string', ) else: - if original_value[:2] != '0x': + if raw_value[:2] != '0x': self.invalidate_value( - original_value, + raw_value, msg='hex string must be prefixed with 0x' ) @@ -267,6 +287,7 @@ def validate_value(self, value): exc=ValueOutOfBounds, msg="less than total byte size for bytes{} encoding".format(byte_size), ) + return value @staticmethod def encode_fn(value): @@ -280,6 +301,21 @@ def from_type_str(cls, abi_type, registry): ) +class BytesDecoder(decoding.FixedByteSizeDecoder): + is_big_endian = False + + @staticmethod + def decoder_fn(data): + return data + + @parse_type_str('bytes') + def from_type_str(cls, abi_type, registry): + return cls( + value_bit_size=abi_type.sub * 8, + data_byte_size=abi_type.sub, + ) + + class TextStringEncoder(encoding.TextStringEncoder): @classmethod def validate_value(cls, value): @@ -840,7 +876,7 @@ def build_strict_registry(): ) registry.register( BaseEquals('bytes', with_sub=True), - ExactLengthBytesEncoder, decoding.BytesDecoder, + ExactLengthBytesEncoder, BytesDecoder, label='bytes', ) registry.register( diff --git a/web3/_utils/filters.py b/web3/_utils/filters.py index ae44202c43..0814a47d11 100644 --- a/web3/_utils/filters.py +++ b/web3/_utils/filters.py @@ -209,6 +209,7 @@ def match_fn(w3, match_values_and_abi, data): 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: