使用 pyethereum 驗證personal.sign 簽名
我正在嘗試使用 pyethereum 驗證來自 Metamask 的簽名消息。我似乎無法從簽名中恢復正確的地址。缺乏文件無濟於事。
在客戶端,我使用
web3.personal.sign()
.var signer = web3.eth.defaultAccount || web3.eth.accounts[0]; var original_message = "I am but a stack exchange post"; var message = "0x" + original_message.toHex(); var message_hash = web3.sha3('\u0019Ethereum Signed Message:\n' + message.length.toString() + message); var signature; web3.personal.sign(message, signer, function(err, res) { if (err) console.error(err); signature = res; console.log({ "signer": signer, "message": message, "message_hash": message_hash, "signature": signature, }) }); { message: "0x4920616d20627574206120737461636b2065786368616e676520706f7374" message_hash: "0x1a0126ceafb4579293016a4cc3ca0ec753c7d497cda8b3e6ece095c832d92590" signature: "0x0cf7e2e1cbaf249175b8e004118a182eb378a0b78a7a741e72a0a34e970b59194aa4d9419352d181a4d1827abbad279ad4f5a7b60da5751b82fec4dde6f380a51b" signer: "0x9283099a29556fcf8fff5b2cea2d4f67cb7a7a8b" }
然後我將簽名、消息的雜湊和地址發送到後端,我有這樣的東西:
>>> from ethereum.utils import ecrecover_to_pub, sha3 >>> from eth_utils.hexidecimal import encode_hex, decode_hex, add_0x_prefix >>> signer = "0x9283099a29556fcf8fff5b2cea2d4f67cb7a7a8b" >>> message_hash = "0x1a0126ceafb4579293016a4cc3ca0ec753c7d497cda8b3e6ece095c832d92590" >>> signature = "0x0cf7e2e1cbaf249175b8e004118a182eb378a0b78a7a741e72a0a34e970b59194aa4d9419352d181a4d1827abbad279ad4f5a7b60da5751b82fec4dde6f380a51b" >>> >>> r = int(signature[0:66], 16) >>> s = int(add_0x_prefix(signature[66:130]), 16) >>> v = int(add_0x_prefix(signature[130:132]), 16) >>> if v not in (27,28): ... v += 27 ... >>> pubkey = ecrecover_to_pub(decode_hex(message_hash), v, r, s) >>> assert(encode_hex(sha3(pubkey)[-20:]) == signer) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
我嘗試了幾種方法來解決編碼問題,但我認為我仍然遺漏了一些東西(希望很明顯)。有任何想法嗎?
Python 中的一切看起來都是正確的。JavaScript 中的消息雜湊生成不正確。
在 Javascript 中準備消息雜湊
消息雜湊必須使用原始消息生成,而不是先對其進行十六進制編碼。
實際上,這意味著替換這個 JavaScript:
var message = "0x" + original_message.toHex(); var message_hash = web3.sha3( '\u0019Ethereum Signed Message:\n' + message.length.toString() + message );
有了這個:
var message_hash = web3.sha3( '\u0019Ethereum Signed Message:\n' + original_message.length.toString() + original_message );
這將為您提供消息雜湊:
0x6e099d83ea72d1ef62e39a501fe000c1458ba5a511510a0e9348b0dfeb298803
當您使用該消息雜湊時,您將恢復正確的簽名者:
0x9283099a29556fcf8fff5b2cea2d4f67cb7a7a8b
一個更好的解決方案:
hashMessage()
使用 web3.js v1,您可以呼叫:
message_hash = web3.eth.accounts.hashMessage("I am but a stack exchange post")
.V1 處於測試階段,截至 2017 年 10 月
另一個常見錯誤
(來自問題的早期草稿)
在散列消息以進行恢復時省略前綴。
因為
web3.personal.sign()
為您添加了前綴,所以在對消息進行散列以進行恢復時很容易忘記前綴。最好的解決方案是再次將 web3.js v1 用於web3.eth.accounts.hashMessage()
.最簡單的選擇
從 Web3.py v4 開始,內置支持恢復消息簽名者,例如:
from web3.auto import w3 # If you have the original message, you need to hash it first from eth_account.messages import defunct_hash_message message_hash = defunct_hash_message(text=original_message) # If you begin with the message hash, start here: signer = w3.eth.account.recoverHash(message_hash, signature=signature)
為什麼叫它
defunct_hash_message
?– 不幸的是,消息標準沒有得到很好的支持。它在許多節點和硬體客戶端中的實現略有不同。有一些新的消息格式正在討論中,有望很快獲得更廣泛、更一致的採用。目前,Web3.py 僅顯式支持 geth 樣式的消息格式,使用defunct_hash_message
方法。