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

Metamask签名验证:Remix中ecrecover正常,Go-web3中失败

问题根源分析

你的问题核心在于字符串到以太坊原生类型的转换逻辑错误,尤其是bytes32类型的解码和v值的处理,导致合约接收到的参数和签名时的原始数据不匹配,最终恢复出错误地址。

1. bytes32参数转换错误

你直接将十六进制字符串(如0x...)用[]byte()转换为字节数组,这是错误的——[]byte("0xabc")会把每个字符(包括0、x、a等)转成对应的ASCII字节,而非把十六进制字符串解码为32字节的哈希值。正确做法是用hex.DecodeString先去掉0x前缀,再解码为字节数组。

2. v值的溢出与格式转换问题

MetaMask使用EIP-155签名时(对应Avax测试网ChainID 43113),返回的v值是chainId*2 + 35或chainId*2 + 36(即86261或86262),你直接转为uint8会导致数值溢出(uint8最大值为255)。另外Solidity的ecrecover要求v值为27或28,需先将EIP-155格式的v转换为标准格式。

3. 冗余代码清理

调用pure类型的合约函数无需设置账号和ChainID,这些代码完全多余,应删除。

修正后的Go代码

import (
    "encoding/hex"
    "strconv"
    "strings"
    "log"
    "github.com/ethereum/go-ethereum/common"
)

func VerifySignature(_data global.SignatureData, walletAddress string) bool {
    web3, err := web3.NewWeb3(global.AvaxRpcURL)
    if err != nil {
        log.Println(err)
        return false
    }

    tokenAddr := "0xF686F5D7165e8Ce1C606978F424a2DBd4a37e122"
    contract, err := web3.Eth.NewContract(constants.VerificationABI, tokenAddr)
    if err != nil {
        log.Println(err)
        return false
    }

    // 处理HashedMessage:解码十六进制字符串为bytes32
    msgHex := strings.TrimPrefix(_data.HashedMessage, "0x")
    msgBytes, err := hex.DecodeString(msgHex)
    if err != nil || len(msgBytes) != 32 {
        log.Println("invalid hashed message")
        return false
    }
    msg := [32]byte{}
    copy(msg[:], msgBytes)

    // 处理v值:转换为EIP-155标准格式的27/28
    vInt, err := strconv.Atoi(_data.V)
    if err != nil {
        log.Println(err)
        return false
    }
    avaxTestnetChainId := 43113
    standardV := vInt - 2*avaxTestnetChainId - 35
    if standardV != 27 && standardV != 28 {
        log.Println("invalid v value")
        return false
    }
    v := uint8(standardV)

    // 处理r值:解码十六进制字符串为bytes32
    rHex := strings.TrimPrefix(_data.R, "0x")
    rBytes, err := hex.DecodeString(rHex)
    if err != nil || len(rBytes) != 32 {
        log.Println("invalid r value")
        return false
    }
    r := [32]byte{}
    copy(r[:], rBytes)

    // 处理s值:解码十六进制字符串为bytes32
    sHex := strings.TrimPrefix(_data.S, "0x")
    sBytes, err := hex.DecodeString(sHex)
    if err != nil || len(sBytes) != 32 {
        log.Println("invalid s value")
        return false
    }
    s := [32]byte{}
    copy(s[:], sBytes)

    signer, err := contract.Call("VerifyMessage", msg, v, r, s)
    if err != nil {
        log.Println(err)
        return false
    }

    // 统一地址大小写避免匹配失败
    return strings.ToLower(signer.(common.Address).String()) == strings.ToLower(walletAddress)
}

额外注意事项

你的Solidity合约存在逻辑漏洞:标准以太坊消息签名会自动添加"\x19Ethereum Signed Message:\n32"前缀,当前合约仅对_hashedMessage做哈希,未拼接前缀,若MetaMask/web3js是按标准流程签名,需修正Solidity代码:

contract Verify {
    function VerifyMessage(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public pure returns (address) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        // 拼接前缀后再哈希
        bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
        address signer = ecrecover(prefixedHashMessage, _v, _r, _s);
        return signer;
    }
}

内容的提问来源于stack exchange,提问作者Thomas Parker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:10:36