Skip to content

Commit 8175a19

Browse files
authored
Add unsafe_sign_hash migration path (#263)
1 parent e1ef1ef commit 8175a19

File tree

6 files changed

+96
-7
lines changed

6 files changed

+96
-7
lines changed

eth_account/account.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,8 +592,9 @@ def signHash(self, message_hash, private_key):
592592
Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
593593
which cannot accidentally sign a transaction.
594594
595-
.. CAUTION:: Deprecated for :meth:`~eth_account.account.Account.sign_message`.
596-
This method will be removed in v0.6
595+
.. CAUTION:: Deprecated for
596+
:meth:`~eth_account.account.Account.unsafe_sign_hash`.
597+
This method will be removed in v0.13
597598
598599
:param message_hash: the 32-byte message hash to be signed
599600
:type message_hash: hex str, bytes or int
@@ -604,12 +605,34 @@ def signHash(self, message_hash, private_key):
604605
:rtype: ~eth_account.datastructures.SignedMessage
605606
"""
606607
warnings.warn(
607-
"signHash is deprecated in favor of sign_message",
608+
"signHash is deprecated in favor of sign_message or unsafe_sign_hash"
609+
" depending on your use case",
608610
category=DeprecationWarning,
609611
stacklevel=2,
610612
)
611613
return self._sign_hash(message_hash, private_key)
612614

615+
@combomethod
616+
def unsafe_sign_hash(self, message_hash, private_key):
617+
"""
618+
Sign the provided hash.
619+
620+
.. WARNING:: *Never* sign a hash that you didn't generate,
621+
it can be an arbitrary transaction. For example, it might
622+
send all of your account's ether to an attacker.
623+
Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
624+
which cannot accidentally sign a transaction.
625+
626+
:param message_hash: the 32-byte message hash to be signed
627+
:type message_hash: hex str, bytes or int
628+
:param private_key: the key to sign the message with
629+
:type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
630+
:returns: Various details about the signature - most
631+
importantly the fields: v, r, and s
632+
:rtype: ~eth_account.datastructures.SignedMessage
633+
"""
634+
return self._sign_hash(message_hash, private_key)
635+
613636
@combomethod
614637
def _sign_hash(
615638
self,

eth_account/messages.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,9 @@ def defunct_hash_message(
331331
"""
332332
Convert the provided message into a message hash, to be signed.
333333
334-
.. CAUTION:: Intended for use with the deprecated
335-
:meth:`eth_account.account.Account.signHash`.
334+
.. CAUTION:: Intended for use with
335+
:meth:`eth_account.account.Account.unsafe_sign_hash`.
336+
or the deprecated :meth:`eth_account.account.Account.signHash`.
336337
This is for backwards compatibility only. All new implementations
337338
should use :meth:`encode_defunct` instead.
338339

eth_account/signers/base.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,26 @@ def signHash(self, message_hash):
5353
but without specifying the private key.
5454
5555
.. CAUTION:: Deprecated for
56-
:meth:`~eth_account.signers.base.BaseAccount.sign_message`.
57-
To be removed in v0.6
56+
:meth:`~eth_account.signers.base.BaseAccount.unsafe_sign_hash`.
57+
To be removed in v0.13
58+
59+
:param bytes message_hash: 32 byte hash of the message to sign
60+
"""
61+
62+
@abstractmethod
63+
def unsafe_sign_hash(self, message_hash):
64+
"""
65+
Sign the hash of a message.
66+
67+
.. WARNING:: *Never* sign a hash that you didn't generate,
68+
it can be an arbitrary transaction. For example, it might
69+
send all of your account's ether to an attacker.
70+
Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
71+
which cannot accidentally sign a transaction.
72+
73+
This uses the same structure
74+
as in :meth:`~eth_account.account.Account.unsafe_sign_hash`
75+
but without specifying the private key.
5876
5977
:param bytes message_hash: 32 byte hash of the message to sign
6078
"""

eth_account/signers/local.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ def signHash(self, message_hash):
7070
private_key=self.key,
7171
)
7272

73+
def unsafe_sign_hash(self, message_hash):
74+
return self._publicapi.unsafe_sign_hash(
75+
message_hash,
76+
private_key=self.key,
77+
)
78+
7379
def sign_message(self, signable_message):
7480
"""
7581
Generate a string with the encrypted key.

newsfragments/263.deprecation.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Provide an upgrade path to remove ``signHash``

tests/core/test_accounts.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,46 @@ def test_sign_message_against_sign_hash_as_hex(keyed_acct, message_bytes):
441441
assert signed_via_hash_hex == signed_via_message_hex
442442

443443

444+
@given(st.binary())
445+
def test_sign_message_against_unsafe_sign_hash_as_bytes(keyed_acct, message_bytes):
446+
# sign via hash
447+
msg_hash = defunct_hash_message(message_bytes)
448+
signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash)
449+
450+
# sign via message
451+
signable_message = encode_defunct(message_bytes)
452+
signed_via_message = keyed_acct.sign_message(signable_message)
453+
454+
assert signed_via_hash == signed_via_message
455+
456+
457+
@given(st.binary())
458+
def test_sign_message_against_unsafe_sign_hash_as_hex(keyed_acct, message_bytes):
459+
message_hex = to_hex(message_bytes)
460+
461+
# sign via hash
462+
msg_hash_hex = defunct_hash_message(hexstr=message_hex)
463+
signed_via_hash_hex = keyed_acct.unsafe_sign_hash(msg_hash_hex)
464+
465+
# sign via message
466+
signable_message_hex = encode_defunct(hexstr=message_hex)
467+
signed_via_message_hex = keyed_acct.sign_message(signable_message_hex)
468+
469+
assert signed_via_hash_hex == signed_via_message_hex
470+
471+
472+
@given(st.text())
473+
def test_sign_message_against_unsafe_sign_hash_as_text(keyed_acct, message_text):
474+
# sign via hash
475+
msg_hash = defunct_hash_message(text=message_text)
476+
signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash)
477+
478+
# sign via message
479+
signable_message = encode_defunct(text=message_text)
480+
signed_via_message = keyed_acct.sign_message(signable_message)
481+
assert signed_via_hash == signed_via_message
482+
483+
444484
@pytest.mark.parametrize(
445485
"message, key, expected_bytes, expected_hash, v, r, s, signature",
446486
(

0 commit comments

Comments
 (0)