commit 0c71201e7274e9dd2ccec2ee782afb8f37669ed2
parent 4fdfc132b11a4656f138e0d5d51f692a3d85ce48
Author: lash <dev@holbrook.no>
Date: Tue, 28 Mar 2023 17:00:27 +0100
WIP add verify example against contract, calldata fails
Diffstat:
16 files changed, 362 insertions(+), 223 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+__pycache__
+*.pyc
+build/
+dist/
diff --git a/python/eth_erc712/__init__.py b/python/eth_erc712/__init__.py
@@ -0,0 +1 @@
+from .base import ERC712Encoder, EIP712Domain, EIP712DomainEncoder
diff --git a/python/eth_erc712/base.py b/python/eth_erc712/base.py
@@ -0,0 +1,135 @@
+# standard imports
+import logging
+import sha3
+
+# external imports
+from chainlib.eth.contract import ABIContractType
+from chainlib.eth.contract import ABIContractEncoder
+from chainlib.hash import keccak256
+from chainlib.hash import keccak256_string_to_hex
+
+logg = logging.getLogger(__name__)
+
+
+class ERC712Encoder(ABIContractEncoder):
+
+ def __init__(self, struct_name):
+ super(ERC712Encoder, self).__init__()
+ self.method(struct_name)
+ self.encode = self.get_contents
+
+
+ def add(self, k, t, v):
+ typ_checked = ABIContractType(t.value)
+ self.typ_literal(t.value + ' ' + k)
+ m = getattr(self, t.value)
+ m(v)
+
+
+ def string(self, s):
+ v = keccak256_string_to_hex(s)
+ self.contents.append(v)
+ self.add_type(ABIContractType.STRING)
+ self.__log_latest_erc712(s)
+
+
+ def bytes(self, s):
+ v = keccak256_string_to_hex(s)
+ self.contents.append(v)
+ self.add_type(ABIContractType.BYTES)
+ self.__log_latest_erc712(s)
+
+
+ def __log_latest_erc712(self, v):
+ l = len(self.types) - 1
+ logg.debug('Encoder added {} -> {} ({})'.format(v, self.contents[l], self.types[l].value))
+
+
+ def encode_type(self):
+ v = self.get_method()
+ r = keccak256(v)
+ logg.debug('typehash material {} -> {}'.format(v, r.hex()))
+ return r
+
+
+ def encode_data(self):
+ return b''.join(list(map(lambda x: bytes.fromhex(x), self.contents)))
+
+
+ def get_contents(self):
+ return self.encode_type() + self.encode_data()
+
+
+class EIP712Domain(ERC712Encoder):
+
+ def __init__(self, name=None, version=None, chain_id=None, verifying_contract=None, salt=None):
+ super(EIP712Domain, self).__init__('EIP712Domain')
+ if name != None:
+ self.add('name', ABIContractType.STRING, name)
+ if version != None:
+ self.add('version', ABIContractType.STRING, version)
+ if chain_id != None:
+ self.add('chainId', ABIContractType.UINT256, chain_id)
+ if verifying_contract != None:
+ self.add('verifyingContract', ABIContractType.ADDRESS, verifying_contract)
+ if salt != None:
+ self.add('salt', ABIContractType.BYTES32, salt)
+
+
+ def get_contents(self):
+ v = self.encode_type() + self.encode_data()
+ r = keccak256(v)
+ logg.debug('domainseparator material {} -> {}'.format(v.hex(), r.hex()))
+ return r
+
+
+class EIP712DomainEncoder(ERC712Encoder):
+
+ def __init__(self, struct_name, domain):
+ assert domain.__class__.__name__ == 'EIP712Domain'
+ self.domain = domain
+ self.__cache_data = b''
+ super(EIP712DomainEncoder, self).__init__(struct_name)
+
+
+ def __cache(self):
+ if not self.dirty:
+ return
+ domain = self.domain.get_contents()
+ contents = super(EIP712DomainEncoder, self).get_contents()
+ self.__cache_data = domain + contents
+
+
+ def get_contents(self):
+ self.__cache()
+ return self.__cache_data
+
+
+ def get_domain(self):
+ self.__cache()
+ return self.__cache_data[:32]
+
+
+ def get_data_hash(self):
+ self.__cache()
+ return self.__cache_data[32:64]
+
+
+ def get_typed_data(self):
+ self.__cache()
+ return self.__cache_data[64:]
+
+
+ def __str__(self):
+ self.__cache()
+ domain = self.get_domain()
+ data_hash = self.get_data_hash()
+ data = self.get_typed_data()
+ s = 'erc712domain\t{}\nerc712type\t{}\nerc712data\n'.format(
+ domain.hex(),
+ data_hash.hex(),
+ )
+ for i in range(0, len(data), 32):
+ s += '\t' + data[i:i+32].hex() + '\n'
+
+ return s
diff --git a/python/eth_erc712/data/ERC712Example.bin b/python/eth_erc712/data/ERC712Example.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b50620000d46040518060a001604052806040518060400160405280600a81526020017f4574686572204d61696c0000000000000000000000000000000000000000000081525081526020016040518060400160405280600181526020017f31000000000000000000000000000000000000000000000000000000000000008152508152602001602a81526020013073ffffffffffffffffffffffffffffffffffffffff1681526020014340815250620000e0640100000000026401000000009004565b60018190555062000230565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f826000015180519060200120836020015180519060200120846040015185606001516040516020016200013b959493929190620001d3565b604051602081830303815290604052805190602001209050919050565b6000819050919050565b6200016d8162000158565b82525050565b6000819050919050565b620001888162000173565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620001bb826200018e565b9050919050565b620001cd81620001ae565b82525050565b600060a082019050620001ea600083018862000162565b620001f9602083018762000162565b62000208604083018662000162565b6200021760608301856200017d565b620002266080830184620001c2565b9695505050505050565b610ac980620002406000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063bfa0b13314610063578063cb11771714610081578063f8a8fd6d146100b1575b600080fd5b61006b6100cf565b60405161007891906104b7565b60405180910390f35b61009b6004803603810190610096919061081d565b6100d5565b6040516100a891906108bb565b60405180910390f35b6100b961019d565b6040516100c691906108bb565b60405180910390f35b60005481565b6000806001546100e4876103c2565b6040516020016100f592919061094e565b60405160208183030381529060405280519060200120905085600001516020015173ffffffffffffffffffffffffffffffffffffffff166001828787876040516000815260200160405260405161014f9493929190610994565b6020604051602081039080840390855afa158015610171573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614915050949350505050565b600080604051806060016040528060405180604001604052806040518060400160405280600381526020017f436f770000000000000000000000000000000000000000000000000000000000815250815260200173cd2a3d9f938e13cd947ec05abc7fe734df8dd82673ffffffffffffffffffffffffffffffffffffffff16815250815260200160405180604001604052806040518060400160405280600381526020017f426f620000000000000000000000000000000000000000000000000000000000815250815260200173bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb73ffffffffffffffffffffffffffffffffffffffff1681525081526020016040518060400160405280600b81526020017f48656c6c6f2c20426f622100000000000000000000000000000000000000000081525081525090506000601c905060007f4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d600102905060007f07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b9156260010290507ff2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f60010260015414610363576103626109d9565b5b7fc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e600102610390856103c2565b1461039e5761039d6109d9565b5b6103aa848484846100d5565b6103b7576103b66109d9565b5b600194505050505090565b60007fa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac26103f2836000015161043b565b6103ff846020015161043b565b84604001518051906020012060405160200161041e9493929190610a08565b604051602081830303815290604052805190602001209050919050565b60007fb9d8c78acf9b987311de6c7b45bb6a9c8e1bf361fa7fd3467a2163f994c79500826000015180519060200120836020015160405160200161048193929190610a5c565b604051602081830303815290604052805190602001209050919050565b6000819050919050565b6104b18161049e565b82525050565b60006020820190506104cc60008301846104a8565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610534826104eb565b810181811067ffffffffffffffff82111715610553576105526104fc565b5b80604052505050565b60006105666104d2565b9050610572828261052b565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff8211156105a1576105a06104fc565b5b6105aa826104eb565b9050602081019050919050565b82818337600083830152505050565b60006105d96105d484610586565b61055c565b9050828152602081018484840111156105f5576105f4610581565b5b6106008482856105b7565b509392505050565b600082601f83011261061d5761061c61057c565b5b813561062d8482602086016105c6565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061066182610636565b9050919050565b61067181610656565b811461067c57600080fd5b50565b60008135905061068e81610668565b92915050565b6000604082840312156106aa576106a96104e6565b5b6106b4604061055c565b9050600082013567ffffffffffffffff8111156106d4576106d3610577565b5b6106e084828501610608565b60008301525060206106f48482850161067f565b60208301525092915050565b600060608284031215610716576107156104e6565b5b610720606061055c565b9050600082013567ffffffffffffffff8111156107405761073f610577565b5b61074c84828501610694565b600083015250602082013567ffffffffffffffff8111156107705761076f610577565b5b61077c84828501610694565b602083015250604082013567ffffffffffffffff8111156107a05761079f610577565b5b6107ac84828501610608565b60408301525092915050565b600060ff82169050919050565b6107ce816107b8565b81146107d957600080fd5b50565b6000813590506107eb816107c5565b92915050565b6107fa8161049e565b811461080557600080fd5b50565b600081359050610817816107f1565b92915050565b60008060008060808587031215610837576108366104dc565b5b600085013567ffffffffffffffff811115610855576108546104e1565b5b61086187828801610700565b9450506020610872878288016107dc565b935050604061088387828801610808565b925050606061089487828801610808565b91505092959194509250565b60008115159050919050565b6108b5816108a0565b82525050565b60006020820190506108d060008301846108ac565b92915050565b600081905092915050565b7f1901000000000000000000000000000000000000000000000000000000000000600082015250565b60006109176002836108d6565b9150610922826108e1565b600282019050919050565b6000819050919050565b6109486109438261049e565b61092d565b82525050565b60006109598261090a565b91506109658285610937565b6020820191506109758284610937565b6020820191508190509392505050565b61098e816107b8565b82525050565b60006080820190506109a960008301876104a8565b6109b66020830186610985565b6109c360408301856104a8565b6109d060608301846104a8565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000608082019050610a1d60008301876104a8565b610a2a60208301866104a8565b610a3760408301856104a8565b610a4460608301846104a8565b95945050505050565b610a5681610656565b82525050565b6000606082019050610a7160008301866104a8565b610a7e60208301856104a8565b610a8b6040830184610a4d565b94935050505056fea2646970667358221220007ab409ce7b5f3486cfc31fdec39b0e47d8290b106b2ec117428d0ea12b374764736f6c63430008130033
+\ No newline at end of file
diff --git a/python/eth_erc712/data/ERC712Example.json b/python/eth_erc712/data/ERC712Example.json
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"salt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"test","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Example.Person","name":"from","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Example.Person","name":"to","type":"tuple"},{"internalType":"string","name":"contents","type":"string"}],"internalType":"struct Example.Mail","name":"mail","type":"tuple"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
diff --git a/python/eth_erc712/data/ERC712Example.metadata.json b/python/eth_erc712/data/ERC712Example.metadata.json
@@ -0,0 +1 @@
+{"compiler":{"version":"0.8.19+commit.7dd6d404"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"salt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"test","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Example.Person","name":"from","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Example.Person","name":"to","type":"tuple"},{"internalType":"string","name":"contents","type":"string"}],"internalType":"struct Example.Mail","name":"mail","type":"tuple"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"ERC712Example.sol":"Example"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"ERC712Example.sol":{"keccak256":"0x745d4f0ccb2db6e551488cd7e3b7a0c50ce059992451afbaa962a0069143a70b","license":"CC0-1.0","urls":["bzz-raw://4c9920151c49a4219b3ca687a67b0a30d0e47d0023d6a9af7ca98f91cf7a708f","dweb:/ipfs/QmVbyN4hsg5DYchTd7ovAah2oe3tNRWWdCfmUmtFejHK2B"]}},"version":1}
diff --git a/python/eth_erc721/unittest/__init__.py b/python/eth_erc712/unittest/__init__.py
diff --git a/python/eth_erc712/unittest/base.py b/python/eth_erc712/unittest/base.py
@@ -0,0 +1,50 @@
+# standard imports
+import os
+import logging
+
+# external imports
+from chainlib.eth.unittest.ethtester import EthTesterCase
+from chainlib.eth.address import to_checksum_address
+from chainlib.connection import RPCConnection
+from chainlib.eth.nonce import RPCNonceOracle
+from chainlib.eth.gas import OverrideGasOracle
+from chainlib.eth.gas import Gas
+from chainlib.eth.tx import receipt
+
+# local imports
+from eth_erc712 import EIP712Domain
+
+script_dir = os.path.realpath(os.path.dirname(__file__))
+data_dir = os.path.join(script_dir, '..', 'data')
+
+logg = logging.getLogger(__name__)
+
+
+class TestERC712(EthTesterCase):
+
+ def setUp(self):
+ super(TestERC712, self).setUp()
+ nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
+ gas_oracle = OverrideGasOracle(limit=1000000)
+ fp = os.path.join(data_dir, 'ERC712Example.bin')
+ f = open(fp, 'r')
+ bytecode = f.read()
+ f.close()
+ c = Gas(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
+ self.conn = RPCConnection.connect(self.chain_spec, 'default')
+ (tx_hash, o) = c.create(self.accounts[0], None, 0, data=bytecode)
+ r = self.conn.do(o)
+ o = receipt(r)
+ r = self.conn.do(o)
+ self.address = to_checksum_address(r['contract_address'])
+ logg.debug('erc712 example smart contract published with hash {} address {}'.format(r, self.address))
+
+ address = os.urandom(20).hex()
+ salt = os.urandom(32).hex()
+ self.domain = EIP712Domain(
+ name='Ether Mail',
+ version='1',
+ chain_id=42,
+ verifying_contract=to_checksum_address('0xcccccccccccccccccccccccccccccccccccccccc'),
+ salt=salt,
+ )
diff --git a/python/eth_erc721/__init__.py b/python/eth_erc721/__init__.py
@@ -1 +0,0 @@
-from .base import ERC712Encoder, EIP712Domain
diff --git a/python/eth_erc721/base.py b/python/eth_erc721/base.py
@@ -1,77 +0,0 @@
-# standard imports
-import logging
-import sha3
-
-# external imports
-from chainlib.eth.contract import ABIContractType
-from chainlib.eth.contract import ABIContractEncoder
-from chainlib.hash import keccak256
-from chainlib.hash import keccak256_string_to_hex
-
-logg = logging.getLogger(__name__)
-
-
-class ERC712Encoder(ABIContractEncoder):
-
- def __init__(self, struct_name):
- super(ERC712Encoder, self).__init__()
- self.method(struct_name)
- self.ks = []
- self.encode = self.get_contents
-
-
- def add(self, k, t, v):
- typ_checked = ABIContractType(t.value)
- self.typ_literal(t.value + ' ' + k)
- m = getattr(self, t.value)
- m(v)
-
-
- def string(self, s):
- self.types.append(ABIContractType.STRING)
- v = keccak256_string_to_hex(s)
- self.contents.append(v)
- self.__log_latest_erc712(s)
-
-
- def bytes(self, s):
- self.types.append(ABIContractType.BYTES)
- v = keccak256_string_to_hex(s)
- self.contents.append(v)
- self.__log_latest_erc712(s)
-
-
- def __log_latest_erc712(self, v):
- l = len(self.types) - 1
- logg.debug('Encoder added {} -> {} ({})'.format(v, self.contents[l], self.types[l].value))
-
-
- def encode_type(self):
- v = self.get_method()
- r = keccak256(v)
- logg.debug('typehash material {} -> {}'.format(v, r.hex()))
- return r
-
-
- def encode_data(self):
- return b''.join(list(map(lambda x: bytes.fromhex(x), self.contents)))
-
-
- def get_contents(self):
- return b'\x19\x01' + self.encode_type() + self.encode_data()
-
-
-class EIP712Domain(ERC712Encoder):
-
- def __init__(self, name=None, version=None, chain_id=None, verifying_contract=None, salt=None):
- super(EIP712Domain, self).__init__('EIP712Domain')
- if name != None:
- self.add('name', ABIContractType.STRING, name)
- if version != None:
- self.add('version', ABIContractType.STRING, version)
- if chain_id != None:
- self.add('chainId', ABIContractType.UINT256, chain_id)
- if verifying_contract != None:
- self.add('verifyingContract', ABIContractType.ADDRESS, verifying_contract)
- if salt != None:
- self.add('salt', ABIContractType.BYTES32, salt)
diff --git a/python/eth_erc721/unittest/base.py b/python/eth_erc721/unittest/base.py
@@ -1,23 +0,0 @@
-# standard imports
-import os
-
-# external imports
-from chainlib.eth.unittest.ethtester import EthTesterCase
-from chainlib.eth.address import to_checksum_address
-
-# local imports
-from eth_erc721 import EIP712Domain
-
-
-class TestERC712(EthTesterCase):
-
- def setUp(self):
- address = os.urandom(20).hex()
- salt = os.urandom(32).hex()
- self.struct = EIP712Domain(
- name='Ether Mail',
- version='1',
- chain_id=42,
- verifying_contract=to_checksum_address('0xcccccccccccccccccccccccccccccccccccccccc'),
- salt=salt,
- )
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -1,2 +1 @@
-chainlib-eth==0.4.17
-pysha3==1.0.2
+chainlib-eth~=0.4.18
diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py
@@ -5,10 +5,17 @@ import os
# external imports
from chainlib.eth.contract import ABIContractType
+from chainlib.eth.tx import TxFactory
+from chainlib.eth.tx import TxFormat
+from chainlib.jsonrpc import JSONRPCRequest
+from chainlib.eth.contract import ABIContractEncoder
+from chainlib.eth.contract import ABIContractType
+from hexathon import add_0x
# local imports
-from eth_erc721.unittest import TestERC712 as TestERC712Base
-from eth_erc721 import ERC712Encoder
+from eth_erc712.unittest import TestERC712 as TestERC712Base
+from eth_erc712 import ERC712Encoder
+from eth_erc712 import EIP712DomainEncoder
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@@ -22,12 +29,11 @@ class ExamplePerson(ERC712Encoder):
self.add('wallet', ABIContractType.ADDRESS, wallet)
-class ExampleMail(ERC712Encoder):
-
- def __init__(self, from_name, from_wallet, to_name, to_wallet, contents):
+class ExampleMail(EIP712DomainEncoder):
+ def __init__(self, from_name, from_wallet, to_name, to_wallet, contents, *args, **kwargs):
self.pfrom = ExamplePerson(from_name, from_wallet)
self.pto = ExamplePerson(to_name, to_wallet)
- super(ExampleMail, self).__init__('Mail')
+ super(ExampleMail, self).__init__('Mail', *args, **kwargs)
self.typ_literal('Person from')
self.typ_literal('Person to')
self.add('contents', ABIContractType.STRING, contents)
@@ -51,21 +57,46 @@ class ExampleMail(ERC712Encoder):
class TestERC712(TestERC712Base):
def test_domain_separator(self):
- r = self.struct.encode_type()
+ r = self.domain.encode_type()
self.assertEqual(r, bytes.fromhex('d87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472'))
def test_domain_data(self):
- r = self.struct.get()
+ r = self.domain.get()
print(r.hex())
def test_mail(self):
a = os.urandom(20).hex()
b = os.urandom(20).hex()
- o = ExampleMail('Pinky Inky', a, 'Clyde Blinky', b, 'barbarbar')
- r = o.get()
- print(r.hex())
+ mail = ExampleMail('Pinky Inky', a, 'Clyde Blinky', b, 'barbarbar', domain=self.domain)
+ sig = self.signer.sign_typed_message(self.accounts[0], mail.get_domain(), mail.get_data_hash())
+ sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big')
+ logg.debug('message is:\n{}\nsigned by {}'.format(mail, self.accounts[0]))
+
+ c = TxFactory(self.chain_spec)
+ j = JSONRPCRequest()
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('verify')
+ enc.typ_literal('Mail')
+ enc.typ(ABIContractType.UINT8)
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.BYTES32)
+ enc.bytes(mail.get_typed_data().hex())
+ enc.uintn(sig[64], 8)
+ enc.bytes32(sig[:32])
+ enc.bytes32(sig[32:64])
+ data = add_0x(enc.get())
+ tx = c.template(self.accounts[0], self.address)
+ tx = c.set_code(tx, data)
+ o['params'].append(c.normalize(tx))
+ o['params'].append('latest')
+ o = j.finalize(o)
+ r = self.rpc.do(o)
+ r = strip_0x(r)
+ self.assertEqual(int(r, 16), 1)
if __name__ == '__main__':
diff --git a/solidity/ERC712Example.sol b/solidity/ERC712Example.sol
@@ -0,0 +1,113 @@
+pragma solidity >0.6.11;
+
+// SPDX-License-Identifier: CC0-1.0
+// This file was copied from https://eips.ethereum.org/assets/eip-712/Example.sol
+// Slightly edited to make it work with test harness parameters
+
+contract Example {
+
+ struct EIP712Domain {
+ string name;
+ string version;
+ uint256 chainId;
+ address verifyingContract;
+ bytes32 salt;
+ }
+
+ struct Person {
+ string name;
+ address wallet;
+ }
+
+ struct Mail {
+ Person from;
+ Person to;
+ string contents;
+ }
+
+ bytes32 public salt;
+
+ bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
+ "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
+ );
+
+ bytes32 constant PERSON_TYPEHASH = keccak256(
+ "Person(string name,address wallet)"
+ );
+
+ bytes32 constant MAIL_TYPEHASH = keccak256(
+ "Mail(Person from,Person to,string contents)Person(string name,address wallet)"
+ );
+
+ bytes32 DOMAIN_SEPARATOR;
+
+ constructor () public {
+ DOMAIN_SEPARATOR = hash(EIP712Domain({
+ name: "Ether Mail",
+ version: '1',
+ chainId: 42,
+ verifyingContract: address(this),
+ salt: blockhash(block.number)
+ }));
+ }
+
+ function hash(EIP712Domain memory eip712Domain) internal pure returns (bytes32) {
+ return keccak256(abi.encode(
+ EIP712DOMAIN_TYPEHASH,
+ keccak256(bytes(eip712Domain.name)),
+ keccak256(bytes(eip712Domain.version)),
+ eip712Domain.chainId,
+ eip712Domain.verifyingContract
+ ));
+ }
+
+ function hash(Person memory person) internal pure returns (bytes32) {
+ return keccak256(abi.encode(
+ PERSON_TYPEHASH,
+ keccak256(bytes(person.name)),
+ person.wallet
+ ));
+ }
+
+ function hash(Mail memory mail) internal pure returns (bytes32) {
+ return keccak256(abi.encode(
+ MAIL_TYPEHASH,
+ hash(mail.from),
+ hash(mail.to),
+ keccak256(bytes(mail.contents))
+ ));
+ }
+
+ function verify(Mail memory mail, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
+ // Note: we need to use `encodePacked` here instead of `encode`.
+ bytes32 digest = keccak256(abi.encodePacked(
+ "\x19\x01",
+ DOMAIN_SEPARATOR,
+ hash(mail)
+ ));
+ return ecrecover(digest, v, r, s) == mail.from.wallet;
+ }
+
+ function test() public view returns (bool) {
+ // Example signed message
+ Mail memory mail = Mail({
+ from: Person({
+ name: "Cow",
+ wallet: 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826
+ }),
+ to: Person({
+ name: "Bob",
+ wallet: 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB
+ }),
+ contents: "Hello, Bob!"
+ });
+ uint8 v = 28;
+ bytes32 r = 0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d;
+ bytes32 s = 0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562;
+
+ assert(DOMAIN_SEPARATOR == 0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f);
+ assert(hash(mail) == 0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e);
+ assert(verify(mail, v, r, s));
+ return true;
+ }
+}
diff --git a/solidity/Example.sol b/solidity/Example.sol
@@ -1,109 +0,0 @@
-pragma solidity ^0.4.24;
-
-// This file was copied from https://eips.ethereum.org/assets/eip-712/Example.sol
-// SPDX-License-Identifier: CC0-1.0
-
-contract Example {
-
- struct EIP712Domain {
- string name;
- string version;
- uint256 chainId;
- address verifyingContract;
- }
-
- struct Person {
- string name;
- address wallet;
- }
-
- struct Mail {
- Person from;
- Person to;
- string contents;
- }
-
- bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
- "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
- );
-
- bytes32 constant PERSON_TYPEHASH = keccak256(
- "Person(string name,address wallet)"
- );
-
- bytes32 constant MAIL_TYPEHASH = keccak256(
- "Mail(Person from,Person to,string contents)Person(string name,address wallet)"
- );
-
- bytes32 DOMAIN_SEPARATOR;
-
- constructor () public {
- DOMAIN_SEPARATOR = hash(EIP712Domain({
- name: "Ether Mail",
- version: '1',
- chainId: 1,
- // verifyingContract: this
- verifyingContract: 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC
- }));
- }
-
- function hash(EIP712Domain eip712Domain) internal pure returns (bytes32) {
- return keccak256(abi.encode(
- EIP712DOMAIN_TYPEHASH,
- keccak256(bytes(eip712Domain.name)),
- keccak256(bytes(eip712Domain.version)),
- eip712Domain.chainId,
- eip712Domain.verifyingContract
- ));
- }
-
- function hash(Person person) internal pure returns (bytes32) {
- return keccak256(abi.encode(
- PERSON_TYPEHASH,
- keccak256(bytes(person.name)),
- person.wallet
- ));
- }
-
- function hash(Mail mail) internal pure returns (bytes32) {
- return keccak256(abi.encode(
- MAIL_TYPEHASH,
- hash(mail.from),
- hash(mail.to),
- keccak256(bytes(mail.contents))
- ));
- }
-
- function verify(Mail mail, uint8 v, bytes32 r, bytes32 s) internal view returns (bool) {
- // Note: we need to use `encodePacked` here instead of `encode`.
- bytes32 digest = keccak256(abi.encodePacked(
- "\x19\x01",
- DOMAIN_SEPARATOR,
- hash(mail)
- ));
- return ecrecover(digest, v, r, s) == mail.from.wallet;
- }
-
- function test() public view returns (bool) {
- // Example signed message
- Mail memory mail = Mail({
- from: Person({
- name: "Cow",
- wallet: 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826
- }),
- to: Person({
- name: "Bob",
- wallet: 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB
- }),
- contents: "Hello, Bob!"
- });
- uint8 v = 28;
- bytes32 r = 0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d;
- bytes32 s = 0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562;
-
- assert(DOMAIN_SEPARATOR == 0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f);
- assert(hash(mail) == 0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e);
- assert(verify(mail, v, r, s));
- return true;
- }
-}
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -0,0 +1,12 @@
+SOLC = /usr/bin/solc
+
+all:
+ $(SOLC) --bin ERC712Example.sol --evm-version byzantium | awk 'NR>3' > ERC712Example.bin
+ $(SOLC) --abi ERC712Example.sol --evm-version byzantium | awk 'NR>3' > ERC712Example.json
+ $(SOLC) --metadata ERC712Example.sol --evm-version byzantium | awk 'NR>3' > ERC712Example.metadata.json
+ truncate -s -1 ERC712Example.bin
+
+install: all
+ cp -v *.json ../python/eth_erc712/data/
+ cp -v *.bin ../python/eth_erc712/data/
+