0

I tested signature verification of the code presented HERE as follows:

running testRPC :

root@ubuntu:/home/s# testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0xa1fe30d3b82fd7c563b6fc9c44f3990bfcef3ed4
(1) 0xfadc848eea30a47a19ec8a62a42471de3e073b89
(2) 0x162e8acc48158f4e8961865f09f8d029d625161f
(3) 0x8f132b6c06b8a93ebc8689371c44fc8271ac9d73
(4) 0x4741548cb9a157522b353e94b9393ec537b1e352
(5) 0x6f786e92be3b0da9142f657a391bf7f91ae81910
(6) 0x3e90f71ea98ea71957f0cc9a1a5c4e0ad530a381
(7) 0xb3032b6bad1406fd6041839c4c9a0e544d3bd9a5
(8) 0x10f4925ac27d37df3ca1e8a4df37d3a94645a1a3
(9) 0x63021d2642b7f7581b963408436f69a6329195a8

Private Keys
==================
(0) f9059a43ab6d30687a95a5dcd2551b02f98a6abc557cacf17fb5a7586c30fc9e
(1) b74d7dad53e04db199f06f57f4e53e4bb0914d9d5118d367b6f7a39998bb45b7
(2) 837d2cb30033f7c218d6cd6fd4f7703f950495dae89c797e7dd01c6a469ed64c
(3) f7c1d7df52b765609315127f664363fea5df108bf414788e6eb4905005638b5e
(4) ec4b55a3ae654a5c2aeeeeb068c47c84808065a4a466148844763e0bdf7e87cf
(5) d7e75efcb275f2496790c86b9a5951ab86a9361d0e55604f9409cd9a6844b595
(6) adee6af8313963b68ce440d4ade36e7e1163d2bb9dd351152d73c6aee1b5ae95
(7) 6f3b9662e341aaae1131522c691834e0eb4a8a7987b8c39f412334ea4fc8050d
(8) d48189838281fd47802fe755767531433af116428379fdabadcf92779a6e157c
(9) acde15308f46d5f96ad229eef0a8b6b20e864e05cbf7063e83249cd1b6080425

HD Wallet
==================
Mnemonic:      other shell crater issue merit system age kitchen south mother lecture embrace
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

And I chose the following addresses as signer / sender and recipient :

sender address = 0xa1fe30d3b82fd7c563b6fc9c44f3990bfcef3ed4
recipient address = 0xfadc848eea30a47a19ec8a62a42471de3e073b89

Deploying contract:

> const thisContract = new web3.eth.Contract(abi);
undefined
> 
> thisContract.deploy({  
... 
...         data: bytecode,
... 
...         arguments: [ "0xfadc848eea30a47a19ec8a62a42471de3e073b89", 2592000]
... 
...     }).send({
... 
...        from: "0xa1fe30d3b82fd7c563b6fc9c44f3990bfcef3ed4",
... 
...        gas: 5000000 ,
... 
...        gasPrice: '3000000000',
... 
...        value: 5000 
... 
...     },
... 
...     function(error, transactionHash) {
... 
...         console.log(error);
... 
...         console.log(transactionHash);
... 
...         console.log('function exec');
... 
...     }).then(function(newContractInstance) {
... 
...     console.log('Contract Instance:' + newContractInstance.options.address);
... 
... });
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: 
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> null
0xee77c9011a4fb4d18cdf6804007ddc6b2b05828c7b8cac28f784f7b26c0021ef
function exec
Contract Instance:0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5

Getting contract balance :

> web3.eth.getBalance('0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5').then(console.log) // contract
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: 
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> 5000

Here is JavaScript signing file (Note: I run this code after running testRPC and because of receiving some errors I had to modify some parts of code(I mentioned list of modifications at top of the code (Please see: Modifications are as follows:))):

// Modifications are as follows:
// ethereumjs.ABI.soliditySHA3 -> ethereumjs.soliditySHA3
// web3.personal.sign -> web3.eth.sign
// web3.eth.defaultAccount -> "<signer address>" This address is changed depending on addresses generated by testRPC
// ethereumjs.ABI.soliditySHA3 -> ethereumjs.soliditySHA3
// ethereumjs.Util.fromRpcSig -> ethJsUtil.fromRpcSig
// ethereumjs.Util.ecrecover -> ethJsUtil.ecrecover
// ethereumjs.Util.pubToAddress -> ethJsUtil.pubToAddress
//ethereumjs.Util.stripHexPrefix -> ethJsUtil.stripHexPrefix

Web3 = require("web3")
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

var ethereumjs = require('ethereumjs-abi') //ethereumjs->ethjsabi
var ethJsUtil = require('ethereumjs-util') //ethJsUtil->ethjsutil

