Ethereum white paper:
See https://github.com/ethereum/wiki/wiki/Ethash, Ethereum's hash is a varient referred to as Keccak hash
function justHash(bytes data) public pure returns (bytes32) {
return keccak256(datea);
}
truffle(develop)> message = 'hello, world!'
'hello, world!'
truffle(develop)> hashMessage = web3.sha3(message);
'0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2'
truffle(develop)> vsale.justHash(message);
'0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2'
see
For remote (online) signature produced by a geth with prefix, consider code sample:
function buyWithKyc(uint8 v, bytes32 r, bytes32 s) public payable {
bytes32 senderAddressHash = keccak256(msg.sender);
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(prefix, senderAddressHash);
address recover = ecrecover(prefixedHash, v, r, s);
require(recover == kycSigner);
......
For offline signature produced by Web3j without prefix, consider code sample:
function buyWithKyc(uint8 v, bytes32 r, bytes32 s) public payable {
bytes32 senderAddressHash = keccak256(msg.sender);
address recover = ecrecover(senderAddressHash, v, r, s);
require(recover == kycSigner);
pragma solidity ^0.4.18;
contract SignVerify {
function verifyNoPrefix(bytes32 data, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
return ecrecover(data, v, r, s);
}
function prefixHash(bytes data) public pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(prefix, data);
return prefixedHash;
}
function justHash(bytes data) public pure returns (bytes32) {
return keccak256(data);
}
function verifyWithPrefix(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure returns(address) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(prefix, hash);
return ecrecover(prefixedHash, v, r, s);
}
}
NOTE: THIS IS TESTED ON A SIMULATOR (GANACHE), GETH HAS SLIGHTLY DIFFERENT BEHAVIOR
truffle(develop)> compile
truffle(develop)> migrate --reset
Using network 'develop'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x4057db833b2401281bc978588a1648d0fd0d5dfe1b03ab9c09346387aff950f7
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_token_sale.js
Replacing VscToken...
... 0x3436474f0d575259b655f5bd1d1e89734da7c1b2dffdc72c090261407fd7c485
VscToken: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Replacing VscSale...
... 0x66d235c9af41266b1f1dd5e92fe14693939142b496246b44d0e29b9111a39a12
VscSale: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
Saving successful migration to network...
... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
Saving artifacts...
Running migration: 3_sign_verify.js
Replacing SignVerify...
... 0xa4fba339ac0dc34159057acf49e18b141c5a18bbbfdceb2cea7916f39709d026
SignVerify: 0x9fbda871d559710256a2502a2517b794b482db40
Saving successful migration to network...
... 0xe3e7ffb1810175cddb3670470f6c955fcb30f380ca43fd15ed823fa93047f5ce
Saving artifacts...
truffle(develop)> SignVerify.deployed().then(function(i){sv=i;});
undefined
// message and hash
truffle(develop)> message = 'hello, world!'
'hello, world!'
truffle(develop)> hashMessage = web3.sha3(message);
'0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2'
truffle(develop)> sv.justHash(message);
'0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2'
// CONCLUSION: web3.sha3 works the same as solidity keccak256
// sign
truffle(develop)> addr = '0x627306090abab3a6e1400e9345bc60c78a8bef57'
'0x627306090abab3a6e1400e9345bc60c78a8bef57'
truffle(develop)> sig = web3.eth.sign(addr, web3.sha3(message));
'0xc56b827b03d2937acb6e658be5acc2a29d93bdfb43f6bde4ac86c79bf9db8a3d2def112317d523c3cbd9cf8dd8600aba1513f862ff82f6742da21fbe5e128dff01'
truffle(develop)> r = sig.substr(0,66) ;
'0xc56b827b03d2937acb6e658be5acc2a29d93bdfb43f6bde4ac86c79bf9db8a3d'
truffle(develop)> s = "0x" + sig.substr(66,64) ;
'0x2def112317d523c3cbd9cf8dd8600aba1513f862ff82f6742da21fbe5e128dff'
truffle(develop)> v = Number.parseInt(sig.substr(130,2))+27;
28
// CONCLUSION: sign accepts a hex hash string
// if use JSON RPC
// bing@binginspiron:~$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_sign","params":["0x627306090abab3a6e1400e9345bc60c78a8bef57", "0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2"],"id":1}' http://localhost:9545
// {"id":1,"jsonrpc":"2.0","result":"0xc56b827b03d2937acb6e658be5acc2a29d93bdfb43f6bde4ac86c79bf9db8a3d2def112317d523c3cbd9cf8dd8600aba1513f862ff82f6742da21fbe5e128dff01"}
// means: send a hex string (the hash)
// verify
truffle(develop)> sv.verifyWithPrefix(web3.sha3(message),v,r,s).then(function(res){console.log(res);return res===addr;});
0x627306090abab3a6e1400e9345bc60c78a8bef57
true
// CONCLUSION: remember: sign= prefix+sha3(message)=>sha3=>sign, verify = prefix+sha3(message)=>sha3, plus v,r,s => verify
// =============================== Alternative ==============================
truffle(develop)> sv.prefixHash(web3.sha3(message))
'0xbd5de6a5526167c24b6de8047a9c73c9565f7d68c1f80b28bd3467b578dc828e'
truffle(develop)> sv.verifyNoPrefix('0xbd5de6a5526167c24b6de8047a9c73c9565f7d68c1f80b28bd3467b578dc828e',v,r,s)
'0x627306090abab3a6e1400e9345bc60c78a8bef57'
// CONCLUSION: just split verifyWithPrefix into two steps, and check the intermediate result
// =============================== Alternative ==============================
// THIS IS TESTED ON Ropsten, SIGNATURE's V is 1B which is 27
bing@binginspiron:~/git/vsc/ethereum$ truffle console --network testnet
truffle(testnet)> message = 'hello, world!'
'hello, world!'
truffle(testnet)> hashMessage = web3.sha3(message);
'0xfbc3a5b569f80319726d3cc77c708b0d34633e5672aac0699ea6ffa500d0bee2'
truffle(testnet)> addr = "0x30514f8346771d10d8c74f873abfc4050fb17acb"
'0x30514f8346771d10d8c74f873abfc4050fb17acb'
truffle(testnet)> sig = web3.eth.sign(addr, web3.sha3(message));
'0x546c5148760c3f07039ca1b79105fa4fd64dcd56d196e37df2cc9c70efc235ff4e2fd15d5f9982327afe5e91ab9fd5a2245f086b0270a4b92b4e289cba4694521b'
truffle(testnet)> migrate --reset
Using network 'testnet'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xd87a4f46718a66c4a91a13ccfc8c222997f37265d0650fb7567f440165f4e79e
Migrations: 0x906b5a7de3a8578383ec9b64338764d645320017
Saving successful migration to network...
... 0x4be5f3ef032d65d7f06cf1e8075c5c50ae2b185cb9116703f1089b2fa5b11a67
Saving artifacts...
Running migration: 2_deploy_token_sale.js
Deploying VscToken...
... 0x6107c51252401527d86d09e30925fc1d1e56e457da6ea768ce351cda5cbef922
VscToken: 0xf6dceeee960fd6e15abcbba21ccf971eb0f98e87
Deploying VscSale...
... 0xb833a1d22dd2e340e58a879a9997c4e81cdf2e26ff1e6662fa714ec32d337872
VscSale: 0x95dd5c6d0ea6a20997fe0812d6a2cb268dcf9a9b
Saving successful migration to network...
... 0xa5658bc3e44bfb79c7130c0aac82df155ffa406eaaae6d3a9f4ca92e1f4d92b9
Saving artifacts...
Running migration: 3_sign_verify.js
Deploying SignVerify...
... 0xfb8dd0bd8bd7acfb0746c93c3c1600e0a2c523ddd50cb7d424d732b52075c3dc
SignVerify: 0x903ac8154848b642dfd16f20999bfb0e1630ebf9
Saving successful migration to network...
... 0xfef20f3d56311e9682ddd369740326d86deace8bd4998c0f660831230f14d34e
Saving artifacts...
truffle(testnet)> SignVerify.deployed().then(function(i){sv=i;});
undefined
truffle(testnet)> r = sig.substr(0,66) ;
'0x546c5148760c3f07039ca1b79105fa4fd64dcd56d196e37df2cc9c70efc235ff'
truffle(testnet)> s = "0x" + sig.substr(66,64) ;
'0x4e2fd15d5f9982327afe5e91ab9fd5a2245f086b0270a4b92b4e289cba469452'
truffle(testnet)> sig.substr(130,2);
'1b'
truffle(testnet)> sig
'0x546c5148760c3f07039ca1b79105fa4fd64dcd56d196e37df2cc9c70efc235ff4e2fd15d5f9982327afe5e91ab9fd5a2245f086b0270a4b92b4e289cba4694521b'
truffle(testnet)> v = Number.parseInt(sig.substr(130,2),16);
27
truffle(testnet)> sv.verifyWithPrefix(web3.sha3(message),v,r,s).then(function(res){console.log(res);return res===addr;});
0x30514f8346771d10d8c74f873abfc4050fb17acb
true
truffle(testnet)>
A function may need for converting hex string to string
function hexToString (hex) { var string = ''; for (var i = 0; i < hex.length; i += 2) {string += String.fromCharCode(parseInt(hex.substr(i, 2), 16));}return string;}