Skip to content

Commit f61dd4a

Browse files
committed
Add type validation to struct value setters/getters
1 parent f6657fb commit f61dd4a

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

eip712_structs/struct.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,40 @@ def from_message(cls, message_dict: dict) -> 'StructTuple':
251251

252252
return result
253253

254+
@classmethod
255+
def _assert_key_is_member(cls, key):
256+
member_names = {tup[0] for tup in cls.get_members()}
257+
if key not in member_names:
258+
raise KeyError(f'"{key}" is not defined for this struct.')
259+
260+
@classmethod
261+
def _assert_property_type(cls, key, value):
262+
"""Eagerly check for a correct member type"""
263+
members = dict(cls.get_members())
264+
typ = members[key]
265+
266+
if isinstance(typ, type) and issubclass(typ, EIP712Struct):
267+
# We expect an EIP712Struct instance. Assert that's true, and check the struct signature too.
268+
if not isinstance(value, EIP712Struct) or value._encode_type(False) != typ._encode_type(False):
269+
raise ValueError(f'Given value is of type {type(value)}, but we expected {typ}')
270+
else:
271+
# Since it isn't a nested struct, its an EIP712Type
272+
try:
273+
typ.encode_value(value)
274+
except Exception as e:
275+
raise ValueError(f'The python type {type(value)} does not appear '
276+
f'to be supported for data type {typ}.') from e
277+
254278
def __getitem__(self, key):
255279
"""Provide access directly to the underlying value dictionary"""
280+
self._assert_key_is_member(key)
256281
return self.values.__getitem__(key)
257282

258283
def __setitem__(self, key, value):
259284
"""Provide access directly to the underlying value dictionary"""
285+
self._assert_key_is_member(key)
286+
self._assert_property_type(key, value)
287+
260288
return self.values.__setitem__(key, value)
261289

262290
def __delete__(self, instance):

tests/test_encode_data.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,38 @@ class Foo(EIP712Struct):
205205
foo['b'] = test_bytes_2
206206

207207
assert foo['b'] == test_bytes_2
208+
209+
with pytest.raises(KeyError):
210+
foo['x'] = 'unacceptable'
211+
212+
# Check behavior when accessing a member that wasn't defined for the struct.
213+
with pytest.raises(KeyError):
214+
foo['x']
215+
# Lets cheat a lil bit for robustness- add an invalid 'x' member to the value dict, and check the error still raises
216+
foo.values['x'] = 'test'
217+
with pytest.raises(KeyError):
218+
foo['x']
219+
foo.values.pop('x')
220+
221+
with pytest.raises(ValueError):
222+
foo['s'] = b'unacceptable'
223+
with pytest.raises(ValueError):
224+
# Bytes do accept strings, but it has to be hex formatted.
225+
foo['b'] = 'unacceptable'
226+
227+
# Test behavior when attempting to set nested structs as values
228+
class Bar(EIP712Struct):
229+
s = String()
230+
f = Foo
231+
232+
class Baz(EIP712Struct):
233+
s = String()
234+
baz = Baz(s=test_str)
235+
236+
bar = Bar(s=test_str)
237+
bar['f'] = foo
238+
assert bar['f'] == foo
239+
240+
with pytest.raises(ValueError):
241+
# Expects a Foo type, so should throw an error
242+
bar['f'] = baz

0 commit comments

Comments
 (0)