Metamask

使用 pyethereum 驗證personal.sign 簽名

  • May 11, 2018

我正在嘗試使用 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方法。

引用自:https://ethereum.stackexchange.com/questions/27735