def test_default_domain(default_domain_manager): assert eip712_structs.default_domain is None class Foo(EIP712Struct): s = String() foo = Foo(s='hello world') domain = make_domain(name='domain') other_domain = make_domain(name='other domain') # When neither methods provide a domain, expect a ValueError with pytest.raises(ValueError, match='Domain must be provided'): foo.to_message() with pytest.raises(ValueError, match='Domain must be provided'): foo.signable_bytes() # But we can still provide a domain explicitly explicit_msg = foo.to_message(domain) explicit_bytes = foo.signable_bytes(domain) # Setting it lets us forgo providing it eip712_structs.default_domain = domain implicit_msg = foo.to_message() implicit_bytes = foo.signable_bytes() # Either method should produce the same result assert implicit_msg == explicit_msg assert implicit_bytes == explicit_bytes # Using a different domain should not use any current default domain assert implicit_msg != foo.to_message(other_domain) assert implicit_bytes != foo.signable_bytes(other_domain)
def test_domain_sep_create(): salt = os.urandom(32) domain_struct = make_domain(name='name', salt=salt) expected_result = 'EIP712Domain(string name,bytes32 salt)' assert domain_struct.encode_type() == expected_result expected_data = b''.join([keccak(text='name'), salt]) assert domain_struct.encode_value() == expected_data with pytest.raises(ValueError, match='At least one argument must be given'): make_domain()
def hash_struct(tx, domain=None, verifyingContract=None): if domain and verifyingContract: raise RuntimeError("verifyingContract supplied but ignored") verifying_address = verifyingContract.address if verifyingContract else NULL_ADDRESS inputs = [ Input(blknum=i.blknum, txindex=i.txindex, oindex=i.oindex) for i in tx.inputs ] outputs = [ Output(owner=o.output_guard, currency=o.token, amount=o.amount) for o in tx.outputs ] domain = domain or make_domain( name='OMG Network', version='1', verifyingContract=verifying_address, salt=bytes.fromhex( 'fad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83') ) type = Transaction().encode_type() + Input.encode_type( ) + Output.encode_type() values = [_hash_typed(t) for t in inputs] + [_hash_typed(t) for t in outputs ] + [tx.metadata or NULL_HASH] return keccak(b'\x19\x01' + _hash_typed(domain) + _hash_struct(type, b''.join(values)))
def build_domain_separator(cls, ethereum_network: EthereumNetwork): return make_domain( name="Gnosis Protocol", version="v2", chainId=str(ethereum_network.value), verifyingContract=cls. settlement_contract_addresses[ethereum_network], )
def test_signable_bytes(): class Foo(EIP712Struct): s = String() i = Int(256) domain = make_domain(name='hello') foo = Foo(s='hello', i=1234) start_bytes = b'\x19\x01' exp_domain_bytes = keccak(domain.type_hash() + domain.encode_value()) exp_struct_bytes = keccak(foo.type_hash() + foo.encode_value()) sign_bytes = foo.signable_bytes(domain) assert sign_bytes[0:2] == start_bytes assert sign_bytes[2:34] == exp_domain_bytes assert sign_bytes[34:] == exp_struct_bytes
def hash_struct(tx, domain=None, verifying_contract=None): if domain and verifying_contract: raise RuntimeError("verifyingContract supplied but ignored") verifying_address = hex_to_binary( verifying_contract.address) if verifying_contract else NULL_ADDRESS domain = domain or make_domain( name='OMG Network', version='1', verifyingContract=verifying_address, salt=hex_to_binary( 'fad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83') ) return keccak(b'\x19\x01' + domain.hash_struct() + struct_tx_from_tx(tx).hash_struct())
def test_flat_struct_to_message(): class Foo(EIP712Struct): s = String() domain = make_domain(name='domain') foo = Foo(s='foobar') expected_result = { 'primaryType': 'Foo', 'types': { 'EIP712Domain': [{ 'name': 'name', 'type': 'string', }], 'Foo': [{ 'name': 's', 'type': 'string', }] }, 'domain': { 'name': 'domain', }, 'message': { 's': 'foobar' } } message = foo.to_message(domain) assert message == expected_result # Now test in reverse... new_struct, domain = EIP712Struct.from_message(expected_result) assert new_struct.type_name == 'Foo' members_list = new_struct.get_members() assert len(members_list) == 1 assert members_list[0][0] == 's' assert members_list[0][1].type_name == 'string' assert new_struct['s'] == 'foobar'
def test_domain_sep_types(): salt = os.urandom(32) contract = os.urandom(20) domain_struct = make_domain(name='name', version='version', chainId=1, verifyingContract=contract, salt=salt) encoded_data = [ keccak(text='name'), keccak(text='version'), int(1).to_bytes(32, 'big', signed=False), bytes(12) + contract, salt ] expected_result = 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)' assert domain_struct.encode_type() == expected_result expected_data = b''.join(encoded_data) assert domain_struct.encode_value() == expected_data
def _eip712_payload(self) -> StructTuple: data = self.data.hex() if self.data else "" safe_version = Version(self.safe_version) cls = EIP712SafeTx if safe_version >= Version( "1.0.0") else EIP712LegacySafeTx message = cls( to=self.to, value=self.value, data=data, operation=self.operation, safeTxGas=self.safe_tx_gas, baseGas=self.base_gas, dataGas=self.base_gas, gasPrice=self.gas_price, gasToken=self.gas_token, refundReceiver=self.refund_receiver, nonce=self.safe_nonce, ) domain = make_domain( verifyingContract=self.safe_address, chainId=self.chain_id if safe_version >= Version("1.3.0") else None, ) return StructTuple(message, domain)
def test_bytes_json_encoder(): class Foo(EIP712Struct): b = Bytes(32) domain = make_domain(name='domain') bytes_val = os.urandom(32) foo = Foo(b=bytes_val) result = foo.to_message_json(domain) expected_substring = f'"b": "0x{bytes_val.hex()}"' assert expected_substring in result reconstructed = EIP712Struct.from_message(json.loads(result)) assert reconstructed.domain == domain assert reconstructed.message == foo class UnserializableObject: pass obj = UnserializableObject() # Fabricate this failure case to test that the custom json encoder's fallback path works as expected. foo.values['b'] = obj with pytest.raises(TypeError, match='not JSON serializable'): foo.to_message_json(domain)
import pytest from plasma_core.constants import NULL_ADDRESS from plasma_core.transaction import Transaction from eip712_structs import make_domain from plasma_core.utils.eip712_struct_hash import hash_struct verifyingContract = bytes.fromhex('44de0ec539b8c4a4b530c78620fe8320167f2f74') test_domain = make_domain( name='OMG Network', version='1', verifyingContract=verifyingContract, salt=bytes.fromhex('fad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83') ) owner = bytes.fromhex('2258a5279850f6fb78888a7e45ea2a5eb1b3c436') token = bytes.fromhex('0123456789abcdef000000000000000000000000') metadata = bytes.fromhex('853a8d8af99c93405a791b97d57e819e538b06ffaa32ad70da2582500bc18d43') inputs = [ (1, 0, 0), (1000, 2, 3), (101000, 1337, 3) ] outputs = [ (owner, NULL_ADDRESS, 100), (token, NULL_ADDRESS, 111), (owner, token, 1337) ]
from plasma_core.constants import NULL_ADDRESS from plasma_core.transaction import Transaction from eip712_structs import make_domain from plasma_core.utils.eip712_struct_hash import hash_struct # TODO: Run the tests on new tx format # Metamask currently does not implement dynamic arrays pytestmark = pytest.mark.skip( reason="Dynamic arrays and optional fields in tx format") test_domain = make_domain( name='OMG Network', version='1', verifyingContract=bytes.fromhex( '44de0ec539b8c4a4b530c78620fe8320167f2f74'), salt=bytes.fromhex( 'fad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83')) owner = bytes.fromhex('2258a5279850f6fb78888a7e45ea2a5eb1b3c436') token = bytes.fromhex('0123456789abcdef000000000000000000000000') metadata = bytes.fromhex( '853a8d8af99c93405a791b97d57e819e538b06ffaa32ad70da2582500bc18d43') inputs = [(1, 0, 0), (1000, 2, 3), (101000, 1337, 3)] outputs = [(owner, NULL_ADDRESS, 100), (token, NULL_ADDRESS, 111), (owner, token, 1337)] # NOTE: following test vectors were confirmed against contracts code
def test_nested_struct_to_message(): class Bar(EIP712Struct): s = String() class Foo(EIP712Struct): s = String() bar = Bar domain = make_domain(name='domain') foo = Foo( s="foo", bar=Bar(s="bar") ) expected_result = { 'primaryType': 'Foo', 'types': { 'EIP712Domain': [{ 'name': 'name', 'type': 'string', }], 'Foo': [{ 'name': 's', 'type': 'string', }, { 'name': 'bar', 'type': 'Bar', }], 'Bar': [{ 'name': 's', 'type': 'string', }] }, 'domain': { 'name': 'domain', }, 'message': { 's': 'foo', 'bar': { 's': 'bar', } } } message = foo.to_message(domain) assert message == expected_result # And test in reverse... new_struct, new_domain = EIP712Struct.from_message(expected_result) assert new_struct.type_name == 'Foo' members = new_struct.get_members() assert len(members) == 2 assert members[0][0] == 's' and members[0][1].type_name == 'string' assert members[1][0] == 'bar' and members[1][1].type_name == 'Bar' bar_val = new_struct['bar'] assert bar_val.type_name == 'Bar' assert bar_val['s'] == 'bar' assert foo.hash_struct() == new_struct.hash_struct()