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 BEHAVIORtruffle(develop)> compiletruffle(develop)> migrate --resetUsing network 'develop'.Running migration: 1_initial_migration.js Replacing Migrations... ... 0x4057db833b2401281bc978588a1648d0fd0d5dfe1b03ab9c09346387aff950f7 Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956Saving artifacts...Running migration: 2_deploy_token_sale.js Replacing VscToken... ... 0x3436474f0d575259b655f5bd1d1e89734da7c1b2dffdc72c090261407fd7c485 VscToken: 0x345ca3e014aaf5dca488057592ee47305d9b3e10 Replacing VscSale... ... 0x66d235c9af41266b1f1dd5e92fe14693939142b496246b44d0e29b9111a39a12 VscSale: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbfSaving successful migration to network... ... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7dbSaving artifacts...Running migration: 3_sign_verify.js Replacing SignVerify... ... 0xa4fba339ac0dc34159057acf49e18b141c5a18bbbfdceb2cea7916f39709d026 SignVerify: 0x9fbda871d559710256a2502a2517b794b482db40Saving successful migration to network... ... 0xe3e7ffb1810175cddb3670470f6c955fcb30f380ca43fd15ed823fa93047f5ceSaving artifacts...truffle(develop)> SignVerify.deployed().then(function(i){sv=i;});undefined// message and hashtruffle(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
// signtruffle(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) // verifytruffle(develop)> sv.verifyWithPrefix(web3.sha3(message),v,r,s).then(function(res){console.log(res);return res===addr;});0x627306090abab3a6e1400e9345bc60c78a8bef57true// 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 27bing@binginspiron:~/git/vsc/ethereum$ truffle console --network testnettruffle(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 --resetUsing network 'testnet'.Running migration: 1_initial_migration.js Deploying Migrations... ... 0xd87a4f46718a66c4a91a13ccfc8c222997f37265d0650fb7567f440165f4e79e Migrations: 0x906b5a7de3a8578383ec9b64338764d645320017Saving successful migration to network... ... 0x4be5f3ef032d65d7f06cf1e8075c5c50ae2b185cb9116703f1089b2fa5b11a67Saving artifacts...Running migration: 2_deploy_token_sale.js Deploying VscToken... ... 0x6107c51252401527d86d09e30925fc1d1e56e457da6ea768ce351cda5cbef922 VscToken: 0xf6dceeee960fd6e15abcbba21ccf971eb0f98e87 Deploying VscSale... ... 0xb833a1d22dd2e340e58a879a9997c4e81cdf2e26ff1e6662fa714ec32d337872 VscSale: 0x95dd5c6d0ea6a20997fe0812d6a2cb268dcf9a9bSaving successful migration to network... ... 0xa5658bc3e44bfb79c7130c0aac82df155ffa406eaaae6d3a9f4ca92e1f4d92b9Saving artifacts...Running migration: 3_sign_verify.js Deploying SignVerify... ... 0xfb8dd0bd8bd7acfb0746c93c3c1600e0a2c523ddd50cb7d424d732b52075c3dc SignVerify: 0x903ac8154848b642dfd16f20999bfb0e1630ebf9Saving successful migration to network... ... 0xfef20f3d56311e9682ddd369740326d86deace8bd4998c0f660831230f14d34eSaving artifacts...truffle(testnet)> SignVerify.deployed().then(function(i){sv=i;});undefinedtruffle(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);27truffle(testnet)> sv.verifyWithPrefix(web3.sha3(message),v,r,s).then(function(res){console.log(res);return res===addr;});0x30514f8346771d10d8c74f873abfc4050fb17acbtruetruffle(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;}