1 Commits

Author SHA1 Message Date
071a882c3d Initial support for bitcoin and ripple alphabets 2018-11-19 00:46:33 +00:00
3 changed files with 670 additions and 562 deletions

View File

@@ -1,9 +1,17 @@
const alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; const alphabets = {
const base = alphabet.length; flickr: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ",
bitcoin: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
ripple: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"
};
const base = alphabets.flickr.length;
// Create a lookup table to fetch character index // Create a lookup table to fetch character index
const alphabetLookup = [...alphabet].reduce((lookup, char, index) => { const alphabetLookups = Object.keys(alphabets).reduce((lookup, name) => {
lookup[char] = index; lookup[name] = [...alphabets[name]].reduce((memo, char, index) => {
memo[char] = index;
return memo;
}, {});
return lookup; return lookup;
}, {}); }, {});
@@ -25,13 +33,13 @@ function assertString(str) {
} }
} }
function assertBase58Character(character) { function assertBase58Character(character, alphabet) {
if (alphabetLookup[character] === undefined) { if (alphabetLookups[alphabet][character] === undefined) {
throw new Error("Value passed is not a valid Base58 string."); throw new Error("Value passed is not a valid Base58 string.");
} }
} }
exports.int_to_base58 = exports.encode = function(num) { exports.int_to_base58 = exports.encode = function(num, alphabet = "flickr") {
let str = ""; let str = "";
let modulus; let modulus;
@@ -41,18 +49,18 @@ exports.int_to_base58 = exports.encode = function(num) {
while (num >= base) { while (num >= base) {
modulus = num % base; modulus = num % base;
str = alphabet[modulus] + str; str = alphabets[alphabet][modulus] + str;
num = Math.floor(num / base); num = Math.floor(num / base);
} }
return alphabet[num] + str; return alphabets[alphabet][num] + str;
}; };
exports.base58_to_int = exports.decode = function(str) { exports.base58_to_int = exports.decode = function(str, alphabet = "flickr") {
assertString(str); assertString(str);
return [...str].reverse().reduce((num, character, index) => { return [...str].reverse().reduce((num, character, index) => {
assertBase58Character(character); assertBase58Character(character, alphabet);
return num + alphabetLookup[character] * Math.pow(base, index); return num + alphabetLookups[alphabet][character] * Math.pow(base, index);
}, 0); }, 0);
}; };

View File

@@ -2,9 +2,9 @@ const assert = require("assert");
const examples = require("./examples"); const examples = require("./examples");
const base58 = require(".."); const base58 = require("..");
function exampleRunner(callback) { function exampleRunner(alphabet, callback) {
Object.keys(examples).forEach(function(str) { Object.keys(alphabet).forEach(function(str) {
callback(str, examples[str]); callback(str, alphabet[str]);
}); });
} }
@@ -13,18 +13,20 @@ describe("Base58", function() {
var valid = true; var valid = true;
var count = 0; var count = 0;
exampleRunner(function(str, num) { for (var alphabetName in examples.alphabets) {
count++; exampleRunner(examples.alphabets[alphabetName], function(str, num) {
if (typeof str !== "string") { count++;
valid = false; if (typeof str !== "string") {
} valid = false;
if (typeof num !== "number") { }
valid = false; if (typeof num !== "number") {
} valid = false;
}); }
});
assert.strictEqual(count > 0, true, "Expected there to be examples"); assert.strictEqual(count > 0, true, "Expected there to be examples");
assert.strictEqual(valid, true, "Expected the examples to be valid"); assert.strictEqual(valid, true, "Expected the examples to be valid");
}
}); });
describe("backwards compatibility", function() { describe("backwards compatibility", function() {
@@ -38,17 +40,49 @@ describe("Base58", function() {
}); });
describe(".int_to_base58()", function() { describe(".int_to_base58()", function() {
it("encodes number to Base58 string", function() { for (var alphabetName in examples.alphabets) {
exampleRunner(function(str, num) { describe("when using " + alphabetName + " alphabet", function() {
assert.strictEqual(base58.int_to_base58(num), str); it("encodes number to Base58 string", function() {
}); exampleRunner(examples.alphabets[alphabetName], function(str, num) {
}); let result = base58.int_to_base58(num, alphabetName);
describe("when passed a string only containing numbers", function() { assert.strictEqual(result, str);
it("encodes string after first converting it to an integer", function() { });
exampleRunner(function(str, num) {
assert.strictEqual(base58.int_to_base58(num.toString()), str);
}); });
describe("when passed a string only containing numbers", function() {
it("encodes string after first converting it to an integer", function() {
exampleRunner(examples.alphabets[alphabetName], function(str, num) {
let result = base58.int_to_base58(num.toString(), alphabetName);
assert.strictEqual(result, str);
});
});
});
});
}
describe("when alphabet is not specified", function() {
it("encodes number to Base58 string with flickr alphabet", function() {
exampleRunner(examples.alphabets.flickr, function(str, num) {
let result = base58.int_to_base58(num);
assert.strictEqual(result, str);
});
});
describe("when passed a string only containing numbers", function() {
it(
"encodes string with flickr alphabet " +
"after first converting it to an integer",
function() {
exampleRunner(examples.alphabets.flickr, function(str, num) {
let result = base58.int_to_base58(num.toString());
assert.strictEqual(result, str);
});
}
);
}); });
}); });
@@ -114,35 +148,81 @@ describe("Base58", function() {
}); });
describe(".base58_to_int()", function() { describe(".base58_to_int()", function() {
it("decodes base58 string to number", function() { for (var alphabetName in examples.alphabets) {
exampleRunner(function(str, num) { describe("when using " + alphabetName + " alphabet", function() {
assert.strictEqual(base58.base58_to_int(str), num); it("decodes base58 string to number", function() {
}); exampleRunner(examples.alphabets[alphabetName], function(str, num) {
}); let result = base58.base58_to_int(str, alphabetName);
describe("when passed a non string", function() { assert.strictEqual(result, num);
it("throws an error", function() { });
assert.throws( });
function() {
base58.base58_to_int(123);
},
function(err) {
return err.message === "Value passed is not a string.";
}
);
});
});
describe("when passed a non base58 string", function() { describe("when passed a non string", function() {
it("throws an error", function() { it("throws an error", function() {
assert.throws( assert.throws(
function() { function() {
base58.base58_to_int(">_<"); base58.base58_to_int(123, alphabetName);
}, },
function(err) { function(err) {
return err.message === "Value passed is not a valid Base58 string."; return err.message === "Value passed is not a string.";
} }
); );
});
});
describe("when passed a non base58 string", function() {
it("throws an error", function() {
assert.throws(
function() {
base58.base58_to_int(">_<", alphabetName);
},
function(err) {
return (
err.message === "Value passed is not a valid Base58 string."
);
}
);
});
});
});
}
describe("when alphabet is not specified", function() {
it("decodes base58 string to number with flickr alphabet", function() {
exampleRunner(examples.alphabets.flickr, function(str, num) {
let result = base58.base58_to_int(str);
assert.strictEqual(result, num);
});
});
describe("when passed a non string", function() {
it("throws an error", function() {
assert.throws(
function() {
base58.base58_to_int(123);
},
function(err) {
return err.message === "Value passed is not a string.";
}
);
});
});
describe("when passed a non base58 string", function() {
it("throws an error", function() {
assert.throws(
function() {
base58.base58_to_int(">_<");
},
function(err) {
return (
err.message === "Value passed is not a valid Base58 string."
);
}
);
});
}); });
}); });
}); });

File diff suppressed because it is too large Load Diff