Skip to content

Commit

Permalink
Add support for eth_signTypedData RPC call
Browse files Browse the repository at this point in the history
  • Loading branch information
Bhargavasomu committed Apr 9, 2019
1 parent cba9538 commit 6f9c682
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tests/integration/go_ethereum/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ def test_eth_chainId(self, web3):
pytest.xfail('eth_chainId not implemented in geth 1.7.2')
super().test_eth_chainId(web3)

def test_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
pytest.xfail('eth_signTypedData JSON RPC call has not been released in geth')
super().test_eth_signTypedData(
web3, unlocked_account_dual_type
)


class GoEthereumVersionModuleTest(VersionModuleTest):
pass
Expand Down
8 changes: 8 additions & 0 deletions tests/integration/parity/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ def test_eth_getLogs_without_logs(self, web3, block_with_txn_with_log):
result = web3.eth.getLogs(filter_params)
assert len(result) == 0

def test_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
pytest.xfail('eth_signTypedData JSON RPC call has not been released in parity')
super().test_eth_signTypedData(
web3, unlocked_account_dual_type
)


class ParityTraceModuleTest(TraceModuleTest):
pass
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/parity/test_parity_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def parity_command_arguments(
'--password', passwordfile,
'--jsonrpc-port', rpc_port,
'--jsonrpc-apis', 'all',
'--jsonrpc-experimental',
'--no-ipc',
'--no-ws',
'--whisper',
Expand All @@ -65,6 +66,7 @@ def parity_import_blocks_command(parity_binary, rpc_port, datadir, passwordfile)
'--password', passwordfile,
'--jsonrpc-port', str(rpc_port),
'--jsonrpc-apis', 'all',
'--jsonrpc-experimental',
'--no-ipc',
'--no-ws',
'--tracing', 'on',
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/parity/test_parity_ipc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def parity_command_arguments(
'--unlock', author,
'--password', passwordfile,
'--ipc-apis', 'all',
'--jsonrpc-experimental',
'--no-jsonrpc',
'--no-ws',
'--whisper',
Expand All @@ -63,6 +64,7 @@ def parity_import_blocks_command(parity_binary, ipc_path, datadir, passwordfile)
'--base-path', datadir,
'--password', passwordfile,
'--ipc-apis', 'all',
'--jsonrpc-experimental',
'--no-jsonrpc',
'--no-ws',
'--tracing', 'on',
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/parity/test_parity_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def parity_command_arguments(
'--ws-port', ws_port,
'--ws-origins', '*',
'--ws-apis', 'all',
'--jsonrpc-experimental',
'--no-ipc',
'--no-jsonrpc',
'--whisper',
Expand All @@ -68,6 +69,7 @@ def parity_import_blocks_command(parity_binary, ws_port, datadir, passwordfile):
'--ws-port', str(ws_port),
'--ws-origins', '*',
'--ws-apis', 'all',
'--jsonrpc-experimental',
'--no-ipc',
'--no-jsonrpc',
'--tracing', 'on',
Expand Down
106 changes: 106 additions & 0 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-

import json
import pytest

from eth_abi import (
Expand Down Expand Up @@ -202,6 +203,111 @@ def test_eth_sign(self, web3, unlocked_account_dual_type):
)
assert new_signature != signature

def test_eth_signTypedData(self, web3, unlocked_account_dual_type):
validJSONMessage = '''
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Person": [
{"name": "name", "type": "string"},
{"name": "wallet", "type": "address"}
],
"Mail": [
{"name": "from", "type": "Person"},
{"name": "to", "type": "Person"},
{"name": "contents", "type": "string"}
]
},
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": "0x01",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
}
}
'''
signature = HexBytes(web3.eth.signTypedData(
json.loads(validJSONMessage),
HexBytes(unlocked_account_dual_type).hex()
))
assert len(signature) == 32 + 32 + 1

# def test_eth_signTypedData(self, web3, unlocked_account_dual_type):
# validJSONMessage = '''
# {
# "types": {
# "EIP712Domain": [
# {"name": "name", "type": "string"},
# {"name": "version", "type": "string"},
# {"name": "chainId", "type": "uint256"},
# {"name": "verifyingContract", "type": "address"}
# ],
# "Person": [
# {"name": "name", "type": "string"},
# {"name": "wallet", "type": "address"}
# ],
# "Mail": [
# {"name": "from", "type": "Person"},
# {"name": "to", "type": "Person"},
# {"name": "contents", "type": "string"}
# ]
# },
# "primaryType": "Mail",
# "domain": {
# "name": "Ether Mail",
# "version": "1",
# "chainId": "0x01",
# "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
# },
# "message": {
# "from": {
# "name": "Cow",
# "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
# },
# "to": {
# "name": "Bob",
# "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
# },
# "contents": "Hello, Bob!"
# }
# }
# '''
# import json
# signature = web3.eth.signTypedData(
# HexBytes(unlocked_account_dual_type).hex(),
# json.loads(validJSONMessage),
# "web3py-test"
# )
# signature = HexBytes(signature)
# # assert isinstance(signature, HexBytes)
# # signature = bytes(signature)
# # assert is_bytes(signature)
# # assert len(signature) == 32 + 32 + 1
#
# expected_signature = HexBytes(
# "0xc8b56aaeefd10ab4005c2455daf28d9082af661ac347cd"
# "b612d5b5e11f339f2055be831bf57a6e6cb5f6d93448fa35"
# "c1bd56fe1d745ffa101e74697108668c401c"
# )
# assert signature == expected_signature

def test_eth_signTransaction(self, web3, unlocked_account):
txn_params = {
'from': unlocked_account,
Expand Down
120 changes: 120 additions & 0 deletions web3/_utils/module_testing/personal_module.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import pytest

from eth_utils import (
Expand Down Expand Up @@ -89,6 +90,65 @@ def test_personal_sign_and_ecrecover(self,
signer = web3.geth.personal.ecRecover(message, signature)
assert is_same_address(signer, unlockable_account_dual_type)

@pytest.mark.xfail(reason="personal_signTypedData JSON RPC call has not been released in geth")
def test_personal_sign_typed_data(self,
web3,
unlockable_account_dual_type,
unlockable_account_pw):
from hexbytes import HexBytes
typed_message = '''
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Person": [
{"name": "name", "type": "string"},
{"name": "wallet", "type": "address"}
],
"Mail": [
{"name": "from", "type": "Person"},
{"name": "to", "type": "Person"},
{"name": "contents", "type": "string"}
]
},
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": "0x01",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
}
}
'''
signature = HexBytes(web3.geth.personal.signTypedData(
json.loads(typed_message),
HexBytes(unlockable_account_dual_type).hex(),
unlockable_account_pw
))

expected_signature = HexBytes(
"0xc8b56aaeefd10ab4005c2455daf28d9082af661ac347cd"
"b612d5b5e11f339f2055be831bf57a6e6cb5f6d93448fa35"
"c1bd56fe1d745ffa101e74697108668c401c"
)
assert signature == expected_signature
assert len(signature) == 32 + 32 + 1


class ParityPersonalModuleTest():
def test_personal_listAccounts(self, web3):
Expand Down Expand Up @@ -169,3 +229,63 @@ def test_personal_sign_and_ecrecover(self,
)
signer = web3.parity.personal.ecRecover(message, signature)
assert is_same_address(signer, unlockable_account_dual_type)

def test_personal_sign_typed_data(self,
web3,
unlockable_account_dual_type,
unlockable_account_pw):
from hexbytes import HexBytes
typed_message = '''
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Person": [
{"name": "name", "type": "string"},
{"name": "wallet", "type": "address"}
],
"Mail": [
{"name": "from", "type": "Person"},
{"name": "to", "type": "Person"},
{"name": "contents", "type": "string"}
]
},
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": "0x01",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
}
}
'''
signature = HexBytes(web3.geth.personal.signTypedData(
json.loads(typed_message),
HexBytes(unlockable_account_dual_type).hex(),
unlockable_account_pw
))

expected_signature = HexBytes(
"0xc8b56aaeefd10ab4005c2455daf28d9082af661ac347cd"
"b612d5b5e11f339f2055be831bf57a6e6cb5f6d93448fa35"
"c1bd56fe1d745ffa101e74697108668c401c"
)
assert signature == expected_signature
assert len(signature) == 32 + 32 + 1
# signer = web3.geth.personal.ecRecover(message, signature)
# assert is_same_address(signer, unlockable_account_dual_type)
6 changes: 6 additions & 0 deletions web3/_utils/personal.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
)


signTypedData = Method(
"personal_signTypedData",
mungers=[default_root_munger],
)


ecRecover = Method(
"personal_ecRecover",
mungers=[default_root_munger],
Expand Down
1 change: 1 addition & 0 deletions web3/_utils/rpc_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
'eth_sendTransaction': TRANSACTION_PARAMS_ABIS,
'eth_signTransaction': TRANSACTION_PARAMS_ABIS,
'eth_sign': ['address', 'bytes'],
'eth_signTypedData': ['address', None],
'eth_submitHashrate': ['uint', 'bytes32'],
'eth_submitWork': ['bytes8', 'bytes32', 'bytes32'],
# personal
Expand Down
5 changes: 5 additions & 0 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ def signTransaction(self, transaction):
"eth_signTransaction", [transaction],
)

def signTypedData(self, account, jsonMessage):
return self.web3.manager.request_blocking(
"eth_signTypedData", [account, jsonMessage],
)

@apply_to_return_value(HexBytes)
def call(self, transaction, block_identifier=None):
# TODO: move to middleware
Expand Down
2 changes: 2 additions & 0 deletions web3/geth.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
newAccount,
sendTransaction,
sign,
signTypedData,
unlockAccount,
)
from web3._utils.txpool import (
Expand Down Expand Up @@ -58,6 +59,7 @@ class GethPersonal(ModuleV2):
newAccount = newAccount
sendTransaction = sendTransaction
sign = sign
signTypedData = signTypedData
unlockAccount = unlockAccount


Expand Down
1 change: 1 addition & 0 deletions web3/middleware/exception_retry_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
'eth_getCompilers',
'eth_getWork',
'eth_sign',
'eth_signTypedData',
'eth_sendRawTransaction',
'personal_importRawKey',
'personal_newAccount',
Expand Down
1 change: 1 addition & 0 deletions web3/middleware/pythonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ def to_hexbytes(num_bytes, val, variable_length=False):
'eth_sendTransaction': to_hexbytes(32),
'eth_signTransaction': apply_formatter_if(is_not_null, signed_tx_formatter),
'eth_sign': HexBytes,
'eth_signTypedData': HexBytes,
'eth_syncing': apply_formatter_if(is_not_false, syncing_formatter),
# personal
'personal_importRawKey': to_checksum_address,
Expand Down
Loading

0 comments on commit 6f9c682

Please sign in to comment.