You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 04:24:03