function constructPaymentMessage(contractAddress, amount) {
  return ethereumjs.soliditySHA3(
    ["address", "uint256"],
    [contractAddress, amount],
  );
}

function signMessage(message, callback) {

    return sig = web3.eth.sign("0x" + message.toString("hex"),
    "<signer address: This address is changed depending on addresses generated by testRPC>").
    then(console.log);

//console.log("signature= ", sig);

}


function signPayment(contractAddress, amount, callback) {

    var message = constructPaymentMessage(contractAddress, amount);

    sig = signMessage(message, callback);

    //console.log("Signature= ", sig);
}

//

function prefixed(hash) {
  return ethereumjs.soliditySHA3(
    ["string", "bytes32"],
    ["\x19Ethereum Signed Message:\n32", hash]
  );
}

function recoverSigner(message, signature) {
  //var split = ethereumjs.Util.fromRpcSig(signature);  

  var split = res = ethJsUtil.fromRpcSig(signature);

  var publicKey = ethJsUtil.ecrecover(message, split.v, split.r, split.s);
  var signer = ethJsUtil.pubToAddress(publicKey).toString("hex");
  return signer;
}

function isValidSignature(contractAddress, amount, signature, expectedSigner) {

  var message = prefixed(constructPaymentMessage(contractAddress, amount));

  var signer = recoverSigner(message, signature);

  console.log("Signer Address= ", signer);

  return signer.toLowerCase() ==
    ethJsUtil.stripHexPrefix(expectedSigner).toLowerCase();
}

signPayment('<contract address>', 50, function(err, something) {});
//signature= 

//result = isValidSignature('<contract address>', 50,
//"<signature>", "<signer address>");

//console.log("Signature Verification Result= ", result);

Signing :

After inserting the values of signer address and contract address in above code, I run it : node filename.js and I get the signature.

signPayment('0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5', 50, function(err, something) {}); 
    //signature= 0xb6dec803b061504e22bccccffdfe26980ddaa33d650268361bec5ef4bd4a863f6ff2ee7d524f63cd82ef5634a063cd5e21f164c2ab1a6c952131308d6f46fb1200

This signature using JavaScript is verified however apparently by Solidity code it is not verified:

result = isValidSignature('0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5', 50,  "0xb6dec803b061504e22bccccffdfe26980ddaa33d650268361bec5ef4bd4a863f6ff2ee7d524f63cd82ef5634a063cd5e21f164c2ab1a6c952131308d6f46fb1200", "0xa1fe30d3b82fd7c563b6fc9c44f3990bfcef3ed4");
console.log("Signature Verification Result= ", result);
//result = true (Note that this verification is done using JavaScript and ethereumjs and not by Solidity that is my  problem)

Call function close to transfer ether and verifying signature and destroy contract

> var contractInstance = new web3.eth.Contract(abi, '0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5');
undefined
> 
> contractInstance.options.address;
'0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5'
> 
> 
> contractInstance.methods.close(50, "0xb6dec803b061504e22bccccffdfe26980ddaa33d650268361bec5ef4bd4a863f6ff2ee7d524f63cd82ef5634a063cd5e21f164c2ab1a6c952131308d6f46fb1200").send({ from: '0xfadc848eea30a47a19ec8a62a42471de3e073b89' }, function(error, result) {
... 
...     console.log(error);
... 
...     console.log(result)
... 
... });
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: 
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] },
  _events: undefined,
  emit: [Function: emit],
  on: [Function: on],
  once: [Function: once],
  off: [Function: removeListener],
  listeners: [Function: listeners],
  addListener: [Function: on],
  removeListener: [Function: removeListener],
  removeAllListeners: [Function: removeAllListeners] }
> Error: Returned error: VM Exception while processing transaction: revert
    at Object.ErrorResponse (/home/s/node_modules/web3-core-helpers/src/errors.js:29:16)
    at /home/s/node_modules/web3-core-requestmanager/src/index.js:140:36
    at XMLHttpRequest.request.onreadystatechange (/home/s/node_modules/web3-providers-http/src/index.js:77:13)
    at XMLHttpRequestEventTarget.dispatchEvent (/home/s/node_modules/xhr2/lib/xhr2.js:64:18)
    at XMLHttpRequest._setReadyState (/home/s/node_modules/xhr2/lib/xhr2.js:354:12)
    at XMLHttpRequest._onHttpResponseEnd (/home/s/node_modules/xhr2/lib/xhr2.js:509:12)
    at IncomingMessage.<anonymous> (/home/s/node_modules/xhr2/lib/xhr2.js:469:24)
    at IncomingMessage.emit (events.js:185:15)
    at IncomingMessage.emit (domain.js:440:23)
    at endReadableNT (_stream_readable.js:1106:12)
    at process._tickCallback (internal/process/next_tick.js:178:19)
