eth-keys: Common API for Ethereum key operations
eth-keys is a Python library providing a common API for Ethereum key operations, including private key, public key, and signature management. It is currently at version 0.7.0 and is actively maintained by the Ethereum Foundation, with development hosted on GitHub.
Common errors
-
ImportError: cannot import name 'force_text'
cause This error often occurs when `eth-keys` is updated, leading to an incompatibility with an older version of `eth-utils` or `web3.py` that still expects `force_text` which has been removed or refactored in newer `eth-utils` versions.fixUpgrade `web3.py` and other related Ethereum libraries to their latest compatible versions, or downgrade `eth-utils` to a version compatible with your `web3.py` installation (e.g., `<1.0.0` for older `web3.py` versions). If using `web3.py` version 4.x beta or later, `eth-utils` should be `1.0.0` or higher. ```bash pip install --upgrade web3 eth-utils eth-keys # If upgrading causes other issues, try downgrading eth-utils: pip install eth-utils==0.8.0 # or another version compatible with your web3.py ``` -
TypeError: invalid private key
cause This error is raised when instantiating `eth_keys.keys.PrivateKey` with input that is not a valid 256-bit private key (e.g., incorrect length, not a hexadecimal string, or not a byte string). Ethereum private keys are typically 64-character hexadecimal strings.fixEnsure the private key is a 32-byte (64-character hex) string or bytes object. It must be a valid number (not zero and less than the elliptic curve's order). ```python from eth_keys import keys import os # Correct: 32 bytes (64 hex characters) private_key_bytes = os.urandom(32) private_key = keys.PrivateKey(private_key_bytes) print(f"Valid Private Key: {private_key}") # If you have a hex string, ensure it's converted to bytes hex_private_key_str = '0x' + private_key_bytes.hex() private_key_from_hex = keys.PrivateKey(bytes.fromhex(hex_private_key_str[2:])) print(f"Valid Private Key from hex string: {private_key_from_hex}") # Incorrect example (too short, will raise TypeError) # try: # invalid_private_key = keys.PrivateKey(b'\x01\x02\x03') # except TypeError as e: # print(f"Error: {e}") ``` -
ModuleNotFoundError: No module named 'ethereum'
cause The `ethereum` package is deprecated and its functionalities have been split into more modular libraries like `eth-keys`, `eth-account`, `eth-utils`, `web3.py`, etc. This error indicates that code is trying to import from the old, monolithic `ethereum` package.fixInstall the specific `eth-` libraries you need (e.g., `eth-keys`, `eth-account`, `web3.py`) and update your import statements to reflect the new package structure. ```bash pip install eth-keys eth-account web3 ``` Then, update your imports from: ```python # Old (will cause error) from ethereum import keys ``` To: ```python # New (correct) from eth_keys import keys from eth_account import Account from web3 import Web3 ``` -
eth_keys.exceptions.BadSignature: Bad signature
cause This error occurs during signature verification (e.g., using `signature.verify_msg_hash` or `signature.recover_public_key_from_msg_hash`) when the provided signature does not correctly correspond to the message hash and the public key of the signer. This could be due to a corrupted signature, an incorrect message hash, or the wrong public key being used for verification.fixEnsure that the message, the message hash, the private key used for signing, and the public key used for verification are all consistent and correctly handled. The message should be hashed consistently before signing and verifying. ```python from eth_keys import keys from eth_utils import keccak # 1. Generate a private key private_key_bytes = os.urandom(32) pk = keys.PrivateKey(private_key_bytes) public_key = pk.public_key # 2. Prepare the message and hash it message = b'Hello, Ethereum!' message_hash = keccak(message) # 3. Sign the message hash signature = pk.sign_msg_hash(message_hash) # 4. Verify the signature with the public key and message hash try: # This method implicitly recovers the public key and checks it is_valid = signature.verify_msg_hash(message_hash, public_key) print(f"Signature valid: {is_valid}") # Or, recover public key and compare recovered_public_key = signature.recover_public_key_from_msg_hash(message_hash) print(f"Recovered Public Key matches: {recovered_public_key == public_key}") except keys.exceptions.BadSignature as e: print(f"Signature verification failed: {e}") # Example of incorrect usage (e.g., wrong message hash for verification) # wrong_message_hash = keccak(b'Wrong message') # try: # signature.verify_msg_hash(wrong_message_hash, public_key) # except keys.exceptions.BadSignature as e: # print(f"Expected error for wrong message hash: {e}") ```
Warnings
- breaking The library and PyPI package were renamed from `ethereum-keys` to `eth-keys` in November 2017. Ensure you are importing from `eth_keys` and not the old package name.
- gotcha For optimal performance, `eth-keys` defaults to using the `CoinCurveECCBackend` if the `coincurve` library is installed. However, `coincurve` is not automatically installed as a dependency and must be installed separately. If not installed, it falls back to the pure Python `NativeECCBackend`, which is slower.
- gotcha The `PublicKey` class constructor expects a 64-byte `bytes` string. Common public key formats like 65-byte (with a leading `\x04` byte) or 33-byte (compressed, starting with `\x02` or `\x03`) require pre-processing (e.g., stripping the first byte for 65-byte format or using `PublicKey.from_compressed_bytes`).
- gotcha A private key consisting of all zero bytes (`b'\x00' * 32`) is mathematically invalid for generating a public key in elliptic-curve cryptography. While an address might be derived and accept funds, any transaction signed with this 'private key' will be rejected by the network as having an 'invalid sender'.
- gotcha Never hardcode, commit to version control, or expose private keys directly in source code. Private keys are critical secrets that should be handled securely, ideally loaded from environment variables or a secure key management system.
Install
-
pip install eth-keys
Imports
- PrivateKey
from eth_keys.keys import PrivateKey
from eth_keys import keys; pk = keys.PrivateKey(...)
- PublicKey
from eth_keys import keys; pub_key = keys.PublicKey(...)
- Signature
from eth_keys import keys; signature = keys.Signature(...)
- KeyAPI
from eth_keys import KeyAPI
Quickstart
from eth_keys import keys
# Generate a new private key (random bytes in a real scenario)
pk_bytes = b'\x01' * 32 # Example private key bytes
pk = keys.PrivateKey(pk_bytes)
# Get the public key
pub_key = pk.public_key
# Sign a message
message = b'a message'
signature = pk.sign_msg(message)
# Verify the signature
is_valid = signature.verify_msg(message, pub_key)
print(f"Private Key: {pk.to_hex()}")
print(f"Public Key: {pub_key.to_hex()}")
print(f"Ethereum Address: {pub_key.to_checksum_address()}")
print(f"Signature: {signature.to_hex()}")
print(f"Signature Valid: {is_valid}")
# Recover public key from signature
recovered_pub_key = signature.recover_public_key_from_msg(message)
print(f"Recovered Public Key Matches: {recovered_pub_key == pub_key}")