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.
close()actually succeed? Check thestatusfield in the transaction receipt. My guess is thatclosefailed, so presumably the signature is wrong in some way. (An simple way to verify would be to remove therequireinclose()that checks the signature and see if that makes the transaction work.) – user19510 Jul 16 '18 at 20:44function isValidSignature(uint256 amount, bytes signature)returnsfalse. However, I checked signature verification byJavaScriptcode (function isValidSignature(contractAddress, amount, signature, expectedSigner)) and it returnstrue. As screen shot shows, I calledcloseby the sameamountand the samesignature. Meanwhile, do not we need to insertcontract addressas input parameter infunction close?(like JavaScript code?)I'll check it again tomorrow and share the result here. (I callclosewithoutrequire) I hope it works. Thanks – Questioner Jul 16 '18 at 21:26close(), your contract address is wrong. (You've used therecipient's address instead of the contract's.) – user19510 Jul 16 '18 at 21:34recipient addressasfrom. because I thought themsg.sendermust berecipient address. I'm wrong ? Thanks – Questioner Jul 16 '18 at 21:43fromwhen 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:440x2e8c7b208503759e2CF4a9EDA8Ae52e816dc3d38... Sorry ... I test it again. Thank you very much and I hope tomorrow I gettrue. Thanks – Questioner Jul 16 '18 at 21:48VM Exception while processing transaction: revert. I shared what I did in text and updated my question. Thank you – Questioner Jul 17 '18 at 12:28signer address. Thank you – Questioner Jul 17 '18 at 14:56returnMsgI used :var receipt = web3.eth.getTransactionReceipt('0xb6dc221924f4ce6fffe184297b68649cf5ec2ab64c8413a20c9f0d1d8233f04c').then(console.log);to gettx statusandcontractAddress: nullis it normal ? and where is value ofmessagethat is returned ? Thanks – Questioner Jul 17 '18 at 15:31returnMsg()function, transactions don't have return values. You need tocallthe function rather than send a transaction. If you make the function aviewfunction, this will just happen automatically. – user19510 Jul 17 '18 at 21:25return recoverSigner(message, signature)to be sure whichvalue / addressis returned. Do you think it is possible ? Thanks – Questioner Jul 17 '18 at 21:30viewfunction. – user19510 Jul 17 '18 at 21:31Ethereum: Signing and Validatingcode HERE : link that is mentioned : "NOTE: THIS WILL NOT WORK ON TESTRPC" without mentioning the reason. – Questioner Jul 17 '18 at 22:24close. The signature is verified by JS code, however, in Solidity apparently does not work. So, does it mean that I cannot usetestrpcfor signing payment ? Btw I do not use MetaMast. Thanks – Questioner Jul 17 '18 at 22:34web3 = 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 implementedweb3.personal.signat all. Did you modify the code from the blog post? If so, please just share your whole code. – user19510 Jul 17 '18 at 22:40web3.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:46testrpc, 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 in1bor1c. You should also really switch toganache-cli. (testrpcis deprecated and stale.) – user19510 Jul 18 '18 at 12:36web3I useweb3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));in Javascript file. Thanks – Questioner Jul 18 '18 at 12:39JavaScriptcode without any modification ? (instead of usingnode filename.js?) to generate signature? And also in this case is it not easier to useRopsteninstead ofganache-cli? Thanks – Questioner Jul 18 '18 at 12:46ganache-cliis just fine (and faster than Ropsten). MetaMask can be pointed at localhost:8545. – user19510 Jul 18 '18 at 12:48web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));andrequire('ethereumjs-abi')andrequire('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.Ropstenby MetaMask ? Thanks – Questioner Jul 18 '18 at 12:56ganache-cliit 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