3

I am trying to recover the signer of a TypedData message (eip712).

I am able to sign and verify with ethersJS and validate in the contract as well.

With Go, I can sign and verify as well but the verification fail in the contract.

Turn out this code is working fine.

The TypedData;

signerData := TypedData{
        Types: Types{
            "Quote": []Type{
                {Name: "cost", Type: "uint256"},
                {Name: "deadline", Type: "uint256"},
            },
            "EIP712Domain": []Type{
                {Name: "name", Type: "string"},
                {Name: "chainId", Type: "uint256"},
                {Name: "version", Type: "string"},
                {Name: "verifyingContract", Type: "string"},
                {Name: "salt", Type: "string"},
            },
        },
        PrimaryType: "Quote",
        Domain: TypedDataDomain{
            Name:              "MyDomain",
            Version:           "1",
            ChainId:           ethMath.NewHexOrDecimal256(ChainId.Int64()),
            VerifyingContract: contractAddress.String(),
            Salt:              "0",
        },
        Message: TypedDataMessage{
            "cost":     "0",          
            "deadline": "1659095005", 
        },
    }

Encoding the data;

func EncodeForSigning(typedData TypedData) (hash common.Hash, err error) {
    domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())

    if err != nil {
        return
    }

    typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
    if err != nil {
        return
    }

    rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
    hash = common.BytesToHash(crypto.Keccak256(rawData))
    return
}

Sign TypedData;

func (s Signer) SignTypedData(typedData TypedData) (sig []byte, err error) {
    hash, err := EncodeForSigning(typedData)
    if err != nil {
        return
    }

    sig, err = crypto.Sign(hash.Bytes(), s.key)
    if err != nil {
        return
    }
    
    sig[64] += 27

    return
}

I am able to verify it in Go;

func Ecrecover(hash, signature []byte) (addr common.Address, err error) {
    sig := make([]byte, len(signature))
    copy(sig, signature)
    if len(sig) != 65 {
        err = fmt.Errorf("invalid length of signture: %d", len(sig))
        return
    }

    if sig[64] != 27 && sig[64] != 28 && sig[64] != 1 && sig[64] != 0 {
        err = fmt.Errorf("invalid signature type")
        return
    }
    if sig[64] >= 27 {
        sig[64] -= 27
    }

    recoverPub, err := crypto.Ecrecover(hash, sig)
    if err != nil {
        err = fmt.Errorf("can not ecrecover: %v", err)
        return
    }
    pubKey, err := crypto.UnmarshalPubkey(recoverPub)
    if err != nil {
        err = fmt.Errorf("can not unmarshal pubkey: %v", err)
        return
    }

    addr = crypto.PubkeyToAddress(*pubKey)
    return
}

However in the contract it fails, I have tried with ECDSA and _hashTypedDataV4, they both return the same address but it is wrong.

function validateQuote(uint256 _cost,uint256 _deadline, bytes memory _signature) public view returns (address) {

        Quote memory quote = Quote({
            cost: _cost,
            deadline: _deadline
        });

        bytes32 msghash = _hashTypedDataV4(hash(quote));

        uint8 v;
        bytes32 r;
        bytes32 s;

         (v, r, s) = splitSignature(_signature);

        (address signer, ) = ECDSA.tryRecover(msghash,_signature);

        console.log("v4 signer", signer);
        console.log("v", v);

        return (verifyQuote(quote, v,r,s)); 
    }

The Hexadecimal representation of the signature is the same in go and in solidity, however the signer returned by solidity is not valid.

V = 27

The go validation return the correct address

//validate signature
hash, err = EncodeForSigning(signerData)
signerAddress, err := Ecrecover(hash.Bytes(), sig)

fmt.Println("Signer Recovery", signerAddress) // good

What would be the cause of the failure on the contract side?

I was able to validate that same signature with Ethers where V=28 The same signature with go set's V to 27. Not sure it's related.

2
  • Any update on this? I am also facing same issue locally signing and verification is all good but contract side, verification is failing. May 2, 2023 at 15:59
  • Same here also failing on contract side
    – PopFendi
    Jul 26, 2023 at 11:58

1 Answer 1

0
ethers.utils.splitSignature(signature).compact

You can try this with ether.js after generating the signature with go.I'm not sure if it works, but you can try it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.