Repository: ethereum/eth-account

Issue:
signHash is deprecated in favor of sign_message
After I updated to the last version of `eth-account` I see this warning on my tests. At first I thought it would be just renaming the function, but I realized I cannot sign a custom hash anymore. I understand this can be a way of preventing users to sign hashes of transactions by mistake, but I think a developer should have the possibility of signing hashes and in my case I need it for some contracts that rely on signing hashes and not messages.

## How can it be fixed?

I would propose not deprecating the old method or adding a raw `SignableMessage` that allows to sign hashes.

It could be possible that there's a way to do it and I didn't figure it out, sorry if that's the case


Comments:

Patch:
diff --git a/eth_account/account.py b/eth_account/account.py
index 93fbc44..6590ec9 100644
--- a/eth_account/account.py
+++ b/eth_account/account.py
@@ -12,7 +12,6 @@ from typing import (
     Union,
     cast,
 )
-import warnings
 
 from eth_keyfile import (
     create_keyfile_json,
@@ -568,7 +567,7 @@ class Account:
             >>> # If you're curious about the internal fields of SignableMessage, take a look at EIP-191, linked above
             >>> key = "0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"
             >>> Account.sign_message(msghash, key)
-            SignedMessage(messageHash=HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'),
+            SignedMessage(message_hash=HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'),
              r=104389933075820307925104709181714897380569894203213074526835978196648170704563,
              s=28205917190874851400050446352651915501321657673772411533993420917949420456142,
              v=28,
@@ -582,7 +581,7 @@ class Account:
         return cast(SignedMessage, self._sign_hash(message_hash, private_key))
 
     @combomethod
-    def signHash(self, message_hash, private_key):
+    def unsafe_sign_hash(self, message_hash, private_key):
         """
         Sign the provided hash.
 
@@ -592,9 +591,6 @@ class Account:
             Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
             which cannot accidentally sign a transaction.
 
-        .. CAUTION:: Deprecated for :meth:`~eth_account.account.Account.sign_message`.
-            This method will be removed in v0.6
-
         :param message_hash: the 32-byte message hash to be signed
         :type message_hash: hex str, bytes or int
         :param private_key: the key to sign the message with
@@ -603,11 +599,6 @@ class Account:
           importantly the fields: v, r, and s
         :rtype: ~eth_account.datastructures.SignedMessage
         """
-        warnings.warn(
-            "signHash is deprecated in favor of sign_message",
-            category=DeprecationWarning,
-            stacklevel=2,
-        )
         return self._sign_hash(message_hash, private_key)
 
     @combomethod
@@ -624,7 +615,7 @@ class Account:
 
         (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash_bytes)
         return SignedMessage(
-            messageHash=msg_hash_bytes,
+            message_hash=msg_hash_bytes,
             r=r,
             s=s,
             v=v,
@@ -917,7 +908,7 @@ class Account:
             ... }
             >>> key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
             >>> signed_message = Account.sign_typed_data(key, domain_data, message_types, message_data)
-            >>> signed_message.messageHash
+            >>> signed_message.message_hash
             HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
 
             >>> # 1-argument usage
@@ -963,7 +954,7 @@ class Account:
             ...     },
             ... }
             >>> signed_message_2 = Account.sign_typed_data(key, full_message=full_message)
-            >>> signed_message_2.messageHash
+            >>> signed_message_2.message_hash
             HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
             >>> signed_message_2 == signed_message
             True
diff --git a/eth_account/datastructures.py b/eth_account/datastructures.py
index 14d6f9d..c0c41d2 100644
--- a/eth_account/datastructures.py
+++ b/eth_account/datastructures.py
@@ -26,7 +26,7 @@ class SignedTransaction(NamedTuple):
 
 
 class SignedMessage(NamedTuple):
-    messageHash: HexBytes
+    message_hash: HexBytes
     r: int
     s: int
     v: int
diff --git a/eth_account/messages.py b/eth_account/messages.py
index 721de31..2924e6f 100644
--- a/eth_account/messages.py
+++ b/eth_account/messages.py
@@ -229,7 +229,7 @@ def encode_structured_data(
 
         >>> signed_msg_from_dict == signed_msg_from_str == signed_msg_from_hexstr
         True
-        >>> signed_msg_from_dict.messageHash
+        >>> signed_msg_from_dict.message_hash
         HexBytes('0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2')
 
     .. _EIP-712: https://eips.ethereum.org/EIPS/eip-712
@@ -331,8 +331,8 @@ def defunct_hash_message(
     """
     Convert the provided message into a message hash, to be signed.
 
-    .. CAUTION:: Intended for use with the deprecated
-        :meth:`eth_account.account.Account.signHash`.
+    .. CAUTION:: Intended for use with
+        :meth:`eth_account.account.Account.unsafe_sign_hash`.
         This is for backwards compatibility only. All new implementations
         should use :meth:`encode_defunct` instead.
 
@@ -443,7 +443,7 @@ def encode_typed_data(
         >>> key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         >>> signable_message = encode_typed_data(domain_data, message_types, message_data)
         >>> signed_message = Account.sign_message(signable_message, key)
-        >>> signed_message.messageHash
+        >>> signed_message.message_hash
         HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
         >>> # the message can be signed in one step using Account.sign_typed_data
         >>> signed_typed_data = Account.sign_typed_data(key, domain_data, message_types, message_data)
@@ -494,7 +494,7 @@ def encode_typed_data(
         ... }
         >>> signable_message_2 = encode_typed_data(full_message=full_message)
         >>> signed_message_2 = Account.sign_message(signable_message_2, key)
-        >>> signed_message_2.messageHash
+        >>> signed_message_2.message_hash
         HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
         >>> signed_message_2 == signed_message
         True
diff --git a/eth_account/signers/base.py b/eth_account/signers/base.py
index 508cdca..5782815 100644
--- a/eth_account/signers/base.py
+++ b/eth_account/signers/base.py
@@ -44,18 +44,20 @@ class BaseAccount(ABC):
         """
 
     @abstractmethod
-    def signHash(self, message_hash):
+    def unsafe_sign_hash(self, message_hash):
         """
         Sign the hash of a message.
 
+        .. WARNING:: *Never* sign a hash that you didn't generate,
+            it can be an arbitrary transaction. For example, it might
+            send all of your account's ether to an attacker.
+            Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
+            which cannot accidentally sign a transaction.
+
         This uses the same structure
-        as in :meth:`~eth_account.account.Account.signHash`
+        as in :meth:`~eth_account.account.Account.unsafe_sign_hash`
         but without specifying the private key.
 
-        .. CAUTION:: Deprecated for
-            :meth:`~eth_account.signers.base.BaseAccount.sign_message`.
-            To be removed in v0.6
-
         :param bytes message_hash: 32 byte hash of the message to sign
         """
 
diff --git a/eth_account/signers/local.py b/eth_account/signers/local.py
index 7752426..b066ff6 100644
--- a/eth_account/signers/local.py
+++ b/eth_account/signers/local.py
@@ -64,8 +64,8 @@ class LocalAccount(BaseAccount):
             self.key, password, kdf=kdf, iterations=iterations
         )
 
-    def signHash(self, message_hash):
-        return self._publicapi.signHash(
+    def unsafe_sign_hash(self, message_hash):
+        return self._publicapi.unsafe_sign_hash(
             message_hash,
             private_key=self.key,
         )
diff --git a/newsfragments/260.removal.rst b/newsfragments/260.removal.rst
new file mode 100644
index 0000000..3f9abfe
--- /dev/null
+++ b/newsfragments/260.removal.rst
@@ -0,0 +1,1 @@
+Remove ``signHash`` in favor of ``unsafe_sign_hash``
diff --git a/newsfragments/265.removal.rst b/newsfragments/265.removal.rst
new file mode 100644
index 0000000..fe9c02d
--- /dev/null
+++ b/newsfragments/265.removal.rst
@@ -0,0 +1,1 @@
+Remove ``messageHash`` in favor of ``message_hash``


Test patch:
diff --git a/tests/core/test_accounts.py b/tests/core/test_accounts.py
index 5a7b023..1f12462 100644
--- a/tests/core/test_accounts.py
+++ b/tests/core/test_accounts.py
@@ -402,8 +402,7 @@ def test_eth_account_hash_message_hexstr(acct, message, expected):
 def test_sign_message_against_sign_hash_as_text(keyed_acct, message_text):
     # sign via hash
     msg_hash = defunct_hash_message(text=message_text)
-    with pytest.deprecated_call():
-        signed_via_hash = keyed_acct.signHash(msg_hash)
+    signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash)
 
     # sign via message
     signable_message = encode_defunct(text=message_text)
@@ -415,8 +414,7 @@ def test_sign_message_against_sign_hash_as_text(keyed_acct, message_text):
 def test_sign_message_against_sign_hash_as_bytes(keyed_acct, message_bytes):
     # sign via hash
     msg_hash = defunct_hash_message(message_bytes)
-    with pytest.deprecated_call():
-        signed_via_hash = keyed_acct.signHash(msg_hash)
+    signed_via_hash = keyed_acct.unsafe_sign_hash(msg_hash)
 
     # sign via message
     signable_message = encode_defunct(message_bytes)
@@ -431,8 +429,7 @@ def test_sign_message_against_sign_hash_as_hex(keyed_acct, message_bytes):
 
     # sign via hash
     msg_hash_hex = defunct_hash_message(hexstr=message_hex)
-    with pytest.deprecated_call():
-        signed_via_hash_hex = keyed_acct.signHash(msg_hash_hex)
+    signed_via_hash_hex = keyed_acct.unsafe_sign_hash(msg_hash_hex)
 
     # sign via message
     signable_message_hex = encode_defunct(hexstr=message_hex)
@@ -502,7 +499,7 @@ def test_eth_account_sign(
 ):
     signable = encode_defunct(text=message)
     signed = acct.sign_message(signable, private_key=key)
-    assert signed.messageHash == signed["messageHash"] == expected_hash
+    assert signed.message_hash == signed["message_hash"] == expected_hash
     assert signed.v == signed["v"] == v
     assert signed.r == signed["r"] == r
     assert signed.s == signed["s"] == s