エラーメッセージ
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 に委譲しているため、同じエラーを送出します。
発生する理由
v が別に保存された、または失われた
よくある`r` 列と `s` 列に保存するバックエンドや、署名をスライスするフロントエンドでは、最後のバイトが失われます。v がないと、公開鍵を一意に復元できません。
EIP-2098 のコンパクト署名
ときどき64 バイトのコンパクト署名は、リカバリビットを s の最上位ビットに折り込みます。eth-account はこれをそのままでは受け付けません。
16 進のテキストをバイト列として渡している
ときどき`signature_hex.encode()` を渡すと、65 バイトの署名ではなく 132 バイトの ASCII になります。16 進文字列そのものを渡すか、`0x` を除いて `bytes.fromhex(...)` を渡してください。
修正方法
- 1.
ウォレットが返した署名をそのまま渡す
`personal_sign` / `eth_signTypedData_v4` は 65 バイトの 16 進文字列を返します。最初から最後まで、そのままの形で受け渡してください。
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])