You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
7.3 KiB
262 lines
7.3 KiB
// Copyright (c) 2014-2018, MyMonero.com
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// v--- These should maybe be injected into context
|
|
const JSBigInt = require("./biginteger").BigInteger;
|
|
|
|
var cnBase58 = (function() {
|
|
var b58 = {};
|
|
//
|
|
var alphabet_str =
|
|
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
var alphabet = [];
|
|
for (var i = 0; i < alphabet_str.length; i++) {
|
|
alphabet.push(alphabet_str.charCodeAt(i));
|
|
}
|
|
var encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
|
|
|
|
var alphabet_size = alphabet.length;
|
|
var full_block_size = 8;
|
|
var full_encoded_block_size = 11;
|
|
|
|
var UINT64_MAX = new JSBigInt(2).pow(64);
|
|
|
|
function hextobin(hex) {
|
|
if (hex.length % 2 !== 0) throw "Hex string has invalid length!";
|
|
var res = new Uint8Array(hex.length / 2);
|
|
for (var i = 0; i < hex.length / 2; ++i) {
|
|
res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function bintohex(bin) {
|
|
var out = [];
|
|
for (var i = 0; i < bin.length; ++i) {
|
|
out.push(("0" + bin[i].toString(16)).slice(-2));
|
|
}
|
|
return out.join("");
|
|
}
|
|
|
|
function strtobin(str) {
|
|
var res = new Uint8Array(str.length);
|
|
for (var i = 0; i < str.length; i++) {
|
|
res[i] = str.charCodeAt(i);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function bintostr(bin) {
|
|
var out = [];
|
|
for (var i = 0; i < bin.length; i++) {
|
|
out.push(String.fromCharCode(bin[i]));
|
|
}
|
|
return out.join("");
|
|
}
|
|
|
|
function uint8_be_to_64(data) {
|
|
if (data.length < 1 || data.length > 8) {
|
|
throw "Invalid input length";
|
|
}
|
|
var res = JSBigInt.ZERO;
|
|
var twopow8 = new JSBigInt(2).pow(8);
|
|
var i = 0;
|
|
switch (9 - data.length) {
|
|
case 1:
|
|
res = res.add(data[i++]);
|
|
case 2:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 3:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 4:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 5:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 6:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 7:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
case 8:
|
|
res = res.multiply(twopow8).add(data[i++]);
|
|
break;
|
|
default:
|
|
throw "Impossible condition";
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function uint64_to_8be(num, size) {
|
|
var res = new Uint8Array(size);
|
|
if (size < 1 || size > 8) {
|
|
throw "Invalid input length";
|
|
}
|
|
var twopow8 = new JSBigInt(2).pow(8);
|
|
for (var i = size - 1; i >= 0; i--) {
|
|
res[i] = num.remainder(twopow8).toJSValue();
|
|
num = num.divide(twopow8);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
b58.encode_block = function(data, buf, index) {
|
|
if (data.length < 1 || data.length > full_encoded_block_size) {
|
|
throw "Invalid block length: " + data.length;
|
|
}
|
|
var num = uint8_be_to_64(data);
|
|
var i = encoded_block_sizes[data.length] - 1;
|
|
// while num > 0
|
|
while (num.compare(0) === 1) {
|
|
var div = num.divRem(alphabet_size);
|
|
// remainder = num % alphabet_size
|
|
var remainder = div[1];
|
|
// num = num / alphabet_size
|
|
num = div[0];
|
|
buf[index + i] = alphabet[remainder.toJSValue()];
|
|
i--;
|
|
}
|
|
return buf;
|
|
};
|
|
|
|
b58.encode = function(hex) {
|
|
var data = hextobin(hex);
|
|
if (data.length === 0) {
|
|
return "";
|
|
}
|
|
var full_block_count = Math.floor(data.length / full_block_size);
|
|
var last_block_size = data.length % full_block_size;
|
|
var res_size =
|
|
full_block_count * full_encoded_block_size +
|
|
encoded_block_sizes[last_block_size];
|
|
|
|
var res = new Uint8Array(res_size);
|
|
var i;
|
|
for (i = 0; i < res_size; ++i) {
|
|
res[i] = alphabet[0];
|
|
}
|
|
for (i = 0; i < full_block_count; i++) {
|
|
res = b58.encode_block(
|
|
data.subarray(
|
|
i * full_block_size,
|
|
i * full_block_size + full_block_size,
|
|
),
|
|
res,
|
|
i * full_encoded_block_size,
|
|
);
|
|
}
|
|
if (last_block_size > 0) {
|
|
res = b58.encode_block(
|
|
data.subarray(
|
|
full_block_count * full_block_size,
|
|
full_block_count * full_block_size + last_block_size,
|
|
),
|
|
res,
|
|
full_block_count * full_encoded_block_size,
|
|
);
|
|
}
|
|
return bintostr(res);
|
|
};
|
|
|
|
b58.decode_block = function(data, buf, index) {
|
|
if (data.length < 1 || data.length > full_encoded_block_size) {
|
|
throw "Invalid block length: " + data.length;
|
|
}
|
|
|
|
var res_size = encoded_block_sizes.indexOf(data.length);
|
|
if (res_size <= 0) {
|
|
throw "Invalid block size";
|
|
}
|
|
var res_num = new JSBigInt(0);
|
|
var order = new JSBigInt(1);
|
|
for (var i = data.length - 1; i >= 0; i--) {
|
|
var digit = alphabet.indexOf(data[i]);
|
|
if (digit < 0) {
|
|
throw "Invalid symbol";
|
|
}
|
|
var product = order.multiply(digit).add(res_num);
|
|
// if product > UINT64_MAX
|
|
if (product.compare(UINT64_MAX) === 1) {
|
|
throw "Overflow";
|
|
}
|
|
res_num = product;
|
|
order = order.multiply(alphabet_size);
|
|
}
|
|
if (
|
|
res_size < full_block_size &&
|
|
new JSBigInt(2).pow(8 * res_size).compare(res_num) <= 0
|
|
) {
|
|
throw "Overflow 2";
|
|
}
|
|
buf.set(uint64_to_8be(res_num, res_size), index);
|
|
return buf;
|
|
};
|
|
|
|
b58.decode = function(enc) {
|
|
enc = strtobin(enc);
|
|
if (enc.length === 0) {
|
|
return "";
|
|
}
|
|
var full_block_count = Math.floor(enc.length / full_encoded_block_size);
|
|
var last_block_size = enc.length % full_encoded_block_size;
|
|
var last_block_decoded_size = encoded_block_sizes.indexOf(
|
|
last_block_size,
|
|
);
|
|
if (last_block_decoded_size < 0) {
|
|
throw "Invalid encoded length";
|
|
}
|
|
var data_size =
|
|
full_block_count * full_block_size + last_block_decoded_size;
|
|
var data = new Uint8Array(data_size);
|
|
for (var i = 0; i < full_block_count; i++) {
|
|
data = b58.decode_block(
|
|
enc.subarray(
|
|
i * full_encoded_block_size,
|
|
i * full_encoded_block_size + full_encoded_block_size,
|
|
),
|
|
data,
|
|
i * full_block_size,
|
|
);
|
|
}
|
|
if (last_block_size > 0) {
|
|
data = b58.decode_block(
|
|
enc.subarray(
|
|
full_block_count * full_encoded_block_size,
|
|
full_block_count * full_encoded_block_size +
|
|
last_block_size,
|
|
),
|
|
data,
|
|
full_block_count * full_block_size,
|
|
);
|
|
}
|
|
return bintohex(data);
|
|
};
|
|
|
|
return b58;
|
|
})();
|
|
|
|
exports.cnBase58 = cnBase58;
|