Beta 7 with some repo names changed.

This commit is contained in:
Luis Fernando Molina committed 2020-11-15 13:05:19 +01:00
1 parent 0f7b40cca7
commit 963c19493a
3408 files changed
+462217 -15516

No files matched your search

+402
View File
@@ -0,0 +1,402 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file utils.js
* @author Marek Kotewicz <marek@parity.io>
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var ethjsUnit = require('ethjs-unit');
var utils = require('./utils.js');
var soliditySha3 = require('./soliditySha3.js');
var randombytes = require('randombytes');
var BN = require('bn.js');
/**
* Fires an error in an event emitter and callback and returns the eventemitter
*
* @method _fireError
* @param {Object} error a string, a error, or an object with {message, data}
* @param {Object} emitter
* @param {Function} reject
* @param {Function} callback
* @param {any} optionalData
* @return {Object} the emitter
*/
var _fireError = function (error, emitter, reject, callback, optionalData) {
/*jshint maxcomplexity: 10 */
// add data if given
if (_.isObject(error) && !(error instanceof Error) && error.data) {
if (_.isObject(error.data) || _.isArray(error.data)) {
error.data = JSON.stringify(error.data, null, 2);
}
error = error.message + "\n" + error.data;
}
if (_.isString(error)) {
error = new Error(error);
}
if (_.isFunction(callback)) {
callback(error, optionalData);
}
if (_.isFunction(reject)) {
// suppress uncatched error if an error listener is present
// OR suppress uncatched error if an callback listener is present
if (emitter &&
(_.isFunction(emitter.listeners) &&
emitter.listeners('error').length) || _.isFunction(callback)) {
emitter.catch(function () { });
}
// reject later, to be able to return emitter
setTimeout(function () {
reject(error);
}, 1);
}
if (emitter && _.isFunction(emitter.emit)) {
// emit later, to be able to return emitter
setTimeout(function () {
emitter.emit('error', error, optionalData);
emitter.removeAllListeners();
}, 1);
}
return emitter;
};
/**
* Should be used to create full function/event name from json abi
*
* @method _jsonInterfaceMethodToString
* @param {Object} json
* @return {String} full function/event name
*/
var _jsonInterfaceMethodToString = function (json) {
if (_.isObject(json) && json.name && json.name.indexOf('(') !== -1) {
return json.name;
}
return json.name + '(' + _flattenTypes(false, json.inputs).join(',') + ')';
};
/**
* Should be used to flatten json abi inputs/outputs into an array of type-representing-strings
*
* @method _flattenTypes
* @param {bool} includeTuple
* @param {Object} puts
* @return {Array} parameters as strings
*/
var _flattenTypes = function (includeTuple, puts) {
// console.log("entered _flattenTypes. inputs/outputs: " + puts)
var types = [];
puts.forEach(function (param) {
if (typeof param.components === 'object') {
if (param.type.substring(0, 5) !== 'tuple') {
throw new Error('components found but type is not tuple; report on GitHub');
}
var suffix = '';
var arrayBracket = param.type.indexOf('[');
if (arrayBracket >= 0) {
suffix = param.type.substring(arrayBracket);
}
var result = _flattenTypes(includeTuple, param.components);
// console.log("result should have things: " + result)
if (_.isArray(result) && includeTuple) {
// console.log("include tuple word, and its an array. joining...: " + result.types)
types.push('tuple(' + result.join(',') + ')' + suffix);
}
else if (!includeTuple) {
// console.log("don't include tuple, but its an array. joining...: " + result)
types.push('(' + result.join(',') + ')' + suffix);
}
else {
// console.log("its a single type within a tuple: " + result.types)
types.push('(' + result + ')');
}
}
else {
// console.log("its a type and not directly in a tuple: " + param.type)
types.push(param.type);
}
});
return types;
};
/**
* Returns a random hex string by the given bytes size
*
* @param {Number} size
* @returns {string}
*/
var randomHex = function (size) {
return '0x' + randombytes(size).toString('hex');
};
/**
* Should be called to get ascii from it's hex representation
*
* @method hexToAscii
* @param {String} hex
* @returns {String} ascii string representation of hex value
*/
var hexToAscii = function (hex) {
if (!utils.isHexStrict(hex))
throw new Error('The parameter must be a valid HEX string.');
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x') {
i = 2;
}
for (; i < l; i += 2) {
var code = parseInt(hex.substr(i, 2), 16);
str += String.fromCharCode(code);
}
return str;
};
/**
* Should be called to get hex representation (prefixed by 0x) of ascii string
*
* @method asciiToHex
* @param {String} str
* @returns {String} hex representation of input string
*/
var asciiToHex = function (str) {
if (!str)
return "0x00";
var hex = "";
for (var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
var n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return "0x" + hex;
};
/**
* Returns value of unit in Wei
*
* @method getUnitValue
* @param {String} unit the unit to convert to, default ether
* @returns {BN} value of the unit (in Wei)
* @throws error if the unit is not correct:w
*/
var getUnitValue = function (unit) {
unit = unit ? unit.toLowerCase() : 'ether';
if (!ethjsUnit.unitMap[unit]) {
throw new Error('This unit "' + unit + '" doesn\'t exist, please use the one of the following units' + JSON.stringify(ethjsUnit.unitMap, null, 2));
}
return unit;
};
/**
* Takes a number of wei and converts it to any other ether unit.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method fromWei
* @param {Number|String} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert to, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var fromWei = function (number, unit) {
unit = getUnitValue(unit);
if (!utils.isBN(number) && !_.isString(number)) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.fromWei(number, unit) : ethjsUnit.fromWei(number, unit).toString(10);
};
/**
* Takes a number of a unit and converts it to wei.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method toWei
* @param {Number|String|BN} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert from, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var toWei = function (number, unit) {
unit = getUnitValue(unit);
if (!utils.isBN(number) && !_.isString(number)) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.toWei(number, unit) : ethjsUnit.toWei(number, unit).toString(10);
};
/**
* Converts to a checksum address
*
* @method toChecksumAddress
* @param {String} address the given HEX address
* @return {String}
*/
var toChecksumAddress = function (address) {
if (typeof address === 'undefined')
return '';
if (!/^(0x)?[0-9a-f]{40}$/i.test(address))
throw new Error('Given address "' + address + '" is not a valid Ethereum address.');
address = address.toLowerCase().replace(/^0x/i, '');
var addressHash = utils.sha3(address).replace(/^0x/i, '');
var checksumAddress = '0x';
for (var i = 0; i < address.length; i++) {
// If ith character is 8 to f then make it uppercase
if (parseInt(addressHash[i], 16) > 7) {
checksumAddress += address[i].toUpperCase();
}
else {
checksumAddress += address[i];
}
}
return checksumAddress;
};
/**
* Returns -1 if a<b, 1 if a>b; 0 if a == b.
* For more details on this type of function, see
* developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
*
* @method compareBlockNumbers
*
* @param {String|Number|BN} a
*
* @param {String|Number|BN} b
*
* @returns {Number} -1, 0, or 1
*/
var compareBlockNumbers = function (a, b) {
if (a == b) {
return 0;
}
else if (("genesis" == a || "earliest" == a || 0 == a) && ("genesis" == b || "earliest" == b || 0 == b)) {
return 0;
}
else if ("genesis" == a || "earliest" == a) {
// b !== a, thus a < b
return -1;
}
else if ("genesis" == b || "earliest" == b) {
// b !== a, thus a > b
return 1;
}
else if (a == "latest") {
if (b == "pending") {
return -1;
}
else {
// b !== ("pending" OR "latest"), thus a > b
return 1;
}
}
else if (b === "latest") {
if (a == "pending") {
return 1;
}
else {
// b !== ("pending" OR "latest"), thus a > b
return -1;
}
}
else if (a == "pending") {
// b (== OR <) "latest", thus a > b
return 1;
}
else if (b == "pending") {
return -1;
}
else {
let bnA = new BN(a);
let bnB = new BN(b);
if (bnA.lt(bnB)) {
return -1;
}
else if (bnA.eq(bnB)) {
return 0;
}
else {
return 1;
}
}
};
module.exports = {
_fireError: _fireError,
_jsonInterfaceMethodToString: _jsonInterfaceMethodToString,
_flattenTypes: _flattenTypes,
// extractDisplayName: extractDisplayName,
// extractTypeName: extractTypeName,
randomHex: randomHex,
_: _,
BN: utils.BN,
isBN: utils.isBN,
isBigNumber: utils.isBigNumber,
isHex: utils.isHex,
isHexStrict: utils.isHexStrict,
sha3: utils.sha3,
sha3Raw: utils.sha3Raw,
keccak256: utils.sha3,
soliditySha3: soliditySha3.soliditySha3,
soliditySha3Raw: soliditySha3.soliditySha3Raw,
encodePacked: soliditySha3.encodePacked,
isAddress: utils.isAddress,
checkAddressChecksum: utils.checkAddressChecksum,
toChecksumAddress: toChecksumAddress,
toHex: utils.toHex,
toBN: utils.toBN,
bytesToHex: utils.bytesToHex,
hexToBytes: utils.hexToBytes,
hexToNumberString: utils.hexToNumberString,
hexToNumber: utils.hexToNumber,
toDecimal: utils.hexToNumber,
numberToHex: utils.numberToHex,
fromDecimal: utils.numberToHex,
hexToUtf8: utils.hexToUtf8,
hexToString: utils.hexToUtf8,
toUtf8: utils.hexToUtf8,
utf8ToHex: utils.utf8ToHex,
stringToHex: utils.utf8ToHex,
fromUtf8: utils.utf8ToHex,
hexToAscii: hexToAscii,
toAscii: hexToAscii,
asciiToHex: asciiToHex,
fromAscii: asciiToHex,
unitMap: ethjsUnit.unitMap,
toWei: toWei,
fromWei: fromWei,
padLeft: utils.leftPad,
leftPad: utils.leftPad,
padRight: utils.rightPad,
rightPad: utils.rightPad,
toTwosComplement: utils.toTwosComplement,
isBloom: utils.isBloom,
isUserEthereumAddressInBloom: utils.isUserEthereumAddressInBloom,
isContractAddressInBloom: utils.isContractAddressInBloom,
isTopic: utils.isTopic,
isTopicInBloom: utils.isTopicInBloom,
isInBloom: utils.isInBloom,
compareBlockNumbers: compareBlockNumbers
};
+244
View File
@@ -0,0 +1,244 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file soliditySha3.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var BN = require('bn.js');
var utils = require('./utils.js');
var _elementaryName = function (name) {
/*jshint maxcomplexity:false */
if (name.startsWith('int[')) {
return 'int256' + name.slice(3);
}
else if (name === 'int') {
return 'int256';
}
else if (name.startsWith('uint[')) {
return 'uint256' + name.slice(4);
}
else if (name === 'uint') {
return 'uint256';
}
else if (name.startsWith('fixed[')) {
return 'fixed128x128' + name.slice(5);
}
else if (name === 'fixed') {
return 'fixed128x128';
}
else if (name.startsWith('ufixed[')) {
return 'ufixed128x128' + name.slice(6);
}
else if (name === 'ufixed') {
return 'ufixed128x128';
}
return name;
};
// Parse N from type<N>
var _parseTypeN = function (type) {
var typesize = /^\D+(\d+).*$/.exec(type);
return typesize ? parseInt(typesize[1], 10) : null;
};
// Parse N from type[<N>]
var _parseTypeNArray = function (type) {
var arraySize = /^\D+\d*\[(\d+)\]$/.exec(type);
return arraySize ? parseInt(arraySize[1], 10) : null;
};
var _parseNumber = function (arg) {
var type = typeof arg;
if (type === 'string') {
if (utils.isHexStrict(arg)) {
return new BN(arg.replace(/0x/i, ''), 16);
}
else {
return new BN(arg, 10);
}
}
else if (type === 'number') {
return new BN(arg);
}
else if (utils.isBigNumber(arg)) {
return new BN(arg.toString(10));
}
else if (utils.isBN(arg)) {
return arg;
}
else {
throw new Error(arg + ' is not a number');
}
};
var _solidityPack = function (type, value, arraySize) {
/*jshint maxcomplexity:false */
var size, num;
type = _elementaryName(type);
if (type === 'bytes') {
if (value.replace(/^0x/i, '').length % 2 !== 0) {
throw new Error('Invalid bytes characters ' + value.length);
}
return value;
}
else if (type === 'string') {
return utils.utf8ToHex(value);
}
else if (type === 'bool') {
return value ? '01' : '00';
}
else if (type.startsWith('address')) {
if (arraySize) {
size = 64;
}
else {
size = 40;
}
if (!utils.isAddress(value)) {
throw new Error(value + ' is not a valid address, or the checksum is invalid.');
}
return utils.leftPad(value.toLowerCase(), size);
}
size = _parseTypeN(type);
if (type.startsWith('bytes')) {
if (!size) {
throw new Error('bytes[] not yet supported in solidity');
}
// must be 32 byte slices when in an array
if (arraySize) {
size = 32;
}
if (size < 1 || size > 32 || size < value.replace(/^0x/i, '').length / 2) {
throw new Error('Invalid bytes' + size + ' for ' + value);
}
return utils.rightPad(value, size * 2);
}
else if (type.startsWith('uint')) {
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid uint' + size + ' size');
}
num = _parseNumber(value);
if (num.bitLength() > size) {
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength());
}
if (num.lt(new BN(0))) {
throw new Error('Supplied uint ' + num.toString() + ' is negative');
}
return size ? utils.leftPad(num.toString('hex'), size / 8 * 2) : num;
}
else if (type.startsWith('int')) {
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid int' + size + ' size');
}
num = _parseNumber(value);
if (num.bitLength() > size) {
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength());
}
if (num.lt(new BN(0))) {
return num.toTwos(size).toString('hex');
}
else {
return size ? utils.leftPad(num.toString('hex'), size / 8 * 2) : num;
}
}
else {
// FIXME: support all other types
throw new Error('Unsupported or invalid type: ' + type);
}
};
var _processSolidityEncodePackedArgs = function (arg) {
/*jshint maxcomplexity:false */
if (_.isArray(arg)) {
throw new Error('Autodetection of array types is not supported.');
}
var type, value = '';
var hexArg, arraySize;
// if type is given
if (_.isObject(arg) && (arg.hasOwnProperty('v') || arg.hasOwnProperty('t') || arg.hasOwnProperty('value') || arg.hasOwnProperty('type'))) {
type = arg.hasOwnProperty('t') ? arg.t : arg.type;
value = arg.hasOwnProperty('v') ? arg.v : arg.value;
// otherwise try to guess the type
}
else {
type = utils.toHex(arg, true);
value = utils.toHex(arg);
if (!type.startsWith('int') && !type.startsWith('uint')) {
type = 'bytes';
}
}
if ((type.startsWith('int') || type.startsWith('uint')) && typeof value === 'string' && !/^(-)?0x/i.test(value)) {
value = new BN(value);
}
// get the array size
if (_.isArray(value)) {
arraySize = _parseTypeNArray(type);
if (arraySize && value.length !== arraySize) {
throw new Error(type + ' is not matching the given array ' + JSON.stringify(value));
}
else {
arraySize = value.length;
}
}
if (_.isArray(value)) {
hexArg = value.map(function (val) {
return _solidityPack(type, val, arraySize).toString('hex').replace('0x', '');
});
return hexArg.join('');
}
else {
hexArg = _solidityPack(type, value, arraySize);
return hexArg.toString('hex').replace('0x', '');
}
};
/**
* Hashes solidity values to a sha3 hash using keccak 256
*
* @method soliditySha3
* @return {Object} the sha3
*/
var soliditySha3 = function () {
/*jshint maxcomplexity:false */
var args = Array.prototype.slice.call(arguments);
var hexArgs = _.map(args, _processSolidityEncodePackedArgs);
// console.log(args, hexArgs);
// console.log('0x'+ hexArgs.join(''));
return utils.sha3('0x' + hexArgs.join(''));
};
/**
* Hashes solidity values to a sha3 hash using keccak 256 but does return the hash of value `null` instead of `null`
*
* @method soliditySha3Raw
* @return {Object} the sha3
*/
var soliditySha3Raw = function () {
return utils.sha3Raw('0x' + _.map(Array.prototype.slice.call(arguments), _processSolidityEncodePackedArgs).join(''));
};
/**
* Encode packed args to hex
*
* @method encodePacked
* @return {String} the hex encoded arguments
*/
var encodePacked = function () {
/*jshint maxcomplexity:false */
var args = Array.prototype.slice.call(arguments);
var hexArgs = _.map(args, _processSolidityEncodePackedArgs);
return '0x' + hexArgs.join('').toLowerCase();
};
module.exports = {
soliditySha3: soliditySha3,
soliditySha3Raw: soliditySha3Raw,
encodePacked: encodePacked
};
+474
View File
@@ -0,0 +1,474 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file utils.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var BN = require('bn.js');
var numberToBN = require('number-to-bn');
var utf8 = require('utf8');
var Hash = require("eth-lib/lib/hash");
var ethereumBloomFilters = require('ethereum-bloom-filters');
/**
* Returns true if object is BN, otherwise false
*
* @method isBN
* @param {Object} object
* @return {Boolean}
*/
var isBN = function (object) {
return BN.isBN(object);
};
/**
* Returns true if object is BigNumber, otherwise false
*
* @method isBigNumber
* @param {Object} object
* @return {Boolean}
*/
var isBigNumber = function (object) {
return object && object.constructor && object.constructor.name === 'BigNumber';
};
/**
* Takes an input and transforms it into an BN
*
* @method toBN
* @param {Number|String|BN} number, string, HEX string or BN
* @return {BN} BN
*/
var toBN = function (number) {
try {
return numberToBN.apply(null, arguments);
}
catch (e) {
throw new Error(e + ' Given value: "' + number + '"');
}
};
/**
* Takes and input transforms it into BN and if it is negative value, into two's complement
*
* @method toTwosComplement
* @param {Number|String|BN} number
* @return {String}
*/
var toTwosComplement = function (number) {
return '0x' + toBN(number).toTwos(256).toString(16, 64);
};
/**
* Checks if the given string is an address
*
* @method isAddress
* @param {String} address the given HEX address
* @return {Boolean}
*/
var isAddress = function (address) {
// check if it has the basic requirements of an address
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
return false;
// If it's ALL lowercase or ALL upppercase
}
else if (/^(0x|0X)?[0-9a-f]{40}$/.test(address) || /^(0x|0X)?[0-9A-F]{40}$/.test(address)) {
return true;
// Otherwise check each case
}
else {
return checkAddressChecksum(address);
}
};
/**
* Checks if the given string is a checksummed address
*
* @method checkAddressChecksum
* @param {String} address the given HEX address
* @return {Boolean}
*/
var checkAddressChecksum = function (address) {
// Check each case
address = address.replace(/^0x/i, '');
var addressHash = sha3(address.toLowerCase()).replace(/^0x/i, '');
for (var i = 0; i < 40; i++) {
// the nth letter should be uppercase if the nth digit of casemap is 1
if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) {
return false;
}
}
return true;
};
/**
* Should be called to pad string to expected length
*
* @method leftPad
* @param {String} string to be padded
* @param {Number} chars that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var leftPad = function (string, chars, sign) {
var hasPrefix = /^0x/i.test(string) || typeof string === 'number';
string = string.toString(16).replace(/^0x/i, '');
var padding = (chars - string.length + 1 >= 0) ? chars - string.length + 1 : 0;
return (hasPrefix ? '0x' : '') + new Array(padding).join(sign ? sign : "0") + string;
};
/**
* Should be called to pad string to expected length
*
* @method rightPad
* @param {String} string to be padded
* @param {Number} chars that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var rightPad = function (string, chars, sign) {
var hasPrefix = /^0x/i.test(string) || typeof string === 'number';
string = string.toString(16).replace(/^0x/i, '');
var padding = (chars - string.length + 1 >= 0) ? chars - string.length + 1 : 0;
return (hasPrefix ? '0x' : '') + string + (new Array(padding).join(sign ? sign : "0"));
};
/**
* Should be called to get hex representation (prefixed by 0x) of utf8 string
*
* @method utf8ToHex
* @param {String} str
* @returns {String} hex representation of input string
*/
var utf8ToHex = function (str) {
str = utf8.encode(str);
var hex = "";
// remove \u0000 padding from either side
str = str.replace(/^(?:\u0000)*/, '');
str = str.split("").reverse().join("");
str = str.replace(/^(?:\u0000)*/, '');
str = str.split("").reverse().join("");
for (var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
// if (code !== 0) {
var n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
// }
}
return "0x" + hex;
};
/**
* Should be called to get utf8 from it's hex representation
*
* @method hexToUtf8
* @param {String} hex
* @returns {String} ascii string representation of hex value
*/
var hexToUtf8 = function (hex) {
if (!isHexStrict(hex))
throw new Error('The parameter "' + hex + '" must be a valid HEX string.');
var str = "";
var code = 0;
hex = hex.replace(/^0x/i, '');
// remove 00 padding from either side
hex = hex.replace(/^(?:00)*/, '');
hex = hex.split("").reverse().join("");
hex = hex.replace(/^(?:00)*/, '');
hex = hex.split("").reverse().join("");
var l = hex.length;
for (var i = 0; i < l; i += 2) {
code = parseInt(hex.substr(i, 2), 16);
// if (code !== 0) {
str += String.fromCharCode(code);
// }
}
return utf8.decode(str);
};
/**
* Converts value to it's number representation
*
* @method hexToNumber
* @param {String|Number|BN} value
* @return {String}
*/
var hexToNumber = function (value) {
if (!value) {
return value;
}
if (_.isString(value) && !isHexStrict(value)) {
throw new Error('Given value "' + value + '" is not a valid hex string.');
}
return toBN(value).toNumber();
};
/**
* Converts value to it's decimal representation in string
*
* @method hexToNumberString
* @param {String|Number|BN} value
* @return {String}
*/
var hexToNumberString = function (value) {
if (!value)
return value;
if (_.isString(value) && !isHexStrict(value)) {
throw new Error('Given value "' + value + '" is not a valid hex string.');
}
return toBN(value).toString(10);
};
/**
* Converts value to it's hex representation
*
* @method numberToHex
* @param {String|Number|BN} value
* @return {String}
*/
var numberToHex = function (value) {
if (_.isNull(value) || _.isUndefined(value)) {
return value;
}
if (!isFinite(value) && !isHexStrict(value)) {
throw new Error('Given input "' + value + '" is not a number.');
}
var number = toBN(value);
var result = number.toString(16);
return number.lt(new BN(0)) ? '-0x' + result.substr(1) : '0x' + result;
};
/**
* Convert a byte array to a hex string
*
* Note: Implementation from crypto-js
*
* @method bytesToHex
* @param {Array} bytes
* @return {String} the hex string
*/
var bytesToHex = function (bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
/* jshint ignore:start */
hex.push((bytes[i] >>> 4).toString(16));
hex.push((bytes[i] & 0xF).toString(16));
/* jshint ignore:end */
}
return '0x' + hex.join("");
};
/**
* Convert a hex string to a byte array
*
* Note: Implementation from crypto-js
*
* @method hexToBytes
* @param {string} hex
* @return {Array} the byte array
*/
var hexToBytes = function (hex) {
hex = hex.toString(16);
if (!isHexStrict(hex)) {
throw new Error('Given value "' + hex + '" is not a valid hex string.');
}
hex = hex.replace(/^0x/i, '');
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
};
/**
* Auto converts any given value into it's hex representation.
*
* And even stringifys objects before.
*
* @method toHex
* @param {String|Number|BN|Object|Buffer} value
* @param {Boolean} returnType
* @return {String}
*/
var toHex = function (value, returnType) {
/*jshint maxcomplexity: false */
if (isAddress(value)) {
return returnType ? 'address' : '0x' + value.toLowerCase().replace(/^0x/i, '');
}
if (_.isBoolean(value)) {
return returnType ? 'bool' : value ? '0x01' : '0x00';
}
if (Buffer.isBuffer(value)) {
return '0x' + value.toString('hex');
}
if (_.isObject(value) && !isBigNumber(value) && !isBN(value)) {
return returnType ? 'string' : utf8ToHex(JSON.stringify(value));
}
// if its a negative number, pass it through numberToHex
if (_.isString(value)) {
if (value.indexOf('-0x') === 0 || value.indexOf('-0X') === 0) {
return returnType ? 'int256' : numberToHex(value);
}
else if (value.indexOf('0x') === 0 || value.indexOf('0X') === 0) {
return returnType ? 'bytes' : value;
}
else if (!isFinite(value)) {
return returnType ? 'string' : utf8ToHex(value);
}
}
return returnType ? (value < 0 ? 'int256' : 'uint256') : numberToHex(value);
};
/**
* Check if string is HEX, requires a 0x in front
*
* @method isHexStrict
* @param {String} hex to be checked
* @returns {Boolean}
*/
var isHexStrict = function (hex) {
return ((_.isString(hex) || _.isNumber(hex)) && /^(-)?0x[0-9a-f]*$/i.test(hex));
};
/**
* Check if string is HEX
*
* @method isHex
* @param {String} hex to be checked
* @returns {Boolean}
*/
var isHex = function (hex) {
return ((_.isString(hex) || _.isNumber(hex)) && /^(-0x|0x)?[0-9a-f]*$/i.test(hex));
};
/**
* Returns true if given string is a valid Ethereum block header bloom.
*
* @method isBloom
* @param {String} bloom encoded bloom filter
* @return {Boolean}
*/
var isBloom = function (bloom) {
return ethereumBloomFilters.isBloom(bloom);
};
/**
* Returns true if the ethereum users address is part of the given bloom
* note: false positives are possible.
*
* @method isUserEthereumAddressInBloom
* @param {String} ethereumAddress encoded bloom filter
* @param {String} bloom ethereum addresss
* @return {Boolean}
*/
var isUserEthereumAddressInBloom = function (bloom, ethereumAddress) {
return ethereumBloomFilters.isUserEthereumAddressInBloom(bloom, ethereumAddress);
};
/**
* Returns true if the contract address is part of the given bloom
* note: false positives are possible.
*
* @method isUserEthereumAddressInBloom
* @param {String} bloom encoded bloom filter
* @param {String} contractAddress contract addresss
* @return {Boolean}
*/
var isContractAddressInBloom = function (bloom, contractAddress) {
return ethereumBloomFilters.isContractAddressInBloom(bloom, contractAddress);
};
/**
* Returns true if given string is a valid log topic.
*
* @method isTopic
* @param {String} topic encoded topic
* @return {Boolean}
*/
var isTopic = function (topic) {
return ethereumBloomFilters.isTopic(topic);
};
/**
* Returns true if the topic is part of the given bloom
* note: false positives are possible.
*
* @method isTopicInBloom
* @param {String} bloom encoded bloom filter
* @param {String} topic encoded topic
* @return {Boolean}
*/
var isTopicInBloom = function (bloom, topic) {
return ethereumBloomFilters.isTopicInBloom(bloom, topic);
};
/**
* Returns true if the value is part of the given bloom
* note: false positives are possible.
*
* @method isInBloom
* @param {String} bloom encoded bloom filter
* @param {String | Uint8Array} topic encoded value
* @return {Boolean}
*/
var isInBloom = function (bloom, topic) {
return ethereumBloomFilters.isInBloom(bloom, topic);
};
/**
* Hashes values to a sha3 hash using keccak 256
*
* To hash a HEX string the hex must have 0x in front.
*
* @method sha3
* @return {String} the sha3 string
*/
var SHA3_NULL_S = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
var sha3 = function (value) {
if (isBN(value)) {
value = value.toString();
}
if (isHexStrict(value) && /^0x/i.test((value).toString())) {
value = hexToBytes(value);
}
var returnValue = Hash.keccak256(value); // jshint ignore:line
if (returnValue === SHA3_NULL_S) {
return null;
}
else {
return returnValue;
}
};
// expose the under the hood keccak256
sha3._Hash = Hash;
/**
* @method sha3Raw
*
* @param value
*
* @returns {string}
*/
var sha3Raw = function (value) {
value = sha3(value);
if (value === null) {
return SHA3_NULL_S;
}
return value;
};
module.exports = {
BN: BN,
isBN: isBN,
isBigNumber: isBigNumber,
toBN: toBN,
isAddress: isAddress,
isBloom: isBloom,
isUserEthereumAddressInBloom: isUserEthereumAddressInBloom,
isContractAddressInBloom: isContractAddressInBloom,
isTopic: isTopic,
isTopicInBloom: isTopicInBloom,
isInBloom: isInBloom,
checkAddressChecksum: checkAddressChecksum,
utf8ToHex: utf8ToHex,
hexToUtf8: hexToUtf8,
hexToNumber: hexToNumber,
hexToNumberString: hexToNumberString,
numberToHex: numberToHex,
toHex: toHex,
hexToBytes: hexToBytes,
bytesToHex: bytesToHex,
isHex: isHex,
isHexStrict: isHexStrict,
leftPad: leftPad,
rightPad: rightPad,
toTwosComplement: toTwosComplement,
sha3: sha3,
sha3Raw: sha3Raw
};
+57
View File
@@ -0,0 +1,57 @@
// type Var = {
// name: String
// type: "uint256" | "bytes32" | ...
// }
//
// type Method = {
// name: String
// inputs: [Var]
// output: [Var]
// constant: Bool
// payable: Bool
// }
const Bytes = require("./bytes");
const Nat = require("./nat");
const keccak256s = require("./hash").keccak256s;
// (type : String), JSType(type) -> {data: Bytes, dynamic: Bool}
// ABI-encodes a single term.
const encode = (type, value) => {
if (type === "bytes") {
const length = Bytes.length(value);
const nextMul32 = (((length - 1) / 32 | 0) + 1) * 32;
const lengthEncoded = encode("uint256", Nat.fromNumber(length)).data;
const bytesEncoded = Bytes.padRight(nextMul32, value);
return { data: Bytes.concat(lengthEncoded, bytesEncoded), dynamic: true };
} else if (type === "uint256" || type === "bytes32" || type === "address") {
return { data: Bytes.pad(32, value), dynamic: false };
} else {
throw "Eth-lib can't encode ABI type " + type + " yet.";
}
};
// (method : Method), [JSType(method.inputs[i].type)] -> Bytes
// ABI-encodes the transaction data to call a method.
const methodData = (method, params) => {
const methodSig = method.name + "(" + method.inputs.map(i => i.type).join(",") + ")";
const methodHash = keccak256s(methodSig).slice(0, 10);
let encodedParams = params.map((param, i) => encode(method.inputs[i].type, param));
var headBlock = "0x";
let dataBlock = "0x";
for (var i = 0; i < encodedParams.length; ++i) {
if (encodedParams[i].dynamic) {
var dataLoc = encodedParams.length * 32 + Bytes.length(dataBlock);
headBlock = Bytes.concat(headBlock, Bytes.pad(32, Nat.fromNumber(dataLoc)));
dataBlock = Bytes.concat(dataBlock, encodedParams[i].data);
} else {
headBlock = Bytes.concat(headBlock, encodedParams[i].data);
}
}
return Bytes.flatten([methodHash, headBlock, dataBlock]);
};
module.exports = {
encode,
methodData
};
+64
View File
@@ -0,0 +1,64 @@
const Bytes = require("./bytes");
const Nat = require("./nat");
const elliptic = require("elliptic");
const rlp = require("./rlp");
const secp256k1 = new elliptic.ec("secp256k1"); // eslint-disable-line
const { keccak256, keccak256s } = require("./hash");
const create = entropy => {
const innerHex = keccak256(Bytes.concat(Bytes.random(32), entropy || Bytes.random(32)));
const middleHex = Bytes.concat(Bytes.concat(Bytes.random(32), innerHex), Bytes.random(32));
const outerHex = keccak256(middleHex);
return fromPrivate(outerHex);
};
const toChecksum = address => {
const addressHash = keccak256s(address.slice(2));
let checksumAddress = "0x";
for (let i = 0; i < 40; i++) checksumAddress += parseInt(addressHash[i + 2], 16) > 7 ? address[i + 2].toUpperCase() : address[i + 2];
return checksumAddress;
};
const fromPrivate = privateKey => {
const buffer = new Buffer(privateKey.slice(2), "hex");
const ecKey = secp256k1.keyFromPrivate(buffer);
const publicKey = "0x" + ecKey.getPublic(false, 'hex').slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return {
address: address,
privateKey: privateKey
};
};
const encodeSignature = ([v, r, s]) => Bytes.flatten([r, s, v]);
const decodeSignature = hex => [Bytes.slice(64, Bytes.length(hex), hex), Bytes.slice(0, 32, hex), Bytes.slice(32, 64, hex)];
const makeSigner = addToV => (hash, privateKey) => {
const signature = secp256k1.keyFromPrivate(new Buffer(privateKey.slice(2), "hex")).sign(new Buffer(hash.slice(2), "hex"), { canonical: true });
return encodeSignature([Nat.fromString(Bytes.fromNumber(addToV + signature.recoveryParam)), Bytes.pad(32, Bytes.fromNat("0x" + signature.r.toString(16))), Bytes.pad(32, Bytes.fromNat("0x" + signature.s.toString(16)))]);
};
const sign = makeSigner(27); // v=27|28 instead of 0|1...
const recover = (hash, signature) => {
const vals = decodeSignature(signature);
const vrs = { v: Bytes.toNumber(vals[0]), r: vals[1].slice(2), s: vals[2].slice(2) };
const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - vrs.v % 2); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that
const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return address;
};
module.exports = {
create,
toChecksum,
fromPrivate,
sign,
makeSigner,
recover,
encodeSignature,
decodeSignature
};
+241
View File
@@ -0,0 +1,241 @@
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var Api = function Api(provider) {
var Nat = require("./nat");
var Map = require("./map");
var Bytes = require("./bytes");
var keccak256s = require("./hash").keccak256s;
var send = function send(method) {
return function () {
for (var _len = arguments.length, params = Array(_len), _key = 0; _key < _len; _key++) {
params[_key] = arguments[_key];
}
return new Promise(function (resolve, reject) {
return provider.send(method, params, function (err, result) {
return err ? reject(err) : resolve(result);
});
});
};
};
// TODO check inputs
// TODO move to proper file
var encodeABI = function encodeABI(type, value) {
if (type === "bytes") {
var length = Bytes.length(value);
var nextMul32 = (((length - 1) / 32 | 0) + 1) * 32;
var lengthEncoded = encodeABI("uint256", Nat.fromNumber(length)).data;
var bytesEncoded = Bytes.padRight(nextMul32, value);
return { data: Bytes.concat(lengthEncoded, bytesEncoded), dynamic: true };
} else if (type === "uint256" || type === "bytes32" || type === "address") {
return { data: Bytes.pad(32, value), dynamic: false };
} else {
throw "Eth-lib can't encode ABI type " + type + " yet.";
}
};
var sendTransaction = send("eth_sendTransaction");
var sendRawTransaction = send("eth_sendRawTransaction");
var getTransactionReceipt = send("eth_getTransactionReceipt");
var compileSolidity = send("eth_compileSolidity");
var call = send("eth_call");
var getBalance = send("eth_getBalance");
var accounts = send("eth_accounts");
var removeEmptyTo = function removeEmptyTo(tx) {
return tx.to === "" || tx.to === "0x" ? Map.remove("to")(tx) : tx;
};
var waitTransactionReceipt = getTransactionReceipt; // TODO: implement correctly
var addTransactionDefaults = async function addTransactionDefaults(tx) {
var baseDefaults = [tx.chainId || send("net_version")(), tx.gasPrice || send("eth_gasPrice")(), tx.nonce || send("eth_getTransactionCount")(tx.from, "latest"), tx.value || "0x0", tx.data || "0x"];
var _ref = await Promise.all(baseDefaults),
_ref2 = _slicedToArray(_ref, 5),
chainIdNum = _ref2[0],
gasPrice = _ref2[1],
nonce = _ref2[2],
value = _ref2[3],
data = _ref2[4];
var chainId = Nat.fromNumber(chainIdNum);
var from = tx.from;
var to = tx.to === "" || tx.to === "0x" ? null : tx.to;
var gas = tx.gas ? tx.gas : Nat.div(Nat.mul((await send("eth_estimateGas")({
from: tx.from,
to: tx.to,
value: tx.value,
nonce: tx.nonce,
data: tx.data
})), "0x6"), "0x5");
return {
chainId: chainId,
from: from.toLowerCase(),
to: to.toLowerCase(),
gasPrice: gasPrice,
gas: gas,
nonce: nonce,
value: value,
data: data.toLowerCase()
};
};
var sendTransactionWithDefaults = async function sendTransactionWithDefaults(tx) {
return sendTransaction(removeEmptyTo((await addTransactionDefaults(tx))));
};
var callWithDefaults = function callWithDefaults(tx, block) {
return addTransactionDefaults(tx).then(function (tx) {
return call(removeEmptyTo(tx), block || "latest");
});
};
var callMethodData = function callMethodData(method) {
return function () {
for (var _len2 = arguments.length, params = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
params[_key2] = arguments[_key2];
}
var methodSig = method.name + "(" + method.inputs.map(function (i) {
return i.type;
}).join(",") + ")";
var methodHash = keccak256s(methodSig).slice(0, 10);
var encodedParams = params.map(function (param, i) {
return encodeABI(method.inputs[i].type, param);
});
var headBlock = "0x";
var dataBlock = "0x";
for (var i = 0; i < encodedParams.length; ++i) {
if (encodedParams[i].dynamic) {
var dataLoc = encodedParams.length * 32 + Bytes.length(dataBlock);
headBlock = Bytes.concat(headBlock, Bytes.pad(32, Nat.fromNumber(dataLoc)));
dataBlock = Bytes.concat(dataBlock, encodedParams[i].data);
} else {
headBlock = Bytes.concat(headBlock, encodedParams[i].data);
}
}
return Bytes.flatten([methodHash, headBlock, dataBlock]);
};
};
// Address, Address, ContractInterface -> Contract
var contract = function contract(from, address, contractInterface) {
var contract = {};
contract._address = address;
contract._from = from;
contract.broadcast = {};
contractInterface.forEach(function (method) {
if (method && method.name) {
var _call = function _call(type, value) {
return function () {
var transaction = {
from: from,
to: address,
value: value,
data: callMethodData(method).apply(undefined, arguments)
};
return type === "data" ? Promise.resolve(transaction) : method.constant ? callWithDefaults(transaction) : sendTransactionWithDefaults(transaction).then(type === "receipt" ? waitTransactionReceipt : function (x) {
return x;
});
};
};
contract[method.name] = _call("receipt", "0x0");
if (!method.constant) {
contract[method.name + "_data"] = _call("data", "0x0");
contract[method.name + "_pay"] = function (value) {
return function () {
return _call("receipt", value).apply(undefined, arguments);
};
};
contract[method.name + "_pay_txHash"] = function (value) {
return function () {
return _call("txHash", value).apply(undefined, arguments);
};
};
contract[method.name + "_txHash"] = _call("txHash", "0x0");
}
}
});
return contract;
};
// Address, Bytecode -> TxHash
var deployBytecode_txHash = function deployBytecode_txHash(from, code) {
return sendTransactionWithDefaults({ from: from, data: code, to: "" });
};
// Address, Bytecode -> Receipt
var deployBytecode = function deployBytecode(from, code) {
return deployBytecode_txHash(from, code).then(waitTransactionReceipt);
};
// Address, Bytecode, ContractInterface
var deployBytecodeContract = function deployBytecodeContract(from, code, contractInterface) {
return deployBytecode(from, code).then(function (receipt) {
return contract(from, receipt.contractAddress, contractInterface);
});
};
// Address, String, Address -> Contract
var solidityContract = function solidityContract(from, source, at) {
return compileSolidity(source).then(function (_ref3) {
var abiDefinition = _ref3.info.abiDefinition;
return contract(from, at, abiDefinition);
});
};
// Address, String -> TxHash
var deploySolidity_txHash = function deploySolidity_txHash(from, source) {
return compileSolidity(source).then(function (_ref4) {
var code = _ref4.code;
return deployBytecode_txHash(from, code);
});
};
// Address, String -> Receipt
var deploySolidity = function deploySolidity(from, source) {
return deploySolidity_txHash(from, source).then(waitTransactionReceipt);
};
// Address, String -> Contract
var deploySolidityContract = function deploySolidityContract(from, source) {
return compileSolidity(source).then(function (_ref5) {
var code = _ref5.code,
abiDefinition = _ref5.info.abiDefinition;
return deployBytecodeContract(from, code, abiDefinition);
});
};
return {
send: send,
sendTransaction: sendTransaction,
sendRawTransaction: sendRawTransaction,
getTransactionReceipt: getTransactionReceipt,
call: call,
getBalance: getBalance,
accounts: accounts,
waitTransactionReceipt: waitTransactionReceipt,
addTransactionDefaults: addTransactionDefaults,
sendTransactionWithDefaults: sendTransactionWithDefaults,
callWithDefaults: callWithDefaults,
callMethodData: callMethodData,
contract: contract,
deployBytecode_txHash: deployBytecode_txHash,
deployBytecode: deployBytecode,
deployBytecodeContract: deployBytecodeContract,
compileSolidity: compileSolidity,
solidityContract: solidityContract,
deploySolidity_txHash: deploySolidity_txHash,
deploySolidity: deploySolidity,
deploySolidityContract: deploySolidityContract
};
};
module.exports = Api;
+29
View File
@@ -0,0 +1,29 @@
const generate = (num, fn) => {
let a = [];
for (var i = 0; i < num; ++i) a.push(fn(i));
return a;
};
const replicate = (num, val) => generate(num, () => val);
const concat = (a, b) => a.concat(b);
const flatten = a => {
let r = [];
for (let j = 0, J = a.length; j < J; ++j) for (let i = 0, I = a[j].length; i < I; ++i) r.push(a[j][i]);
return r;
};
const chunksOf = (n, a) => {
let b = [];
for (let i = 0, l = a.length; i < l; i += n) b.push(a.slice(i, i + n));
return b;
};
module.exports = {
generate,
replicate,
concat,
flatten,
chunksOf
};
+158
View File
@@ -0,0 +1,158 @@
const A = require("./array.js");
const at = (bytes, index) => parseInt(bytes.slice(index * 2 + 2, index * 2 + 4), 16);
const random = bytes => {
let rnd;
if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues) rnd = window.crypto.getRandomValues(new Uint8Array(bytes));else if (typeof require !== "undefined") rnd = require("c" + "rypto").randomBytes(bytes);else throw "Safe random numbers not available.";
let hex = "0x";
for (let i = 0; i < bytes; ++i) hex += ("00" + rnd[i].toString(16)).slice(-2);
return hex;
};
const length = a => (a.length - 2) / 2;
const flatten = a => "0x" + a.reduce((r, s) => r + s.slice(2), "");
const slice = (i, j, bs) => "0x" + bs.slice(i * 2 + 2, j * 2 + 2);
const reverse = hex => {
let rev = "0x";
for (let i = 0, l = length(hex); i < l; ++i) {
rev += hex.slice((l - i) * 2, (l - i + 1) * 2);
}
return rev;
};
const pad = (l, hex) => hex.length === l * 2 + 2 ? hex : pad(l, "0x" + "0" + hex.slice(2));
const padRight = (l, hex) => hex.length === l * 2 + 2 ? hex : padRight(l, hex + "0");
const toArray = hex => {
let arr = [];
for (let i = 2, l = hex.length; i < l; i += 2) arr.push(parseInt(hex.slice(i, i + 2), 16));
return arr;
};
const fromArray = arr => {
let hex = "0x";
for (let i = 0, l = arr.length; i < l; ++i) {
let b = arr[i];
hex += (b < 16 ? "0" : "") + b.toString(16);
}
return hex;
};
const toUint8Array = hex => new Uint8Array(toArray(hex));
const fromUint8Array = arr => fromArray([].slice.call(arr, 0));
const fromNumber = num => {
let hex = num.toString(16);
return hex.length % 2 === 0 ? "0x" + hex : "0x0" + hex;
};
const toNumber = hex => parseInt(hex.slice(2), 16);
const concat = (a, b) => a.concat(b.slice(2));
const fromNat = bn => bn === "0x0" ? "0x" : bn.length % 2 === 0 ? bn : "0x0" + bn.slice(2);
const toNat = bn => bn[2] === "0" ? "0x" + bn.slice(3) : bn;
const fromAscii = ascii => {
let hex = "0x";
for (let i = 0; i < ascii.length; ++i) hex += ("00" + ascii.charCodeAt(i).toString(16)).slice(-2);
return hex;
};
const toAscii = hex => {
let ascii = "";
for (let i = 2; i < hex.length; i += 2) ascii += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16));
return ascii;
};
// From https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
const fromString = s => {
const makeByte = uint8 => {
const b = uint8.toString(16);
return b.length < 2 ? "0" + b : b;
};
let bytes = "0x";
for (let ci = 0; ci != s.length; ci++) {
let c = s.charCodeAt(ci);
if (c < 128) {
bytes += makeByte(c);
continue;
}
if (c < 2048) {
bytes += makeByte(c >> 6 | 192);
} else {
if (c > 0xd7ff && c < 0xdc00) {
if (++ci == s.length) return null;
let c2 = s.charCodeAt(ci);
if (c2 < 0xdc00 || c2 > 0xdfff) return null;
c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
bytes += makeByte(c >> 18 | 240);
bytes += makeByte(c >> 12 & 63 | 128);
} else {
// c <= 0xffff
bytes += makeByte(c >> 12 | 224);
}
bytes += makeByte(c >> 6 & 63 | 128);
}
bytes += makeByte(c & 63 | 128);
}
return bytes;
};
const toString = bytes => {
let s = '';
let i = 0;
let l = length(bytes);
while (i < l) {
let c = at(bytes, i++);
if (c > 127) {
if (c > 191 && c < 224) {
if (i >= l) return null;
c = (c & 31) << 6 | at(bytes, i) & 63;
} else if (c > 223 && c < 240) {
if (i + 1 >= l) return null;
c = (c & 15) << 12 | (at(bytes, i) & 63) << 6 | at(bytes, ++i) & 63;
} else if (c > 239 && c < 248) {
if (i + 2 >= l) return null;
c = (c & 7) << 18 | (at(bytes, i) & 63) << 12 | (at(bytes, ++i) & 63) << 6 | at(bytes, ++i) & 63;
} else return null;
++i;
}
if (c <= 0xffff) s += String.fromCharCode(c);else if (c <= 0x10ffff) {
c -= 0x10000;
s += String.fromCharCode(c >> 10 | 0xd800);
s += String.fromCharCode(c & 0x3FF | 0xdc00);
} else return null;
}
return s;
};
module.exports = {
random,
length,
concat,
flatten,
slice,
reverse,
pad,
padRight,
fromAscii,
toAscii,
fromString,
toString,
fromNumber,
toNumber,
fromNat,
toNat,
fromArray,
toArray,
fromUint8Array,
toUint8Array
};
+53
View File
@@ -0,0 +1,53 @@
const Bytes = require("./bytes");
const inis = "pbtdkgxjfvlrmnsz".split("");
const mids = "auie".split("");
const back = [inis, mids].map(chars => {
let map = {};
for (let i = 0; i < chars.length; ++i) map[chars[i]] = i;
return map;
});
const syllableFromB64 = b64 => {
const ini = b64 >>> 2 & 15;
const mid = b64 >>> 0 & 3;
return inis[ini] + mids[mid];
};
const syllableToB64 = syllable => {
const ini = back[0][syllable[0]];
const mid = back[1][syllable[1]];
return ini * 4 + mid;
};
const b64sFromBytes = bytes => {
//BoooooBoooooBoooooBooooo
//BoooooooBoooooooBooooooo
let b64s = [],
b64;
for (let i = 0, l = Math.ceil(bytes.length * 8 / 6); i < l; ++i) {
let j = i / 8 * 6 | 0;
b64s.push(i % 4 === 0 ? bytes[j + 0] / 4 | 0 : i % 4 === 1 ? bytes[j + 0] % 4 * 16 + (bytes[j + 1] / 16 | 0) : i % 4 === 2 ? bytes[j + 0] % 16 * 4 + (bytes[j + 1] / 64 | 0) : bytes[j + 0] % 64 * 1);
}
return b64s;
};
const b64sToBytes = b64s => {
let bytes = [];
for (let i = 0, l = Math.floor(b64s.length * 6 / 8); i < l; ++i) {
let j = i / 6 * 8 | 0;
bytes.push(i % 3 === 0 ? b64s[j + 0] % 64 * 4 + (b64s[j + 1] / 16 | 0) : i % 3 === 1 ? b64s[j + 0] % 16 * 16 + (b64s[j + 1] / 4 | 0) : b64s[j + 0] % 4 * 64 + (b64s[j + 1] / 1 | 0));
}
return bytes;
};
const fromBytes = bytes => b64sFromBytes(Bytes.toArray(bytes)).map(syllableFromB64).join("");
const toBytes = syllables => Bytes.fromArray(b64sToBytes(syllables.match(/\w\w/g).map(syllableToB64)));
module.exports = {
fromBytes,
toBytes
};
+10
View File
@@ -0,0 +1,10 @@
const times = (n, f, x) => {
for (let i = 0; i < n; ++i) {
x = f(x);
}
return x;
};
module.exports = {
times
};
+331
View File
@@ -0,0 +1,331 @@
// This was ported from https://github.com/emn178/js-sha3, with some minor
// modifications and pruning. It is licensed under MIT:
//
// Copyright 2015-2016 Chen, Yi-Cyuan
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
const HEX_CHARS = '0123456789abcdef'.split('');
const KECCAK_PADDING = [1, 256, 65536, 16777216];
const SHIFT = [0, 8, 16, 24];
const 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];
const Keccak = bits => ({
blocks: [],
reset: true,
block: 0,
start: 0,
blockCount: 1600 - (bits << 1) >> 5,
outputBlocks: bits >> 5,
s: (s => [].concat(s, s, s, s, s))([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
});
const update = (state, message) => {
var length = message.length,
blocks = state.blocks,
byteCount = state.blockCount << 2,
blockCount = state.blockCount,
outputBlocks = state.outputBlocks,
s = state.s,
index = 0,
i,
code;
// update
while (index < length) {
if (state.reset) {
state.reset = false;
blocks[0] = state.block;
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
if (typeof message !== "string") {
for (i = state.start; index < length && i < byteCount; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
} else {
for (i = state.start; index < length && i < byteCount; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
} else {
code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
}
}
}
state.lastByteIndex = i;
if (i >= byteCount) {
state.start = i - byteCount;
state.block = blocks[blockCount];
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
state.reset = true;
} else {
state.start = i;
}
}
// finalize
i = state.lastByteIndex;
blocks[i >> 2] |= KECCAK_PADDING[i & 3];
if (state.lastByteIndex === byteCount) {
blocks[0] = blocks[blockCount];
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
blocks[blockCount - 1] |= 0x80000000;
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
// toString
var hex = '',
i = 0,
j = 0,
block;
while (j < outputBlocks) {
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
block = s[i];
hex += HEX_CHARS[block >> 4 & 0x0F] + HEX_CHARS[block & 0x0F] + HEX_CHARS[block >> 12 & 0x0F] + HEX_CHARS[block >> 8 & 0x0F] + HEX_CHARS[block >> 20 & 0x0F] + HEX_CHARS[block >> 16 & 0x0F] + HEX_CHARS[block >> 28 & 0x0F] + HEX_CHARS[block >> 24 & 0x0F];
}
if (j % blockCount === 0) {
f(s);
i = 0;
}
}
return "0x" + hex;
};
const f = s => {
var h, l, n, 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;
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];
}
};
const keccak = bits => str => {
var msg;
if (str.slice(0, 2) === "0x") {
msg = [];
for (var i = 2, l = str.length; i < l; i += 2) msg.push(parseInt(str.slice(i, i + 2), 16));
} else {
msg = str;
}
return update(Keccak(bits, bits), msg);
};
module.exports = {
keccak256: keccak(256),
keccak512: keccak(512),
keccak256s: keccak(256),
keccak512s: keccak(512)
};
+12
View File
@@ -0,0 +1,12 @@
module.exports = {
account: require("./account.js"),
nat: require("./nat.js"),
bytes: require("./bytes.js"),
hash: require("./hash.js"),
RLP: require("./rlp.js"),
abi: require("./abi.js"),
transaction: require("./transaction.js"),
rpc: require("./rpc.js"),
desubits: require("./desubits.js"),
passphrase: require("./passphrase.js")
};
+17
View File
@@ -0,0 +1,17 @@
const merge = a => b => {
let c = {};
for (let key in a) c[key] = a[key];
for (let key in b) c[key] = b[key];
return c;
};
const remove = removeKey => a => {
let b = {};
for (let key in a) if (key !== removeKey) b[key] = a[key];
return b;
};
module.exports = {
merge,
remove
};
+44
View File
@@ -0,0 +1,44 @@
const BN = require("bn.js");
const Bytes = require("./bytes");
const fromBN = bn => "0x" + bn.toString("hex");
const toBN = str => new BN(str.slice(2), 16);
const fromString = str => {
const bn = "0x" + (str.slice(0, 2) === "0x" ? new BN(str.slice(2), 16) : new BN(str, 10)).toString("hex");
return bn === "0x0" ? "0x" : bn;
};
const toEther = wei => toNumber(div(wei, fromString("10000000000"))) / 100000000;
const fromEther = eth => mul(fromNumber(Math.floor(eth * 100000000)), fromString("10000000000"));
const toString = a => toBN(a).toString(10);
const fromNumber = a => typeof a === "string" ? /^0x/.test(a) ? a : "0x" + a : "0x" + new BN(a).toString("hex");
const toNumber = a => toBN(a).toNumber();
const toUint256 = a => Bytes.pad(32, a);
const bin = method => (a, b) => fromBN(toBN(a)[method](toBN(b)));
const add = bin("add");
const mul = bin("mul");
const div = bin("div");
const sub = bin("sub");
module.exports = {
toString,
fromString,
toNumber,
fromNumber,
toEther,
fromEther,
toUint256,
add,
mul,
div,
sub
};
+49
View File
@@ -0,0 +1,49 @@
const Hash = require("./hash");
const Bytes = require("./bytes");
const Desubits = require("./desubits");
// Bytes -> Bytes
const bytesAddChecksum = bytes => {
const hash = Hash.keccak256(bytes);
const sum = Bytes.slice(0, 1, hash);
return Bytes.concat(bytes, sum);
};
// Bytes -> Bool
const bytesChecksum = bytes => {
const length = Bytes.length(bytes);
const prefix = Bytes.slice(0, length - 1, bytes);
return bytesAddChecksum(prefix) === bytes;
};
// () ~> Passphrase
const create = () => {
const bytes = bytesAddChecksum(Bytes.random(11));
const seed = Desubits.fromBytes(bytes);
const passphrase = seed.replace(/([a-z]{8})/g, "$1 ");
return passphrase;
};
// Passphrase -> Bytes
const toBytes = passphrase => {
const seed = passphrase.replace(/ /g, "");
const bytes = Desubits.toBytes(passphrase);
return bytes;
};
// Passphrase -> Bool
const checksum = passphrase => bytesChecksum(toBytes(passphrase));
// Passphrase -> Bytes
const toMasterKey = passphrase => {
let hash = Hash.keccak256;
let bytes = toBytes(passphrase);
for (let i = 0, l = Math.pow(2, 12); i < l; ++i) bytes = hash(bytes);
return bytes;
};
module.exports = {
create,
checksum,
toMasterKey
};
+46
View File
@@ -0,0 +1,46 @@
var njsp = require("nano-json-stream-parser");
var request = require("xhr-request-promise");
var EthereumProvider = function EthereumProvider(url) {
var api = {};
var onResponse = {};
var callbacks = {};
var nextId = 0;
var send = void 0;
var parseResponse = njsp(function (json) {
onResponse[json.id] && onResponse[json.id](null, json.result);
});
var genPayload = function genPayload(method, params) {
return {
jsonrpc: "2.0",
id: ++nextId,
method: method,
params: params
};
};
if (/^http/.test(url)) {
api.send = async function (method, params) {
try {
var answer = await request(url, {
method: "POST",
contentType: "application/json-rpc",
body: JSON.stringify(genPayload(method, params)) });
var resp = JSON.parse(answer);
if (resp.error) {
return resp.error.message;
} else {
throw resp.result;
}
} catch (e) {
console.log(e);
}
};
};
return api;
};
module.exports = EthereumProvider;
+66
View File
@@ -0,0 +1,66 @@
// The RLP format
// Serialization and deserialization for the BytesTree type, under the following grammar:
// | First byte | Meaning |
// | ---------- | -------------------------------------------------------------------------- |
// | 0 to 127 | HEX(leaf) |
// | 128 to 183 | HEX(length_of_leaf + 128) + HEX(leaf) |
// | 184 to 191 | HEX(length_of_length_of_leaf + 128 + 55) + HEX(length_of_leaf) + HEX(leaf) |
// | 192 to 247 | HEX(length_of_node + 192) + HEX(node) |
// | 248 to 255 | HEX(length_of_length_of_node + 128 + 55) + HEX(length_of_node) + HEX(node) |
const encode = tree => {
const padEven = str => str.length % 2 === 0 ? str : "0" + str;
const uint = num => padEven(num.toString(16));
const length = (len, add) => len < 56 ? uint(add + len) : uint(add + uint(len).length / 2 + 55) + uint(len);
const dataTree = tree => {
if (typeof tree === "string") {
const hex = tree.slice(2);
const pre = hex.length != 2 || hex >= "80" ? length(hex.length / 2, 128) : "";
return pre + hex;
} else {
const hex = tree.map(dataTree).join("");
const pre = length(hex.length / 2, 192);
return pre + hex;
}
};
return "0x" + dataTree(tree);
};
const decode = hex => {
let i = 2;
const parseTree = () => {
if (i >= hex.length) throw "";
const head = hex.slice(i, i + 2);
return head < "80" ? (i += 2, "0x" + head) : head < "c0" ? parseHex() : parseList();
};
const parseLength = () => {
const len = parseInt(hex.slice(i, i += 2), 16) % 64;
return len < 56 ? len : parseInt(hex.slice(i, i += (len - 55) * 2), 16);
};
const parseHex = () => {
const len = parseLength();
return "0x" + hex.slice(i, i += len * 2);
};
const parseList = () => {
const lim = parseLength() * 2 + i;
let list = [];
while (i < lim) list.push(parseTree());
return list;
};
try {
return parseTree();
} catch (e) {
return [];
}
};
module.exports = { encode, decode };
+30
View File
@@ -0,0 +1,30 @@
const request = require("xhr-request-promise");
const genPayload = (() => {
let nextId = 0;
return (method, params) => ({
jsonrpc: "2.0",
id: ++nextId,
method: method,
params: params
});
})();
const send = url => (method, params) => {
return request(url, {
method: "POST",
contentType: "application/json-rpc",
body: JSON.stringify(genPayload(method, params))
}).then(answer => {
var resp = JSON.parse(answer); // todo: use njsp?
if (resp.error) {
throw new Error(resp.error.message);
} else {
return resp.result;
}
}).catch(e => {
return { error: e.toString() };
});
};
module.exports = send;
+67
View File
@@ -0,0 +1,67 @@
const Account = require("./account");
const Nat = require("./nat");
const Bytes = require("./bytes");
const RLP = require("./rlp");
const keccak256 = require("./hash").keccak256;
// EthereumRPC, IncompleteTransaction -> Promise Transaction
const addDefaults = (rpc, tx) => {
var baseDefaults = [tx.chainId || rpc("net_version", []), tx.gasPrice || rpc("eth_gasPrice", []), tx.nonce || rpc("eth_getTransactionCount", [tx.from, "latest"]), tx.value || "0x0", tx.data || "0x"];
const noAddress = address => !address || address === "" || address === "0x";
return Promise.all(baseDefaults).then(([chainIdNum, gasPrice, nonce, value, data]) => {
var chainId = Nat.fromNumber(chainIdNum);
var gasEstimator = tx.gas ? Promise.resolve(null) : rpc("eth_estimateGas", [{
from: noAddress(tx.from) ? null : tx.from,
to: noAddress(tx.to) ? null : tx.to,
value: tx.value,
nonce: tx.nonce,
data: tx.data
}]);
return gasEstimator.then(gasEstimate => {
if (gasEstimate.error) {
throw gasEstimate.error;
}
return {
chainId: chainId,
from: noAddress(tx.from) ? "0x" : tx.from.toLowerCase(),
to: noAddress(tx.to) ? "0x" : tx.to.toLowerCase(),
gasPrice: gasPrice,
gas: tx.gas ? tx.gas : Nat.div(Nat.mul(gasEstimate, "0x6"), "0x5"),
nonce: nonce,
value: value,
data: data ? data.toLowerCase() : null
};
});
});
};
// Transaction -> Bytes
const signingData = tx => {
return RLP.encode([Bytes.fromNat(tx.nonce), Bytes.fromNat(tx.gasPrice), Bytes.fromNat(tx.gas), tx.to ? tx.to.toLowerCase() : "0x", Bytes.fromNat(tx.value), tx.data, Bytes.fromNat(tx.chainId || "0x1"), "0x", "0x"]);
};
// Transaction, Account -> Bytes
const sign = (tx, account) => {
const data = signingData(tx);
const signature = Account.makeSigner(Nat.toNumber(tx.chainId || "0x1") * 2 + 35)(keccak256(data), account.privateKey);
const rawTransaction = RLP.decode(data).slice(0, 6).concat(Account.decodeSignature(signature));
return RLP.encode(rawTransaction);
};
// Bytes -> Address
const recover = rawTransaction => {
const values = RLP.decode(rawTransaction);
const signature = Account.encodeSignature(values.slice(6, 9));
const recovery = Bytes.toNumber(values[6]);
const extraData = recovery < 35 ? [] : [Bytes.fromNumber(recovery - 35 >> 1), "0x", "0x"];
const data = values.slice(0, 6).concat(extraData);
const dataHex = RLP.encode(data);
return Account.recover(keccak256(dataHex), signature);
};
module.exports = {
addDefaults,
signingData,
sign,
recover
};
+95
View File
@@ -0,0 +1,95 @@
var F = require("forall");
var A = require("./array");
var B = require("./bytes");
var Account = require("./account");
F.Bytes = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by an even number of low-case hex chars (i.e., `0123456789abcdef`)",
rand: function rand() {
return "0x" + A.generate((Math.random() * 16 | 0) * 2, function () {
return (Math.random() * 16 | 0).toString(16);
}).join("");
},
test: function test(value) {
return typeof value === "string" && /^0x([0-9a-f][0-9a-f])*$/.test(value);
}
}).__name("Bytes").__desc("any arbitrary data");
F.NBytes = function (bytes) {
return F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by " + bytes * 2 + " low-case hex chars (i.e., `0123456789abcdef`)",
test: function test(value) {
return F.Bytes.test(value) && value.length === bytes * 2 + 2;
},
rand: function rand() {
return "0x" + A.generate(bytes * 2, function () {
return (Math.random() * 16 | 0).toString(16);
}).join("");
}
}).__name("NBytes(" + bytes + ")").__desc("any arbitrary data of exactly " + bytes + "-byte" + (bytes > 1 ? "s" : ""));
};
F.Nat = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by at least one low-case hex char different from 0, followed by any number of low-case hex chars (i.e., `0123456789abcdef`)",
test: function test(value) {
return typeof value === "string" && /^0x[1-9a-f]([0-9a-f])*$/.test(value);
},
rand: function rand() {
return "0x" + (Math.random() * Math.pow(2, 50) | 0).toString(16);
}
}).__name("Nat").__desc("an arbitrarily long non-negative integer number");
F.Address = F(F.Type, {
form: "a JavaScript String starting with a `0x`, followed by 40 hex chars (i.e., `0123456789abcdefABCDEF`), with the nth hex being uppercase iff the nth hex of the keccak256 of the lowercase address in ASCII is > 7",
test: function test(address) {
return (/^(0x)?[0-9a-f]{40}$/i.test(address) && Account.toChecksum(address.toLowerCase()) === address
);
},
rand: function rand() {
return F.Account.rand().address;
}
}).__name("Address").__desc("an Ethereum public address");
F.Hash = F(F.Type, {
form: F.NBytes(32).form,
test: F.NBytes(32).test,
rand: F.NBytes(32).rand
}).__name("Hash").__desc("a Keccak-256 hash");
F.PrivateKey = F(F.Type, {
form: F.NBytes(32).form,
test: F.NBytes(32).test,
rand: F.NBytes(32).rand
}).__name("PrivateKey").__desc("an Ethereum private key");
F.Account = function () {
var base = F.Struct({
address: F.Address,
privateKey: F.PrivateKey
});
return F(F.Type, {
form: base.form,
test: base.test,
rand: function rand() {
return Account.create("");
}
});
}().__name("Account").__desc("an Ethereum account");
F.BytesTree = F(F.Type, {
form: "either " + F.Bytes.form + ", or a tree of nested JavaScript Arrays of BytesTrees",
test: function test(value) {
return F.Bytes.test(value) || value instanceof Array && value.reduce(function (r, v) {
return F.BytesTree.test(v) && r;
}, true);
},
rand: function rand() {
var list = [];
while (Math.random() < 0.8) {
if (Math.random() < 0.8) list.push(F.Bytes.rand());else list.push(F.BytesTree.rand());
}
return list;
}
}).__name("BytesTree").__desc("a tree of arbitrary binary data");
module.exports = F;
+62
View File
@@ -0,0 +1,62 @@
{
"_from": "eth-lib@0.2.8",
"_id": "eth-lib@0.2.8",
"_inBundle": false,
"_integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==",
"_location": "/web3-utils/eth-lib",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "eth-lib@0.2.8",
"name": "eth-lib",
"escapedName": "eth-lib",
"rawSpec": "0.2.8",
"saveSpec": null,
"fetchSpec": "0.2.8"
},
"_requiredBy": [
"/web3-utils"
],
"_resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz",
"_shasum": "b194058bef4b220ad12ea497431d6cb6aa0623c8",
"_spec": "eth-lib@0.2.8",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\BackendServers\\node_modules\\web3-utils",
"author": {
"name": "Victor Maia"
},
"bugs": {
"url": "https://github.com/maiavictor/eth-lib/issues"
},
"bundleDependencies": false,
"dependencies": {
"bn.js": "^4.11.6",
"elliptic": "^6.4.0",
"xhr-request-promise": "^0.1.2"
},
"deprecated": false,
"description": "Lightweight Ethereum libraries",
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"ethjs-account": "^0.1.1",
"ethjs-signer": "^0.1.1",
"rlp": "^2.0.0"
},
"homepage": "https://github.com/maiavictor/eth-lib#readme",
"keywords": [
"ethereum"
],
"license": "MIT",
"main": "lib/index.js",
"name": "eth-lib",
"repository": {
"type": "git",
"url": "git+https://github.com/maiavictor/eth-lib.git"
},
"scripts": {
"build": "babel src --out-dir=lib",
"test": "mocha test/test.js"
},
"version": "0.2.8"
}
+57
View File
@@ -0,0 +1,57 @@
// type Var = {
// name: String
// type: "uint256" | "bytes32" | ...
// }
//
// type Method = {
// name: String
// inputs: [Var]
// output: [Var]
// constant: Bool
// payable: Bool
// }
const Bytes = require("./bytes");
const Nat = require("./nat");
const keccak256s = require("./hash").keccak256s;
// (type : String), JSType(type) -> {data: Bytes, dynamic: Bool}
// ABI-encodes a single term.
const encode = (type, value) => {
if (type === "bytes") {
const length = Bytes.length(value);
const nextMul32 = (((length - 1) / 32 | 0) + 1) * 32;
const lengthEncoded = encode("uint256", Nat.fromNumber(length)).data;
const bytesEncoded = Bytes.padRight(nextMul32, value);
return {data: Bytes.concat(lengthEncoded, bytesEncoded), dynamic: true};
} else if (type === "uint256" || type === "bytes32" || type === "address") {
return {data: Bytes.pad(32, value), dynamic: false};
} else {
throw "Eth-lib can't encode ABI type " + type + " yet.";
}
}
// (method : Method), [JSType(method.inputs[i].type)] -> Bytes
// ABI-encodes the transaction data to call a method.
const methodData = (method, params) => {
const methodSig = method.name + "(" + method.inputs.map(i => i.type).join(",") + ")";
const methodHash = keccak256s(methodSig).slice(0,10);
let encodedParams = params.map((param,i) => encode(method.inputs[i].type, param));
var headBlock = "0x";
let dataBlock = "0x";
for (var i = 0; i < encodedParams.length; ++i) {
if (encodedParams[i].dynamic) {
var dataLoc = encodedParams.length * 32 + Bytes.length(dataBlock);
headBlock = Bytes.concat(headBlock, Bytes.pad(32, Nat.fromNumber(dataLoc)));
dataBlock = Bytes.concat(dataBlock, encodedParams[i].data);
} else {
headBlock = Bytes.concat(headBlock, encodedParams[i].data);
}
}
return Bytes.flatten([methodHash, headBlock, dataBlock]);
};
module.exports = {
encode,
methodData
};
+76
View File
@@ -0,0 +1,76 @@
const Bytes = require("./bytes");
const Nat = require("./nat");
const elliptic = require("elliptic");
const rlp = require("./rlp");
const secp256k1 = new (elliptic.ec)("secp256k1"); // eslint-disable-line
const {keccak256, keccak256s} = require("./hash");
const create = entropy => {
const innerHex = keccak256(Bytes.concat(Bytes.random(32), entropy || Bytes.random(32)));
const middleHex = Bytes.concat(Bytes.concat(Bytes.random(32), innerHex), Bytes.random(32));
const outerHex = keccak256(middleHex);
return fromPrivate(outerHex);
}
const toChecksum = address => {
const addressHash = keccak256s(address.slice(2));
let checksumAddress = "0x";
for (let i = 0; i < 40; i++)
checksumAddress += parseInt(addressHash[i + 2], 16) > 7
? address[i + 2].toUpperCase()
: address[i + 2];
return checksumAddress;
}
const fromPrivate = privateKey => {
const buffer = new Buffer(privateKey.slice(2), "hex");
const ecKey = secp256k1.keyFromPrivate(buffer);
const publicKey = "0x" + ecKey.getPublic(false, 'hex').slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return {
address: address,
privateKey: privateKey
}
}
const encodeSignature = ([v, r, s]) =>
Bytes.flatten([r,s,v]);
const decodeSignature = (hex) => [
Bytes.slice(64, Bytes.length(hex), hex),
Bytes.slice(0, 32, hex),
Bytes.slice(32, 64, hex)];
const makeSigner = addToV => (hash, privateKey) => {
const signature = secp256k1
.keyFromPrivate(new Buffer(privateKey.slice(2), "hex"))
.sign(new Buffer(hash.slice(2), "hex"), {canonical: true});
return encodeSignature([
Nat.fromString(Bytes.fromNumber(addToV + signature.recoveryParam)),
Bytes.pad(32, Bytes.fromNat("0x" + signature.r.toString(16))),
Bytes.pad(32, Bytes.fromNat("0x" + signature.s.toString(16)))]);
}
const sign = makeSigner(27); // v=27|28 instead of 0|1...
const recover = (hash, signature) => {
const vals = decodeSignature(signature);
const vrs = {v: Bytes.toNumber(vals[0]), r:vals[1].slice(2), s:vals[2].slice(2)};
const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - (vrs.v % 2)); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that
const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return address;
}
module.exports = {
create,
toChecksum,
fromPrivate,
sign,
makeSigner,
recover,
encodeSignature,
decodeSignature
}
+35
View File
@@ -0,0 +1,35 @@
const generate = (num, fn) => {
let a = [];
for (var i = 0; i < num; ++i)
a.push(fn(i));
return a;
};
const replicate = (num, val) =>
generate(num, () => val);
const concat = (a, b) =>
a.concat(b);
const flatten = (a) => {
let r = [];
for (let j = 0, J = a.length; j < J; ++j)
for (let i = 0, I = a[j].length; i < I; ++i)
r.push(a[j][i]);
return r;
}
const chunksOf = (n,a) => {
let b = [];
for (let i = 0, l = a.length; i < l; i += n)
b.push(a.slice(i, i + n));
return b;
}
module.exports = {
generate,
replicate,
concat,
flatten,
chunksOf
}
+179
View File
@@ -0,0 +1,179 @@
const A = require("./array.js");
const at = (bytes, index) =>
parseInt(bytes.slice(index * 2 + 2, index * 2 + 4), 16);
const random = bytes => {
let rnd;
if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues)
rnd = window.crypto.getRandomValues(new Uint8Array(bytes));
else if (typeof require !== "undefined")
rnd = require("c" + "rypto").randomBytes(bytes);
else
throw "Safe random numbers not available.";
let hex = "0x";
for (let i = 0; i < bytes; ++i)
hex += ("00" + rnd[i].toString(16)).slice(-2);
return hex;
};
const length = a =>
(a.length - 2) / 2;
const flatten = (a) =>
"0x" + a.reduce((r,s) => r + s.slice(2), "");
const slice = (i,j,bs) =>
"0x" + bs.slice(i*2+2,j*2+2);
const reverse = hex => {
let rev = "0x";
for (let i = 0, l = length(hex); i < l; ++i) {
rev += hex.slice((l-i)*2, (l-i+1)*2);
}
return rev;
}
const pad = (l,hex) =>
hex.length === l*2+2 ? hex : pad(l,"0x"+"0"+hex.slice(2));
const padRight = (l,hex) =>
hex.length === l*2+2 ? hex : padRight(l,hex+"0");
const toArray = hex => {
let arr = [];
for (let i = 2, l = hex.length; i < l; i += 2)
arr.push(parseInt(hex.slice(i, i + 2), 16));
return arr;
}
const fromArray = arr => {
let hex = "0x";
for (let i = 0, l = arr.length; i < l; ++i) {
let b = arr[i];
hex += (b < 16 ? "0" : "") + b.toString(16);
}
return hex;
}
const toUint8Array = hex =>
new Uint8Array(toArray(hex));
const fromUint8Array = arr =>
fromArray([].slice.call(arr, 0));
const fromNumber = num => {
let hex = num.toString(16);
return hex.length % 2 === 0 ? "0x" + hex : "0x0" + hex ;
};
const toNumber = hex =>
parseInt(hex.slice(2), 16);
const concat = (a, b) =>
a.concat(b.slice(2));
const fromNat = bn =>
bn === "0x0" ? "0x" : bn.length % 2 === 0 ? bn : "0x0" + bn.slice(2);
const toNat = bn =>
bn[2] === "0" ? "0x" + bn.slice(3) : bn;
const fromAscii = ascii => {
let hex = "0x";
for (let i = 0; i < ascii.length; ++i)
hex += ("00" + ascii.charCodeAt(i).toString(16)).slice(-2);
return hex;
};
const toAscii = hex => {
let ascii = "";
for (let i = 2; i < hex.length; i += 2)
ascii += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16));
return ascii;
};
// From https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
const fromString = s => {
const makeByte = uint8 => {
const b = uint8.toString(16);
return b.length < 2 ? "0" + b : b;
};
let bytes = "0x";
for (let ci = 0; ci != s.length; ci++) {
let c = s.charCodeAt(ci);
if (c < 128) {
bytes += makeByte(c);
continue;
}
if (c < 2048) {
bytes += makeByte(c >> 6 | 192);
} else {
if (c > 0xd7ff && c < 0xdc00) {
if (++ci == s.length) return null;
let c2 = s.charCodeAt(ci);
if (c2 < 0xdc00 || c2 > 0xdfff) return null;
c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
bytes += makeByte(c >> 18 | 240);
bytes += makeByte(c>> 12 & 63 | 128);
} else { // c <= 0xffff
bytes += makeByte(c >> 12 | 224);
}
bytes += makeByte(c >> 6 & 63 | 128);
}
bytes += makeByte(c & 63 | 128);
}
return bytes;
};
const toString = (bytes) => {
let s = '';
let i = 0;
let l = length(bytes);
while (i < l) {
let c = at(bytes, i++);
if (c > 127) {
if (c > 191 && c < 224) {
if (i >= l) return null;
c = (c & 31) << 6 | at(bytes, i) & 63;
} else if (c > 223 && c < 240) {
if (i + 1 >= l) return null;
c = (c & 15) << 12 | (at(bytes, i) & 63) << 6 | at(bytes, ++i) & 63;
} else if (c > 239 && c < 248) {
if (i+2 >= l) return null;
c = (c & 7) << 18 | (at(bytes, i) & 63) << 12 | (at(bytes, ++i) & 63) << 6 | at(bytes, ++i) & 63;
} else return null;
++i;
}
if (c <= 0xffff) s += String.fromCharCode(c);
else if (c <= 0x10ffff) {
c -= 0x10000;
s += String.fromCharCode(c >> 10 | 0xd800)
s += String.fromCharCode(c & 0x3FF | 0xdc00)
} else return null;
}
return s;
};
module.exports = {
random,
length,
concat,
flatten,
slice,
reverse,
pad,
padRight,
fromAscii,
toAscii,
fromString,
toString,
fromNumber,
toNumber,
fromNat,
toNat,
fromArray,
toArray,
fromUint8Array,
toUint8Array
}
+62
View File
@@ -0,0 +1,62 @@
const Bytes = require("./bytes");
const inis = "pbtdkgxjfvlrmnsz".split("");
const mids = "auie".split("");
const back = [inis,mids].map(chars => {
let map = {};
for (let i = 0; i < chars.length; ++i)
map[chars[i]] = i;
return map;
});
const syllableFromB64 = b64 => {
const ini = (b64 >>> 2) & 15;
const mid = (b64 >>> 0) & 3;
return inis[ini] + mids[mid];
}
const syllableToB64 = syllable => {
const ini = back[0][syllable[0]];
const mid = back[1][syllable[1]];
return ini * 4 + mid;
}
const b64sFromBytes = bytes => {
//BoooooBoooooBoooooBooooo
//BoooooooBoooooooBooooooo
let b64s = [], b64;
for (let i = 0, l = Math.ceil(bytes.length * 8 / 6); i < l; ++i) {
let j = i / 8 * 6 | 0;
b64s.push
( i % 4 === 0 ? (bytes[j+0] / 4 | 0)
: i % 4 === 1 ? bytes[j+0] % 4 * 16 + (bytes[j+1] / 16 | 0)
: i % 4 === 2 ? bytes[j+0] % 16 * 4 + (bytes[j+1] / 64 | 0)
: bytes[j+0] % 64 * 1);
}
return b64s;
}
const b64sToBytes = b64s => {
let bytes = [];
for (let i = 0, l = Math.floor(b64s.length * 6 / 8); i < l; ++i) {
let j = i / 6 * 8 | 0;
bytes.push
( i % 3 === 0 ? b64s[j+0] % 64 * 4 + (b64s[j+1] / 16 | 0)
: i % 3 === 1 ? b64s[j+0] % 16 * 16 + (b64s[j+1] / 4 | 0)
: b64s[j+0] % 4 * 64 + (b64s[j+1] / 1 | 0));
}
return bytes;
}
const fromBytes = bytes =>
b64sFromBytes(Bytes.toArray(bytes)).map(syllableFromB64).join("");
const toBytes = syllables =>
Bytes.fromArray(b64sToBytes(syllables.match(/\w\w/g).map(syllableToB64)));
module.exports = {
fromBytes,
toBytes
}
+10
View File
@@ -0,0 +1,10 @@
const times = (n, f, x) => {
for (let i = 0; i < n; ++i) {
x = f(x);
}
return x;
}
module.exports = {
times
}
+340
View File
@@ -0,0 +1,340 @@
// This was ported from https://github.com/emn178/js-sha3, with some minor
// modifications and pruning. It is licensed under MIT:
//
// Copyright 2015-2016 Chen, Yi-Cyuan
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
const HEX_CHARS = '0123456789abcdef'.split('');
const KECCAK_PADDING = [1, 256, 65536, 16777216];
const SHIFT = [0, 8, 16, 24];
const 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];
const Keccak = (bits) => ({
blocks: [],
reset: true,
block: 0,
start: 0,
blockCount: (1600 - (bits << 1)) >> 5,
outputBlocks: bits >> 5,
s: (s=>[].concat(s,s,s,s,s))([0,0,0,0,0,0,0,0,0,0])
});
const update = (state, message) => {
var length = message.length,
blocks = state.blocks,
byteCount = state.blockCount << 2,
blockCount = state.blockCount,
outputBlocks = state.outputBlocks,
s = state.s,
index = 0,
i,
code;
// update
while (index < length) {
if (state.reset) {
state.reset = false;
blocks[0] = state.block;
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
if (typeof message !== "string") {
for (i = state.start; index < length && i < byteCount; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
} else {
for (i = state.start; index < length && i < byteCount; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
}
}
}
state.lastByteIndex = i;
if (i >= byteCount) {
state.start = i - byteCount;
state.block = blocks[blockCount];
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
state.reset = true;
} else {
state.start = i;
}
}
// finalize
i = state.lastByteIndex;
blocks[i >> 2] |= KECCAK_PADDING[i & 3];
if (state.lastByteIndex === byteCount) {
blocks[0] = blocks[blockCount];
for (i = 1; i < blockCount + 1; ++i) {
blocks[i] = 0;
}
}
blocks[blockCount - 1] |= 0x80000000;
for (i = 0; i < blockCount; ++i) {
s[i] ^= blocks[i];
}
f(s);
// toString
var hex = '', i = 0, j = 0, block;
while (j < outputBlocks) {
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
block = s[i];
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
}
if (j % blockCount === 0) {
f(s);
i = 0;
}
}
return "0x" + hex;
}
const f = s => {
var h, l, n, 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;
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];
}
}
const keccak = bits => str => {
var msg;
if (str.slice(0,2) === "0x") {
msg = [];
for (var i = 2, l = str.length; i < l; i += 2)
msg.push(parseInt(str.slice(i,i+2), 16));
} else {
msg = str;
}
return update(Keccak(bits, bits), msg);
}
module.exports = {
keccak256: keccak(256),
keccak512: keccak(512),
keccak256s: keccak(256),
keccak512s: keccak(512),
}
+12
View File
@@ -0,0 +1,12 @@
module.exports = {
account: require("./account.js"),
nat: require("./nat.js"),
bytes: require("./bytes.js"),
hash: require("./hash.js"),
RLP: require("./rlp.js"),
abi: require("./abi.js"),
transaction: require("./transaction.js"),
rpc: require("./rpc.js"),
desubits: require("./desubits.js"),
passphrase: require("./passphrase.js")
}
+21
View File
@@ -0,0 +1,21 @@
const merge = a => b => {
let c = {};
for (let key in a)
c[key] = a[key];
for (let key in b)
c[key] = b[key];
return c;
}
const remove = removeKey => a => {
let b = {};
for (let key in a)
if (key !== removeKey)
b[key] = a[key];
return b;
}
module.exports = {
merge,
remove
}
+57
View File
@@ -0,0 +1,57 @@
const BN = require("bn.js");
const Bytes = require("./bytes");
const fromBN = bn =>
"0x" + bn.toString("hex");
const toBN = str =>
new BN(str.slice(2), 16);
const fromString = str => {
const bn = "0x" + (str.slice(0,2) === "0x"
? new BN(str.slice(2), 16)
: new BN(str, 10)).toString("hex");
return bn === "0x0" ? "0x" : bn;
}
const toEther = wei =>
toNumber(div(wei, fromString("10000000000"))) / 100000000;
const fromEther = eth =>
mul(fromNumber(Math.floor(eth * 100000000)), fromString("10000000000"));
const toString = a =>
toBN(a).toString(10);
const fromNumber = a =>
typeof a === "string"
? (/^0x/.test(a) ? a : "0x" + a)
: "0x" + new BN(a).toString("hex");
const toNumber = a =>
toBN(a).toNumber();
const toUint256 = a =>
Bytes.pad(32, a);
const bin = method => (a, b) =>
fromBN(toBN(a)[method](toBN(b)));
const add = bin("add");
const mul = bin("mul");
const div = bin("div");
const sub = bin("sub");
module.exports = {
toString,
fromString,
toNumber,
fromNumber,
toEther,
fromEther,
toUint256,
add,
mul,
div,
sub
}
+51
View File
@@ -0,0 +1,51 @@
const Hash = require("./hash");
const Bytes = require("./bytes");
const Desubits = require("./desubits");
// Bytes -> Bytes
const bytesAddChecksum = (bytes) => {
const hash = Hash.keccak256(bytes);
const sum = Bytes.slice(0, 1, hash);
return Bytes.concat(bytes, sum);
};
// Bytes -> Bool
const bytesChecksum = (bytes) => {
const length = Bytes.length(bytes);
const prefix = Bytes.slice(0, length - 1, bytes);
return bytesAddChecksum(prefix) === bytes;
};
// () ~> Passphrase
const create = () => {
const bytes = bytesAddChecksum(Bytes.random(11));
const seed = Desubits.fromBytes(bytes);
const passphrase = seed.replace(/([a-z]{8})/g, "$1 ");
return passphrase;
};
// Passphrase -> Bytes
const toBytes = (passphrase) => {
const seed = passphrase.replace(/ /g, "");
const bytes = Desubits.toBytes(passphrase);
return bytes;
};
// Passphrase -> Bool
const checksum = (passphrase) =>
bytesChecksum(toBytes(passphrase));
// Passphrase -> Bytes
const toMasterKey = (passphrase) => {
let hash = Hash.keccak256;
let bytes = toBytes(passphrase);
for (let i = 0, l = Math.pow(2,12); i < l; ++i)
bytes = hash(bytes);
return bytes;
}
module.exports = {
create,
checksum,
toMasterKey
}
+72
View File
@@ -0,0 +1,72 @@
// The RLP format
// Serialization and deserialization for the BytesTree type, under the following grammar:
// | First byte | Meaning |
// | ---------- | -------------------------------------------------------------------------- |
// | 0 to 127 | HEX(leaf) |
// | 128 to 183 | HEX(length_of_leaf + 128) + HEX(leaf) |
// | 184 to 191 | HEX(length_of_length_of_leaf + 128 + 55) + HEX(length_of_leaf) + HEX(leaf) |
// | 192 to 247 | HEX(length_of_node + 192) + HEX(node) |
// | 248 to 255 | HEX(length_of_length_of_node + 128 + 55) + HEX(length_of_node) + HEX(node) |
const encode = tree => {
const padEven = str =>
str.length % 2 === 0 ? str : "0" + str;
const uint = num =>
padEven(num.toString(16))
const length = (len, add) =>
len < 56 ? uint(add + len) : uint(add + uint(len).length / 2 + 55) + uint(len);
const dataTree = tree => {
if (typeof tree === "string") {
const hex = tree.slice(2);
const pre = hex.length != 2 || hex >= "80" ? length(hex.length / 2, 128) : "";
return pre + hex;
} else {
const hex = tree.map(dataTree).join("");
const pre = length(hex.length / 2, 192)
return pre + hex;
}
}
return "0x" + dataTree(tree);
};
const decode = hex => {
let i = 2;
const parseTree = () => {
if (i >= hex.length) throw "";
const head = hex.slice(i,i+2);
return head < "80" ? (i+=2, "0x" + head)
: head < "c0" ? parseHex()
: parseList();
}
const parseLength = () => {
const len = parseInt(hex.slice(i,i+=2), 16) % 64;
return len < 56 ? len : parseInt(hex.slice(i, i += (len - 55) * 2), 16);
}
const parseHex = () => {
const len = parseLength();
return "0x" + hex.slice(i, i += len * 2);
}
const parseList = () => {
const lim = parseLength() * 2 + i;
let list = [];
while (i < lim)
list.push(parseTree());
return list;
}
try {
return parseTree();
} catch (e) {
return [];
}
};
module.exports = {encode, decode};
+30
View File
@@ -0,0 +1,30 @@
const request = require("xhr-request-promise");
const genPayload = (() => {
let nextId = 0;
return (method, params) => ({
jsonrpc: "2.0",
id: ++nextId,
method: method,
params: params
});
})();
const send = url => (method, params) => {
return request(url, {
method: "POST",
contentType: "application/json-rpc",
body: JSON.stringify(genPayload(method,params))
}).then(answer => {
var resp = JSON.parse(answer); // todo: use njsp?
if (resp.error) {
throw new Error(resp.error.message);
} else {
return resp.result;
}
}).catch(e => {
return {error: e.toString()};
});
};
module.exports = send;
+85
View File
@@ -0,0 +1,85 @@
const Account = require("./account");
const Nat = require("./nat");
const Bytes = require("./bytes");
const RLP = require("./rlp");
const keccak256 = require("./hash").keccak256;
// EthereumRPC, IncompleteTransaction -> Promise Transaction
const addDefaults = (rpc, tx) => {
var baseDefaults = [
tx.chainId || rpc("net_version", []),
tx.gasPrice || rpc("eth_gasPrice", []),
tx.nonce || rpc("eth_getTransactionCount", [tx.from,"latest"]),
tx.value || "0x0",
tx.data || "0x"
];
const noAddress = address => !address || address === "" || address === "0x";
return Promise.all(baseDefaults).then(([chainIdNum, gasPrice, nonce, value, data]) => {
var chainId = Nat.fromNumber(chainIdNum);
var gasEstimator = tx.gas
? Promise.resolve(null)
: rpc("eth_estimateGas", [{
from: noAddress(tx.from) ? null : tx.from,
to: noAddress(tx.to) ? null : tx.to,
value: tx.value,
nonce: tx.nonce,
data: tx.data
}]);
return gasEstimator.then(gasEstimate => {
if (gasEstimate.error) {
throw gasEstimate.error;
}
return {
chainId: chainId,
from: noAddress(tx.from) ? "0x" : tx.from.toLowerCase(),
to: noAddress(tx.to) ? "0x" : tx.to.toLowerCase(),
gasPrice: gasPrice,
gas: tx.gas ? tx.gas : Nat.div(Nat.mul(gasEstimate, "0x6"), "0x5"),
nonce: nonce,
value: value,
data: data ? data.toLowerCase() : null
}
});
});
};
// Transaction -> Bytes
const signingData = tx => {
return RLP.encode([
Bytes.fromNat(tx.nonce),
Bytes.fromNat(tx.gasPrice),
Bytes.fromNat(tx.gas),
tx.to ? tx.to.toLowerCase() : "0x",
Bytes.fromNat(tx.value),
tx.data,
Bytes.fromNat(tx.chainId || "0x1"),
"0x",
"0x"
]);
};
// Transaction, Account -> Bytes
const sign = (tx, account) => {
const data = signingData(tx);
const signature = Account.makeSigner(Nat.toNumber(tx.chainId || "0x1") * 2 + 35)(keccak256(data), account.privateKey);
const rawTransaction = RLP.decode(data).slice(0,6).concat(Account.decodeSignature(signature));
return RLP.encode(rawTransaction);
};
// Bytes -> Address
const recover = (rawTransaction) => {
const values = RLP.decode(rawTransaction);
const signature = Account.encodeSignature(values.slice(6,9));
const recovery = Bytes.toNumber(values[6]);
const extraData = recovery < 35 ? [] : [Bytes.fromNumber((recovery - 35) >> 1), "0x", "0x"]
const data = values.slice(0,6).concat(extraData);
const dataHex = RLP.encode(data);
return Account.recover(keccak256(dataHex), signature);
};
module.exports = {
addDefaults,
signingData,
sign,
recover
};
@@ -0,0 +1,18 @@
const rnd = require("./randomData.js");
const rlp = require("./../../src/rlp.js");
const ref = {rlp: require("rlp")};
const cps = f => {
for (var t = Date.now(), i = 0; Date.now() - t < 1000; ++i)
f();
return i;
};
let dataTrees = [];
for (let i = 0; i < 64; ++i)
dataTrees.push(rnd.dataTree());
console.log("Benchmarking Eth-Lib's RLP implementation vs the one on NPM.");
const liteCps = cps(() => dataTrees.map((dt) => rlp.decode(rlp.encode(dt))));
const npmCps = cps(() => dataTrees.map((dt) => ref.rlp.decode(ref.rlp.encode(dt))));
console.log("- Eth-Lib: " + liteCps + " calls per second.");
console.log("- Reference: " + npmCps + " calls per second.");
console.log("- Eth-Lib is: " + (liteCps / npmCps).toFixed(2) + " " + (liteCps > npmCps ? "faster" : "slower") + " than reference.");
@@ -0,0 +1,24 @@
// () ~> HexString
const hexString = () => {
let str = "";
while (Math.random() < 0.95)
str = str + ("00" + (Math.random() * 256 | 0).toString(16)).slice(-2);
return "0x" + str;
}
// () ~> DataTree
const dataTree = () => {
let list = [];
while (Math.random() < 0.8) {
if (Math.random() < 0.8)
list.push(hexString());
else
list.push(dataTree());
}
return list;
}
module.exports = {
hexString,
dataTree
}
+141
View File
@@ -0,0 +1,141 @@
const assert = require("assert");
const rnd = require("./lib/randomData.js");
const rlp = require("./../src/rlp.js");
const ref = {rlp: require("rlp")};
// const F = require("./../src/types.js");
const Nat = require("./../src/Nat.js");
const Account = require("./../src/account.js");
const keccak256 = require("./../src/hash.js").keccak256;
const ethjs = {
signer: require("ethjs-signer"),
account: require("ethjs-account")};
describe("RLP", () => {
it("Must operate identically to reference implementation", () => {
// Builds a test set of 256 random dataTrees + 2 default
let dataTrees = [];
dataTrees.push(["0x00112233", "0x00", "0x44", "0x55", "0xf0", "0xff", ["0x66"], ["0x77", "0x88"], "0x", "0x", "0x99aabb"]);
dataTrees.push("0x00112233445566778899aabbccddeeff");
for (let i = 0; i < 256; ++i)
dataTrees.push(rnd.dataTree());
// Tests if they encode and decode identically to the reference implementation
dataTrees.forEach(dataTree => {
const refEnc = "0x" + ref.rlp.encode(dataTree).toString("hex");
const impEnc = rlp.encode(dataTree);
const impDec = rlp.decode(impEnc);
try {
assert(refEnc === impEnc);
assert(JSON.stringify(impDec) === JSON.stringify(dataTree));
} catch (e) {
console.log(JSON.stringify(dataTree, null, 2));
}
});
});
it("Obeys law: `decode(encode(tree)) == tree`", () => {
F.forall([F.BytesTree], tree => JSON.stringify(rlp.decode(rlp.encode(tree))) === JSON.stringify(tree));
});
});
describe("account", function () {
it("must recover the same address that signed", () => {
F.forall([F.Account, F.Bytes], (acc, msg) =>
Account.recover(keccak256(msg), Account.sign(keccak256(msg), acc.privateKey, 0)) === acc.address,
32);
});
it("must match the generated address with EthJS-account", () => {
F.forall([F.NBytes(32)], pvt => {
const ethfpAcc = Account.fromPrivate(pvt);
const ethjsAcc = ethjs.account.privateToAccount(pvt);
return ethfpAcc.address === ethjsAcc.address;
}, 128);
});
it("must match expected values for complex pre-determined tests", () => {
const accounts = [
{
"address": '0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F',
"publicKey": '0x4bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a',
"privateKey": '0x4646464646464646464646464646464646464646464646464646464646464646',
"transactions": [
{
"object": {
"nonce": Nat.fromString("9"),
"gasPrice": Nat.fromString("20000000000"),
"gas": Nat.fromString("21000"),
"to": '0x3535353535353535353535353535353535353535',
"value": Nat.fromString("1000000000000000000"),
"data": "0x"
},
"signature": "0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"
},
]
},
{
"address": '0xFCAd0B19bB29D4674531d6f115237E16AfCE377c',
"publicKey": '0x4646ae5047316b4230d0086c8acec687f00b1cd9d1dc634f6cb358ac0a9a8ffffe77b4dd0a4bfb95851f3b7355c781dd60f8418fc8a65d14907aff47c903a559',
"privateKey": '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
"transactions": [
{
"object": {
"nonce": Nat.fromString("0"),
"gasPrice": Nat.fromString("230000000000"),
"gas": Nat.fromString("21000"),
"to": '0xFCAd0B19bB29D4674531d6f115237E16AfCE377c',
"value": Nat.fromString("1000000000000000000"),
"data": "0x0123abcd"
},
"signature": "0xf8708085358d117c0082520894fcad0b19bb29d4674531d6f115237e16afce377c880de0b6b3a7640000840123abcd25a032dbcf46a64b9892df24d8b961d2a52fd66b1dabd3a0d96940fd6795c01d8711a01b86df9475de7451554557d87b69456e3fa95aa5375584bf63d1ffd647a225d9",
"oldSignature": "0xf8708085358d117c0082520894fcad0b19bb29d4674531d6f115237e16afce377c880de0b6b3a7640000840123abcd1ba04e289b471dd4469d5080ce3726b8359d5b0c649e012bbbdde53f9b6580ad21a2a0333663ea96846c112f3878705c8c24a763d1fbf8f97c174d26e350c7ef0d7263"
},
]
}
];
// For each account
accounts.forEach(function (correctAccount, i) {
// Generates an address from this account's private key
const testAccount = Account.fromPrivate(correctAccount.privateKey);
// Generated address must match
assert(testAccount.address === correctAccount.address);
// For each transaction on this account
correctAccount.transactions.forEach(function (transaction) {
// Signs it, uing post-EIP 155 scheme
const signature = Account.signTransaction(
transaction.object,
correctAccount.privateKey);
// Checks if the signature is as expected
assert(transaction.signature === signature);
//// Checks if we can recover the right address
const recoveredAddress = Account.recoverTransaction(signature);
assert(recoveredAddress === testAccount.address);
//// If the test also provides a pre-EIP 155 signature
if (transaction.oldSignature) {
let txObj = {};
for (let key in transaction.object)
txObj[key] = transaction.object[key].replace(/^0x$/,"0x0");
//Signs it, using pre-EIP 155 scheme (using the ethjs-signer lib)
const oldSignature = ethjs.signer.sign(txObj, correctAccount.privateKey);
//// Checks if the signature is as expected
assert(transaction.oldSignature === oldSignature);
//// Checks if we can recover the right address from old sigs (using web3)
const recoveredAddress = Account.recoverTransaction(oldSignature);
assert(recoveredAddress === testAccount.address);
}
});
});
});
});
+74
View File
@@ -0,0 +1,74 @@
{
"_from": "web3-utils@1.3.0",
"_id": "web3-utils@1.3.0",
"_inBundle": false,
"_integrity": "sha512-2mS5axFCbkhicmoDRuJeuo0TVGQDgC2sPi/5dblfVC+PMtX0efrb8Xlttv/eGkq7X4E83Pds34FH98TP2WOUZA==",
"_location": "/web3-utils",
"_phantomChildren": {
"bn.js": "4.11.9",
"elliptic": "6.5.3",
"xhr-request-promise": "0.1.3"
},
"_requested": {
"type": "version",
"registry": true,
"raw": "web3-utils@1.3.0",
"name": "web3-utils",
"escapedName": "web3-utils",
"rawSpec": "1.3.0",
"saveSpec": null,
"fetchSpec": "1.3.0"
},
"_requiredBy": [
"/web3",
"/web3-core",
"/web3-core-helpers",
"/web3-core-method",
"/web3-eth",
"/web3-eth-abi",
"/web3-eth-accounts",
"/web3-eth-contract",
"/web3-eth-ens",
"/web3-eth-iban",
"/web3-eth-personal",
"/web3-net"
],
"_resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.3.0.tgz",
"_shasum": "5bac16e5e0ec9fe7bdcfadb621655e8aa3cf14e1",
"_spec": "web3-utils@1.3.0",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\BackendServers\\node_modules\\web3",
"bundleDependencies": false,
"dependencies": {
"bn.js": "^4.11.9",
"eth-lib": "0.2.8",
"ethereum-bloom-filters": "^1.0.6",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randombytes": "^2.1.0",
"underscore": "1.9.1",
"utf8": "3.0.0"
},
"deprecated": false,
"description": "Collection of utility functions used in web3.js.",
"devDependencies": {
"dtslint": "^3.4.1",
"typescript": "^3.9.5"
},
"engines": {
"node": ">=8.0.0"
},
"gitHead": "a88944fff1f62b2b6f00e3de52446d24d9e92cc6",
"license": "LGPL-3.0",
"main": "lib/index.js",
"name": "web3-utils",
"repository": {
"type": "git",
"url": "https://github.com/ethereum/web3.js/tree/1.x/packages/web3-utils"
},
"scripts": {
"dtslint": "dtslint --localTs ../../node_modules/typescript/lib types",
"tsc": "tsc -b tsconfig.json"
},
"types": "types/index.d.ts",
"version": "1.3.0"
}
+442
View File
@@ -0,0 +1,442 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file utils.js
* @author Marek Kotewicz <marek@parity.io>
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var ethjsUnit = require('ethjs-unit');
var utils = require('./utils.js');
var soliditySha3 = require('./soliditySha3.js');
var randombytes = require('randombytes');
var BN = require('bn.js');
/**
* Fires an error in an event emitter and callback and returns the eventemitter
*
* @method _fireError
* @param {Object} error a string, a error, or an object with {message, data}
* @param {Object} emitter
* @param {Function} reject
* @param {Function} callback
* @param {any} optionalData
* @return {Object} the emitter
*/
var _fireError = function (error, emitter, reject, callback, optionalData) {
/*jshint maxcomplexity: 10 */
// add data if given
if(_.isObject(error) && !(error instanceof Error) && error.data) {
if(_.isObject(error.data) || _.isArray(error.data)) {
error.data = JSON.stringify(error.data, null, 2);
}
error = error.message +"\n"+ error.data;
}
if(_.isString(error)) {
error = new Error(error);
}
if (_.isFunction(callback)) {
callback(error, optionalData);
}
if (_.isFunction(reject)) {
// suppress uncatched error if an error listener is present
// OR suppress uncatched error if an callback listener is present
if (
emitter &&
(_.isFunction(emitter.listeners) &&
emitter.listeners('error').length) || _.isFunction(callback)
) {
emitter.catch(function(){});
}
// reject later, to be able to return emitter
setTimeout(function () {
reject(error);
}, 1);
}
if(emitter && _.isFunction(emitter.emit)) {
// emit later, to be able to return emitter
setTimeout(function () {
emitter.emit('error', error, optionalData);
emitter.removeAllListeners();
}, 1);
}
return emitter;
};
/**
* Should be used to create full function/event name from json abi
*
* @method _jsonInterfaceMethodToString
* @param {Object} json
* @return {String} full function/event name
*/
var _jsonInterfaceMethodToString = function (json) {
if (_.isObject(json) && json.name && json.name.indexOf('(') !== -1) {
return json.name;
}
return json.name + '(' + _flattenTypes(false, json.inputs).join(',') + ')';
};
/**
* Should be used to flatten json abi inputs/outputs into an array of type-representing-strings
*
* @method _flattenTypes
* @param {bool} includeTuple
* @param {Object} puts
* @return {Array} parameters as strings
*/
var _flattenTypes = function(includeTuple, puts)
{
// console.log("entered _flattenTypes. inputs/outputs: " + puts)
var types = [];
puts.forEach(function(param) {
if (typeof param.components === 'object') {
if (param.type.substring(0, 5) !== 'tuple') {
throw new Error('components found but type is not tuple; report on GitHub');
}
var suffix = '';
var arrayBracket = param.type.indexOf('[');
if (arrayBracket >= 0) { suffix = param.type.substring(arrayBracket); }
var result = _flattenTypes(includeTuple, param.components);
// console.log("result should have things: " + result)
if(_.isArray(result) && includeTuple) {
// console.log("include tuple word, and its an array. joining...: " + result.types)
types.push('tuple(' + result.join(',') + ')' + suffix);
}
else if(!includeTuple) {
// console.log("don't include tuple, but its an array. joining...: " + result)
types.push('(' + result.join(',') + ')' + suffix);
}
else {
// console.log("its a single type within a tuple: " + result.types)
types.push('(' + result + ')');
}
} else {
// console.log("its a type and not directly in a tuple: " + param.type)
types.push(param.type);
}
});
return types;
};
/**
* Returns a random hex string by the given bytes size
*
* @param {Number} size
* @returns {string}
*/
var randomHex = function(size) {
return '0x' + randombytes(size).toString('hex');
};
/**
* Should be called to get ascii from it's hex representation
*
* @method hexToAscii
* @param {String} hex
* @returns {String} ascii string representation of hex value
*/
var hexToAscii = function(hex) {
if (!utils.isHexStrict(hex))
throw new Error('The parameter must be a valid HEX string.');
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x') {
i = 2;
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
str += String.fromCharCode(code);
}
return str;
};
/**
* Should be called to get hex representation (prefixed by 0x) of ascii string
*
* @method asciiToHex
* @param {String} str
* @returns {String} hex representation of input string
*/
var asciiToHex = function(str) {
if(!str)
return "0x00";
var hex = "";
for(var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
var n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return "0x" + hex;
};
/**
* Returns value of unit in Wei
*
* @method getUnitValue
* @param {String} unit the unit to convert to, default ether
* @returns {BN} value of the unit (in Wei)
* @throws error if the unit is not correct:w
*/
var getUnitValue = function (unit) {
unit = unit ? unit.toLowerCase() : 'ether';
if (!ethjsUnit.unitMap[unit]) {
throw new Error('This unit "'+ unit +'" doesn\'t exist, please use the one of the following units' + JSON.stringify(ethjsUnit.unitMap, null, 2));
}
return unit;
};
/**
* Takes a number of wei and converts it to any other ether unit.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method fromWei
* @param {Number|String} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert to, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var fromWei = function(number, unit) {
unit = getUnitValue(unit);
if(!utils.isBN(number) && !_.isString(number)) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.fromWei(number, unit) : ethjsUnit.fromWei(number, unit).toString(10);
};
/**
* Takes a number of a unit and converts it to wei.
*
* Possible units are:
* SI Short SI Full Effigy Other
* - kwei femtoether babbage
* - mwei picoether lovelace
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether -- grand
* - mether
* - gether
* - tether
*
* @method toWei
* @param {Number|String|BN} number can be a number, number string or a HEX of a decimal
* @param {String} unit the unit to convert from, default ether
* @return {String|Object} When given a BN object it returns one as well, otherwise a number
*/
var toWei = function(number, unit) {
unit = getUnitValue(unit);
if(!utils.isBN(number) && !_.isString(number)) {
throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.');
}
return utils.isBN(number) ? ethjsUnit.toWei(number, unit) : ethjsUnit.toWei(number, unit).toString(10);
};
/**
* Converts to a checksum address
*
* @method toChecksumAddress
* @param {String} address the given HEX address
* @return {String}
*/
var toChecksumAddress = function (address) {
if (typeof address === 'undefined') return '';
if(!/^(0x)?[0-9a-f]{40}$/i.test(address))
throw new Error('Given address "'+ address +'" is not a valid Ethereum address.');
address = address.toLowerCase().replace(/^0x/i,'');
var addressHash = utils.sha3(address).replace(/^0x/i,'');
var checksumAddress = '0x';
for (var i = 0; i < address.length; i++ ) {
// If ith character is 8 to f then make it uppercase
if (parseInt(addressHash[i], 16) > 7) {
checksumAddress += address[i].toUpperCase();
} else {
checksumAddress += address[i];
}
}
return checksumAddress;
};
/**
* Returns -1 if a<b, 1 if a>b; 0 if a == b.
* For more details on this type of function, see
* developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
*
* @method compareBlockNumbers
*
* @param {String|Number|BN} a
*
* @param {String|Number|BN} b
*
* @returns {Number} -1, 0, or 1
*/
var compareBlockNumbers = function(a, b) {
if (a == b) {
return 0;
} else if (("genesis" == a || "earliest" == a || 0 == a) && ("genesis" == b || "earliest" == b || 0 == b)) {
return 0;
} else if ("genesis" == a || "earliest" == a) {
// b !== a, thus a < b
return -1;
} else if ("genesis" == b || "earliest" == b) {
// b !== a, thus a > b
return 1;
} else if (a == "latest") {
if (b == "pending") {
return -1;
} else {
// b !== ("pending" OR "latest"), thus a > b
return 1;
}
} else if (b === "latest") {
if (a == "pending") {
return 1;
} else {
// b !== ("pending" OR "latest"), thus a > b
return -1
}
} else if (a == "pending") {
// b (== OR <) "latest", thus a > b
return 1;
} else if (b == "pending") {
return -1;
} else {
let bnA = new BN(a);
let bnB = new BN(b);
if(bnA.lt(bnB)) {
return -1;
} else if(bnA.eq(bnB)) {
return 0;
} else {
return 1;
}
}
};
module.exports = {
_fireError: _fireError,
_jsonInterfaceMethodToString: _jsonInterfaceMethodToString,
_flattenTypes: _flattenTypes,
// extractDisplayName: extractDisplayName,
// extractTypeName: extractTypeName,
randomHex: randomHex,
_: _,
BN: utils.BN,
isBN: utils.isBN,
isBigNumber: utils.isBigNumber,
isHex: utils.isHex,
isHexStrict: utils.isHexStrict,
sha3: utils.sha3,
sha3Raw: utils.sha3Raw,
keccak256: utils.sha3,
soliditySha3: soliditySha3.soliditySha3,
soliditySha3Raw: soliditySha3.soliditySha3Raw,
encodePacked: soliditySha3.encodePacked,
isAddress: utils.isAddress,
checkAddressChecksum: utils.checkAddressChecksum,
toChecksumAddress: toChecksumAddress,
toHex: utils.toHex,
toBN: utils.toBN,
bytesToHex: utils.bytesToHex,
hexToBytes: utils.hexToBytes,
hexToNumberString: utils.hexToNumberString,
hexToNumber: utils.hexToNumber,
toDecimal: utils.hexToNumber, // alias
numberToHex: utils.numberToHex,
fromDecimal: utils.numberToHex, // alias
hexToUtf8: utils.hexToUtf8,
hexToString: utils.hexToUtf8,
toUtf8: utils.hexToUtf8,
utf8ToHex: utils.utf8ToHex,
stringToHex: utils.utf8ToHex,
fromUtf8: utils.utf8ToHex,
hexToAscii: hexToAscii,
toAscii: hexToAscii,
asciiToHex: asciiToHex,
fromAscii: asciiToHex,
unitMap: ethjsUnit.unitMap,
toWei: toWei,
fromWei: fromWei,
padLeft: utils.leftPad,
leftPad: utils.leftPad,
padRight: utils.rightPad,
rightPad: utils.rightPad,
toTwosComplement: utils.toTwosComplement,
isBloom: utils.isBloom,
isUserEthereumAddressInBloom: utils.isUserEthereumAddressInBloom,
isContractAddressInBloom: utils.isContractAddressInBloom,
isTopic: utils.isTopic,
isTopicInBloom: utils.isTopicInBloom,
isInBloom: utils.isInBloom,
compareBlockNumbers: compareBlockNumbers
};
+275
View File
@@ -0,0 +1,275 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file soliditySha3.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var BN = require('bn.js');
var utils = require('./utils.js');
var _elementaryName = function (name) {
/*jshint maxcomplexity:false */
if (name.startsWith('int[')) {
return 'int256' + name.slice(3);
} else if (name === 'int') {
return 'int256';
} else if (name.startsWith('uint[')) {
return 'uint256' + name.slice(4);
} else if (name === 'uint') {
return 'uint256';
} else if (name.startsWith('fixed[')) {
return 'fixed128x128' + name.slice(5);
} else if (name === 'fixed') {
return 'fixed128x128';
} else if (name.startsWith('ufixed[')) {
return 'ufixed128x128' + name.slice(6);
} else if (name === 'ufixed') {
return 'ufixed128x128';
}
return name;
};
// Parse N from type<N>
var _parseTypeN = function (type) {
var typesize = /^\D+(\d+).*$/.exec(type);
return typesize ? parseInt(typesize[1], 10) : null;
};
// Parse N from type[<N>]
var _parseTypeNArray = function (type) {
var arraySize = /^\D+\d*\[(\d+)\]$/.exec(type);
return arraySize ? parseInt(arraySize[1], 10) : null;
};
var _parseNumber = function (arg) {
var type = typeof arg;
if (type === 'string') {
if (utils.isHexStrict(arg)) {
return new BN(arg.replace(/0x/i,''), 16);
} else {
return new BN(arg, 10);
}
} else if (type === 'number') {
return new BN(arg);
} else if (utils.isBigNumber(arg)) {
return new BN(arg.toString(10));
} else if (utils.isBN(arg)) {
return arg;
} else {
throw new Error(arg +' is not a number');
}
};
var _solidityPack = function (type, value, arraySize) {
/*jshint maxcomplexity:false */
var size, num;
type = _elementaryName(type);
if (type === 'bytes') {
if (value.replace(/^0x/i,'').length % 2 !== 0) {
throw new Error('Invalid bytes characters '+ value.length);
}
return value;
} else if (type === 'string') {
return utils.utf8ToHex(value);
} else if (type === 'bool') {
return value ? '01' : '00';
} else if (type.startsWith('address')) {
if(arraySize) {
size = 64;
} else {
size = 40;
}
if(!utils.isAddress(value)) {
throw new Error(value +' is not a valid address, or the checksum is invalid.');
}
return utils.leftPad(value.toLowerCase(), size);
}
size = _parseTypeN(type);
if (type.startsWith('bytes')) {
if(!size) {
throw new Error('bytes[] not yet supported in solidity');
}
// must be 32 byte slices when in an array
if(arraySize) {
size = 32;
}
if (size < 1 || size > 32 || size < value.replace(/^0x/i,'').length / 2 ) {
throw new Error('Invalid bytes' + size +' for '+ value);
}
return utils.rightPad(value, size * 2);
} else if (type.startsWith('uint')) {
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid uint'+size+' size');
}
num = _parseNumber(value);
if (num.bitLength() > size) {
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength());
}
if(num.lt(new BN(0))) {
throw new Error('Supplied uint '+ num.toString() +' is negative');
}
return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
} else if (type.startsWith('int')) {
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid int'+size+' size');
}
num = _parseNumber(value);
if (num.bitLength() > size) {
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength());
}
if(num.lt(new BN(0))) {
return num.toTwos(size).toString('hex');
} else {
return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
}
} else {
// FIXME: support all other types
throw new Error('Unsupported or invalid type: ' + type);
}
};
var _processSolidityEncodePackedArgs = function (arg) {
/*jshint maxcomplexity:false */
if(_.isArray(arg)) {
throw new Error('Autodetection of array types is not supported.');
}
var type, value = '';
var hexArg, arraySize;
// if type is given
if (_.isObject(arg) && (arg.hasOwnProperty('v') || arg.hasOwnProperty('t') || arg.hasOwnProperty('value') || arg.hasOwnProperty('type'))) {
type = arg.hasOwnProperty('t') ? arg.t : arg.type;
value = arg.hasOwnProperty('v') ? arg.v : arg.value;
// otherwise try to guess the type
} else {
type = utils.toHex(arg, true);
value = utils.toHex(arg);
if (!type.startsWith('int') && !type.startsWith('uint')) {
type = 'bytes';
}
}
if ((type.startsWith('int') || type.startsWith('uint')) && typeof value === 'string' && !/^(-)?0x/i.test(value)) {
value = new BN(value);
}
// get the array size
if(_.isArray(value)) {
arraySize = _parseTypeNArray(type);
if(arraySize && value.length !== arraySize) {
throw new Error(type +' is not matching the given array '+ JSON.stringify(value));
} else {
arraySize = value.length;
}
}
if (_.isArray(value)) {
hexArg = value.map(function (val) {
return _solidityPack(type, val, arraySize).toString('hex').replace('0x','');
});
return hexArg.join('');
} else {
hexArg = _solidityPack(type, value, arraySize);
return hexArg.toString('hex').replace('0x','');
}
};
/**
* Hashes solidity values to a sha3 hash using keccak 256
*
* @method soliditySha3
* @return {Object} the sha3
*/
var soliditySha3 = function () {
/*jshint maxcomplexity:false */
var args = Array.prototype.slice.call(arguments);
var hexArgs = _.map(args, _processSolidityEncodePackedArgs);
// console.log(args, hexArgs);
// console.log('0x'+ hexArgs.join(''));
return utils.sha3('0x'+ hexArgs.join(''));
};
/**
* Hashes solidity values to a sha3 hash using keccak 256 but does return the hash of value `null` instead of `null`
*
* @method soliditySha3Raw
* @return {Object} the sha3
*/
var soliditySha3Raw = function () {
return utils.sha3Raw('0x'+ _.map(Array.prototype.slice.call(arguments), _processSolidityEncodePackedArgs).join(''));
};
/**
* Encode packed args to hex
*
* @method encodePacked
* @return {String} the hex encoded arguments
*/
var encodePacked = function () {
/*jshint maxcomplexity:false */
var args = Array.prototype.slice.call(arguments);
var hexArgs = _.map(args, _processSolidityEncodePackedArgs);
return '0x'+ hexArgs.join('').toLowerCase();
};
module.exports = {
soliditySha3: soliditySha3,
soliditySha3Raw: soliditySha3Raw,
encodePacked: encodePacked
};
+542
View File
@@ -0,0 +1,542 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file utils.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
var _ = require('underscore');
var BN = require('bn.js');
var numberToBN = require('number-to-bn');
var utf8 = require('utf8');
var Hash = require("eth-lib/lib/hash");
var ethereumBloomFilters = require('ethereum-bloom-filters');
/**
* Returns true if object is BN, otherwise false
*
* @method isBN
* @param {Object} object
* @return {Boolean}
*/
var isBN = function (object) {
return BN.isBN(object);
};
/**
* Returns true if object is BigNumber, otherwise false
*
* @method isBigNumber
* @param {Object} object
* @return {Boolean}
*/
var isBigNumber = function (object) {
return object && object.constructor && object.constructor.name === 'BigNumber';
};
/**
* Takes an input and transforms it into an BN
*
* @method toBN
* @param {Number|String|BN} number, string, HEX string or BN
* @return {BN} BN
*/
var toBN = function(number){
try {
return numberToBN.apply(null, arguments);
} catch(e) {
throw new Error(e + ' Given value: "'+ number +'"');
}
};
/**
* Takes and input transforms it into BN and if it is negative value, into two's complement
*
* @method toTwosComplement
* @param {Number|String|BN} number
* @return {String}
*/
var toTwosComplement = function (number) {
return '0x'+ toBN(number).toTwos(256).toString(16, 64);
};
/**
* Checks if the given string is an address
*
* @method isAddress
* @param {String} address the given HEX address
* @return {Boolean}
*/
var isAddress = function (address) {
// check if it has the basic requirements of an address
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
return false;
// If it's ALL lowercase or ALL upppercase
} else if (/^(0x|0X)?[0-9a-f]{40}$/.test(address) || /^(0x|0X)?[0-9A-F]{40}$/.test(address)) {
return true;
// Otherwise check each case
} else {
return checkAddressChecksum(address);
}
};
/**
* Checks if the given string is a checksummed address
*
* @method checkAddressChecksum
* @param {String} address the given HEX address
* @return {Boolean}
*/
var checkAddressChecksum = function (address) {
// Check each case
address = address.replace(/^0x/i,'');
var addressHash = sha3(address.toLowerCase()).replace(/^0x/i,'');
for (var i = 0; i < 40; i++ ) {
// the nth letter should be uppercase if the nth digit of casemap is 1
if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) {
return false;
}
}
return true;
};
/**
* Should be called to pad string to expected length
*
* @method leftPad
* @param {String} string to be padded
* @param {Number} chars that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var leftPad = function (string, chars, sign) {
var hasPrefix = /^0x/i.test(string) || typeof string === 'number';
string = string.toString(16).replace(/^0x/i,'');
var padding = (chars - string.length + 1 >= 0) ? chars - string.length + 1 : 0;
return (hasPrefix ? '0x' : '') + new Array(padding).join(sign ? sign : "0") + string;
};
/**
* Should be called to pad string to expected length
*
* @method rightPad
* @param {String} string to be padded
* @param {Number} chars that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var rightPad = function (string, chars, sign) {
var hasPrefix = /^0x/i.test(string) || typeof string === 'number';
string = string.toString(16).replace(/^0x/i,'');
var padding = (chars - string.length + 1 >= 0) ? chars - string.length + 1 : 0;
return (hasPrefix ? '0x' : '') + string + (new Array(padding).join(sign ? sign : "0"));
};
/**
* Should be called to get hex representation (prefixed by 0x) of utf8 string
*
* @method utf8ToHex
* @param {String} str
* @returns {String} hex representation of input string
*/
var utf8ToHex = function(str) {
str = utf8.encode(str);
var hex = "";
// remove \u0000 padding from either side
str = str.replace(/^(?:\u0000)*/,'');
str = str.split("").reverse().join("");
str = str.replace(/^(?:\u0000)*/,'');
str = str.split("").reverse().join("");
for(var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
// if (code !== 0) {
var n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
// }
}
return "0x" + hex;
};
/**
* Should be called to get utf8 from it's hex representation
*
* @method hexToUtf8
* @param {String} hex
* @returns {String} ascii string representation of hex value
*/
var hexToUtf8 = function(hex) {
if (!isHexStrict(hex))
throw new Error('The parameter "'+ hex +'" must be a valid HEX string.');
var str = "";
var code = 0;
hex = hex.replace(/^0x/i,'');
// remove 00 padding from either side
hex = hex.replace(/^(?:00)*/,'');
hex = hex.split("").reverse().join("");
hex = hex.replace(/^(?:00)*/,'');
hex = hex.split("").reverse().join("");
var l = hex.length;
for (var i=0; i < l; i+=2) {
code = parseInt(hex.substr(i, 2), 16);
// if (code !== 0) {
str += String.fromCharCode(code);
// }
}
return utf8.decode(str);
};
/**
* Converts value to it's number representation
*
* @method hexToNumber
* @param {String|Number|BN} value
* @return {String}
*/
var hexToNumber = function (value) {
if (!value) {
return value;
}
if (_.isString(value) && !isHexStrict(value)) {
throw new Error('Given value "'+value+'" is not a valid hex string.');
}
return toBN(value).toNumber();
};
/**
* Converts value to it's decimal representation in string
*
* @method hexToNumberString
* @param {String|Number|BN} value
* @return {String}
*/
var hexToNumberString = function (value) {
if (!value) return value;
if (_.isString(value) && !isHexStrict(value)) {
throw new Error('Given value "'+value+'" is not a valid hex string.');
}
return toBN(value).toString(10);
};
/**
* Converts value to it's hex representation
*
* @method numberToHex
* @param {String|Number|BN} value
* @return {String}
*/
var numberToHex = function (value) {
if (_.isNull(value) || _.isUndefined(value)) {
return value;
}
if (!isFinite(value) && !isHexStrict(value)) {
throw new Error('Given input "'+value+'" is not a number.');
}
var number = toBN(value);
var result = number.toString(16);
return number.lt(new BN(0)) ? '-0x' + result.substr(1) : '0x' + result;
};
/**
* Convert a byte array to a hex string
*
* Note: Implementation from crypto-js
*
* @method bytesToHex
* @param {Array} bytes
* @return {String} the hex string
*/
var bytesToHex = function(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
/* jshint ignore:start */
hex.push((bytes[i] >>> 4).toString(16));
hex.push((bytes[i] & 0xF).toString(16));
/* jshint ignore:end */
}
return '0x'+ hex.join("");
};
/**
* Convert a hex string to a byte array
*
* Note: Implementation from crypto-js
*
* @method hexToBytes
* @param {string} hex
* @return {Array} the byte array
*/
var hexToBytes = function(hex) {
hex = hex.toString(16);
if (!isHexStrict(hex)) {
throw new Error('Given value "'+ hex +'" is not a valid hex string.');
}
hex = hex.replace(/^0x/i,'');
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
};
/**
* Auto converts any given value into it's hex representation.
*
* And even stringifys objects before.
*
* @method toHex
* @param {String|Number|BN|Object|Buffer} value
* @param {Boolean} returnType
* @return {String}
*/
var toHex = function (value, returnType) {
/*jshint maxcomplexity: false */
if (isAddress(value)) {
return returnType ? 'address' : '0x'+ value.toLowerCase().replace(/^0x/i,'');
}
if (_.isBoolean(value)) {
return returnType ? 'bool' : value ? '0x01' : '0x00';
}
if (Buffer.isBuffer(value)) {
return '0x' + value.toString('hex');
}
if (_.isObject(value) && !isBigNumber(value) && !isBN(value)) {
return returnType ? 'string' : utf8ToHex(JSON.stringify(value));
}
// if its a negative number, pass it through numberToHex
if (_.isString(value)) {
if (value.indexOf('-0x') === 0 || value.indexOf('-0X') === 0) {
return returnType ? 'int256' : numberToHex(value);
} else if(value.indexOf('0x') === 0 || value.indexOf('0X') === 0) {
return returnType ? 'bytes' : value;
} else if (!isFinite(value)) {
return returnType ? 'string' : utf8ToHex(value);
}
}
return returnType ? (value < 0 ? 'int256' : 'uint256') : numberToHex(value);
};
/**
* Check if string is HEX, requires a 0x in front
*
* @method isHexStrict
* @param {String} hex to be checked
* @returns {Boolean}
*/
var isHexStrict = function (hex) {
return ((_.isString(hex) || _.isNumber(hex)) && /^(-)?0x[0-9a-f]*$/i.test(hex));
};
/**
* Check if string is HEX
*
* @method isHex
* @param {String} hex to be checked
* @returns {Boolean}
*/
var isHex = function (hex) {
return ((_.isString(hex) || _.isNumber(hex)) && /^(-0x|0x)?[0-9a-f]*$/i.test(hex));
};
/**
* Returns true if given string is a valid Ethereum block header bloom.
*
* @method isBloom
* @param {String} bloom encoded bloom filter
* @return {Boolean}
*/
var isBloom = function (bloom) {
return ethereumBloomFilters.isBloom(bloom);
};
/**
* Returns true if the ethereum users address is part of the given bloom
* note: false positives are possible.
*
* @method isUserEthereumAddressInBloom
* @param {String} ethereumAddress encoded bloom filter
* @param {String} bloom ethereum addresss
* @return {Boolean}
*/
var isUserEthereumAddressInBloom = function (bloom, ethereumAddress) {
return ethereumBloomFilters.isUserEthereumAddressInBloom(bloom, ethereumAddress);
};
/**
* Returns true if the contract address is part of the given bloom
* note: false positives are possible.
*
* @method isUserEthereumAddressInBloom
* @param {String} bloom encoded bloom filter
* @param {String} contractAddress contract addresss
* @return {Boolean}
*/
var isContractAddressInBloom = function (bloom, contractAddress) {
return ethereumBloomFilters.isContractAddressInBloom(bloom, contractAddress);
};
/**
* Returns true if given string is a valid log topic.
*
* @method isTopic
* @param {String} topic encoded topic
* @return {Boolean}
*/
var isTopic = function (topic) {
return ethereumBloomFilters.isTopic(topic);
};
/**
* Returns true if the topic is part of the given bloom
* note: false positives are possible.
*
* @method isTopicInBloom
* @param {String} bloom encoded bloom filter
* @param {String} topic encoded topic
* @return {Boolean}
*/
var isTopicInBloom = function (bloom, topic) {
return ethereumBloomFilters.isTopicInBloom(bloom, topic);
};
/**
* Returns true if the value is part of the given bloom
* note: false positives are possible.
*
* @method isInBloom
* @param {String} bloom encoded bloom filter
* @param {String | Uint8Array} topic encoded value
* @return {Boolean}
*/
var isInBloom = function (bloom, topic) {
return ethereumBloomFilters.isInBloom(bloom, topic);
};
/**
* Hashes values to a sha3 hash using keccak 256
*
* To hash a HEX string the hex must have 0x in front.
*
* @method sha3
* @return {String} the sha3 string
*/
var SHA3_NULL_S = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
var sha3 = function (value) {
if (isBN(value)) {
value = value.toString();
}
if (isHexStrict(value) && /^0x/i.test((value).toString())) {
value = hexToBytes(value);
}
var returnValue = Hash.keccak256(value); // jshint ignore:line
if(returnValue === SHA3_NULL_S) {
return null;
} else {
return returnValue;
}
};
// expose the under the hood keccak256
sha3._Hash = Hash;
/**
* @method sha3Raw
*
* @param value
*
* @returns {string}
*/
var sha3Raw = function(value) {
value = sha3(value);
if (value === null) {
return SHA3_NULL_S;
}
return value;
};
module.exports = {
BN: BN,
isBN: isBN,
isBigNumber: isBigNumber,
toBN: toBN,
isAddress: isAddress,
isBloom: isBloom,
isUserEthereumAddressInBloom: isUserEthereumAddressInBloom,
isContractAddressInBloom: isContractAddressInBloom,
isTopic: isTopic,
isTopicInBloom: isTopicInBloom,
isInBloom: isInBloom,
checkAddressChecksum: checkAddressChecksum,
utf8ToHex: utf8ToHex,
hexToUtf8: hexToUtf8,
hexToNumber: hexToNumber,
hexToNumberString: hexToNumberString,
numberToHex: numberToHex,
toHex: toHex,
hexToBytes: hexToBytes,
bytesToHex: bytesToHex,
isHex: isHex,
isHexStrict: isHexStrict,
leftPad: leftPad,
rightPad: rightPad,
toTwosComplement: toTwosComplement,
sha3: sha3,
sha3Raw: sha3Raw
};
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib"
},
"include": [
"./src"
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6"],
"target": "es6",
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noEmit": true,
"allowSyntheticDefaultImports": false,
"baseUrl": ".",
"paths": {
"web3-utils": ["."]
}
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"semicolon": false,
"no-import-default-of-export-equals": false,
"file-name-casing": [true, "kebab-case"],
"whitespace": false,
"no-redundant-jsdoc": false,
"max-line-length": false
}
}