go-ethereum实现EIP-191 personal_sign与ethers.js签名不一致如何解决
Golang实现与ethers.js逻辑一致的personal_sign签名方案
要实现和ethers.js中wallet.signMessage逻辑完全一致的personal_sign签名,常规sign实现无法对齐结果,具体差异和修正方案如下:
原始实现对比
Ethers.js 实现代码
// 计算数据的keccak256哈希 let dataHash = ethers.utils.keccak256( ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign)) ); // 哈希结果:0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13 let signature = await wallet.signMessage(dataHash); // 生成签名示例:0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c
初始Golang实现(结果不一致)
func signHash(data []byte) common.Hash { msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) return crypto.Keccak256Hash([]byte(msg)) } privateKey, err := crypto.HexToECDSA(hexPrivateKey) if err != nil { log.Fatal(err) } dataHash := crypto.Keccak256Hash(dataToSign) // 哈希结果与Ethers.js完全一致:0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13 signHash := signHash(dataHash.Bytes()) signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey) if err != nil { log.Fatal(err) } // 生成签名示例:0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00
问题原因
两份实现的原始数据哈希完全一致,最终签名不同的核心原因是EIP-191签名前缀的拼接逻辑差异:初始Golang实现直接将字节数据拼接到前缀后,而ethers.js的signMessage方法接收哈希值时,会先将字节转为十六进制字符串再拼接前缀。
修正方案
正确的signHash实现如下,调整后生成的签名即可与ethers.js完全对齐:
func signHash(data []byte) common.Hash { hexData := hexutil.Encode(data) msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(hexData), hexData) return crypto.Keccak256Hash([]byte(msg)) }
内容的提问来源于stack exchange,提问作者Franco
相关产品推荐
相关产品推荐

