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 support for eth_signTypedData and personal_signTypedData RPC call #1319

Merged
merged 2 commits into from
May 1, 2019
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
21 changes: 17 additions & 4 deletions docs/web3.eth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,19 @@ The following methods are available on the ``web3.eth`` namespace.
'0x1a8bbe6eab8c72a219385681efefe565afd3accee35f516f8edf5ae82208fbd45a58f9f9116d8d88ba40fcd29076d6eada7027a3b412a9db55a0164547810cc401'


.. py:method:: Eth.signTypedData(account, jsonMessage)

* Delegates to ``eth_signTypedData`` RPC Method

Please note that the ``jsonMessage`` argument is the loaded JSON Object
and **NOT** the JSON String itself.
kclowes marked this conversation as resolved.
Show resolved Hide resolved

Signs the ``Structured Data`` (or ``Typed Data``) with the private key of the given ``account``.
The account must be unlocked.

``account`` may be a hex address or an ENS name


.. py:method:: Eth.call(transaction, block_identifier=web3.eth.defaultBlock)

* Delegates to ``eth_call`` RPC Method
Expand Down Expand Up @@ -864,8 +877,8 @@ with the filtering API.
* Delegates to ``eth_submitHashrate`` RPC Method

.. code-block:: python
>>> node_id = '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c'

>>> node_id = '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c'
>>> web3.eth.submitHashrate(5000, node_id)
True

Expand All @@ -875,14 +888,14 @@ with the filtering API.
* Delegates to ``eth_submitWork`` RPC Method.

.. code-block:: python

>>> web3.eth.submitWork(
1,
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000',
)
True


Contracts
---------
Expand Down
3 changes: 2 additions & 1 deletion docs/web3.geth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ The following methods are available on the ``web3.geth.personal`` namespace.
>>> web3.geth.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'the-passphrase')
True


.. py:method:: sendTransaction(self, transaction, passphrase)

* Delegates to ``personal_sendTransaction`` RPC Method
Expand Down Expand Up @@ -502,7 +503,7 @@ Full documentation for Geth-supported endpoints can be found `here <https://gith

.. py:method:: Shh.newMessageFilter(self, criteria)

* Create a new filter id. This filter id can be used with ``ShhFilter`` to poll for new messages that match the set of criteria.
* Create a new filter id. This filter id can be used with ``ShhFilter`` to poll for new messages that match the set of criteria.

* Parameters:
* ``symKeyID``: When using symmetric key encryption, holds the symmetric key ID.
Expand Down
19 changes: 15 additions & 4 deletions docs/web3.parity.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,24 @@ The following methods are available on the ``web3.parity.personal`` namespace.
>>> web3.parity.personal.unlockAccount('0xd3cda913deb6f67967b99d67acdfa1712c293601', 'the-passphrase')
True


.. py:method:: sendTransaction(self, transaction, passphrase)

* Delegates to ``personal_sendTransaction`` RPC Method

Sends the transaction.


.. py:method:: signTypedData(self, jsonMessage, account, passphrase)

* Delegates to ``personal_signTypedData`` RPC Method

Please note that the ``jsonMessage`` argument is the loaded JSON Object
and **NOT** the JSON String itself.

Signs the ``Structured Data`` (or ``Typed Data``) with the passphrase of the given ``account``


ParityShh
---------

Expand Down Expand Up @@ -121,14 +132,14 @@ Full documentation for Parity-supported endpoints can be found `here <https://wi

.. py:method:: Shh.newMessageFilter(self, criteria)

* Return the filter ID that can be used with ``ShhFilter`` to poll for new messages that match the set of criteria.
* Return the filter ID that can be used with ``ShhFilter`` to poll for new messages that match the set of criteria.

* Parameters:
* ``decryptWith``: 32 bytes - Identity of key used for description. Null if listening for broadcasts.
* ``from``: 64 bytes - If present, only accept messages signed by this key.
* ``topics``: Array of possible topics (or partial topics). Should be non-empty.
* Returns the newly created filter id.

* Returns the newly created filter id.

.. code-block:: python

Expand Down Expand Up @@ -176,7 +187,7 @@ Full documentation for Parity-supported endpoints can be found `here <https://wi
True

.. py:method:: Shh.unsubscribe(self, filter_id)

* Close a subscribed filter.

* Returns ``True`` if the filter subscription was sucesfully closed, otherwise ``False``
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/go_ethereum/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ def test_eth_chainId(self, web3):
pytest.xfail('eth_chainId not implemented in geth 1.7.2')
super().test_eth_chainId(web3)

@pytest.mark.xfail(reason='eth_signTypedData has not been released in geth')
def test_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
super().test_eth_signTypedData(
web3, unlocked_account_dual_type
)

@pytest.mark.xfail(reason='eth_signTypedData has not been released in geth')
def test_invalid_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
super().test_invalid_eth_signTypedData(
web3, unlocked_account_dual_type
)


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

@pytest.mark.xfail(reason='eth_signTypedData has not been released in Parity')
def test_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
super().test_eth_signTypedData(
web3, unlocked_account_dual_type
)

@pytest.mark.xfail(reason='eth_signTypedData has not been released in Parity')
def test_invalid_eth_signTypedData(self,
web3,
unlocked_account_dual_type):
super().test_invalid_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',
kclowes marked this conversation as resolved.
Show resolved Hide resolved
'--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
1 change: 1 addition & 0 deletions tests/integration/test_ethereum_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def func_wrapper(self, eth_tester, *args, **kwargs):

class TestEthereumTesterEthModule(EthModuleTest):
test_eth_sign = not_implemented(EthModuleTest.test_eth_sign, ValueError)
test_eth_signTypedData = not_implemented(EthModuleTest.test_eth_signTypedData, ValueError)
test_eth_signTransaction = not_implemented(EthModuleTest.test_eth_signTransaction, ValueError)
test_eth_submitHashrate = not_implemented(EthModuleTest.test_eth_submitHashrate, ValueError)
test_eth_submitWork = not_implemented(EthModuleTest.test_eth_submitWork, ValueError)
Expand Down
99 changes: 99 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,104 @@ def test_eth_sign(self, web3, unlocked_account_dual_type):
)
assert new_signature != signature

def test_eth_signTypedData(self, web3, unlocked_account_dual_type, skip_if_testrpc):
validJSONMessage = '''
Bhargavasomu marked this conversation as resolved.
Show resolved Hide resolved
{
"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!"
}
}
'''
skip_if_testrpc(web3)
signature = HexBytes(web3.eth.signTypedData(
unlocked_account_dual_type,
json.loads(validJSONMessage)
))
assert len(signature) == 32 + 32 + 1

def test_invalid_eth_signTypedData(self,
web3,
unlocked_account_dual_type,
skip_if_testrpc):
skip_if_testrpc(web3)
invalid_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[2]"},
{"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!"
}
}
'''
with pytest.raises(ValueError,
match=r".*Expected 2 items for array type Person\[2\], got 1 items.*"):
web3.eth.signTypedData(
unlocked_account_dual_type,
json.loads(invalid_typed_message)
)

def test_eth_signTransaction(self, web3, unlocked_account):
txn_params = {
'from': unlocked_account,
Expand Down
Loading