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 thestatus
field in the transaction receipt. My guess is thatclose
failed, so presumably the signature is wrong in some way. (An simple way to verify would be to remove therequire
inclose()
that checks the signature and see if that makes the transaction work.)close()
, your contract address is wrong. (You've used therecipient
's address instead of the contract's.)from
when sending the transaction is correct. It's the contract address that's wrong:new web3.eth.Contract(abi, <wrong address here>
.returnMsg()
function, transactions don't have return values. You need tocall
the function rather than send a transaction. If you make the function aview
function, this will just happen automatically.view
function.