eth-erc712

ERC712 typed data sign material builder
Info | Log | Files | Refs

test_basic.py (7385B)


      1 # standard imports
      2 import unittest
      3 import logging
      4 import os
      5 
      6 # external imports
      7 from chainlib.eth.contract import ABIContractType
      8 from chainlib.eth.tx import TxFactory
      9 from chainlib.eth.tx import TxFormat
     10 from chainlib.jsonrpc import JSONRPCRequest
     11 from chainlib.eth.contract import ABIContractEncoder
     12 from chainlib.eth.contract import ABIContractType
     13 from hexathon import add_0x
     14 from hexathon import strip_0x
     15 
     16 # local imports
     17 from eth_erc712.unittest import TestERC712 as TestERC712Base
     18 from eth_erc712 import ERC712Encoder
     19 from eth_erc712 import EIP712DomainEncoder
     20 
     21 logging.basicConfig(level=logging.DEBUG)
     22 logg = logging.getLogger()
     23 
     24 
     25 class ExamplePerson(ERC712Encoder):
     26 
     27     def __init__(self, name, wallet):
     28         super(ExamplePerson, self).__init__('Person')
     29         self.add('name', ABIContractType.STRING, name)
     30         self.add('wallet', ABIContractType.ADDRESS, wallet)
     31 
     32 
     33 class ExampleMail(EIP712DomainEncoder):
     34     def __init__(self, from_name, from_wallet, to_name, to_wallet, contents, domain):
     35         self.pfrom = ExamplePerson(from_name, from_wallet)
     36         self.pto = ExamplePerson(to_name, to_wallet)
     37         super(ExampleMail, self).__init__('Mail', domain)
     38         self.typ_literal('Person from')
     39         self.typ_literal('Person to')
     40         self.add('contents', ABIContractType.STRING, contents)
     41 
     42 
     43     # In general implementation, remember to sort structs alphabetically
     44     def get_method(self):
     45         typ = super(ExampleMail, self).get_method()
     46         typ += self.pto.get_method()
     47         logg.debug('Method is composite type: ' + typ)
     48         return typ
     49 
     50 
     51     def encode_data(self):
     52         content = super(ExampleMail, self).encode_data()
     53         from_content = self.pfrom.encode_data()
     54         to_content = self.pto.encode_data()
     55         return from_content + to_content + content
     56 
     57 
     58 class TestERC712(TestERC712Base):
     59 
     60     def test_domain_separator(self):
     61         r = self.domain.encode_type()
     62         self.assertEqual(r, bytes.fromhex('d87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472'))
     63 
     64 
     65     def test_domain_data(self):
     66         r = self.domain.get()
     67         print(r.hex())
     68 
     69 
     70     def test_mail_manual(self):
     71         a = os.urandom(20)
     72         b = os.urandom(20)
     73         mail_from_name = 'Pinky Inky'
     74         mail_from_address = a.hex()
     75         mail_to_name = 'Clyde Blinky'
     76         mail_to_address = b.hex()
     77         mail_contents = 'barbarbar'
     78 
     79         mail = ExampleMail(
     80                 mail_from_name,
     81                 mail_from_address,
     82                 mail_to_name,
     83                 mail_to_address,
     84                 mail_contents,
     85                 self.domain,
     86                 )
     87         sig = self.signer.sign_typed_message(self.accounts[0], mail.get_domain(), mail.get_hash())
     88         sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big')
     89         logg.debug('message is:\n{}\nsigned by {}'.format(mail, self.accounts[0]))
     90 
     91         logg.debug('encode data from')
     92         enc = ABIContractEncoder()
     93         enc.string(mail_from_name)
     94         enc.address(mail_from_address)
     95         data_from = enc.get_contents()
     96  
     97         logg.debug('encode data to')
     98         enc = ABIContractEncoder()
     99         enc.string(mail_to_name)
    100         enc.address(mail_to_address)
    101         data_to = enc.get_contents()
    102 
    103         logg.debug('encode data contents')
    104         enc = ABIContractEncoder()
    105         enc.string(mail_contents)
    106         data_contents = enc.get_contents()
    107 
    108         logg.debug('encode struct data pointers')
    109         enc = ABIContractEncoder()
    110         enc.uint256(0x60)
    111         enc.uint256(0xe0)
    112         enc.uint256(0x160)
    113         struct_pointers = enc.get_contents()
    114 
    115         logg.debug('encode complete calldata')
    116         c = TxFactory(self.chain_spec)
    117         j = JSONRPCRequest()
    118         o = j.template()
    119         o['method'] = 'eth_call'
    120         enc = ABIContractEncoder()
    121         enc.method('verify')
    122         enc.typ_literal('((string,address),(string,address),string)')
    123         enc.typ(ABIContractType.UINT8)
    124         enc.typ(ABIContractType.BYTES32)
    125         enc.typ(ABIContractType.BYTES32)
    126         enc.uint256(0x80) # start of struct data pointer
    127         enc.uintn(sig[64], 8)
    128         enc.bytes32(sig[:32])
    129         enc.bytes32(sig[32:64])
    130         data = enc.get()
    131         data += struct_pointers
    132         data += data_from
    133         data += data_to
    134         data += data_contents
    135         for i in range(8, len(data), 64):
    136             logg.info('calldata {} {}'.format((i-8).to_bytes(2, byteorder='big').hex(), data[i:i+64]))
    137         data = add_0x(data)
    138         tx = c.template(self.accounts[0], self.address)
    139         tx = c.set_code(tx, data)
    140         o['params'].append(c.normalize(tx))
    141         o['params'].append('latest')
    142         o = j.finalize(o)
    143         r = self.rpc.do(o)
    144         r = strip_0x(r)
    145         self.assertEqual(int(r, 16), 1)
    146 
    147 
    148     def test_mail_chainlib_abi(self):
    149         a = os.urandom(20)
    150         b = os.urandom(20)
    151         mail_from_name = 'Pinky Inky'
    152         mail_from_address = a.hex()
    153         mail_to_name = 'Clyde Blinky'
    154         mail_to_address = b.hex()
    155         mail_contents = 'barbarbar'
    156 
    157         mail = ExampleMail(
    158                 mail_from_name,
    159                 mail_from_address,
    160                 mail_to_name,
    161                 mail_to_address,
    162                 mail_contents,
    163                 self.domain,
    164                 )
    165         sig = self.signer.sign_typed_message(self.accounts[0], mail.get_domain(), mail.get_hash())
    166         sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big')
    167         logg.debug('message is:\n{}\nsigned by {}'.format(mail, self.accounts[0]))
    168 
    169         logg.debug('encode data from')
    170         enc_from = ABIContractEncoder()
    171         enc_from.typ(ABIContractType.STRING)
    172         enc_from.typ(ABIContractType.ADDRESS)
    173         enc_from.string(mail_from_name)
    174         enc_from.address(mail_from_address)
    175  
    176         logg.debug('encode data to')
    177         enc_to = ABIContractEncoder()
    178         enc_to.typ(ABIContractType.STRING)
    179         enc_to.typ(ABIContractType.ADDRESS)
    180         enc_to.string(mail_to_name)
    181         enc_to.address(mail_to_address)
    182 
    183         logg.debug('encode data contents')
    184         enc_mail = ABIContractEncoder()
    185         enc_mail.typ(enc_from)
    186         enc_mail.typ(enc_to)
    187         enc_mail.typ(ABIContractType.STRING)
    188         enc_mail.tuple(enc_from)
    189         enc_mail.tuple(enc_to)
    190         enc_mail.string(mail_contents)
    191 
    192         logg.debug('encode complete calldata')
    193         c = TxFactory(self.chain_spec)
    194         j = JSONRPCRequest()
    195         o = j.template()
    196         o['method'] = 'eth_call'
    197         enc = ABIContractEncoder()
    198         enc.method('verify')
    199         enc.typ(enc_mail)
    200         enc.typ(ABIContractType.UINT8)
    201         enc.typ(ABIContractType.BYTES32)
    202         enc.typ(ABIContractType.BYTES32)
    203         enc.tuple(enc_mail)
    204         enc.uintn(sig[64], 8)
    205         enc.bytes32(sig[:32])
    206         enc.bytes32(sig[32:64])
    207         data = enc.get()
    208         for i in range(8, len(data), 64):
    209             logg.info('calldata {} {}'.format((i-8).to_bytes(2, byteorder='big').hex(), data[i:i+64]))
    210         data = add_0x(data)
    211         tx = c.template(self.accounts[0], self.address)
    212         tx = c.set_code(tx, data)
    213         o['params'].append(c.normalize(tx))
    214         o['params'].append('latest')
    215         o = j.finalize(o)
    216         r = self.rpc.do(o)
    217         r = strip_0x(r)
    218         self.assertEqual(int(r, 16), 1)
    219 
    220 
    221 if __name__ == '__main__':
    222     unittest.main()