Split up utils.
This commit is contained in:
parent
9625745f4c
commit
91543a0029
112
utils/address.js
Normal file
112
utils/address.js
Normal file
@ -0,0 +1,112 @@
|
||||
|
||||
var BN = require('bn.js');
|
||||
|
||||
var convert = require('./convert.js');
|
||||
var keccak256 = require('./keccak256.js');
|
||||
|
||||
function getChecksumAddress(address) {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
throw new Error('invalid address');
|
||||
}
|
||||
|
||||
address = address.toLowerCase();
|
||||
|
||||
var hashed = address.substring(2).split('');
|
||||
for (var i = 0; i < hashed.length; i++) {
|
||||
hashed[i] = hashed[i].charCodeAt(0);
|
||||
}
|
||||
hashed = convert.arrayify(keccak256(hashed));
|
||||
|
||||
address = address.substring(2).split('');
|
||||
for (var i = 0; i < 40; i += 2) {
|
||||
if ((hashed[i >> 1] >> 4) >= 8) {
|
||||
address[i] = address[i].toUpperCase();
|
||||
}
|
||||
if ((hashed[i >> 1] & 0x0f) >= 8) {
|
||||
address[i + 1] = address[i + 1].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
return '0x' + address.join('');
|
||||
}
|
||||
|
||||
// See: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
var ibanChecksum = (function() {
|
||||
|
||||
// Create lookup table
|
||||
var ibanLookup = {};
|
||||
for (var i = 0; i < 10; i++) { ibanLookup[String(i)] = String(i); }
|
||||
for (var i = 0; i < 26; i++) { ibanLookup[String.fromCharCode(65 + i)] = String(10 + i); }
|
||||
|
||||
// How many decimal digits can we process? (for 64-bit float, this is 15)
|
||||
var safeDigits = Math.floor(Math.log10(Number.MAX_SAFE_INTEGER));
|
||||
|
||||
return function(address) {
|
||||
address = address.toUpperCase();
|
||||
address = address.substring(4) + address.substring(0, 2) + '00';
|
||||
|
||||
var expanded = address.split('');
|
||||
for (var i = 0; i < expanded.length; i++) {
|
||||
expanded[i] = ibanLookup[expanded[i]];
|
||||
}
|
||||
expanded = expanded.join('');
|
||||
|
||||
// Javascript can handle integers safely up to 15 (decimal) digits
|
||||
while (expanded.length >= safeDigits){
|
||||
var block = expanded.substring(0, safeDigits);
|
||||
expanded = parseInt(block, 10) % 97 + expanded.substring(block.length);
|
||||
}
|
||||
|
||||
var checksum = String(98 - (parseInt(expanded, 10) % 97));
|
||||
while (checksum.length < 2) { checksum = '0' + checksum; }
|
||||
|
||||
return checksum;
|
||||
};
|
||||
})();
|
||||
|
||||
function getAddress(address, icapFormat) {
|
||||
var result = null;
|
||||
|
||||
if (typeof(address) !== 'string') { throw new Error('invalid address'); }
|
||||
|
||||
if (address.match(/^(0x)?[0-9a-fA-F]{40}$/)) {
|
||||
|
||||
// Missing the 0x prefix
|
||||
if (address.substring(0, 2) !== '0x') { address = '0x' + address; }
|
||||
|
||||
result = getChecksumAddress(address);
|
||||
|
||||
// It is a checksummed address with a bad checksum
|
||||
if (address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) && result !== address) {
|
||||
throw new Error('invalid address checksum');
|
||||
}
|
||||
|
||||
// Maybe ICAP? (we only support direct mode)
|
||||
} else if (address.match(/^XE[0-9]{2}[0-9A-Za-z]{30,31}$/)) {
|
||||
|
||||
// It is an ICAP address with a bad checksum
|
||||
if (address.substring(2, 4) !== ibanChecksum(address)) {
|
||||
throw new Error('invalid address icap checksum');
|
||||
}
|
||||
|
||||
result = (new BN(address.substring(4), 36)).toString(16);
|
||||
while (result.length < 40) { result = '0' + result; }
|
||||
result = getChecksumAddress('0x' + result);
|
||||
|
||||
} else {
|
||||
throw new Error('invalid address - ' + address);
|
||||
}
|
||||
|
||||
if (icapFormat) {
|
||||
var base36 = (new BN(result.substring(2), 16)).toString(36).toUpperCase();
|
||||
while (base36.length < 30) { base36 = '0' + base36; }
|
||||
return 'XE' + ibanChecksum('XE00' + base36) + base36;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
getAddress: getAddress,
|
||||
}
|
146
utils/bignumber.js
Normal file
146
utils/bignumber.js
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* BigNumber
|
||||
*
|
||||
* A wrapper around the BN.js object. In the future we can swap out
|
||||
* the underlying BN.js library for something smaller.
|
||||
*/
|
||||
|
||||
var BN = require('bn.js');
|
||||
|
||||
var defineProperty = require('./properties.js').defineProperty;
|
||||
var convert = require('./convert.js');
|
||||
|
||||
function BigNumber(value) {
|
||||
if (!(this instanceof BigNumber)) { throw new Error('missing new'); }
|
||||
|
||||
if (convert.isHexString(value)) {
|
||||
if (value == '0x') { value = '0x0'; }
|
||||
value = new BN(value.substring(2), 16);
|
||||
|
||||
} else if (typeof(value) === 'string' && value.match(/^-?[0-9]*$/)) {
|
||||
if (value == '') { value = '0'; }
|
||||
value = new BN(value);
|
||||
|
||||
} else if (typeof(value) === 'number' && parseInt(value) == value) {
|
||||
value = new BN(value);
|
||||
|
||||
} else if (BN.isBN(value)) {
|
||||
//value = value
|
||||
|
||||
} else if (value instanceof BigNumber) {
|
||||
value = value._bn;
|
||||
|
||||
} else if (convert.isArrayish(value)) {
|
||||
value = new BN(convert.hexlify(value).substring(2), 16);
|
||||
|
||||
} else {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
|
||||
defineProperty(this, '_bn', value);
|
||||
}
|
||||
|
||||
defineProperty(BigNumber, 'constantNegativeOne', bigNumberify(-1));
|
||||
defineProperty(BigNumber, 'constantZero', bigNumberify(0));
|
||||
defineProperty(BigNumber, 'constantOne', bigNumberify(1));
|
||||
defineProperty(BigNumber, 'constantTwo', bigNumberify(2));
|
||||
defineProperty(BigNumber, 'constantWeiPerEther', bigNumberify(new BN('1000000000000000000')));
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'fromTwos', function(value) {
|
||||
return new BigNumber(this._bn.fromTwos(value));
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'toTwos', function(value) {
|
||||
return new BigNumber(this._bn.toTwos(value));
|
||||
});
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'add', function(other) {
|
||||
return new BigNumber(this._bn.add(bigNumberify(other)._bn));
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'sub', function(other) {
|
||||
return new BigNumber(this._bn.sub(bigNumberify(other)._bn));
|
||||
});
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'div', function(other) {
|
||||
return new BigNumber(this._bn.div(bigNumberify(other)._bn));
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'mul', function(other) {
|
||||
return new BigNumber(this._bn.mul(bigNumberify(other)._bn));
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'mod', function(other) {
|
||||
return new BigNumber(this._bn.mod(bigNumberify(other)._bn));
|
||||
});
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'maskn', function(value) {
|
||||
return new BigNumber(this._bn.maskn(value));
|
||||
});
|
||||
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'eq', function(other) {
|
||||
return this._bn.eq(bigNumberify(other)._bn);
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'lt', function(other) {
|
||||
return this._bn.lt(bigNumberify(other)._bn);
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'lte', function(other) {
|
||||
return this._bn.lte(bigNumberify(other)._bn);
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'gte', function(other) {
|
||||
return this._bn.gte(bigNumberify(other)._bn);
|
||||
});
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'isZero', function() {
|
||||
return this._bn.isZero();
|
||||
});
|
||||
|
||||
|
||||
defineProperty(BigNumber.prototype, 'toNumber', function(base) {
|
||||
return this._bn.toNumber();
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'toString', function(base) {
|
||||
return this._bn.toString(base || 10);
|
||||
});
|
||||
|
||||
defineProperty(BigNumber.prototype, 'toHexString', function() {
|
||||
var hex = this._bn.toString(16);
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return '0x' + hex;
|
||||
});
|
||||
|
||||
|
||||
function isBigNumber(value) {
|
||||
return (value instanceof BigNumber);
|
||||
}
|
||||
|
||||
function bigNumberify(value, name) {
|
||||
if (value instanceof BigNumber) { return value; }
|
||||
|
||||
try {
|
||||
return new BigNumber(value);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
if (name) {
|
||||
throw new Error('invalid arrayify object (' + name + ')');
|
||||
}
|
||||
throw new Error('invalid arrayify object');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isBigNumber: isBigNumber,
|
||||
bigNumberify: bigNumberify
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
module.exports.XMLHttpRequest = XMLHttpRequest;
|
||||
} catch(error) {
|
||||
console.log('Warning: XMLHttpRequest is not defined');
|
||||
module.exports.XMLHttpRequest = null;
|
||||
}
|
20
utils/contract-address.js
Normal file
20
utils/contract-address.js
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
var getAddress = require('./address.js').getAddress;
|
||||
var convert = require('./convert.js');
|
||||
var keccak256 = require('./keccak256.js');
|
||||
var rlp = require('./rlp.js');
|
||||
|
||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||
function getContractAddress(transaction) {
|
||||
if (!transaction.from) { throw new Error('missing from address'); }
|
||||
var nonce = transaction.nonce;
|
||||
|
||||
return getAddress('0x' + keccak256(rlp.encode([
|
||||
getAddress(transaction.from),
|
||||
convert.hexlify(nonce, 'nonce')
|
||||
])).substring(26));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getContractAddress: getContractAddress,
|
||||
}
|
168
utils/convert.js
Normal file
168
utils/convert.js
Normal file
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Conversion Utilities
|
||||
*
|
||||
*/
|
||||
|
||||
var defineProperty = require('./properties.js').defineProperty;
|
||||
|
||||
|
||||
function isArrayish(value) {
|
||||
if (!value || parseInt(value.length) != value.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var v = value[i];
|
||||
if (v < 0 || v >= 256 || parseInt(v) != v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function arrayify(value, name) {
|
||||
|
||||
if (value && value.toHexString) {
|
||||
value = value.toHexString();
|
||||
}
|
||||
|
||||
if (isHexString(value)) {
|
||||
value = value.substring(2);
|
||||
if (value.length % 2) { value = '0' + value; }
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < value.length; i += 2) {
|
||||
result.push(parseInt(value.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return new Uint8Array(result);
|
||||
}
|
||||
|
||||
if (isArrayish(value)) {
|
||||
return new Uint8Array(value);
|
||||
}
|
||||
|
||||
console.log('AA', typeof(value), value);
|
||||
|
||||
if (name) {
|
||||
throw new Error('invalid arrayify object (' + name + ')');
|
||||
}
|
||||
throw new Error('invalid arrayify object');
|
||||
}
|
||||
|
||||
function concat(objects) {
|
||||
var arrays = [];
|
||||
var length = 0;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
var object = arrayify(objects[i])
|
||||
arrays.push(object);
|
||||
length += object.length;
|
||||
}
|
||||
|
||||
var result = new Uint8Array(length);
|
||||
var offset = 0;
|
||||
for (var i = 0; i < arrays.length; i++) {
|
||||
result.set(arrays[i], offset);
|
||||
offset += arrays[i].length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
function stripZeros(value) {
|
||||
value = arrayify(value);
|
||||
|
||||
if (value.length === 0) { return value; }
|
||||
|
||||
// Find the first non-zero entry
|
||||
var start = 0;
|
||||
while (value[start] === 0) { start++ }
|
||||
|
||||
// If we started with zeros, strip them
|
||||
if (start) {
|
||||
value = value.slice(start);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function padZeros(value, length) {
|
||||
if (length < value.length) { throw new Error('cannot pad'); }
|
||||
var result = new Uint8Array(length);
|
||||
result.set(value, length - value.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function isHexString(value, length) {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
return false
|
||||
}
|
||||
if (length && value.length !== 2 + 2 * length) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
var HexCharacters = '0123456789abcdef';
|
||||
|
||||
function hexlify(value, name) {
|
||||
|
||||
if (value && value.toHexString) {
|
||||
return value.toHexString();
|
||||
}
|
||||
|
||||
if (typeof(value) === 'number') {
|
||||
if (value < 0) {
|
||||
throw new Error('cannot hexlify negative value');
|
||||
}
|
||||
|
||||
var hex = '';
|
||||
while (value) {
|
||||
hex = HexCharacters[value & 0x0f] + hex;
|
||||
value = parseInt(value / 16);
|
||||
}
|
||||
|
||||
if (hex.length) {
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return '0x' + hex;
|
||||
}
|
||||
|
||||
return '0x00';
|
||||
}
|
||||
|
||||
if (isHexString(value)) {
|
||||
if (value.length % 2) {
|
||||
value = '0x0' + value.substring(2);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if (isArrayish(value)) {
|
||||
var result = [];
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var v = value[i];
|
||||
result.push(HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f]);
|
||||
}
|
||||
return '0x' + result.join('');
|
||||
}
|
||||
|
||||
console.log('ERROR', typeof(value), value);
|
||||
|
||||
if (name) {
|
||||
throw new Error('invalid hexlifiy value (' + name + ')');
|
||||
}
|
||||
throw new Error('invalid hexlify value');
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
arrayify: arrayify,
|
||||
isArrayish: isArrayish,
|
||||
|
||||
concat: concat,
|
||||
|
||||
padZeros: padZeros,
|
||||
stripZeros: stripZeros,
|
||||
|
||||
hexlify: hexlify,
|
||||
isHexString: isHexString,
|
||||
};
|
53
utils/hmac.js
Normal file
53
utils/hmac.js
Normal file
@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
// See: https://github.com/dominictarr/hmac
|
||||
//
|
||||
// The only difference between this and the original is this uses Uint8Array instead of Buffer
|
||||
|
||||
|
||||
// @TODO: Use the hmac in hash.js instead
|
||||
|
||||
var convert = require('./convert.js');
|
||||
|
||||
var zeroBuffer = new Uint8Array(128)
|
||||
|
||||
function Hmac (createHash, blocksize, key) {
|
||||
if(!(this instanceof Hmac)) { throw new Error('missing new'); }
|
||||
|
||||
this._opad = opad
|
||||
this._createHash = createHash
|
||||
|
||||
if(blocksize !== 128 && blocksize !== 64) {
|
||||
throw new Error('blocksize must be either 64 for or 128 , but was:' + blocksize);
|
||||
}
|
||||
|
||||
key = this._key = convert.arrayify(key);
|
||||
|
||||
if(key.length > blocksize) {
|
||||
key = this._createHash().update(key).digest()
|
||||
} else if(key.length < blocksize) {
|
||||
key = convert.concat([key, zeroBuffer], blocksize)
|
||||
}
|
||||
|
||||
var ipad = this._ipad = new Uint8Array(blocksize)
|
||||
var opad = this._opad = new Uint8Array(blocksize)
|
||||
|
||||
for(var i = 0; i < blocksize; i++) {
|
||||
ipad[i] = key[i] ^ 0x36
|
||||
opad[i] = key[i] ^ 0x5C
|
||||
}
|
||||
|
||||
this._hash = this._createHash().update(ipad)
|
||||
}
|
||||
|
||||
Hmac.prototype.update = function (data, enc) {
|
||||
this._hash.update(data, enc)
|
||||
return this;
|
||||
}
|
||||
|
||||
Hmac.prototype.digest = function (enc) {
|
||||
var h = this._hash.digest()
|
||||
return this._createHash().update(this._opad).update(h).digest(enc)
|
||||
}
|
||||
|
||||
module.exports = Hmac
|
12
utils/keccak256.js
Normal file
12
utils/keccak256.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var sha3 = require('js-sha3');
|
||||
|
||||
var convert = require('./convert.js');
|
||||
|
||||
function keccak256(data) {
|
||||
data = convert.arrayify(data);
|
||||
return '0x' + sha3.keccak_256(data);
|
||||
}
|
||||
|
||||
module.exports = keccak256;
|
29
utils/package.json
Normal file
29
utils/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "ethers-utils",
|
||||
"version": "2.0.0",
|
||||
"description": "Utilities for the Ethers Ethereum library.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": "^4.4.0",
|
||||
"hash.js": "^1.0.0",
|
||||
"js-sha3": "0.5.7",
|
||||
"rlp": "2.0.0",
|
||||
"xmlhttprequest": "1.8.0"
|
||||
},
|
||||
"browser": {
|
||||
"./random-bytes.js": "./browser-random-bytes.js",
|
||||
"xmlhttprequest": "./browser-xmlhttprequest.js"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"ethers",
|
||||
"util",
|
||||
"utils",
|
||||
"utilities"
|
||||
],
|
||||
"author": "Richard Moore <me@ricmoo.com>",
|
||||
"license": "MIT"
|
||||
}
|
11
utils/properties.js
Normal file
11
utils/properties.js
Normal file
@ -0,0 +1,11 @@
|
||||
function defineProperty(object, name, value) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
value: value,
|
||||
writable: false,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
defineProperty: defineProperty,
|
||||
};
|
156
utils/rlp.js
Normal file
156
utils/rlp.js
Normal file
@ -0,0 +1,156 @@
|
||||
//See: https://github.com/ethereum/wiki/wiki/RLP
|
||||
|
||||
var convert = require('./convert.js');
|
||||
|
||||
function arrayifyInteger(value) {
|
||||
var result = [];
|
||||
while (value) {
|
||||
result.unshift(value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function unarrayifyInteger(data, offset, length) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result = (result * 256) + data[offset + i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _encode(object) {
|
||||
if (Array.isArray(object)) {
|
||||
var payload = [];
|
||||
object.forEach(function(child) {
|
||||
payload = payload.concat(_encode(child));
|
||||
});
|
||||
|
||||
if (payload.length <= 55) {
|
||||
payload.unshift(0xc0 + payload.length)
|
||||
return payload;
|
||||
}
|
||||
|
||||
var length = arrayifyInteger(payload.length);
|
||||
length.unshift(0xf7 + length.length);
|
||||
|
||||
return length.concat(payload);
|
||||
|
||||
} else {
|
||||
object = [].slice.call(convert.arrayify(object));
|
||||
|
||||
if (object.length === 1 && object[0] <= 0x7f) {
|
||||
return object;
|
||||
|
||||
} else if (object.length <= 55) {
|
||||
object.unshift(0x80 + object.length);
|
||||
return object
|
||||
}
|
||||
|
||||
var length = arrayifyInteger(object.length);
|
||||
|
||||
length.unshift(0xb7 + length.length);
|
||||
|
||||
return length.concat(object);
|
||||
}
|
||||
}
|
||||
|
||||
function encode(object) {
|
||||
return convert.hexlify(_encode(object));
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
// returns { consumed: number, result: Object }
|
||||
function _decode(data, offset) {
|
||||
if (data.length === 0) { throw new Error('invalid rlp data'); }
|
||||
|
||||
// Array with extra length prefix
|
||||
if (data[offset] >= 0xf8) {
|
||||
var lengthLength = data[offset] - 0xf7;
|
||||
if (offset + 1 + lengthLength > data.length) {
|
||||
throw new Error('too short');
|
||||
}
|
||||
|
||||
var length = unarrayifyInteger(data, offset + 1, lengthLength);
|
||||
if (offset + 1 + lengthLength + length > data.length) {
|
||||
throw new Error('to short');
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
var childOffset = offset + 1 + lengthLength;
|
||||
while (childOffset < offset + 1 + lengthLength + length) {
|
||||
var decoded = _decode(data, childOffset);
|
||||
|
||||
result.push(decoded.result);
|
||||
|
||||
childOffset += decoded.consumed;
|
||||
if (childOffset > offset + 1 + lengthLength + length) {
|
||||
throw new Error('hungry child');
|
||||
}
|
||||
}
|
||||
|
||||
return {consumed: (1 + lengthLength + length), result: result};
|
||||
|
||||
} else if (data[offset] >= 0xc0) {
|
||||
var length = data[offset] - 0xc0;
|
||||
if (offset + 1 + length > data.length) {
|
||||
throw new Error('invalid rlp data');
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
var childOffset = offset + 1;
|
||||
while (childOffset < offset + 1 + length) {
|
||||
var decoded = _decode(data, childOffset);
|
||||
result.push(decoded.result);
|
||||
|
||||
childOffset += decoded.consumed;
|
||||
if (childOffset > offset + 1 + length) {
|
||||
throw new Error('invalid rlp data');
|
||||
}
|
||||
}
|
||||
|
||||
return { consumed: (1 + length), result: result };
|
||||
|
||||
} else if (data[offset] >= 0xb8) {
|
||||
var lengthLength = data[offset] - 0xb7;
|
||||
if (offset + 1 + lengthLength > data.length) {
|
||||
throw new Error('invalid rlp data');
|
||||
}
|
||||
|
||||
var length = unarrayifyInteger(data, offset + 1, lengthLength);
|
||||
if (offset + 1 + lengthLength + length > data.length) {
|
||||
throw new Error('invalid rlp data');
|
||||
}
|
||||
|
||||
var result = convert.hexlify(data.slice(offset + 1 + lengthLength, offset + 1 + lengthLength + length));
|
||||
return { consumed: (1 + lengthLength + length), result: result }
|
||||
|
||||
} else if (data[offset] >= 0x80) {
|
||||
var length = data[offset] - 0x80;
|
||||
if (offset + 1 + length > data.offset) {
|
||||
throw new Error('invlaid rlp data');
|
||||
}
|
||||
|
||||
var result = convert.hexlify(data.slice(offset + 1, offset + 1 + length));
|
||||
return { consumed: (1 + length), result: result }
|
||||
}
|
||||
return { consumed: 1, result: convert.hexlify(data[offset]) };
|
||||
}
|
||||
|
||||
function decode(data) {
|
||||
data = convert.arrayify(data);
|
||||
var decoded = _decode(data, 0);
|
||||
if (decoded.consumed !== data.length) {
|
||||
throw new Error('invalid rlp data');
|
||||
}
|
||||
return decoded.result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encode: encode,
|
||||
decode: decode,
|
||||
}
|
23
utils/sha2.js
Normal file
23
utils/sha2.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
var hash = require('hash.js');
|
||||
|
||||
var convert = require('./convert.js');
|
||||
|
||||
function sha256(data) {
|
||||
data = convert.arrayify(data);
|
||||
return '0x' + (hash.sha256().update(data).digest('hex'));
|
||||
}
|
||||
|
||||
function sha512(data) {
|
||||
data = convert.arrayify(data);
|
||||
return '0x' + (hash.sha512().update(data).digest('hex'));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sha256: sha256,
|
||||
sha512: sha512,
|
||||
|
||||
createSha256: hash.sha256,
|
||||
createSha512: hash.sha512,
|
||||
}
|
113
utils/utf8.js
Normal file
113
utils/utf8.js
Normal file
@ -0,0 +1,113 @@
|
||||
|
||||
var convert = require('./convert.js');
|
||||
|
||||
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
||||
function utf8ToBytes(str) {
|
||||
var result = [];
|
||||
var offset = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str.charCodeAt(i);
|
||||
if (c < 128) {
|
||||
result[offset++] = c;
|
||||
} else if (c < 2048) {
|
||||
result[offset++] = (c >> 6) | 192;
|
||||
result[offset++] = (c & 63) | 128;
|
||||
} else if (((c & 0xFC00) == 0xD800) && (i + 1) < str.length && ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
|
||||
// Surrogate Pair
|
||||
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
|
||||
result[offset++] = (c >> 18) | 240;
|
||||
result[offset++] = ((c >> 12) & 63) | 128;
|
||||
result[offset++] = ((c >> 6) & 63) | 128;
|
||||
result[offset++] = (c & 63) | 128;
|
||||
} else {
|
||||
result[offset++] = (c >> 12) | 224;
|
||||
result[offset++] = ((c >> 6) & 63) | 128;
|
||||
result[offset++] = (c & 63) | 128;
|
||||
}
|
||||
}
|
||||
|
||||
return convert.arrayify(result);
|
||||
};
|
||||
|
||||
|
||||
// http://stackoverflow.com/questions/13356493/decode-utf-8-with-javascript#13691499
|
||||
function bytesToUtf8(bytes) {
|
||||
bytes = convert.arrayify(bytes);
|
||||
|
||||
var result = '';
|
||||
var i = 0;
|
||||
|
||||
// Invalid bytes are ignored
|
||||
while(i < bytes.length) {
|
||||
var c = bytes[i++];
|
||||
if (c >> 7 == 0) {
|
||||
// 0xxx xxxx
|
||||
result += String.fromCharCode(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Invalid starting byte
|
||||
if (c >> 6 == 0x02) { continue; }
|
||||
|
||||
// Multibyte; how many bytes left for thus character?
|
||||
var extraLength = null;
|
||||
if (c >> 5 == 0x06) {
|
||||
extraLength = 1;
|
||||
} else if (c >> 4 == 0x0e) {
|
||||
extraLength = 2;
|
||||
} else if (c >> 3 == 0x1e) {
|
||||
extraLength = 3;
|
||||
} else if (c >> 2 == 0x3e) {
|
||||
extraLength = 4;
|
||||
} else if (c >> 1 == 0x7e) {
|
||||
extraLength = 5;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do we have enough bytes in our data?
|
||||
if (i + extraLength > bytes.length) {
|
||||
|
||||
// If there is an invalid unprocessed byte, try to continue
|
||||
for (; i < bytes.length; i++) {
|
||||
if (bytes[i] >> 6 != 0x02) { break; }
|
||||
}
|
||||
if (i != bytes.length) continue;
|
||||
|
||||
// All leftover bytes are valid.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove the UTF-8 prefix from the char (res)
|
||||
var res = c & ((1 << (8 - extraLength - 1)) - 1);
|
||||
|
||||
var count;
|
||||
for (count = 0; count < extraLength; count++) {
|
||||
var nextChar = bytes[i++];
|
||||
|
||||
// Is the char valid multibyte part?
|
||||
if (nextChar >> 6 != 0x02) {break;};
|
||||
res = (res << 6) | (nextChar & 0x3f);
|
||||
}
|
||||
|
||||
if (count != extraLength) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res <= 0xffff) {
|
||||
result += String.fromCharCode(res);
|
||||
continue;
|
||||
}
|
||||
|
||||
res -= 0x10000;
|
||||
result += String.fromCharCode(((res >> 10) & 0x3ff) + 0xd800, (res & 0x3ff) + 0xdc00);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
toUtf8Bytes: utf8ToBytes,
|
||||
toUtf8String: bytesToUtf8,
|
||||
};
|
Loading…
Reference in New Issue
Block a user