Added contract support.
This commit is contained in:
parent
9a309ed7b7
commit
89a6fac231
370
index.js
370
index.js
@ -6,55 +6,25 @@ var rlp = require('rlp');
|
||||
var scrypt = require('scrypt-js');
|
||||
var uuid = require('uuid');
|
||||
|
||||
var BN = require('./node_modules/elliptic/node_modules/bn.js/lib/bn.js');
|
||||
var Contract = require('./lib/contract.js');
|
||||
|
||||
var sha3 = require('./lib/sha3.js');
|
||||
var utils = require('./lib/utils.js');
|
||||
var BN = utils.BN;
|
||||
|
||||
var secp256k1 = new (elliptic.ec)('secp256k1');
|
||||
|
||||
|
||||
function stripZeros(buffer) {
|
||||
var i = 0;
|
||||
for (i = 0; i < buffer.length; i++) {
|
||||
if (buffer[i] !== 0) { break; }
|
||||
}
|
||||
return (i > 0) ? buffer.slice(i): buffer;
|
||||
}
|
||||
|
||||
function bnToBuffer(bn) {
|
||||
var hex = bn.toString(16);
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return stripZeros(new Buffer(hex, 'hex'));
|
||||
}
|
||||
|
||||
function hexOrBuffer(value, name) {
|
||||
if (!Buffer.isBuffer(value)) {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
var error = new Error(name ? ('invalid ' + name) : 'invalid hex or buffer');
|
||||
error.reason = 'invalid hex string';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
|
||||
value = value.substring(2);
|
||||
if (value.length % 2) { value = '0' + value; }
|
||||
value = new Buffer(value, 'hex');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
// @TODO: Make our own implementation of rlp; the existing one is MLP-2.0, we want MIT. :)
|
||||
function rlpEncodeLength(length) {
|
||||
}
|
||||
|
||||
function rlpArray(items) {
|
||||
|
||||
var output = new Buffer(0);
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
function defineProperty(object, name, value) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
@ -62,14 +32,15 @@ function defineProperty(object, name, value) {
|
||||
});
|
||||
}
|
||||
|
||||
var utils = {};
|
||||
defineProperty(utils, 'defineProperty', defineProperty);
|
||||
var exportUtils = {};
|
||||
|
||||
defineProperty(utils, 'aes', aes);
|
||||
defineProperty(utils, 'BN', BN);
|
||||
defineProperty(utils, 'Buffer', Buffer);
|
||||
defineProperty(utils, 'scrypt', scrypt);
|
||||
defineProperty(utils, 'sha3', sha3);
|
||||
// These may go away in the future...
|
||||
defineProperty(exportUtils, '_aes', aes);
|
||||
defineProperty(exportUtils, '_scrypt', scrypt);
|
||||
|
||||
defineProperty(exportUtils, 'BN', BN);
|
||||
defineProperty(exportUtils, 'Buffer', Buffer);
|
||||
defineProperty(exportUtils, 'sha3', utils.sha3);
|
||||
|
||||
function getChecksumAddress(address) {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
@ -77,7 +48,7 @@ function getChecksumAddress(address) {
|
||||
}
|
||||
|
||||
address = address.substring(2).toLowerCase();
|
||||
var hash = sha3(address);
|
||||
var hash = utils.sha3(address);
|
||||
|
||||
address = address.split('');
|
||||
for (var i = 0; i < 40; i += 2) {
|
||||
@ -126,7 +97,88 @@ var ibanChecksum = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
defineProperty(utils, 'getAddress', function(address) {
|
||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||
defineProperty(exportUtils, 'getContractAddress', function(transaction) {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
throw new Error('invalid from');
|
||||
}
|
||||
|
||||
return '0x' + utils.sha3(rlp.encode([
|
||||
utils.hexOrBuffer(transaction.from),
|
||||
utils.hexOrBuffer(utils.hexlify(transaction.nonce, 'nonce'))
|
||||
])).slice(12).toString('hex');
|
||||
});
|
||||
|
||||
var transactionFields = [
|
||||
{name: 'nonce', maxLength: 32, },
|
||||
{name: 'gasPrice', maxLength: 32, },
|
||||
{name: 'gasLimit', maxLength: 32, },
|
||||
{name: 'to', length: 20, },
|
||||
{name: 'value', maxLength: 32, },
|
||||
{name: 'data'},
|
||||
];
|
||||
|
||||
|
||||
function Wallet(privateKey) {
|
||||
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
|
||||
|
||||
if (typeof(privateKey) === 'string' && privateKey.match(/^0x[0-9A-Fa-f]{64}$/)) {
|
||||
privateKey = new Buffer(privateKey.substring(2), 'hex');
|
||||
} else if (!Buffer.isBuffer(privateKey) || privateKey.length !== 32) {
|
||||
throw new Error('invalid private key');
|
||||
}
|
||||
|
||||
var keyPair = secp256k1.keyFromPrivate(privateKey);
|
||||
|
||||
var publicKey = (new Buffer(keyPair.getPublic(false, 'hex'), 'hex')).slice(1);
|
||||
var address = Wallet.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
|
||||
defineProperty(this, 'address', address);
|
||||
|
||||
defineProperty(this, 'sign', function(transaction) {
|
||||
var raw = [];
|
||||
transactionFields.forEach(function(fieldInfo) {
|
||||
var value = transaction[fieldInfo.name] || (new Buffer(0));
|
||||
value = utils.hexOrBuffer(utils.hexlify(value), fieldInfo.name);
|
||||
|
||||
// Fixed-width field
|
||||
if (fieldInfo.length && value.length !== fieldInfo.length) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'wrong length';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Variable-width (with a maximum)
|
||||
if (fieldInfo.maxLength) {
|
||||
value = utils.stripZeros(value);
|
||||
if (value.length > fieldInfo.maxLength) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'too long';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
raw.push(value);
|
||||
});
|
||||
|
||||
var digest = utils.sha3(rlp.encode(raw));
|
||||
|
||||
var signature = keyPair.sign(digest, {canonical: true});
|
||||
var s = signature.s;
|
||||
var v = signature.recoveryParam;
|
||||
|
||||
raw.push(new Buffer([27 + v]));
|
||||
raw.push(utils.bnToBuffer(signature.r));
|
||||
raw.push(utils.bnToBuffer(s));
|
||||
|
||||
return ('0x' + rlp.encode(raw).toString('hex'));
|
||||
});
|
||||
}
|
||||
|
||||
defineProperty(Wallet, 'utils', exportUtils);
|
||||
|
||||
defineProperty(Wallet, 'getAddress', function(address) {
|
||||
var result = null;
|
||||
|
||||
if (typeof(address) !== 'string') { throw new Error('invalid address'); }
|
||||
@ -162,87 +214,195 @@ defineProperty(utils, 'getAddress', function(address) {
|
||||
return result;
|
||||
});
|
||||
|
||||
defineProperty(utils, 'getIcapAddress', function(address) {
|
||||
address = utils.getAddress(address).substring(2);
|
||||
defineProperty(Wallet, 'getIcapAddress', function(address) {
|
||||
address = Wallet.getAddress(address).substring(2);
|
||||
var base36 = (new BN(address, 16)).toString(36).toUpperCase();
|
||||
while (base36.length < 30) { base36 = '0' + base36; }
|
||||
return 'XE' + ibanChecksum('XE00' + base36) + base36;
|
||||
});
|
||||
|
||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||
defineProperty(utils, 'getContractAddress', function(transaction) {
|
||||
return '0x' + utils.sha3(rlp.encode([
|
||||
hexOrBuffer(transaction.from, 'from'),
|
||||
hexOrBuffer(transaction.nonce, 'nonce')
|
||||
])).slice(12).toString('hex');
|
||||
});
|
||||
defineProperty(Wallet, '_Contract', Contract);
|
||||
|
||||
var transactionFields = [
|
||||
{name: 'nonce', maxLength: 32, },
|
||||
{name: 'gasPrice', maxLength: 32, },
|
||||
{name: 'gasLimit', maxLength: 32, },
|
||||
{name: 'to', length: 20, },
|
||||
{name: 'value', maxLength: 32, },
|
||||
{name: 'data'},
|
||||
];
|
||||
var allowedTransactionKeys = {
|
||||
data: true, from: true, gasLimit: true, gasPrice:true, to: true, value: true
|
||||
}
|
||||
function AttachedContract(web3, wallet, contractAddress, contract) {
|
||||
utils.defineProperty(this, 'web3', web3);
|
||||
utils.defineProperty(this, 'wallet', wallet);
|
||||
utils.defineProperty(this, 'contractAddress', contractAddress);
|
||||
|
||||
utils.defineProperty(this, '_contract', contract);
|
||||
|
||||
function Wallet(privateKey) {
|
||||
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
|
||||
|
||||
if (typeof(privateKey) === 'string' && privateKey.match(/^0x[0-9A-Fa-f]{64}$/)) {
|
||||
privateKey = new Buffer(privateKey.substring(2), 'hex');
|
||||
} else if (!Buffer.isBuffer(privateKey) || privateKey.length !== 32) {
|
||||
throw new Error('invalid private key');
|
||||
function getWeb3Promise(method) {
|
||||
var params = Array.prototype.slice.call(arguments, 1);
|
||||
return new Promise(function(resolve, reject) {
|
||||
params.push(function(error, result) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
web3.eth[method].apply(web3, params);
|
||||
});
|
||||
}
|
||||
|
||||
var keyPair = secp256k1.keyFromPrivate(privateKey);
|
||||
var self = this;
|
||||
|
||||
var publicKey = (new Buffer(keyPair.getPublic(false, 'hex'), 'hex')).slice(1);
|
||||
defineProperty(this, 'address', '0x' + sha3(publicKey).slice(12).toString('hex'));
|
||||
var filters = {};
|
||||
function setupFilter(call, callback) {
|
||||
var info = filters[call.name];
|
||||
|
||||
defineProperty(this, 'sign', function(transaction) {
|
||||
var raw = [];
|
||||
transactionFields.forEach(function(fieldInfo) {
|
||||
var value = transaction[fieldInfo.name] || (new Buffer(0));
|
||||
value = hexOrBuffer(value, fieldInfo.name);
|
||||
// Stop and remove the filter
|
||||
if (!callback) {
|
||||
if (info) { info.filter.stopWatching(); }
|
||||
delete filters[call.name];
|
||||
return;
|
||||
}
|
||||
|
||||
// Fixed-width field
|
||||
if (fieldInfo.length && value.length !== fieldInfo.length) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'wrong length';
|
||||
error.value = value;
|
||||
throw error;
|
||||
if (typeof(callback) !== 'function') {
|
||||
throw new Error('invalid callback');
|
||||
}
|
||||
|
||||
// Already have a filter, just update the callback
|
||||
if (info) {
|
||||
info.callback = callback;
|
||||
return;
|
||||
}
|
||||
|
||||
info = {callback: callback};
|
||||
filters[call.name] = info;
|
||||
|
||||
// Start a new filter
|
||||
info.filter = web3.eth.filter({
|
||||
address: contractAddress,
|
||||
topics: call.topics
|
||||
}, function(error, result) {
|
||||
// @TODO: Emit errors to .onerror? Maybe?
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Variable-width (with a maximum)
|
||||
if (fieldInfo.maxLength) {
|
||||
value = stripZeros(value);
|
||||
if (value.length > fieldInfo.maxLength) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'too long';
|
||||
error.value = value;
|
||||
throw error;
|
||||
try {
|
||||
info.callback.apply(self, call.parse(result.data));
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function runMethod(method) {
|
||||
return function() {
|
||||
var transaction = {}
|
||||
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
if (params.length == contract[method].inputs.length + 1) {
|
||||
transaction = params.pop();
|
||||
if (typeof(transaction) !== 'object') {
|
||||
throw new Error('invalid transaction overrides');
|
||||
}
|
||||
for (var key in transaction) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raw.push(value);
|
||||
var call = contract[method].apply(contract, params);
|
||||
switch (call.type) {
|
||||
case 'call':
|
||||
['data', 'gasLimit', 'gasPrice', 'to', 'value'].forEach(function(key) {
|
||||
if (transaction[key] != null) {
|
||||
throw new Error('call cannot override ' + key) ;
|
||||
}
|
||||
});
|
||||
transaction.data = call.data;
|
||||
if (transaction.from == null) {
|
||||
transaction.from = wallet.address;
|
||||
}
|
||||
transaction.to = contractAddress;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
getWeb3Promise('call', transaction).then(function(value) {
|
||||
resolve(call.parse(value));
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
case 'transaction':
|
||||
['data', 'from', 'to'].forEach(function(key) {
|
||||
if (transaction[key] != null) {
|
||||
throw new Error('transaction cannot override ' + key) ;
|
||||
}
|
||||
});
|
||||
transaction.data = call.data;
|
||||
transaction.to = contractAddress;
|
||||
if (transaction.gasLimit == null) {
|
||||
transaction.gasLimit = 3000000;
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
Promise.all([
|
||||
getWeb3Promise('getTransactionCount', wallet.address, 'pending'),
|
||||
getWeb3Promise('getGasPrice'),
|
||||
]).then(function(results) {
|
||||
if (transaction.nonce == null) {
|
||||
transaction.nonce = results[0];
|
||||
} else if (console.warn) {
|
||||
console.warn('Overriding suggested nonce: ' + results[0]);
|
||||
}
|
||||
if (transaction.gasPrice == null) {
|
||||
transaction.gasPrice = results[1];
|
||||
} else if (console.warn) {
|
||||
console.warn('Overriding suggested gasPrice: ' + utils.hexlify(results[1]));
|
||||
}
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
/*
|
||||
data: call.data,
|
||||
gasLimit: 3000000,
|
||||
gasPrice: results[1],
|
||||
nonce: results[0],
|
||||
to: contractAddress,
|
||||
});
|
||||
*/
|
||||
getWeb3Promise('sendRawTransaction', signedTransaction).then(function(txid) {
|
||||
resolve(txid);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
contract.methods.forEach(function(method) {
|
||||
utils.defineProperty(this, method, runMethod(method));
|
||||
}, this);
|
||||
|
||||
contract.events.forEach(function(method) {
|
||||
var call = contract[method].apply(contract, []);
|
||||
Object.defineProperty(self, 'on' + call.name.toLowerCase(), {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
console.log('get');
|
||||
var info = filters[call.name];
|
||||
if (!info || !info[call.name]) { return null; }
|
||||
return info.callback;
|
||||
},
|
||||
set: function(value) {
|
||||
console.log('set');
|
||||
setupFilter(call, value);
|
||||
}
|
||||
});
|
||||
|
||||
var digest = utils.sha3(rlp.encode(raw));
|
||||
|
||||
var signature = keyPair.sign(digest, {canonical: true});
|
||||
var s = signature.s;
|
||||
var v = signature.recoveryParam;
|
||||
|
||||
raw.push(new Buffer([27 + v]));
|
||||
raw.push(bnToBuffer(signature.r));
|
||||
raw.push(bnToBuffer(s));
|
||||
|
||||
return ('0x' + rlp.encode(raw).toString('hex'));
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
|
||||
defineProperty(Wallet, 'utils', utils);
|
||||
defineProperty(Wallet.prototype, 'getContract', function(web3, address, abi) {
|
||||
return new AttachedContract(web3, this, address, new Contract(abi));
|
||||
});
|
||||
|
||||
module.exports = Wallet;
|
||||
|
127
lib/contract.js
Normal file
127
lib/contract.js
Normal file
@ -0,0 +1,127 @@
|
||||
// "coder" is not MIT... We need to find/make an MIT compatible implementation
|
||||
var coder = require('../node_modules/web3/lib/solidity/coder.js');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
function defineFrozen(object, name, value) {
|
||||
var frozen = JSON.stringify(value);
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
get: function() { return JSON.parse(frozen); }
|
||||
});
|
||||
}
|
||||
|
||||
function getKeys(params, key) {
|
||||
if (!Array.isArray(params)) { throw new Error('invalid params'); }
|
||||
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
if (typeof(params[i][key]) !== 'string') { throw new Error('invalid abi'); }
|
||||
// @TODO: Check type are valid?
|
||||
result.push(params[i][key]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function Contract(abi) {
|
||||
if (!(this instanceof Contract)) { throw new Error('missing new'); }
|
||||
|
||||
//defineProperty(this, 'address', address);
|
||||
|
||||
// Wrap this up as JSON so we can return a "copy" and avoid mutation
|
||||
defineFrozen(this, 'abi', abi);
|
||||
|
||||
var methods = [], events = [];
|
||||
abi.forEach(function(method) {
|
||||
|
||||
switch (method.type) {
|
||||
case 'function':
|
||||
methods.push(method.name);
|
||||
func = (function() {
|
||||
var inputTypes = getKeys(method.inputs, 'type');
|
||||
var outputTypes = getKeys(method.outputs, 'type');
|
||||
|
||||
var func = function() {
|
||||
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
|
||||
var result = {
|
||||
name: method.name,
|
||||
signature: signature,
|
||||
};
|
||||
|
||||
var params = Array.prototype.slice.call(arguments, 0);
|
||||
|
||||
if (params.length < inputTypes.length) {
|
||||
throw new Error('missing parameter');
|
||||
} else if (params.length > inputTypes.length) {
|
||||
throw new Error('too many parameters');
|
||||
}
|
||||
|
||||
signature = '0x' + utils.sha3(signature).slice(0, 4).toString('hex');
|
||||
|
||||
result.data = signature + coder.encodeParams(inputTypes, params);
|
||||
if (method.constant) {
|
||||
result.type = 'call';
|
||||
result.parse = function(data) {
|
||||
return coder.decodeParams(
|
||||
outputTypes,
|
||||
utils.hexOrBuffer(data).toString('hex')
|
||||
)
|
||||
};
|
||||
} else {
|
||||
result.type = 'transaction';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
|
||||
defineFrozen(func, 'outputs', getKeys(method.outputs, 'name'));
|
||||
|
||||
return func;
|
||||
})();
|
||||
break;
|
||||
|
||||
case 'event':
|
||||
events.push(method.name);
|
||||
func = (function() {
|
||||
var inputTypes = getKeys(method.inputs, 'type');
|
||||
var func = function() {
|
||||
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
|
||||
var result = {
|
||||
inputs: method.inputs,
|
||||
name: method.name,
|
||||
type: 'filter',
|
||||
signature: signature,
|
||||
topics: ['0x' + utils.sha3(signature).toString('hex')],
|
||||
};
|
||||
result.parse = function(data) {
|
||||
return coder.decodeParams(
|
||||
inputTypes,
|
||||
utils.hexOrBuffer(data).toString('hex')
|
||||
);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
|
||||
return func;
|
||||
})();
|
||||
break;
|
||||
|
||||
default:
|
||||
func = (function() {
|
||||
return function() {
|
||||
return {type: 'unknown'}
|
||||
}
|
||||
})();
|
||||
break;
|
||||
}
|
||||
utils.defineProperty(this, method.name, func);
|
||||
}, this);
|
||||
|
||||
defineFrozen(this, 'methods', methods);
|
||||
defineFrozen(this, 'events', events);
|
||||
}
|
||||
|
||||
module.exports = Contract;
|
192
lib/sha3.js
192
lib/sha3.js
@ -1,192 +0,0 @@
|
||||
// See: https://github.com/emn178/js-sha3
|
||||
|
||||
var padding = [1, 256, 65536, 16777216];
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var SHIFT = [0, 8, 16, 24];
|
||||
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||
|
||||
var blocks = [], s = [];
|
||||
|
||||
function keccak(message) {
|
||||
|
||||
var block, code, end = false, index = 0, start = 0, length = message.length,
|
||||
n, i, h, l, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||
var blockCount = 34;
|
||||
var byteCount = blockCount * 4;
|
||||
|
||||
for (i = 0; i < 50; ++i) { s[i] = 0; }
|
||||
|
||||
block = 0;
|
||||
do {
|
||||
blocks[0] = block;
|
||||
for (i = 1; i < blockCount + 1; ++i) { blocks[i] = 0; }
|
||||
|
||||
for (i = start; index < length && i < byteCount; ++index) {
|
||||
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
|
||||
start = i - byteCount;
|
||||
if (index == length) {
|
||||
blocks[i >> 2] |= padding[i & 3];
|
||||
++index;
|
||||
}
|
||||
|
||||
block = blocks[blockCount];
|
||||
if (index > length && i < byteCount) {
|
||||
blocks[blockCount - 1] |= 0x80000000;
|
||||
end = true;
|
||||
}
|
||||
|
||||
for (i = 0;i < blockCount;++i) { s[i] ^= blocks[i]; }
|
||||
|
||||
for (n = 0; n < 48; n += 2) {
|
||||
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||
|
||||
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||
s[0] ^= h; s[1] ^= l; s[10] ^= h; s[11] ^= l;
|
||||
s[20] ^= h; s[21] ^= l; s[30] ^= h; s[31] ^= l;
|
||||
s[40] ^= h; s[41] ^= l;
|
||||
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||
s[2] ^= h; s[3] ^= l; s[12] ^= h; s[13] ^= l;
|
||||
s[22] ^= h; s[23] ^= l; s[32] ^= h; s[33] ^= l;
|
||||
s[42] ^= h; s[43] ^= l;
|
||||
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||
s[4] ^= h; s[5] ^= l; s[14] ^= h; s[15] ^= l;
|
||||
s[24] ^= h; s[25] ^= l; s[34] ^= h; s[35] ^= l;
|
||||
s[44] ^= h; s[45] ^= l;
|
||||
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||
s[6] ^= h; s[7] ^= l; s[16] ^= h; s[17] ^= l;
|
||||
s[26] ^= h; s[27] ^= l; s[36] ^= h; s[37] ^= l;
|
||||
s[46] ^= h; s[47] ^= l;
|
||||
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||
s[8] ^= h; s[9] ^= l; s[18] ^= h; s[19] ^= l;
|
||||
s[28] ^= h; s[29] ^= l; s[38] ^= h; s[39] ^= l;
|
||||
s[48] ^= h; s[49] ^= l;
|
||||
|
||||
b0 = s[0]; b1 = s[1];
|
||||
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||
|
||||
s[0] = b0 ^ (~b2 & b4); s[1] = b1 ^ (~b3 & b5);
|
||||
s[10] = b10 ^ (~b12 & b14); s[11] = b11 ^ (~b13 & b15);
|
||||
s[20] = b20 ^ (~b22 & b24); s[21] = b21 ^ (~b23 & b25);
|
||||
s[30] = b30 ^ (~b32 & b34); s[31] = b31 ^ (~b33 & b35);
|
||||
s[40] = b40 ^ (~b42 & b44); s[41] = b41 ^ (~b43 & b45);
|
||||
s[2] = b2 ^ (~b4 & b6); s[3] = b3 ^ (~b5 & b7);
|
||||
s[12] = b12 ^ (~b14 & b16); s[13] = b13 ^ (~b15 & b17);
|
||||
s[22] = b22 ^ (~b24 & b26); s[23] = b23 ^ (~b25 & b27);
|
||||
s[32] = b32 ^ (~b34 & b36); s[33] = b33 ^ (~b35 & b37);
|
||||
s[42] = b42 ^ (~b44 & b46); s[43] = b43 ^ (~b45 & b47);
|
||||
s[4] = b4 ^ (~b6 & b8); s[5] = b5 ^ (~b7 & b9);
|
||||
s[14] = b14 ^ (~b16 & b18); s[15] = b15 ^ (~b17 & b19);
|
||||
s[24] = b24 ^ (~b26 & b28); s[25] = b25 ^ (~b27 & b29);
|
||||
s[34] = b34 ^ (~b36 & b38); s[35] = b35 ^ (~b37 & b39);
|
||||
s[44] = b44 ^ (~b46 & b48); s[45] = b45 ^ (~b47 & b49);
|
||||
s[6] = b6 ^ (~b8 & b0); s[7] = b7 ^ (~b9 & b1);
|
||||
s[16] = b16 ^ (~b18 & b10); s[17] = b17 ^ (~b19 & b11);
|
||||
s[26] = b26 ^ (~b28 & b20); s[27] = b27 ^ (~b29 & b21);
|
||||
s[36] = b36 ^ (~b38 & b30); s[37] = b37 ^ (~b39 & b31);
|
||||
s[46] = b46 ^ (~b48 & b40); s[47] = b47 ^ (~b49 & b41);
|
||||
s[8] = b8 ^ (~b0 & b2); s[9] = b9 ^ (~b1 & b3);
|
||||
s[18] = b18 ^ (~b10 & b12); s[19] = b19 ^ (~b11 & b13);
|
||||
s[28] = b28 ^ (~b20 & b22); s[29] = b29 ^ (~b21 & b23);
|
||||
s[38] = b38 ^ (~b30 & b32); s[39] = b39 ^ (~b31 & b33);
|
||||
s[48] = b48 ^ (~b40 & b42); s[49] = b49 ^ (~b41 & b43);
|
||||
|
||||
s[0] ^= RC[n]; s[1] ^= RC[n + 1];
|
||||
}
|
||||
} while(!end);
|
||||
|
||||
var hex = '';
|
||||
for (i = 0, n = 8; i < n; ++i) {
|
||||
h = s[i];
|
||||
hex += HEX_CHARS[(h >> 4) & 0x0F] + HEX_CHARS[h & 0x0F] +
|
||||
HEX_CHARS[(h >> 12) & 0x0F] + HEX_CHARS[(h >> 8) & 0x0F] +
|
||||
HEX_CHARS[(h >> 20) & 0x0F] + HEX_CHARS[(h >> 16) & 0x0F] +
|
||||
HEX_CHARS[(h >> 28) & 0x0F] + HEX_CHARS[(h >> 24) & 0x0F];
|
||||
}
|
||||
|
||||
return hex;
|
||||
};
|
||||
|
||||
// Quick sanity check
|
||||
if (keccak(new Buffer('ricmoo')) !== 'b05e424817fb90aa7a79e9da5c5f94070a316219c6ebb863a9ff7ca357dc9fa9') {
|
||||
throw new Error('problem with sh3?!');
|
||||
}
|
||||
|
||||
module.exports = function(data) {
|
||||
if (typeof(data) === 'string') {
|
||||
data = new Buffer(data, 'utf8');
|
||||
} else if (!Buffer.isBuffer(data)) {
|
||||
throw new Error('must be a sting');
|
||||
}
|
||||
|
||||
return new Buffer(keccak(data), 'hex');
|
||||
}
|
258
lib/utils.js
Normal file
258
lib/utils.js
Normal file
@ -0,0 +1,258 @@
|
||||
|
||||
var BN = require('../node_modules/elliptic/node_modules/bn.js/lib/bn.js');
|
||||
|
||||
|
||||
// See: https://github.com/emn178/js-sha3
|
||||
var padding = [1, 256, 65536, 16777216];
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var SHIFT = [0, 8, 16, 24];
|
||||
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||
|
||||
var blocks = [], s = [];
|
||||
|
||||
function keccak(message) {
|
||||
|
||||
var block, code, end = false, index = 0, start = 0, length = message.length,
|
||||
n, i, h, l, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||
var blockCount = 34;
|
||||
var byteCount = blockCount * 4;
|
||||
|
||||
for (i = 0; i < 50; ++i) { s[i] = 0; }
|
||||
|
||||
block = 0;
|
||||
do {
|
||||
blocks[0] = block;
|
||||
for (i = 1; i < blockCount + 1; ++i) { blocks[i] = 0; }
|
||||
|
||||
for (i = start; index < length && i < byteCount; ++index) {
|
||||
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
|
||||
start = i - byteCount;
|
||||
if (index == length) {
|
||||
blocks[i >> 2] |= padding[i & 3];
|
||||
++index;
|
||||
}
|
||||
|
||||
block = blocks[blockCount];
|
||||
if (index > length && i < byteCount) {
|
||||
blocks[blockCount - 1] |= 0x80000000;
|
||||
end = true;
|
||||
}
|
||||
|
||||
for (i = 0;i < blockCount;++i) { s[i] ^= blocks[i]; }
|
||||
|
||||
for (n = 0; n < 48; n += 2) {
|
||||
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||
|
||||
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||
s[0] ^= h; s[1] ^= l; s[10] ^= h; s[11] ^= l;
|
||||
s[20] ^= h; s[21] ^= l; s[30] ^= h; s[31] ^= l;
|
||||
s[40] ^= h; s[41] ^= l;
|
||||
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||
s[2] ^= h; s[3] ^= l; s[12] ^= h; s[13] ^= l;
|
||||
s[22] ^= h; s[23] ^= l; s[32] ^= h; s[33] ^= l;
|
||||
s[42] ^= h; s[43] ^= l;
|
||||
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||
s[4] ^= h; s[5] ^= l; s[14] ^= h; s[15] ^= l;
|
||||
s[24] ^= h; s[25] ^= l; s[34] ^= h; s[35] ^= l;
|
||||
s[44] ^= h; s[45] ^= l;
|
||||
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||
s[6] ^= h; s[7] ^= l; s[16] ^= h; s[17] ^= l;
|
||||
s[26] ^= h; s[27] ^= l; s[36] ^= h; s[37] ^= l;
|
||||
s[46] ^= h; s[47] ^= l;
|
||||
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||
s[8] ^= h; s[9] ^= l; s[18] ^= h; s[19] ^= l;
|
||||
s[28] ^= h; s[29] ^= l; s[38] ^= h; s[39] ^= l;
|
||||
s[48] ^= h; s[49] ^= l;
|
||||
|
||||
b0 = s[0]; b1 = s[1];
|
||||
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||
|
||||
s[0] = b0 ^ (~b2 & b4); s[1] = b1 ^ (~b3 & b5);
|
||||
s[10] = b10 ^ (~b12 & b14); s[11] = b11 ^ (~b13 & b15);
|
||||
s[20] = b20 ^ (~b22 & b24); s[21] = b21 ^ (~b23 & b25);
|
||||
s[30] = b30 ^ (~b32 & b34); s[31] = b31 ^ (~b33 & b35);
|
||||
s[40] = b40 ^ (~b42 & b44); s[41] = b41 ^ (~b43 & b45);
|
||||
s[2] = b2 ^ (~b4 & b6); s[3] = b3 ^ (~b5 & b7);
|
||||
s[12] = b12 ^ (~b14 & b16); s[13] = b13 ^ (~b15 & b17);
|
||||
s[22] = b22 ^ (~b24 & b26); s[23] = b23 ^ (~b25 & b27);
|
||||
s[32] = b32 ^ (~b34 & b36); s[33] = b33 ^ (~b35 & b37);
|
||||
s[42] = b42 ^ (~b44 & b46); s[43] = b43 ^ (~b45 & b47);
|
||||
s[4] = b4 ^ (~b6 & b8); s[5] = b5 ^ (~b7 & b9);
|
||||
s[14] = b14 ^ (~b16 & b18); s[15] = b15 ^ (~b17 & b19);
|
||||
s[24] = b24 ^ (~b26 & b28); s[25] = b25 ^ (~b27 & b29);
|
||||
s[34] = b34 ^ (~b36 & b38); s[35] = b35 ^ (~b37 & b39);
|
||||
s[44] = b44 ^ (~b46 & b48); s[45] = b45 ^ (~b47 & b49);
|
||||
s[6] = b6 ^ (~b8 & b0); s[7] = b7 ^ (~b9 & b1);
|
||||
s[16] = b16 ^ (~b18 & b10); s[17] = b17 ^ (~b19 & b11);
|
||||
s[26] = b26 ^ (~b28 & b20); s[27] = b27 ^ (~b29 & b21);
|
||||
s[36] = b36 ^ (~b38 & b30); s[37] = b37 ^ (~b39 & b31);
|
||||
s[46] = b46 ^ (~b48 & b40); s[47] = b47 ^ (~b49 & b41);
|
||||
s[8] = b8 ^ (~b0 & b2); s[9] = b9 ^ (~b1 & b3);
|
||||
s[18] = b18 ^ (~b10 & b12); s[19] = b19 ^ (~b11 & b13);
|
||||
s[28] = b28 ^ (~b20 & b22); s[29] = b29 ^ (~b21 & b23);
|
||||
s[38] = b38 ^ (~b30 & b32); s[39] = b39 ^ (~b31 & b33);
|
||||
s[48] = b48 ^ (~b40 & b42); s[49] = b49 ^ (~b41 & b43);
|
||||
|
||||
s[0] ^= RC[n]; s[1] ^= RC[n + 1];
|
||||
}
|
||||
} while(!end);
|
||||
|
||||
var hex = '';
|
||||
for (i = 0, n = 8; i < n; ++i) {
|
||||
h = s[i];
|
||||
hex += HEX_CHARS[(h >> 4) & 0x0F] + HEX_CHARS[h & 0x0F] +
|
||||
HEX_CHARS[(h >> 12) & 0x0F] + HEX_CHARS[(h >> 8) & 0x0F] +
|
||||
HEX_CHARS[(h >> 20) & 0x0F] + HEX_CHARS[(h >> 16) & 0x0F] +
|
||||
HEX_CHARS[(h >> 28) & 0x0F] + HEX_CHARS[(h >> 24) & 0x0F];
|
||||
}
|
||||
|
||||
return hex;
|
||||
};
|
||||
|
||||
// Quick sanity check
|
||||
if (keccak(new Buffer('ricmoo')) !== 'b05e424817fb90aa7a79e9da5c5f94070a316219c6ebb863a9ff7ca357dc9fa9') {
|
||||
throw new Error('problem with sh3?!');
|
||||
}
|
||||
|
||||
function sha3(data) {
|
||||
if (typeof(data) === 'string') {
|
||||
data = new Buffer(data, 'utf8');
|
||||
} else if (!Buffer.isBuffer(data)) {
|
||||
throw new Error('must be a sting');
|
||||
}
|
||||
|
||||
return new Buffer(keccak(data), 'hex');
|
||||
}
|
||||
|
||||
function defineProperty(object, name, value) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function stripZeros(buffer) {
|
||||
var i = 0;
|
||||
for (i = 0; i < buffer.length; i++) {
|
||||
if (buffer[i] !== 0) { break; }
|
||||
}
|
||||
return (i > 0) ? buffer.slice(i): buffer;
|
||||
}
|
||||
|
||||
function bnToBuffer(bn) {
|
||||
var hex = bn.toString(16);
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return stripZeros(new Buffer(hex, 'hex'));
|
||||
}
|
||||
|
||||
function hexOrBuffer(value, name) {
|
||||
if (!Buffer.isBuffer(value)) {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
var error = new Error(name ? ('invalid ' + name) : 'invalid hex or buffer');
|
||||
error.reason = 'invalid hex string';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
|
||||
value = value.substring(2);
|
||||
if (value.length % 2) { value = '0' + value; }
|
||||
value = new Buffer(value, 'hex');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function hexlify(value, name) {
|
||||
if (typeof(value) === 'number') {
|
||||
return '0x' + bnToBuffer(new BN(value)).toString('hex');
|
||||
} else if (value.mod || value.modulo) {
|
||||
return '0x' + bnToBuffer(value).toString('hex')
|
||||
} else {
|
||||
return '0x' + hexOrBuffer(value).toString('hex');
|
||||
}
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
BN: BN,
|
||||
|
||||
defineProperty: defineProperty,
|
||||
|
||||
bnToBuffer: bnToBuffer,
|
||||
hexOrBuffer: hexOrBuffer,
|
||||
hexlify: hexlify,
|
||||
stripZeros: stripZeros,
|
||||
|
||||
sha3: sha3,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user