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.

Questioner
  • 2,670
  • 2
  • 33
  • 62
  • 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 '18 at 20:44
  • @smarx Thank you, Yes, in my opinion function isValidSignature(uint256 amount, bytes signature) returns false. However, I checked signature verification by JavaScript code (function isValidSignature(contractAddress, amount, signature, expectedSigner)) and it returns true. As screen shot shows, I called close by the same amount and the same signature. Meanwhile, do not we need to insert contract address as input parameter in function close?(like JavaScript code?)I'll check it again tomorrow and share the result here. (I call close without require) I hope it works. Thanks – Questioner Jul 16 '18 at 21:26
  • 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 '18 at 21:34
  • @smarx Thanks. OK, Sure, I share text. Yes, I inserted recipient address as from. because I thought the msg.sender must be recipient address. I'm wrong ? Thanks – Questioner Jul 16 '18 at 21:43
  • 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 '18 at 21:44
  • @smarx Yes, You're right ... it must be 0x2e8c7b208503759e2CF4a9EDA8Ae52e816dc3d38 ... Sorry ... I test it again. Thank you very much and I hope tomorrow I get true. Thanks – Questioner Jul 16 '18 at 21:48
  • @smarx Hi, I tested contract again and this time I receive : VM Exception while processing transaction: revert. I shared what I did in text and updated my question. Thank you – Questioner Jul 17 '18 at 12:28
  • @smarx I added a new note (Note 2) to my question about how to get signer address. Thank you – Questioner Jul 17 '18 at 14:56
  • @smarx Sorry, concerning new added function : returnMsg I used : var receipt = web3.eth.getTransactionReceipt('0xb6dc221924f4ce6fffe184297b68649cf5ec2ab64c8413a20c9f0d1d8233f04c').then(console.log); to get tx status and contractAddress: null is it normal ? and where is value of message that is returned ? Thanks – Questioner Jul 17 '18 at 15:31
  • 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 '18 at 21:25
  • @smarx Thank you, my purpose is to get the output of return recoverSigner(message, signature) to be sure which value / address is returned. Do you think it is possible ? Thanks – Questioner Jul 17 '18 at 21:30
  • 1
    Yes, it's possible. Just make it a view function. – user19510 Jul 17 '18 at 21:31
  • @smarx There is a Ethereum: Signing and Validating code HERE : link that is mentioned : "NOTE: THIS WILL NOT WORK ON TESTRPC" without mentioning the reason. – Questioner Jul 17 '18 at 22:24
  • 1
    Are you using testrpc to sign? What's your web3 provider? (This would typically be MetaMask.) – user19510 Jul 17 '18 at 22:27
  • @smarx Yes, first I run testrpc (as I mention in the question) and then choose two generated addresses by testrpc as sender/signer and recipient. I use the same addresses for deploying contract and calling function close. The signature is verified by JS code, however, in Solidity apparently does not work. So, does it mean that I cannot use testrpc for signing payment ? Btw I do not use MetaMast. Thanks – Questioner Jul 17 '18 at 22:34
  • 1
    Could you please share the line that starts web3 = new Web3(...)? I want to know what's in there. If you're connecting directly to testrpc, then I'm a bit confused, because I didn't think testrpc implemented web3.personal.sign at all. Did you modify the code from the blog post? If so, please just share your whole code. – user19510 Jul 17 '18 at 22:40
  • 1
    Testrpc is fine, but you may need to use MetaMask (as you would in a real-world implementation) to connect to it. Some tweaks to the code could probably make a direct connection to testrpc work, but it's probably not worth doing (since you would never use that in a real implementation). – user19510 Jul 17 '18 at 22:41
  • @smarx Yes, since I was receiving some error, I modified a little the JS code (ex. in case of web3.personal.sign). At the moment, I do not have access to the code, but tomorrow morning I share the entire JS code with all modifications. I think the problem is going to be solved ! Thanks – Questioner Jul 17 '18 at 22:46
  • @smarx I shared JS code with all modification in question. Please see: "Here is JavaScript signing file...". If you need any other information please let me know. Thanks – Questioner Jul 18 '18 at 08:56
  • 1
    Indeed, the problem is that you're connecting directly to testrpc, which signs messages differently. As I said, it's possible to adapt the code to work, but there's no real point. (You'd just have to throw away those changes when you deployed the code for real.) You also appear to be using node.js, but it makes little sense to run this code there. If you really want to make this code run for some reason, modify the signatures you get to end in 1b or 1c. You should also really switch to ganache-cli. (testrpc is deprecated and stale.) – user19510 Jul 18 '18 at 12:36
  • @smarx and concerning web3 I use web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); in Javascript file. Thanks – Questioner Jul 18 '18 at 12:39
  • @smarx Thank you, So what is the best way to run your JavaScript code without any modification ? (instead of using node filename.js ?) to generate signature? And also in this case is it not easier to use Ropsten instead of ganache-cli ? Thanks – Questioner Jul 18 '18 at 12:46
  • 1
    In the browser, with MetaMask. ganache-cli is just fine (and faster than Ropsten). MetaMask can be pointed at localhost:8545. – user19510 Jul 18 '18 at 12:48
  • @smarx Thank you, and with browser, do I need to add web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); and require('ethereumjs-abi') and require('ethereumjs-util') at top of the code in Console of browser ? And also before running JS code in browser, do I need to connect to ex. Ropsten by MetaMask ? Thanks – Questioner Jul 18 '18 at 12:56
  • 1
    Those libraries have instructions for how to include them in script tags. Any tutorial on MetaMask will tell you how to use it. – user19510 Jul 18 '18 at 12:58
  • @smarx concerning ganache-cli it seems that I need to modify JavaScript code (as I did) and it may lead to generate signature differently. – Questioner Jul 18 '18 at 13:18
  • @smarx I appreciate if you could share a link which explains how to add those library in browser Console to generate signature. Thanks a lot – Questioner Jul 18 '18 at 13:38
  • 1
    https://github.com/ethereumjs/browser-builds/blob/master/README.md – user19510 Jul 18 '18 at 13:41
  • Comments are not for extended discussion; this conversation has been moved to chat. – Tjaden Hess Jul 18 '18 at 14:20

1 Answers1

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.

Rick Park
  • 3,194
  • 2
  • 8
  • 25