简体   繁体   English

从 go-ethereum 实现 Ethereum Personal_sign (EIP-191) 给出了与 ethers.js 不同的签名

[英]Implementing Ethereum personal_sign (EIP-191) from go-ethereum gives different signature from ethers.js

I am attempting to generate a personal_sign in Golang like its implemented in ethers.js.我试图在 Golang 中生成一个personal_sign ,就像它在 ethers.js 中实现的一样。 Similar question but that ended up using the regular sign over the personal sign_implementation . 类似的问题,但最终在个人sign_implementation上使用了常规sign

Ethers醚类

// keccak256 hash of the data
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) //0x8d218fc37d2fd952b2d115046b786b787e44d105cccf156882a2e74ad993ee13

signHash := signHash(dataHash.Bytes())

signatureBytes, err := crypto.Sign(signHash.Bytes(), privateKey)
if err != nil {
    log.Fatal(err)
}


// signatureBytes 0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00

As you can see the hash is the same, but the signature is different:如您所见,哈希值相同,但签名不同:

0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c Ethers 0x469b07327fc41a2d85b7e69bcf4a9184098835c47cc7575375e3a306c3718ae35702af84f3a62aafeb8aab6a455d761274263d79e7fc99fbedfeaf759d8dc9361c

0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00 Golang 0xec56178d3dca77c3cee7aed83cdca2ffa2bec8ef1685ce5103cfa72c27beb61313d91b9ad9b9a644b0edf6352cb69f2f8acd25297e3c64cd060646242e0455ea00 Golang

Looking at the source code of Ethers.js I can't find anything different aside how the padding is managed.查看 Ethers.js 的源代码,除了填充的管理方式之外,我找不到任何不同的东西。

Edit Check the approved answer编辑检查批准的答案

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

There is a bug in the JavaScript code. JavaScript 代码中存在错误。

From the documentation of signer.signMessage() (see the Note section), it appears that a string is UTF8 encoded and binary data must be passed as TypedArray or Array .signer.signMessage()的文档(请参阅注释部分)来看,字符串似乎是 UTF8 编码的,并且二进制数据必须作为TypedArrayArray传递。
The Keccak hash is returned hex encoded, ie as string, and is therefore UTF8 encoded, which is incorrect. Keccak 哈希返回十六进制编码,即作为字符串,因此是 UTF8 编码,这是不正确的。 Instead, it must be converted to a TypedArray .相反,它必须转换为TypedArray For this purpose the library provides the function ethers.utils.arrayify() .为此,库提供了函数ethers.utils.arrayify()

The following JavaScript is based on the posted code, but performs the required hex decoding:以下 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>

which produces the following signature:产生以下签名:

0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d1c

The Go code below is based on the unmodified posted Go code, but using key and data from the JavaScript code for a comparison:下面的 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))
}

The Go Code gives the following signature: Go 代码给出了以下签名:

0xfcc3e9431c139b5f943591af78c280b939595ce9df66210b7b8bb69565bdd2af7081a8acc0cbb5ea55bd0d673b176797966a5180c11ac297b7e6344c5822e66d01

Both signatures match except for the last byte.除了最后一个字节,两个签名都匹配。

The JavaScript code returns the signature in the format r|s|v (see here ). JavaScript 代码以r|s|v格式返回签名(请参阅此处)。 v is one byte in size and is just the value in which both signatures differ. v大小为 1 个字节,只是两个签名不同的值。

It is v = 27 + rid where rid is the recovery ID.它是v = 27 + rid ,其中rid是恢复ID。 The recovery ID has values between 0 and 3, so v has values between 27 and 30 or 0x1b and 0x1e (see here ).恢复 ID 的值介于 0 和 3 之间,因此v值介于 27 和 30 或 0x1b 和 0x1e 之间(请参阅此处)。

The Go code, on the other hand, returns the recovery ID in the last byte instead of v .另一方面,Go 代码在最后一个字节而不是v返回恢复 ID。 So that the signature of the Go code matches that of the JavaScript code in the last byte as well, the recovery ID must be replaced by v :为了使 Go 代码的签名也与最后一个字节中的 JavaScript 代码的签名相匹配,恢复 ID 必须替换为v

signatureBytes[64] += 27
fmt.Println("0x" + hex.EncodeToString(signatureBytes))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Go-Ethereum 中的两个未知参数 function - two unknow parameters in Go-Ethereum function 在 go-ethereum 安装中创建命令? - Make command in go-ethereum installation? 使用 glide 安装 go-ethereum 依赖项和tendermint 依赖项 - install go-ethereum dependencies and tendermint dependencies with glide 如何通过 go-ethereum 创建 eth 帐户? - How can I create eth account via go-ethereum? 为什么命令“ go clean -n -r -igithub.com/ethereum/go-ethereum…”不起作用? - Why command “go clean -n -r -i github.com/ethereum/go-ethereum…” does not work? 如何将 AWS 区块链服务与 go-ethereum 一起使用? - How to use the AWS blockchain service with go-ethereum? 没有 abi 的 go-ethereum 编码或解码输入数据 - go-ethereum encoding or decoding input data without abi 如何从Go调用以太坊合约? - How to call an Ethereum contract from Go? Istanbul-tools安装错误:github.com/ethereum/go-ethereum/crypto/bn256/cloudflare.gfpMul:重定位目标runtime.support_bmi2未定义 - Istanbul-tools installation error: github.com/ethereum/go-ethereum/crypto/bn256/cloudflare.gfpMul: relocation target runtime.support_bmi2 not defined “不支持交易类型”尝试使用 Go-Ethereum、Solidity、Go 部署简单合约时。 不会发生在 Remix 中 - "transaction type not supported" When trying to deploy a simple contract using Go-Ethereum, Solidity, Go. Doesn't happen in Remix
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM