def sign_message(self, signable_message: SignableMessage, private_key): r""" Sign the provided message. This API supports any messaging format that will encode to EIP-191_ messages. If you would like historical compatibility with :meth:`w3.eth.sign() <web3.eth.Eth.sign>` you can use :meth:`~eth_account.messages.encode_defunct`. Other options are the "validator", or "structured data" standards. (Both of these are in *DRAFT* status currently, so be aware that the implementation is not guaranteed to be stable). You can import all supported message encoders in ``eth_account.messages``. :param signable_message: the encoded message for signing :param private_key: the key to sign the message with :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey` :returns: Various details about the signature - most importantly the fields: v, r, and s :rtype: ~eth_account.datastructures.SignedMessage .. doctest:: python >>> msg = "I♥SF" >>> from eth_account.messages import encode_defunct >>> msghash = encode_defunct(text=msg) >>> msghash SignableMessage(version=b'E', header=b'thereum Signed Message:\n6', body=b'I\xe2\x99\xa5SF') >>> # If you're curious about the internal fields of SignableMessage, take a look at EIP-191, linked above # noqa: E501 >>> key = "0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364" >>> Account.sign_message(msghash, key) SignedMessage(messageHash=HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'), r=104389933075820307925104709181714897380569894203213074526835978196648170704563, s=28205917190874851400050446352651915501321657673772411533993420917949420456142, v=28, signature=HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c')) .. _EIP-191: https://eips.ethereum.org/EIPS/eip-191 """ message_hash = _hash_eip191_message(signable_message) return self._sign_hash(message_hash, private_key)
def get_ecrecover(self, signedData: str, data: str = None, dataHash: str = None): """ Get address associated with the signature (ecrecover) """ if (data is None) and (dataHash is None): cprint( "Missing Argument, either 'dataHash' or 'data' must be passed?", "red") return 0 try: if data is not None: from eth_account.messages import encode_defunct, _hash_eip191_message hex_message_hash = w3.toHex( _hash_eip191_message(encode_defunct(hexstr=data))) elif dataHash is not None: hex_message_hash = dataHash sig = w3.toBytes(hexstr=signedData) v, hex_r, hex_s = ( w3.toInt(sig[-1]), w3.toHex(sig[:32]), w3.toHex(sig[32:64]), ) address = w3.eth.account.recoverHash(hex_message_hash, signature=sig) # TODO: verify this! seems that sometimes it returns wrong address # test with : # data="0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" # signedData="0xe7225f986f192f859a9bf84e34b2b7001dfa11aeb5c7164f81a2bee0d79943e2587be1faa11502eba0f803bb0ee071a082b6fe40fba025f3309263a1eef52c711c" # Correct address: 0xb60e8dd61c5d32be8058bb8eb970870f07233155 //based on https://wiki.parity.io/JSONRPC-personal-module.html#personal_ecrecover # Returned address: 0x02F0D4b967a73D6907a221DB6106446F1d3d4CDB cprint("Address: {}".format(address), "green") # TODO: make this print pretty json cprint("r: {}\ns: {}\nv: {} ".format(hex_r, hex_s, v), "white") except Exception as e: cprint("failed to get address: {} \n".format(e), "yellow")
def recover_message(self, signable_message: SignableMessage, vrs=None, signature=None): r""" Get the address of the account that signed the given message. You must specify exactly one of: vrs or signature :param signable_message: the message that was signed :param vrs: the three pieces generated by an elliptic curve signature :type vrs: tuple(v, r, s), each element is hex str, bytes or int :param signature: signature bytes concatenated as r+s+v :type signature: hex str or bytes or int :returns: address of signer, hex-encoded & checksummed :rtype: str .. doctest:: python >>> from eth_account.messages import encode_defunct >>> from eth_account import Account >>> message = encode_defunct(text="I♥SF") >>> vrs = ( ... 28, ... '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', ... '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recover_message(message, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' # All of these recover calls are equivalent: # variations on vrs >>> vrs = ( ... '0x1c', ... '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', ... '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recover_message(message, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' >>> # Caution about this approach: likely problems if there are leading 0s >>> vrs = ( ... 0x1c, ... 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3, ... 0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce) >>> Account.recover_message(message, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' >>> vrs = ( ... b'\x1c', ... b'\xe6\xca\x9b\xbaX\xc8\x86\x11\xfa\xd6jl\xe8\xf9\x96\x90\x81\x95Y8\x07\xc4\xb3\x8b\xd5(\xd2\xcf\xf0\x9dN\xb3', # noqa: E501 ... b'>[\xfb\xbfM>9\xb1\xa2\xfd\x81jv\x80\xc1\x9e\xbe\xba\xf3\xa1A\xb29\x93J\xd4<\xb3?\xce\xc8\xce') # noqa: E501 >>> Account.recover_message(message, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' # variations on signature >>> signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c' # noqa: E501 >>> Account.recover_message(message, signature=signature) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' >>> signature = b'\xe6\xca\x9b\xbaX\xc8\x86\x11\xfa\xd6jl\xe8\xf9\x96\x90\x81\x95Y8\x07\xc4\xb3\x8b\xd5(\xd2\xcf\xf0\x9dN\xb3>[\xfb\xbfM>9\xb1\xa2\xfd\x81jv\x80\xc1\x9e\xbe\xba\xf3\xa1A\xb29\x93J\xd4<\xb3?\xce\xc8\xce\x1c' # noqa: E501 >>> Account.recover_message(message, signature=signature) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' >>> # Caution about this approach: likely problems if there are leading 0s >>> signature = 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c # noqa: E501 >>> Account.recover_message(message, signature=signature) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' """ message_hash = _hash_eip191_message(signable_message) return self._recover_hash(message_hash, vrs, signature)
def test_hashed_structured_data(message_encodings): structured_msg = encode_structured_data(**message_encodings) hashed_structured_msg = _hash_eip191_message(structured_msg) expected_hash_value_hex = "4e3c4173651b3054c0635dfae57a3b65ae1527ed0ba9ff76cecb6d9807687a7f" assert hashed_structured_msg.hex() == expected_hash_value_hex
def msg_hash(self): return _hash_eip191_message(self.msg)
def test_hashed_structured_data(message_encodings): structured_msg = encode_structured_data(**message_encodings) hashed_structured_msg = _hash_eip191_message(structured_msg) expected_hash_value_hex = "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" assert hashed_structured_msg.hex() == expected_hash_value_hex