1

In my contract I'm verifying if some data was correctly signed by the owner. It looks like this:

require(
    owner() ==
        ecrecover(keccak256(abi.encodePacked(this, aCustomId)), v, r, s),
        "owner should sign aCustomId"
    );

Which means i should be retrieving v, r, s from a backend, which I currently do like this:

import * as ethUtil from 'ethereumjs-util';

const hash = soliditySha3(
  'myContractAddress',
  '11111',
);
const signature = ethUtil.ecsign(
    ethUtil.toBuffer(hash),
    wallet.getPrivateKey(),
);

return {
      r: ethUtil.bufferToHex(signature.r),
      s: ethUtil.bufferToHex(signature.s),
      v: signature.v,
      aCustomId: 11111,
};
// return the v, r, s

however, it seems like it's not correct as I keep getting "owner should sign aCustomId".

How can i create the correct hash so my v,r,s parameters are correct for recovering later ?

I'm using solidity version 0.6.12

The contract is called something like this:

myContract.methods
    .someMethod(
        11111,
        vValueAsInt,
        "sValue",
        "rValue",
    )
    .send({ from: account }, function(
        error: any,
        transactionHash: any
    ) {
        console.log(transactionHash);
        console.log(error);
    });
4
  • Which version of solidity are you using ? Also please provide the js code which calls the contract function.
    – clement
    Nov 30, 2020 at 16:31
  • Solidity is version 0.6.12, added js code in the original post
    – Maxim
    Dec 1, 2020 at 6:28
  • Where is the code returning the r,s,v parameters ?
    – clement
    Dec 1, 2020 at 7:51
  • ah, that was on the backend, i also added that part now
    – Maxim
    Dec 1, 2020 at 12:18

1 Answer 1

1

Here is a working code with web3js 1.3.0, ethereumjs-utils 5.2.5 and solidity 0.6.12 :

Javascript part

//hash the data
const hash = web3.utils.soliditySha3(
myContractAddress,
aCustomId,
);
//prefix the hash 
const prefixedHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(hash));

//get the ECDSA signature and its r,s,v parameters
const privateKey = new Buffer(myPrivateKey, "hex")
const signature = ethUtil.ecsign(prefixedHash, privateKey);
r = ethUtil.bufferToHex(signature.r),
s = ethUtil.bufferToHex(signature.s),
v = signature.v;

//call the contract method
myContract.methods.verifySig(accountAddress, aCustomId, r, s, v) 
.send({ from: accountAddress }).on('receipt', function(receipt){
  console.log(receipt);
})

Note that we use hashPersonalMessage in addition to soliditySha3 in order to add a prefix to the data before signing it. This prefix is : "\x19Ethereum Signed Message:\n32". It's a security standard which prevents attacks such as a malicious attempt to make a user sign a transaction while making him believe that he is signing a message.

Smart contract

function verifySig(address owner, uint256 aCustomId, bytes32 r, bytes32 s, uint8 v) public view {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 hash = keccak256(abi.encodePacked(address(this), aCustomId));
bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash));
require(ecrecover(prefixedHash, v, r, s) == owner);
}

The two important things here are :

  • To get the address of the contract we use address(this) (and not this).
  • We do the same operations that we did in the backend : first we hash the data, and then we compute the prefixed hash.
0

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.