undefined

Error:

error: VM Exception while processing transaction: revert

And here is testrpc message :

eth_sendTransaction

  Transaction: 0x2dbc6d601e48f6cab5327f162ff415d4e0b11264685a7ceed90de8d77adc2d8a
  Gas usage: 31867
  Block Number: 12
  Block Time: Tue Jul 17 2018 13:56:35 GMT+0200 (CEST)
  Runtime Error: revert

And when I get contract balance it's still 5000 :

> web3.eth.getBalance('0xa6D8893aAF5121AEfb496aB305e0DA42F71aAaa5').then(console.log) // contract 
Promise {
  <pending>,
  domain: 
   Domain {
     domain: null,
     _events: 
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> 5000

Note 1:

When I remove signature from function close as follows, transferring ether is done, since when I get contract balance it becomes zero. However, with signature I receive the above error message.

function close(uint256 amount) public {

        recipient.transfer(amount);
        selfdestruct(sender);
    }

Note 2:

Meanwhile I added a new function as follow to get value of message in :

bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));

by which I can get value of recoverSigner(message, signature) :

So I defined following function and deployed contract again :

function returnMsg(uint256 amount) public

    returns (bytes32)

    {
        bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));
        return message;
    }

However, I could not get value of message and I only received transaction receipt as follows:

    > contractInstance.methods.returnMsg(50).send({ from: '0x39969849de82f978e9f356ffa5516f8d25f44ddc' }, function(error, result) {
    ... 
    ...     console.log(error);
    ... 
    ...     console.log(result)
    ... 
    ... });
    Promise {
      <pending>,
      domain: 
       Domain {
         domain: null,
         _events: 
          { removeListener: [Function: updateExceptionCapture],
            newListener: [Function: updateExceptionCapture],
            error: [Function: debugDomainError] },
         _eventsCount: 3,
         _maxListeners: undefined,
         members: [] },
      _events: undefined,
      emit: [Function: emit],
      on: [Function: on],
      once: [Function: once],
      off: [Function: removeListener],
      listeners: [Function: listeners],
      addListener: [Function: on],
      removeListener: [Function: removeListener],
      removeAllListeners: [Function: removeAllListeners] }
    > null
    0xb6dc221924f4ce6fffe184297b68649cf5ec2ab64c8413a20c9f0d1d8233f04c

    // And when I get transaction status "contract address is "null" : "

    > var receipt = web3.eth.getTransactionReceipt('0xb6dc221924f4ce6fffe184297b68649cf5ec2ab64c8413a20c9f0d1d8233f04c').then(console.log);
undefined
> { transactionHash: '0xb6dc221924f4ce6fffe184297b68649cf5ec2ab64c8413a20c9f0d1d8233f04c',
  transactionIndex: 0,
  blockHash: '0xf65f637eba42b5fb29dfa051ea8d3c1bc876395210fe635b3dd7333a8a7f7307',
  blockNumber: 2,
  gasUsed: 22861,
  cumulativeGasUsed: 22861,
  contractAddress: null,
  logs: [],
  status: true }

Important Note: Please note that verifying signature using JavaScript is verified however apparently by Solidity code it is not verified and I think because of this transaction is reverted, Since as I mentioned in my question, without signature verification ether is transferred and contract is destroyed successfully.

29
  • 1
    Did the call to close() actually succeed? Check the status field in the transaction receipt. My guess is that close failed, so presumably the signature is wrong in some way. (An simple way to verify would be to remove the require in close() that checks the signature and see if that makes the transaction work.)
    – user19510
    Jul 16, 2018 at 20:44
  • 1
    In the future, please share text, not screenshots. It's much harder to read the screenshots and impossible to copy/paste. (And of course blind users have no way of knowing what's in there.) It looks like in the screenshot where you're calling close(), your contract address is wrong. (You've used the recipient's address instead of the contract's.)
    – user19510
    Jul 16, 2018 at 21:34
  • 1
    from when sending the transaction is correct. It's the contract address that's wrong: new web3.eth.Contract(abi, <wrong address here>.
    – user19510
    Jul 16, 2018 at 21:44
  • 1
    Regarding your returnMsg() function, transactions don't have return values. You need to call the function rather than send a transaction. If you make the function a view function, this will just happen automatically.
    – user19510
    Jul 17, 2018 at 21:25
  • 1
    Yes, it's possible. Just make it a view function.
    – user19510
    Jul 17, 2018 at 21:31

1 Answer 1

0

You should test your signature checking function alone by means of specific test. Did it works? Integrate it only when you are sure it works.

Your Answer

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

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