From 0066634b5480895d79b0317228a3f270d4e264bc Mon Sep 17 00:00:00 2001 From: Jason Carver Date: Thu, 16 Nov 2017 12:18:11 -0800 Subject: [PATCH] Look up ENS names in soliditySha3 'address' values --- web3/main.py | 19 ++++++++++++-- web3/utils/module_testing/web3_module.py | 32 +++++++++++++++++++++++- web3/utils/normalizers.py | 7 ++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/web3/main.py b/web3/main.py index ebe3a4384f..21ab3a53a8 100644 --- a/web3/main.py +++ b/web3/main.py @@ -39,9 +39,15 @@ RequestManager, ) +from web3.utils.abi import ( + map_abi_data, +) from web3.utils.datastructures import ( HexBytes, ) +from web3.utils.decorators import ( + combomethod, +) from web3.utils.encoding import ( hex_encode_abi_type, to_bytes, @@ -49,6 +55,9 @@ to_hex, to_text, ) +from web3.utils.normalizers import ( + abi_ens_resolver, +) default = object() @@ -132,7 +141,7 @@ def sha3(primitive=None, text=None, hexstr=None): ) ) - @classmethod + @combomethod def soliditySha3(cls, abi_types, values): """ Executes sha3 (keccak256) exactly as Solidity does. @@ -145,10 +154,16 @@ def soliditySha3(cls, abi_types, values): "{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, values) + in zip(abi_types, normalized_values) )) return cls.sha3(hexstr=hex_string) diff --git a/web3/utils/module_testing/web3_module.py b/web3/utils/module_testing/web3_module.py index 4322a14934..278a39f6d1 100644 --- a/web3/utils/module_testing/web3_module.py +++ b/web3/utils/module_testing/web3_module.py @@ -1,10 +1,13 @@ import pytest +from web3 import Web3 + from web3.exceptions import ( InvalidAddress, ) from web3.utils.datastructures import HexBytes +from web3.utils.ens import ens_addresses class Web3ModuleTest(object): @@ -83,7 +86,6 @@ def _check_web3_clientVersion(self, client_version): ], HexBytes("0xd78a84d65721b67e4011b10c99dafdedcdcd7cb30153064f773e210b4762e22f"), ), - ( ['string'], ['testing a string!'], @@ -168,6 +170,34 @@ def test_soliditySha3(self, web3, types, values, expected): actual = web3.soliditySha3(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_soliditySha3_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.soliditySha3(types, values) + + # when called as instance method method, ens lookups can succeed + actual = web3.soliditySha3(types, values) + assert actual == expected + @pytest.mark.parametrize( 'types,values', ( diff --git a/web3/utils/normalizers.py b/web3/utils/normalizers.py index 303496175e..00cf487e27 100644 --- a/web3/utils/normalizers.py +++ b/web3/utils/normalizers.py @@ -14,6 +14,9 @@ to_checksum_address, ) +from web3.exceptions import ( + InvalidAddress, +) from web3.utils.encoding import ( hexstr_if_str, text_if_str, @@ -108,9 +111,9 @@ def abi_address_to_hex(abi_type, data): def abi_ens_resolver(w3, abi_type, val): if abi_type == 'address' and is_ens_name(val): if w3 is None: - raise ValueError("Could not look up name, because no web3 connection available") + raise InvalidAddress("Could not look up name, because no web3 connection available") elif w3.ens is None: - raise ValueError("Could not look up name, because ENS is set to None") + raise InvalidAddress("Could not look up name, because ENS is set to None") else: return (abi_type, validate_name_has_address(w3.ens, val)) else: