217 lines
5.5 KiB
JavaScript
217 lines
5.5 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.CHECKSUM_LENGTH = void 0;
|
|
exports.createChecksum = createChecksum;
|
|
exports.decode = decode;
|
|
exports.decodeWithPrefix = decodeWithPrefix;
|
|
exports.detectCase = detectCase;
|
|
exports.encode = encode;
|
|
exports.expandPrefix = expandPrefix;
|
|
exports.verifyChecksum = verifyChecksum;
|
|
|
|
var _bitConverter = require("./bit-converter");
|
|
|
|
// Alphabet for Bech32
|
|
var CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; // Checksum constant for Bech32m.
|
|
|
|
var BECH32M_CHECKSUM = 0x2bc830a3; // Minimum char code that could be present in the encoded message
|
|
|
|
var MIN_CHAR_CODE = 33; // Maximum char code that could be present in the encoded message
|
|
|
|
var MAX_CHAR_CODE = 126;
|
|
var CHECKSUM_LENGTH = 6; // Reverse lookup for characters
|
|
|
|
exports.CHECKSUM_LENGTH = CHECKSUM_LENGTH;
|
|
|
|
var CHAR_LOOKUP = function () {
|
|
var lookup = new Map();
|
|
|
|
for (var i = 0; i < CHARSET.length; i += 1) {
|
|
lookup.set(CHARSET[i], i);
|
|
}
|
|
|
|
return lookup;
|
|
}(); // Poly generators
|
|
|
|
|
|
var GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
|
|
|
function polymod(values) {
|
|
return values.reduce(function (checksum, value) {
|
|
var bits = checksum >> 25;
|
|
var newChecksum = (checksum & 0x1ffffff) << 5 ^ value;
|
|
return GEN.reduce(function (chk, gen, i) {
|
|
return (bits >> i & 1) === 0 ? chk : chk ^ gen;
|
|
}, newChecksum);
|
|
},
|
|
/* initial checksum */
|
|
1);
|
|
}
|
|
/**
|
|
* Expands a prefix into the specified output buffer.
|
|
*/
|
|
|
|
|
|
function expandPrefix(prefix, outBuffer) {
|
|
for (var i = 0; i < prefix.length; i += 1) {
|
|
var code = prefix.charCodeAt(i);
|
|
outBuffer[i] = code >> 5;
|
|
outBuffer[i + prefix.length + 1] = code & 31;
|
|
}
|
|
|
|
outBuffer[prefix.length] = 0;
|
|
}
|
|
/**
|
|
* Verifies the checksum for a particular buffer.
|
|
*/
|
|
|
|
|
|
function verifyChecksum(buffer) {
|
|
switch (polymod(buffer)) {
|
|
case 1:
|
|
return 'bech32';
|
|
|
|
case BECH32M_CHECKSUM:
|
|
return 'bech32m';
|
|
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
/**
|
|
* Creates a checksum for a buffer and writes it to the last 6 5-bit groups
|
|
* of the buffer.
|
|
*/
|
|
|
|
|
|
function createChecksum(buffer, encoding) {
|
|
var checksumConstant;
|
|
|
|
switch (encoding) {
|
|
case 'bech32':
|
|
checksumConstant = 1;
|
|
break;
|
|
|
|
case 'bech32m':
|
|
checksumConstant = BECH32M_CHECKSUM;
|
|
break;
|
|
|
|
default:
|
|
throw Error("Invalid encoding value: ".concat(encoding, "; expected bech32 or bech32m"));
|
|
}
|
|
|
|
var mod = polymod(buffer) ^ checksumConstant;
|
|
|
|
for (var i = 0; i < CHECKSUM_LENGTH; i += 1) {
|
|
var shift = 5 * (5 - i);
|
|
buffer[buffer.length - CHECKSUM_LENGTH + i] = mod >> shift & 31;
|
|
}
|
|
}
|
|
/**
|
|
* Encodes an array of 5-bit groups into a string.
|
|
*
|
|
* @param {Uint8Array} buffer
|
|
* @returns {string}
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
|
|
function encode(buffer) {
|
|
return buffer.reduce(function (acc, bits) {
|
|
return acc + CHARSET[bits];
|
|
}, '');
|
|
}
|
|
/**
|
|
* Decodes a string into an array of 5-bit groups.
|
|
*
|
|
* @param {string} message
|
|
* @param {Uint8Array} [dst]
|
|
* Optional array to write the output to. If not specified, the array is created.
|
|
* @returns {Uint8Array}
|
|
* Array with the result of decoding
|
|
*
|
|
* @throws {Error}
|
|
* if there are characters in `message` not present in the encoding alphabet
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
|
|
function decode(message, dst) {
|
|
var realDst = dst || (0, _bitConverter.createBitArray)(message.length);
|
|
|
|
for (var i = 0; i < message.length; i += 1) {
|
|
var idx = CHAR_LOOKUP.get(message[i]);
|
|
|
|
if (idx === undefined) {
|
|
throw new Error("Invalid char in message: ".concat(message[i]));
|
|
}
|
|
|
|
realDst[i] = idx;
|
|
}
|
|
|
|
return realDst;
|
|
}
|
|
/**
|
|
* Decodes a string and a human-readable prefix into an array of 5-bit groups.
|
|
* The prefix is expanded as specified by Bech32.
|
|
*
|
|
* @param {string} prefix
|
|
* @param {string} message
|
|
* @returns {Uint8Array}
|
|
* Array with the result of decoding
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
|
|
function decodeWithPrefix(prefix, message) {
|
|
var len = message.length + 2 * prefix.length + 1;
|
|
var dst = (0, _bitConverter.createBitArray)(len);
|
|
expandPrefix(prefix, dst.subarray(0, 2 * prefix.length + 1));
|
|
decode(message, dst.subarray(2 * prefix.length + 1));
|
|
return dst;
|
|
}
|
|
/**
|
|
* Detects the character case used in `message`. If the message doesn't
|
|
* contain either lower-case or upper-case chars, returns `null`.
|
|
*
|
|
* @param {string} message
|
|
* @param {string} messageDescription
|
|
* Human-readable description of the message to put into an error message, should any occur
|
|
* @returns {'lower'|'upper'|null}
|
|
* @throws if the message contains both lowercase and uppercase chars,
|
|
* or contains chars not valid for Bech32(m) encoding
|
|
*/
|
|
|
|
|
|
function detectCase(message) {
|
|
var messageDescription = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'message';
|
|
var hasLowerCase = false;
|
|
var hasUpperCase = false;
|
|
|
|
for (var i = 0; i < message.length; i += 1) {
|
|
var ord = message.charCodeAt(i); // 3. Allowed chars in the encoding
|
|
|
|
if (ord < MIN_CHAR_CODE || ord > MAX_CHAR_CODE) {
|
|
throw new TypeError("Invalid char in ".concat(messageDescription, ": ").concat(ord, "; ") + "should be in ASCII range ".concat(MIN_CHAR_CODE, "-").concat(MAX_CHAR_CODE));
|
|
}
|
|
|
|
hasUpperCase = hasUpperCase || ord >= 65 && ord <= 90;
|
|
hasLowerCase = hasLowerCase || ord >= 97 && ord <= 122;
|
|
}
|
|
|
|
if (hasLowerCase && hasUpperCase) {
|
|
throw new TypeError("Mixed-case ".concat(messageDescription));
|
|
} else if (hasUpperCase) {
|
|
return 'upper';
|
|
} else if (hasLowerCase) {
|
|
return 'lower';
|
|
} else {
|
|
return null;
|
|
}
|
|
} |