跳到主要内容
ecdsa.com
web3.py / eth-account / eth-keys (Python)Web3 钱包与签名

错误信息

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 字节——因此恢复根本无法开始。web3.py 的 `w3.eth.account.recover_message` 也会抛出同样的错误,因为它把处理委托给了 eth-account。

原因

修复方法

  1. 1.

    原样传入钱包返回的完整签名

    `personal_sign` / `eth_signTypedData_v4` 返回 65 字节的十六进制字符串;在整个流程中保持它完整不变。

    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. 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])

相关错误

浏览完整的签名错误库