Сообщение об ошибке
Unexpected recoverable signature length: Expected 65, but got 64 bytes
Также выводится как
- eth_utils.exceptions.ValidationError: Unexpected recoverable signature length: Expected 65, but got 64 bytes
Число байтов после «but got» — длина вашей подписи. Класс исключения — `eth_utils.exceptions.ValidationError`.
web3.py / eth-account / eth-keys (Python) — eth_utils.exceptions.ValidationError from Account.recover_message
Что это означает
Библиотека eth-keys (её используют eth-account и web3.py) собирает подпись с возможностью восстановления только из ровно 65 байт: r (32), s (32) и байт восстановления v. У переданной вами подписи другая длина (в этом случае 64 байта), поэтому восстановление не могло начаться. `w3.eth.account.recover_message` в web3.py выбрасывает ту же ошибку, потому что делегирует работу eth-account.
Почему это происходит
v сохранён отдельно или потерян
частоБэкенды, которые хранят столбцы `r` и `s`, или фронтенды, которые обрезают подпись, теряют последний байт. Без v открытый ключ нельзя восстановить однозначно.
Компактная подпись EIP-2098
иногда64-байтные компактные подписи упаковывают бит восстановления в старший бит s. eth-account не принимает их напрямую.
Hex-текст передан как байты
иногдаЕсли передать `signature_hex.encode()`, получится 132 байта ASCII вместо 65 байт подписи. Передавайте саму hex-строку или `bytes.fromhex(...)` без `0x`.
Как исправить
- 1.
Передавайте подпись целиком, как её вернул кошелёк
`personal_sign` / `eth_signTypedData_v4` возвращают 65-байтную hex-строку; передавайте её без изменений на всём пути.
python from eth_account import Account from eth_account.messages import encode_defunct # Pass the 0x-prefixed hex string exactly as the wallet returned it (132 hex chars + "0x"). signer = Account.recover_message(encode_defunct(text=message), signature=signature_hex) assert signer.lower() == expected_address.lower() - 2.
Разверните компактные подписи
Извлеките yParity из старшего бита второй половины и добавьте в конец v = 27 + yParity.
python def expand_eip2098(sig: bytes) -> bytes: """64-byte compact signature (r || yParityAndS) -> 65-byte r || s || v.""" r, ys = sig[:32], int.from_bytes(sig[32:], "big") y_parity = ys >> 255 s = ys & ((1 << 255) - 1) return r + s.to_bytes(32, "big") + bytes([27 + y_parity])