猿问

从 go-ethereum 实现 Ethereum personal_sign (EIP-191)

我正在尝试在 Golang 中生成一个personal_sign,就像它在 ethers.js 中实现的一样。类似的问题,但最终使用常规sign而不是个人sign_implementation。


醚类


// keccak256 hash of the data

let dataHash = ethers.utils.keccak256(

  ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign))

);


//0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13


let signature = await wallet.signMessage(dataHash); // 0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c


戈朗:




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) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13


signHash := signHash(dataHash.Bytes())


signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)

if err != nil {

    log.Fatal(err)

}



// signatureBytes 0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00

如您所见,哈希相同,但签名不同:


0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c醚类


0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00戈朗


查看 Ethers.js 的源代码,除了填充的管理方式之外,我找不到任何不同之处。


编辑 检查批准的答案


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))

}


呼唤远方
浏览 423回答 1
1回答

繁星coding

JavaScript 代码中有一个错误。从signer.signMessage()(参见注释部分)的文档中,字符串似乎是 UTF8 编码的,并且二进制数据必须作为TypedArrayor传递Array。Keccak 散列返回十六进制编码,即作为字符串,因此是 UTF8 编码,这是不正确的。相反,它必须转换为TypedArray. 为此,库提供了函数ethers.utils.arrayify()。以下 JavaScript 基于发布的代码,但执行所需的十六进制解码:(async () => {&nbsp; &nbsp; let privateKey = "0x8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f";&nbsp; &nbsp; let dataToSign = {"data1":"value1","data2":"value2"};&nbsp; &nbsp; let dataHash = ethers.utils.keccak256(&nbsp; &nbsp; &nbsp; ethers.utils.toUtf8Bytes(JSON.stringify(dataToSign))&nbsp; &nbsp; );&nbsp; &nbsp; dataHashBin = ethers.utils.arrayify(dataHash)&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; let wallet = new ethers.Wallet(privateKey);&nbsp; &nbsp; let signature = await wallet.signMessage(dataHashBin);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; 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 mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "github.com/ethereum/go-ethereum/common"&nbsp; &nbsp; "github.com/ethereum/go-ethereum/crypto"&nbsp; &nbsp; "encoding/hex"&nbsp; &nbsp; "encoding/json"&nbsp; &nbsp; "log")func signHash(data []byte) common.Hash {&nbsp; &nbsp; msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)&nbsp; &nbsp; return crypto.Keccak256Hash([]byte(msg))}func main() {&nbsp; &nbsp; hexPrivateKey := "8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f"&nbsp; &nbsp; dataMap := map[string]string{"data1":"value1","data2":"value2"}&nbsp; &nbsp; dataToSign, _ := json.Marshal(dataMap)&nbsp; &nbsp; privateKey, err := crypto.HexToECDSA(hexPrivateKey)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; dataHash := crypto.Keccak256Hash(dataToSign) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13&nbsp; &nbsp; signHash := signHash(dataHash.Bytes())&nbsp; &nbsp; signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; 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] += 27fmt.Println("0x" + hex.EncodeToString(signatureBytes))
随时随地看视频慕课网APP

相关分类

Go
我要回答