From e3aba38051e3137d1e7e2abc2626fbc14876a477 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Wed, 24 Nov 2021 14:56:41 -0700 Subject: [PATCH] Fix access list storage keys mapping for eth-tester middleware + refactoring --- newsfragments/2224.bugfix.rst | 1 + tests/core/eth-module/test_transactions.py | 51 +++++++ web3/providers/eth_tester/middleware.py | 154 ++++++++++++--------- 3 files changed, 138 insertions(+), 68 deletions(-) create mode 100644 newsfragments/2224.bugfix.rst diff --git a/newsfragments/2224.bugfix.rst b/newsfragments/2224.bugfix.rst new file mode 100644 index 0000000000..a23abee276 --- /dev/null +++ b/newsfragments/2224.bugfix.rst @@ -0,0 +1 @@ +Key mapping fix to eth-tester middleware for access list storage keys \ No newline at end of file diff --git a/tests/core/eth-module/test_transactions.py b/tests/core/eth-module/test_transactions.py index 29bbc8a278..5e8c5c7493 100644 --- a/tests/core/eth-module/test_transactions.py +++ b/tests/core/eth-module/test_transactions.py @@ -16,6 +16,57 @@ RECEIPT_TIMEOUT = 0.2 +@pytest.mark.parametrize( + 'transaction', + ( + { + 'gasPrice': 10 ** 9, + 'accessList': ( + { + 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'storageKeys': ( + '0x0000000000000000000000000000000000000000000000000000000000000003', + '0x0000000000000000000000000000000000000000000000000000000000000007', + ) + }, + { + 'address': '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', + 'storageKeys': () + }, + ), + }, + { + 'maxFeePerGas': 10 ** 9, + 'maxPriorityFeePerGas': 10 ** 9, + 'accessList': ( + { + 'address': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', + 'storageKeys': ( + '0x0000000000000000000000000000000000000000000000000000000000000003', + '0x0000000000000000000000000000000000000000000000000000000000000007', + ) + }, + { + 'address': '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', + 'storageKeys': () + }, + ), + }, + ), + ids=[ + 'storage_keys_access_list_txn', + 'storage_keys_dynamic_fee_txn', + ], +) +def test_eth_tester_send_transaction_validation(web3, transaction): + # Test that eth-tester transaction param validation does not throw for properly formatted + # transactions. This is especially important because we have key mapping differences + # (camelCase to snake_case) mitigated by providers/eth-tester/middleware. + txn_hash = web3.eth.send_transaction(transaction) + receipt = web3.eth.wait_for_transaction_receipt(txn_hash, timeout=RECEIPT_TIMEOUT) + assert receipt.get('blockNumber') is not None + + @pytest.mark.parametrize( 'make_chain_id, expect_success', ( diff --git a/web3/providers/eth_tester/middleware.py b/web3/providers/eth_tester/middleware.py index f72fc212dd..fb865d8aab 100644 --- a/web3/providers/eth_tester/middleware.py +++ b/web3/providers/eth_tester/middleware.py @@ -64,7 +64,58 @@ def is_hexstr(value: Any) -> bool: is_not_named_block = complement(is_named_block) -TRANSACTION_KEY_MAPPINGS = { +# --- Request Mapping --- # + +TRANSACTION_REQUEST_KEY_MAPPING = { + 'gasPrice': 'gas_price', + 'maxFeePerGas': 'max_fee_per_gas', + 'maxPriorityFeePerGas': 'max_priority_fee_per_gas', + 'accessList': 'access_list', +} +transaction_request_remapper = apply_key_map(TRANSACTION_REQUEST_KEY_MAPPING) + + +TRANSACTION_REQUEST_FORMATTERS = { + 'gas': to_integer_if_hex, + 'gasPrice': to_integer_if_hex, + 'value': to_integer_if_hex, + 'nonce': to_integer_if_hex, + 'maxFeePerGas': to_integer_if_hex, + 'maxPriorityFeePerGas': to_integer_if_hex, + 'accessList': apply_formatter_to_array( + apply_key_map({'storageKeys': 'storage_keys'}) + ), +} +transaction_request_formatter = apply_formatters_to_dict(TRANSACTION_REQUEST_FORMATTERS) + +transaction_request_transformer = compose( + transaction_request_remapper, + transaction_request_formatter, +) + + +FILTER_REQUEST_KEY_MAPPING = { + 'fromBlock': 'from_block', + 'toBlock': 'to_block', +} +filter_request_remapper = apply_key_map(FILTER_REQUEST_KEY_MAPPING) + + +FILTER_REQUEST_FORMATTERS = { + 'fromBlock': to_integer_if_hex, + 'toBlock': to_integer_if_hex, +} +filter_request_formatter = apply_formatters_to_dict(FILTER_REQUEST_FORMATTERS) + +filter_request_transformer = compose( + filter_request_remapper, + filter_request_formatter, +) + + +# --- Result Mapping --- # + +TRANSACTION_RESULT_KEY_MAPPING = { 'access_list': 'accessList', 'block_hash': 'blockHash', 'block_number': 'blockNumber', @@ -74,20 +125,29 @@ def is_hexstr(value: Any) -> bool: 'transaction_hash': 'transactionHash', 'transaction_index': 'transactionIndex', } -transaction_key_remapper = apply_key_map(TRANSACTION_KEY_MAPPINGS) +transaction_result_remapper = apply_key_map(TRANSACTION_RESULT_KEY_MAPPING) + + +TRANSACTION_RESULT_FORMATTERS = { + 'to': apply_formatter_if(partial(operator.eq, ''), static_return(None)), + 'access_list': apply_formatter_to_array( + apply_key_map({'storage_keys': 'storageKeys'}), + ), +} +transaction_result_formatter = apply_formatters_to_dict(TRANSACTION_RESULT_FORMATTERS) -LOG_KEY_MAPPINGS = { +LOG_RESULT_KEY_MAPPING = { 'log_index': 'logIndex', 'transaction_index': 'transactionIndex', 'transaction_hash': 'transactionHash', 'block_hash': 'blockHash', 'block_number': 'blockNumber', } -log_key_remapper = apply_key_map(LOG_KEY_MAPPINGS) +log_result_remapper = apply_key_map(LOG_RESULT_KEY_MAPPING) -RECEIPT_KEY_MAPPINGS = { +RECEIPT_RESULT_KEY_MAPPING = { 'block_hash': 'blockHash', 'block_number': 'blockNumber', 'contract_address': 'contractAddress', @@ -97,10 +157,10 @@ def is_hexstr(value: Any) -> bool: 'transaction_hash': 'transactionHash', 'transaction_index': 'transactionIndex', } -receipt_key_remapper = apply_key_map(RECEIPT_KEY_MAPPINGS) +receipt_result_remapper = apply_key_map(RECEIPT_RESULT_KEY_MAPPING) -BLOCK_KEY_MAPPINGS = { +BLOCK_RESULT_KEY_MAPPING = { 'gas_limit': 'gasLimit', 'sha3_uncles': 'sha3Uncles', 'transactions_root': 'transactionsRoot', @@ -113,55 +173,13 @@ def is_hexstr(value: Any) -> bool: 'gas_used': 'gasUsed', 'base_fee_per_gas': 'baseFeePerGas', } -block_key_remapper = apply_key_map(BLOCK_KEY_MAPPINGS) - - -TRANSACTION_PARAMS_MAPPING = { - 'gasPrice': 'gas_price', - 'maxFeePerGas': 'max_fee_per_gas', - 'maxPriorityFeePerGas': 'max_priority_fee_per_gas', - 'accessList': 'access_list', -} -transaction_params_remapper = apply_key_map(TRANSACTION_PARAMS_MAPPING) - - -REQUEST_TRANSACTION_FORMATTERS = { - 'gas': to_integer_if_hex, - 'gasPrice': to_integer_if_hex, - 'value': to_integer_if_hex, - 'nonce': to_integer_if_hex, - 'maxFeePerGas': to_integer_if_hex, - 'maxPriorityFeePerGas': to_integer_if_hex, -} -request_transaction_formatter = apply_formatters_to_dict(REQUEST_TRANSACTION_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) - - -RESPONSE_TRANSACTION_FORMATTERS = { - 'to': apply_formatter_if(partial(operator.eq, ''), static_return(None)), -} -response_transaction_formatter = apply_formatters_to_dict(RESPONSE_TRANSACTION_FORMATTERS) +block_result_remapper = apply_key_map(BLOCK_RESULT_KEY_MAPPING) -RECEIPT_FORMATTERS = { - 'logs': apply_formatter_to_array(log_key_remapper), +RECEIPT_RESULT_FORMATTERS = { + 'logs': apply_formatter_to_array(log_result_remapper), } -receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS) -transaction_params_transformer = compose(transaction_params_remapper, request_transaction_formatter) +receipt_result_formatter = apply_formatters_to_dict(RECEIPT_RESULT_FORMATTERS) ethereum_tester_middleware = construct_formatting_middleware( @@ -191,19 +209,19 @@ def is_hexstr(value: Any) -> bool: to_integer_if_hex, ), RPCEndpoint('eth_newFilter'): apply_formatters_to_args( - filter_params_transformer, + filter_request_transformer, ), RPCEndpoint('eth_getLogs'): apply_formatters_to_args( - filter_params_transformer, + filter_request_transformer, ), RPCEndpoint('eth_sendTransaction'): apply_formatters_to_args( - transaction_params_transformer, + transaction_request_transformer, ), RPCEndpoint('eth_estimateGas'): apply_formatters_to_args( - transaction_params_transformer, + transaction_request_transformer, ), RPCEndpoint('eth_call'): apply_formatters_to_args( - transaction_params_transformer, + transaction_request_transformer, apply_formatter_if(is_not_named_block, to_integer_if_hex), ), RPCEndpoint('eth_uninstallFilter'): apply_formatters_to_args(hex_to_integer), @@ -219,49 +237,49 @@ def is_hexstr(value: Any) -> bool: RPCEndpoint('evm_revert'): apply_formatters_to_args(hex_to_integer), # Personal RPCEndpoint('personal_sendTransaction'): apply_formatters_to_args( - transaction_params_transformer, + transaction_request_transformer, identity, ), }, result_formatters={ RPCEndpoint('eth_getBlockByHash'): apply_formatter_if( is_dict, - block_key_remapper, + block_result_remapper, ), RPCEndpoint('eth_getBlockByNumber'): apply_formatter_if( is_dict, - block_key_remapper, + block_result_remapper, ), RPCEndpoint('eth_getBlockTransactionCountByHash'): apply_formatter_if( is_dict, - transaction_key_remapper, + transaction_result_remapper, ), RPCEndpoint('eth_getBlockTransactionCountByNumber'): apply_formatter_if( is_dict, - transaction_key_remapper, + transaction_result_remapper, ), RPCEndpoint('eth_getTransactionByHash'): apply_formatter_if( is_dict, - compose(transaction_key_remapper, response_transaction_formatter), + compose(transaction_result_remapper, transaction_result_formatter), ), RPCEndpoint('eth_getTransactionReceipt'): apply_formatter_if( is_dict, - compose(receipt_key_remapper, receipt_formatter), + compose(receipt_result_remapper, receipt_result_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( is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), + apply_formatter_to_array(log_result_remapper), ), RPCEndpoint('eth_getFilterChanges'): apply_formatter_if( is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), + apply_formatter_to_array(log_result_remapper), ), RPCEndpoint('eth_getFilterLogs'): apply_formatter_if( is_array_of_dicts, - apply_formatter_to_array(log_key_remapper), + apply_formatter_to_array(log_result_remapper), ), # EVM RPCEndpoint('evm_snapshot'): integer_to_hex,