Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EIP712 support for bytes and uint formatted as string #427

Merged
merged 2 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions gnosis/eth/eip712/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ def _encode_field(name, typ, value):
if value is None:
raise Exception(f"Missing value for field {name} of type {type}")

# Accept string bytes
if "bytes" in typ and isinstance(value, str):
value = bytes.fromhex(value.replace("0x", ""))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value = HexBytes(value)


# Accept string uint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should support any kind of integer, not only uint. This is a very misleading behaviour:

if "int" in typ

if "uint" in typ and isinstance(value, str):
value = int(value)

if typ == "bytes":
return ["bytes32", fast_keccak(value)]

Expand Down
127 changes: 86 additions & 41 deletions gnosis/eth/tests/eip712/test_eip712.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@


class TestEIP712(TestCase):
address = "0x8e12f01dae5fe7f1122dc42f2cb084f2f9e8aa03"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use checksummed addresses unless we are testing not checksummed addresses:

0x8e12f01DAE5FE7f1122Dc42f2cB084F2f9E8aA03

types = {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"},
],
"Mailbox": [
{"name": "owner", "type": "address"},
{"name": "messages", "type": "Message[]"},
],
"Message": [
{"name": "sender", "type": "address"},
{"name": "subject", "type": "string"},
{"name": "isSpam", "type": "bool"},
{"name": "body", "type": "string"},
],
}

msgs = [
{
"sender": address,
"subject": "Hello World",
"body": "The sparrow flies at midnight.",
"isSpam": False,
},
{
"sender": address,
"subject": "You may have already Won! :dumb-emoji:",
"body": "Click here for sweepstakes!",
"isSpam": True,
},
]

mailbox = {"owner": address, "messages": msgs}

def test_eip712_encode_hash(self):
for value in [
{},
Expand All @@ -12,8 +49,6 @@ def test_eip712_encode_hash(self):
with self.assertRaises(ValueError):
eip712_encode_hash(value)

address = "0x8e12f01dae5fe7f1122dc42f2cb084f2f9e8aa03"

wrong_types = {
"EIP712Domain": [
{"name": "name", "type": "stringa"},
Expand All @@ -33,59 +68,69 @@ def test_eip712_encode_hash(self):
],
}

types = {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"},
],
"Mailbox": [
{"name": "owner", "type": "address"},
{"name": "messages", "type": "Message[]"},
],
"Message": [
{"name": "sender", "type": "address"},
{"name": "subject", "type": "string"},
{"name": "isSpam", "type": "bool"},
{"name": "body", "type": "string"},
],
}

msgs = [
{
"sender": address,
"subject": "Hello World",
"body": "The sparrow flies at midnight.",
"isSpam": False,
},
{
"sender": address,
"subject": "You may have already Won! :dumb-emoji:",
"body": "Click here for sweepstakes!",
"isSpam": True,
},
]

mailbox = {"owner": address, "messages": msgs}

payload = {
"types": wrong_types,
"primaryType": "Mailbox",
"domain": {
"name": "MyDApp",
"version": "3.0",
"chainId": 41,
"verifyingContract": address,
"verifyingContract": self.address,
},
"message": mailbox,
"message": self.mailbox,
}

with self.assertRaises(ValueError):
eip712_encode_hash(payload)

payload["types"] = types
payload["types"] = self.types
self.assertEqual(
eip712_encode_hash(payload),
b"\xd5N\xcbf7\xfa\x99\n\xae\x02\x86\xd4 \xacpe\x8d\xb9\x95\xaem\t\xcc\x9b\xb1\xda\xcf6J\x14\x17\xd0",
)

def test_eip712_encode_hash_string_uint(self):
# test string uint (chainId)
payload = {
"types": self.types,
"primaryType": "Mailbox",
"domain": {
"name": "MyDApp",
"version": "3.0",
"chainId": "41",
"verifyingContract": self.address,
},
"message": self.mailbox,
}

self.assertEqual(
eip712_encode_hash(payload),
b"\xd5N\xcbf7\xfa\x99\n\xae\x02\x86\xd4 \xacpe\x8d\xb9\x95\xaem\t\xcc\x9b\xb1\xda\xcf6J\x14\x17\xd0",
)

def test_eip712_encode_hash_string_bytes(self):
types_with_bytes = {
"EIP712Domain": [
{"name": "name", "type": "string"},
],
"Message": [
{"name": "oneByte", "type": "bytes1"},
{"name": "maxByte", "type": "bytes32"},
],
}
payload = {
"types": types_with_bytes,
"primaryType": "Message",
"domain": {
"name": "MyDApp",
},
"message": {
"oneByte": "0x01",
"maxByte": "0x6214da6089b8d8aaa6e6268977746aa0af19fd1ef5d56e225bb3390a697c3ec1",
},
}

self.assertEqual(
eip712_encode_hash(payload).hex(),
"2950cf06416c6c20059f24a965e3baf51a24f4ef49a1e7b1a47ee13ee08cde1f",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use hex() here? I think we should be consistent, either using hex() everywhere (it looks better and more readable IMHO) or using binary representation

Copy link
Member Author

@moisses89 moisses89 Jan 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@moisses89 I think these things should be addressed in a new PR

Your suggestions sounds good for me, then I add the following PR #437
Regarding if use hex() or binary representation for test, I'd prefer hex() because as you mention is more readable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thanks

)