mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
Merge pull request #294 from clark800/baseconverter
Refactor base conversion
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
var _ = require('lodash');
|
||||||
var sjcl = require('./utils').sjcl;
|
var sjcl = require('./utils').sjcl;
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var extend = require('extend');
|
var extend = require('extend');
|
||||||
|
var convertBase = require('./baseconverter');
|
||||||
|
|
||||||
var Base = {};
|
var Base = {};
|
||||||
|
|
||||||
@@ -22,121 +25,50 @@ extend(Base, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function sha256(bytes) {
|
function sha256(bytes) {
|
||||||
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
return sjcl.codec.bytes.fromBits(
|
||||||
}
|
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||||
|
|
||||||
function sha256hash(bytes) {
|
|
||||||
return sha256(sha256(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
function divmod58(number, startAt) {
|
|
||||||
var remainder = 0;
|
|
||||||
for (var i = startAt; i < number.length; i++) {
|
|
||||||
var digit256 = number[i] & 0xFF;
|
|
||||||
var temp = remainder * 256 + digit256;
|
|
||||||
number[i] = (temp / 58);
|
|
||||||
remainder = temp % 58;
|
|
||||||
}
|
|
||||||
return remainder;
|
|
||||||
}
|
|
||||||
|
|
||||||
function divmod256(number58, startAt) {
|
|
||||||
var remainder = 0;
|
|
||||||
for (var i = startAt; i < number58.length; i++) {
|
|
||||||
var digit58 = number58[i] & 0xFF;
|
|
||||||
var temp = remainder * 58 + digit58;
|
|
||||||
number58[i] = (temp / 256);
|
|
||||||
remainder = temp % 256;
|
|
||||||
}
|
|
||||||
return remainder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeString(alphabet, input) {
|
function encodeString(alphabet, input) {
|
||||||
if (input.length == 0) {
|
if (input.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to copy the buffer for calc
|
var leadingZeros = _.takeWhile(input, function(d) {
|
||||||
scratch = input.slice();
|
return d === 0;
|
||||||
|
});
|
||||||
// Count leading zeroes.
|
var out = convertBase(input, 256, 58).map(function(digit) {
|
||||||
var zeroCount = 0;
|
if (digit < 0 || digit >= alphabet.length) {
|
||||||
while (zeroCount < scratch.length &&
|
throw new Error('Value ' + digit + ' is out of bounds for encoding');
|
||||||
scratch[zeroCount] == 0)
|
|
||||||
++zeroCount;
|
|
||||||
|
|
||||||
// The actual encoding.
|
|
||||||
var out = new Array(scratch.length * 2);
|
|
||||||
var j = out.length;
|
|
||||||
var startAt = zeroCount;
|
|
||||||
|
|
||||||
while (startAt < scratch.length) {
|
|
||||||
var mod = divmod58(scratch, startAt);
|
|
||||||
if (scratch[startAt] == 0) {
|
|
||||||
++startAt;
|
|
||||||
}
|
}
|
||||||
out[--j] = alphabet[mod];
|
return alphabet[digit];
|
||||||
}
|
});
|
||||||
|
var prefix = leadingZeros.map(function() {
|
||||||
// Strip extra 'r' if there are some after decoding.
|
return alphabet[0];
|
||||||
while (j < out.length && out[j] == alphabet[0]) ++j;
|
});
|
||||||
// Add as many leading 'r' as there were leading zeros.
|
return prefix.concat(out).join('');
|
||||||
while (--zeroCount >= 0) out[--j] = alphabet[0];
|
|
||||||
while(j--) out.shift();
|
|
||||||
|
|
||||||
return out.join('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeString(indexes, input) {
|
function decodeString(indexes, input) {
|
||||||
var isString = typeof input === 'string';
|
if (input.length === 0) {
|
||||||
|
|
||||||
if (input.length == 0) {
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
input58 = new Array(input.length);
|
var input58 = input.split('').map(function(c) {
|
||||||
|
var charCode = c.charCodeAt(0);
|
||||||
// Transform the String to a base58 byte sequence
|
if (charCode >= indexes.length) {
|
||||||
for (var i = 0; i < input.length; ++i) {
|
throw new Error('Character ' + c + ' is not valid for encoding');
|
||||||
if (isString) {
|
|
||||||
var c = input.charCodeAt(i);
|
|
||||||
}
|
}
|
||||||
|
return indexes[charCode];
|
||||||
var digit58 = -1;
|
});
|
||||||
if (c >= 0 && c < 128) {
|
var leadingZeros = _.takeWhile(input58, function(d) {
|
||||||
digit58 = indexes[c];
|
return d === 0;
|
||||||
}
|
});
|
||||||
if (digit58 < 0) {
|
var out = convertBase(input58, 58, 256);
|
||||||
throw new Error("Illegal character " + c + " at " + i);
|
var prefix = leadingZeros.map(function() {
|
||||||
}
|
return 0;
|
||||||
|
});
|
||||||
input58[i] = digit58;
|
return prefix.concat(out);
|
||||||
}
|
|
||||||
// Count leading zeroes
|
|
||||||
var zeroCount = 0;
|
|
||||||
while (zeroCount < input58.length && input58[zeroCount] == 0) {
|
|
||||||
++zeroCount;
|
|
||||||
}
|
|
||||||
// The encoding
|
|
||||||
out = utils.arraySet(input.length, 0);
|
|
||||||
var j = out.length;
|
|
||||||
|
|
||||||
var startAt = zeroCount;
|
|
||||||
while (startAt < input58.length) {
|
|
||||||
var mod = divmod256(input58, startAt);
|
|
||||||
if (input58[startAt] == 0) {
|
|
||||||
++startAt;
|
|
||||||
}
|
|
||||||
out[--j] = mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do no add extra leading zeroes, move j to first non null byte.
|
|
||||||
while (j < out.length && (out[j] == 0)) ++j;
|
|
||||||
|
|
||||||
j -= zeroCount;
|
|
||||||
while(j--) out.shift();
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Base58(alphabet) {
|
function Base58(alphabet) {
|
||||||
@@ -152,7 +84,7 @@ function Base58(alphabet) {
|
|||||||
|
|
||||||
Base.encoders = {};
|
Base.encoders = {};
|
||||||
Object.keys(alphabets).forEach(function(alphabet) {
|
Object.keys(alphabets).forEach(function(alphabet) {
|
||||||
Base.encoders[alphabet] = Base58(alphabets[alphabet]);
|
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// --> input: big-endian array of bytes.
|
// --> input: big-endian array of bytes.
|
||||||
@@ -165,18 +97,17 @@ Base.encode = function(input, alpha) {
|
|||||||
// <-- array of bytes or undefined.
|
// <-- array of bytes or undefined.
|
||||||
Base.decode = function(input, alpha) {
|
Base.decode = function(input, alpha) {
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
return void(0);
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return this.encoders[alpha || 'ripple'].decode(input);
|
return this.encoders[alpha || 'ripple'].decode(input);
|
||||||
}
|
} catch (e) {
|
||||||
catch(e) {
|
return undefined;
|
||||||
return (void 0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Base.verify_checksum = function(bytes) {
|
Base.verify_checksum = function(bytes) {
|
||||||
var computed = sha256hash(bytes.slice(0, -4)).slice(0, 4);
|
var computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
|
||||||
var checksum = bytes.slice(-4);
|
var checksum = bytes.slice(-4);
|
||||||
var result = true;
|
var result = true;
|
||||||
|
|
||||||
|
|||||||
32
src/js/ripple/baseconverter.js
Normal file
32
src/js/ripple/baseconverter.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function normalize(digitArray) {
|
||||||
|
while (digitArray[0] === 0) {
|
||||||
|
digitArray.shift();
|
||||||
|
}
|
||||||
|
return digitArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function divmod(digitArray, base, divisor) {
|
||||||
|
var remainder = 0;
|
||||||
|
var quotient = [];
|
||||||
|
for (var j = 0; j < digitArray.length; j++) {
|
||||||
|
var temp = remainder * base + parseInt(digitArray[j], 10);
|
||||||
|
quotient.push(Math.floor(temp / divisor));
|
||||||
|
remainder = temp % divisor;
|
||||||
|
}
|
||||||
|
return {quotient: normalize(quotient), remainder: remainder};
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertBase(digitArray, fromBase, toBase) {
|
||||||
|
var result = [];
|
||||||
|
var dividend = digitArray;
|
||||||
|
while (dividend.length > 0) {
|
||||||
|
var qr = divmod(dividend, fromBase, toBase);
|
||||||
|
result.unshift(qr.remainder);
|
||||||
|
dividend = qr.quotient;
|
||||||
|
}
|
||||||
|
return normalize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = convertBase;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
exports.Remote = require('./remote').Remote;
|
exports.Remote = require('./remote').Remote;
|
||||||
exports.Request = require('./request').Request;
|
exports.Request = require('./request').Request;
|
||||||
exports.Amount = require('./amount').Amount;
|
exports.Amount = require('./amount').Amount;
|
||||||
@@ -20,6 +21,7 @@ exports.Wallet = require('./wallet');
|
|||||||
exports.Ledger = require('./ledger').Ledger;
|
exports.Ledger = require('./ledger').Ledger;
|
||||||
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
|
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
|
||||||
exports.RangeSet = require('./rangeset').RangeSet;
|
exports.RangeSet = require('./rangeset').RangeSet;
|
||||||
|
exports.convertBase = require('./baseconverter');
|
||||||
|
|
||||||
// Important: We do not guarantee any specific version of SJCL or for any
|
// Important: We do not guarantee any specific version of SJCL or for any
|
||||||
// specific features to be included. The version and configuration may change at
|
// specific features to be included. The version and configuration may change at
|
||||||
@@ -34,8 +36,8 @@ exports.types = require('./serializedtypes');
|
|||||||
exports.config = require('./config');
|
exports.config = require('./config');
|
||||||
|
|
||||||
// camelCase to under_scored API conversion
|
// camelCase to under_scored API conversion
|
||||||
function attachUnderscored(c) {
|
function attachUnderscored(name) {
|
||||||
var o = exports[c];
|
var o = exports[name];
|
||||||
|
|
||||||
Object.keys(o.prototype).forEach(function(key) {
|
Object.keys(o.prototype).forEach(function(key) {
|
||||||
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
|
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
|
||||||
@@ -50,7 +52,7 @@ function attachUnderscored(c) {
|
|||||||
|
|
||||||
o.prototype[underscored] = o.prototype[key];
|
o.prototype[underscored] = o.prototype[key];
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
['Remote',
|
['Remote',
|
||||||
'Request',
|
'Request',
|
||||||
|
|||||||
53
test/baseconverter-test.js
Normal file
53
test/baseconverter-test.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
'use strict';
|
||||||
|
var assert = require('assert');
|
||||||
|
var convertBase = require('ripple-lib').convertBase;
|
||||||
|
|
||||||
|
// Test cases from RFC-1924 (a joke RFC)
|
||||||
|
var BASE85 = ('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
+ 'abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~');
|
||||||
|
var BASE10 = BASE85.slice(0, 10);
|
||||||
|
var BASE16 = BASE85.slice(0, 16);
|
||||||
|
|
||||||
|
var DATA16 = '108000000000000000080800200C417A';
|
||||||
|
var DATA10 = '21932261930451111902915077091070067066';
|
||||||
|
var DATA85 = '4)+k&C#VzJ4br>0wv%Yp';
|
||||||
|
|
||||||
|
function encode(digitArray, encoding) {
|
||||||
|
return digitArray.map(function(i) {
|
||||||
|
return encoding.charAt(i);
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(encoded, encoding) {
|
||||||
|
return encoded.split('').map(function(c) {
|
||||||
|
return encoding.indexOf(c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertBaseEncoded(value, fromEncoding, toEncoding) {
|
||||||
|
var digitArray = decode(value, fromEncoding);
|
||||||
|
var converted = convertBase(digitArray, fromEncoding.length,
|
||||||
|
toEncoding.length);
|
||||||
|
return encode(converted, toEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('convertBase', function() {
|
||||||
|
it('DEC -> HEX', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA10, BASE10, BASE16), DATA16);
|
||||||
|
});
|
||||||
|
it('HEX -> DEC', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA16, BASE16, BASE10), DATA10);
|
||||||
|
});
|
||||||
|
it('DEC -> B85', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA10, BASE10, BASE85), DATA85);
|
||||||
|
});
|
||||||
|
it('HEX -> B85', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA16, BASE16, BASE85), DATA85);
|
||||||
|
});
|
||||||
|
it('B85 -> DEC', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA85, BASE85, BASE10), DATA10);
|
||||||
|
});
|
||||||
|
it('B85 -> HEX', function () {
|
||||||
|
assert.strictEqual(convertBaseEncoded(DATA85, BASE85, BASE16), DATA16);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user