Signatures

The purpose of Ethereum accounts is to sign messages. Most generally, this task is fulfilled by Account.sign_message() which either takes the message as bytes or hex encoded:

>>> signature = account.sign_message(b'do it')
>>> signature
'0xdc1b25b5085ee83fcabed1c08902e42755aa94eb4f89c1c5def1b995911218014c3dc5ce9d5ec7f1b481a07a7bbab1abed7b30c1411f4e9728be50e9a1054f0000'
>>> from eth_utils import encode_hex
>>> assert account.sign_message(encode_hex(b'do it')) == signature

Commonly, the message is hashed before signing. If you’d like you can do this by yourself:

>>> from eth_utils import keccak
>>> hashed_message = keccak(b'do it')
>>> assert signature == account.sign_message(hashed_message, hash=False)

In some cases, human readable text is to be signed. Instead of passing it directly (where it would be interpreted as hex and hopefully lead to an exception), encode it first:

>>> import codecs
>>> message = codecs.encode('Drö Chönösön möt döm Köntröböss', 'utf-8')
>>> signature = account.sign_message(message)

To subsequently validate a signature two methods are available: Account.is_signer() checks if the account has signed the message and recover_signer() returns the signer’s address.

>>> from eth_accounts import recover_signer
>>> from eth_utils import is_same_address
>>> assert account.is_signer(signature, message)
>>> assert is_same_address(recover_signer(signature, message), account.address)

Following the signing function, here hash=False can be specified as well.

Often, not arbitrary messages but Ethereum transactions are to be signed. Of this, Account.sign_transaction() takes care. It expects the unsigned transaction to be passed as RLP-serializable object, implemented for example in pyethereum or in a basic form in this package. Finally, due to replay protection according to EIP-155 the target network id has to be specified:

>>> from eth_accounts import Transaction
>>> from eth_utils import decode_hex
>>> tx = Transaction(
...     nonce=0,
...     gasprice=30 * 10**9,
...     startgas=21000,
...     to=decode_hex('0x' + 20 * '00'),
...     value=10**18,
...     data=b'',
...     v=0, r=0, s=0  # the signature to calculate
... )
>>> account.sign_transaction(tx, network_id=1)  # main net
>>> tx.v, tx.r, tx.s
(37, 58532937890638004285825567298708718952681745693284428409123298183772432557576, 801127928671903595963053020012875996438042864362744490000919671501425252166)

Validating the signer of a transaction is faciliated by Account.is_sender() and recover_sender():

>>> assert account.is_sender(tx, network_id=1)
>>> from eth_accounts import recover_sender
>>> assert is_same_address(recover_sender(tx, network_id=1), account.address)