1 回答
TA贡献1797条经验 获得超4个赞
JavaScript 代码中有一个错误。
从signer.signMessage()(参见注释部分)的文档中,字符串似乎是 UTF8 编码的,并且二进制数据必须作为TypedArrayor传递Array。
Keccak 散列返回十六进制编码,即作为字符串,因此是 UTF8 编码,这是不正确的。相反,它必须转换为TypedArray. 为此,库提供了函数ethers.utils.arrayify()。
以下 JavaScript 基于发布的代码,但执行所需的十六进制解码:
(async () => {
let privateKey = "0x8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f";
let dataToSign = {"data1":"value1","data2":"value2"};
let dataHash = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign))
);
dataHashBin = ethers.utils.arrayify(dataHash)
let wallet = new ethers.Wallet(privateKey);
let signature = await wallet.signMessage(dataHashBin);
document.getElementById("signature").innerHTML = signature; // 0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d1c
})();
<script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js" type="text/javascript"></script>
<p style="font-family:'Courier New', monospace;" id="signature"></p>
产生以下签名:
0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d1c
下面的 Go 代码基于未修改的已发布 Go 代码,但使用 JavaScript 代码中的键和数据进行比较:
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"encoding/hex"
"encoding/json"
"log"
)
func signHash(data []byte) common.Hash {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256Hash([]byte(msg))
}
func main() {
hexPrivateKey := "8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f"
dataMap := map[string]string{"data1":"value1","data2":"value2"}
dataToSign, _ := json.Marshal(dataMap)
privateKey, err := crypto.HexToECDSA(hexPrivateKey)
if err != nil {
log.Fatal(err)
}
dataHash := crypto.Keccak256Hash(dataToSign) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13
signHash := signHash(dataHash.Bytes())
signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)
if err != nil {
log.Fatal(err)
}
fmt.Println("0x" + hex.EncodeToString(signatureBytes))
}
Go 代码给出以下签名:
0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d01
除了最后一个字节外,两个签名都匹配。
JavaScript 代码以格式返回签名r|s|v(参见此处)。v大小为一个字节,只是两个签名不同的值。
这是恢复 IDv = 27 + rid所在的位置。rid恢复 ID 的值介于 0 和 3 之间,因此v值介于 27 和 30 或 0x1b 和 0x1e 之间(参见此处)。
另一方面,Go 代码在最后一个字节中返回恢复 ID,而不是v. 为了使 Go 代码的签名也与最后一个字节中的 JavaScript 代码的签名匹配,恢复 ID 必须替换为v:
signatureBytes[64] += 27
fmt.Println("0x" + hex.EncodeToString(signatureBytes))
- 1 回答
- 0 关注
- 267 浏览
添加回答
举报