Skip to content

Commit 7355d85

Browse files
committed
catch mismatched abi data and test for namedtuple rename
1 parent 619f6db commit 7355d85

File tree

2 files changed

+68
-45
lines changed

2 files changed

+68
-45
lines changed

tests/core/utilities/test_abi_named_tree.py

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
13
from eth_abi.codec import (
24
ABICodec,
35
)
@@ -11,70 +13,84 @@
1113
foldable_namedtuple,
1214
named_tree,
1315
)
16+
from web3.exceptions import MismatchedABI
1417

1518
from .test_abi import (
1619
TEST_FUNCTION_ABI,
1720
)
1821

19-
abi = TEST_FUNCTION_ABI["inputs"]
20-
inputs = (
22+
full_abi_inputs = TEST_FUNCTION_ABI["inputs"]
23+
full_values = (
2124
(1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), # Value for s
2225
(11, 12), # Value for t
2326
13, # Value for a
2427
[[(14, 15), (16, 17)], [(18, 19)]], # Value for b
2528
)
2629

27-
sample_abi_inputs = {
28-
"inputs": [
29-
{
30-
"components": [
31-
{"name": "a", "type": "uint256"},
32-
{"name": "b", "type": "uint256[]"},
33-
{
34-
"components": [
35-
{"name": "x", "type": "uint256"},
36-
{"name": "y", "type": "uint256"},
37-
],
38-
"name": "c",
39-
"type": "tuple[]",
40-
},
41-
],
42-
"name": "s",
43-
"type": "tuple",
44-
},
45-
{
46-
"components": [
47-
{"name": "x", "type": "uint256"},
48-
{"name": "y", "type": "uint256"},
49-
],
50-
"name": "t",
51-
"type": "tuple",
52-
},
53-
{"name": "a", "type": "uint256"},
54-
{
55-
"components": [
56-
{"name": "x", "type": "uint256"},
57-
{"name": "y", "type": "uint256"},
58-
],
59-
"name": "b",
60-
"type": "tuple[][]",
61-
},
62-
]
63-
}
6430

6531
def test_named_arguments_decode():
66-
decoded = named_tree(abi, inputs)
32+
decoded = named_tree(full_abi_inputs, full_values)
6733
data = dict_to_namedtuple(decoded)
68-
assert data == inputs
34+
assert data == full_values
6935
assert data.s.c[2].y == 10
7036
assert data.t.x == 11
7137
assert data.a == 13
7238

7339

40+
short_abi_inputs_with_disallowed_names = [
41+
{
42+
"components": [
43+
{"name": "from", "type": "uint256"},
44+
{"name": "to", "type": "uint256[]"},
45+
{
46+
"components": [
47+
{"name": "_x", "type": "uint256"},
48+
{"name": "_y", "type": "uint256"},
49+
],
50+
"name": "c",
51+
"type": "tuple[]",
52+
},
53+
],
54+
"name": "s",
55+
"type": "tuple",
56+
},
57+
]
58+
59+
short_values = ((1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]),)
60+
61+
62+
def test_named_arguments_decode_rename():
63+
decoded = named_tree(short_abi_inputs_with_disallowed_names, short_values)
64+
data = dict_to_namedtuple(decoded)
65+
assert data == short_values
66+
assert data._fields == ("s",)
67+
68+
# python keyword "from" renamed to "_0"
69+
assert data.s._fields == ("_0", "to", "c")
70+
71+
# field names starting with "_" - "_x" and "_y" - renamed to "_0" and "_1"
72+
assert data.s.c[0]._fields == ("_0", "_1")
73+
assert data.s.c[2]._1 == 10
74+
assert data.s.to[1] == 3
75+
76+
77+
@pytest.mark.parametrize(
78+
"values",
79+
(
80+
((1, [2, 3, 4], [(5,), (7, 8), (9, 10)]),),
81+
((1, [2, 3, 4], [(5, 6, 11), (7, 8), (9, 10)]),),
82+
((1, [(5, 6), (7, 8), (9, 10)]),),
83+
),
84+
)
85+
def test_named_arguments_decode_with_misshapen_inputs(values):
86+
with pytest.raises(MismatchedABI):
87+
named_tree(short_abi_inputs_with_disallowed_names, values)
88+
89+
7490
def test_namedtuples_encodable():
7591
registry = default_registry.copy()
7692
codec = ABICodec(registry)
77-
kwargs = named_tree(abi, inputs)
93+
kwargs = named_tree(full_abi_inputs, full_values)
7894
args = dict_to_namedtuple(kwargs)
7995
assert check_if_arguments_can_be_encoded(TEST_FUNCTION_ABI, codec, (), kwargs)
8096
assert check_if_arguments_can_be_encoded(TEST_FUNCTION_ABI, codec, args, {})

web3/_utils/abi.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
)
7575
from web3.exceptions import (
7676
FallbackNotFound,
77+
MismatchedABI,
7778
)
7879
from web3.types import (
7980
ABI,
@@ -920,6 +921,7 @@ def named_tree(
920921

921922
# TODO how to handle if names and items end up different len
922923
# return dict(zip(names, items)) if all(names) else items
924+
923925
return dict(zip(names, items))
924926

925927

@@ -933,15 +935,20 @@ def named_subtree(
933935
if abi_type.is_array:
934936
item_type = abi_type.item_type.to_type_str()
935937
item_abi = {**abi, "type": item_type, "name": ""}
936-
# cast(ABIFunctionParams, item_abi)
937-
# breakpoint()
938938
items = [named_subtree(item_abi, item) for item in data]
939939
return items
940940

941941
if isinstance(abi_type, TupleType):
942942
names = [item["name"] for item in abi["components"]]
943943
items = [named_subtree(*item) for item in zip(abi["components"], data)]
944-
return dict(zip(names, items))
944+
945+
if len(names) == len(data):
946+
return dict(zip(names, items))
947+
else:
948+
raise MismatchedABI(
949+
f"ABI fields {names} has length {len(names)} but received "
950+
f"data {data} with length {len(data)}"
951+
)
945952

946953
return data
947954

0 commit comments

Comments
 (0)