[Ethereum] “UPDATED : ” “VM Exception while processing transaction: revert”

contract-developmentpaymentssoliditystate-channelstestrpc

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.

Best Answer

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.