diff --git a/web/ecashaddrjs/src/base32.js b/web/ecashaddrjs/src/base32.js index 09af56b63..43392e11e 100644 --- a/web/ecashaddrjs/src/base32.js +++ b/web/ecashaddrjs/src/base32.js @@ -1,112 +1,112 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ 'use strict'; var validate = require('./validation').validate; /** * Base32 encoding and decoding. * * @module base32 */ /** * Charset containing the 32 symbols used in the base32 encoding. * @private */ var CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; /** * Inverted index mapping each symbol into its index within the charset. * @private */ var CHARSET_INVERSE_INDEX = { q: 0, p: 1, z: 2, r: 3, y: 4, 9: 5, x: 6, 8: 7, g: 8, f: 9, 2: 10, t: 11, v: 12, d: 13, w: 14, 0: 15, s: 16, 3: 17, j: 18, n: 19, 5: 20, 4: 21, k: 22, h: 23, c: 24, e: 25, 6: 26, m: 27, u: 28, a: 29, 7: 30, l: 31, }; /** * Encodes the given array of 5-bit integers as a base32-encoded string. * * @static * @param {Uint8Array} data Array of integers between 0 and 31 inclusive. * @returns {string} * @throws {ValidationError} */ function encode(data) { validate(data instanceof Uint8Array, 'Invalid data: ' + data + '.'); var base32 = ''; for (var i = 0; i < data.length; ++i) { var value = data[i]; validate(0 <= value && value < 32, 'Invalid value: ' + value + '.'); base32 += CHARSET[value]; } return base32; } /** * Decodes the given base32-encoded string into an array of 5-bit integers. * * @static * @param {string} string * @returns {Uint8Array} * @throws {ValidationError} */ function decode(string) { validate( typeof string === 'string', 'Invalid base32-encoded string: ' + string + '.', ); var data = new Uint8Array(string.length); for (var i = 0; i < string.length; ++i) { var value = string[i]; validate( value in CHARSET_INVERSE_INDEX, 'Invalid value: ' + value + '.', ); data[i] = CHARSET_INVERSE_INDEX[value]; } return data; } module.exports = { encode: encode, decode: decode, }; diff --git a/web/ecashaddrjs/src/cashaddr.js b/web/ecashaddrjs/src/cashaddr.js index 5fe852ff4..a2233d15c 100644 --- a/web/ecashaddrjs/src/cashaddr.js +++ b/web/ecashaddrjs/src/cashaddr.js @@ -1,393 +1,393 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ 'use strict'; var base32 = require('./base32'); var bigInt = require('big-integer'); var convertBits = require('./convertBits'); var validation = require('./validation'); var validate = validation.validate; /** * Encoding and decoding of the new Cash Address format for eCash.
* Compliant with the original cashaddr specification: * {@link https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md} * @module cashaddr */ /** * Encodes a hash from a given type into an eCash address with the given prefix. * * @static * @param {string} prefix Cash address prefix. E.g.: 'ecash'. * @param {string} type Type of address to generate. Either 'P2PKH' or 'P2SH'. * @param {Uint8Array} hash Hash to encode represented as an array of 8-bit integers. * @returns {string} * @throws {ValidationError} */ function encode(prefix, type, hash) { validate( typeof prefix === 'string' && isValidPrefix(prefix), 'Invalid prefix: ' + prefix + '.', ); validate(typeof type === 'string', 'Invalid type: ' + type + '.'); validate(hash instanceof Uint8Array, 'Invalid hash: ' + hash + '.'); var prefixData = concat(prefixToUint5Array(prefix), new Uint8Array(1)); var versionByte = getTypeBits(type) + getHashSizeBits(hash); var payloadData = toUint5Array(concat(new Uint8Array([versionByte]), hash)); var checksumData = concat( concat(prefixData, payloadData), new Uint8Array(8), ); var payload = concat( payloadData, checksumToUint5Array(polymod(checksumData)), ); return prefix + ':' + base32.encode(payload); } /** * Decodes the given address into its constituting prefix, type and hash. See [#encode()]{@link encode}. * * @static * @param {string} address Address to decode. E.g.: 'ecash:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2'. * @returns {object} * @throws {ValidationError} */ function decode(address) { validate( typeof address === 'string' && hasSingleCase(address), 'Invalid address: ' + address + '.', ); var pieces = address.toLowerCase().split(':'); // if there is no prefix, it might still be valid let prefix, payload; if (pieces.length === 1) { // Check and see if it has a valid checksum for accepted prefixes let hasValidChecksum = false; for (let i = 0; i < VALID_PREFIXES.length; i += 1) { const testedPrefix = VALID_PREFIXES[i]; const prefixlessPayload = base32.decode(pieces[0]); hasValidChecksum = validChecksum(testedPrefix, prefixlessPayload); if (hasValidChecksum) { // Here's your prefix prefix = testedPrefix; payload = prefixlessPayload; // Stop testing other prefixes break; } } validate( hasValidChecksum, `Prefixless address ${address} does not have valid checksum for any valid prefix (${VALID_PREFIXES.join( ', ', )})`, ); } else { validate(pieces.length === 2, 'Invalid address: ' + address + '.'); prefix = pieces[0]; payload = base32.decode(pieces[1]); validate( validChecksum(prefix, payload), 'Invalid checksum: ' + address + '.', ); } var payloadData = fromUint5Array(payload.subarray(0, -8)); var versionByte = payloadData[0]; var hash = payloadData.subarray(1); validate( getHashSize(versionByte) === hash.length * 8, 'Invalid hash size: ' + address + '.', ); var type = getType(versionByte); return { prefix: prefix, type: type, hash: hash, }; } /** * Error thrown when encoding or decoding fail due to invalid input. * * @constructor ValidationError * @param {string} message Error description. */ var ValidationError = validation.ValidationError; /** * Valid address prefixes. * * @private */ var VALID_PREFIXES = [ 'ecash', 'bitcoincash', 'simpleledger', 'etoken', 'ectest', 'bchtest', 'bchreg', ]; /** * Checks whether a string is a valid prefix; ie., it has a single letter case * and is one of 'ecash', 'ectest', 'etoken', etc * * @private * @param {string} prefix * @returns {boolean} */ function isValidPrefix(prefix) { return ( hasSingleCase(prefix) && VALID_PREFIXES.indexOf(prefix.toLowerCase()) !== -1 ); } /** * Derives an array from the given prefix to be used in the computation * of the address' checksum. * * @private * @param {string} prefix Cash address prefix. E.g.: 'ecash'. * @returns {Uint8Array} */ function prefixToUint5Array(prefix) { var result = new Uint8Array(prefix.length); for (var i = 0; i < prefix.length; ++i) { result[i] = prefix[i].charCodeAt(0) & 31; } return result; } /** * Returns an array representation of the given checksum to be encoded * within the address' payload. * * @private * @param {BigInteger} checksum Computed checksum. * @returns {Uint8Array} */ function checksumToUint5Array(checksum) { var result = new Uint8Array(8); for (var i = 0; i < 8; ++i) { result[7 - i] = checksum.and(31).toJSNumber(); checksum = checksum.shiftRight(5); } return result; } /** * Returns the bit representation of the given type within the version * byte. * * @private * @param {string} type Address type. Either 'P2PKH' or 'P2SH'. * @returns {number} * @throws {ValidationError} */ function getTypeBits(type) { switch (type) { case 'P2PKH': return 0; case 'P2SH': return 8; default: throw new ValidationError('Invalid type: ' + type + '.'); } } /** * Retrieves the address type from its bit representation within the * version byte. * * @private * @param {number} versionByte * @returns {string} * @throws {ValidationError} */ function getType(versionByte) { switch (versionByte & 120) { case 0: return 'P2PKH'; case 8: return 'P2SH'; default: throw new ValidationError( 'Invalid address type in version byte: ' + versionByte + '.', ); } } /** * Returns the bit representation of the length in bits of the given * hash within the version byte. * * @private * @param {Uint8Array} hash Hash to encode represented as an array of 8-bit integers. * @returns {number} * @throws {ValidationError} */ function getHashSizeBits(hash) { switch (hash.length * 8) { case 160: return 0; case 192: return 1; case 224: return 2; case 256: return 3; case 320: return 4; case 384: return 5; case 448: return 6; case 512: return 7; default: throw new ValidationError( 'Invalid hash size: ' + hash.length + '.', ); } } /** * Retrieves the the length in bits of the encoded hash from its bit * representation within the version byte. * * @private * @param {number} versionByte * @returns {number} */ function getHashSize(versionByte) { switch (versionByte & 7) { case 0: return 160; case 1: return 192; case 2: return 224; case 3: return 256; case 4: return 320; case 5: return 384; case 6: return 448; case 7: return 512; } } /** * Converts an array of 8-bit integers into an array of 5-bit integers, * right-padding with zeroes if necessary. * * @private * @param {Uint8Array} data * @returns {Uint8Array} */ function toUint5Array(data) { return convertBits(data, 8, 5); } /** * Converts an array of 5-bit integers back into an array of 8-bit integers, * removing extra zeroes left from padding if necessary. * Throws a {@link ValidationError} if input is not a zero-padded array of 8-bit integers. * * @private * @param {Uint8Array} data * @returns {Uint8Array} * @throws {ValidationError} */ function fromUint5Array(data) { return convertBits(data, 5, 8, true); } /** * Returns the concatenation a and b. * * @private * @param {Uint8Array} a * @param {Uint8Array} b * @returns {Uint8Array} * @throws {ValidationError} */ function concat(a, b) { var ab = new Uint8Array(a.length + b.length); ab.set(a); ab.set(b, a.length); return ab; } /** * Computes a checksum from the given input data as specified for the CashAddr * format: https://github.com/Bitcoin-UAHF/spec/blob/master/cashaddr.md. * * @private * @param {Uint8Array} data Array of 5-bit integers over which the checksum is to be computed. * @returns {BigInteger} */ function polymod(data) { var GENERATOR = [ 0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470, ]; var checksum = bigInt(1); for (var i = 0; i < data.length; ++i) { var value = data[i]; var topBits = checksum.shiftRight(35); checksum = checksum.and(0x07ffffffff).shiftLeft(5).xor(value); for (var j = 0; j < GENERATOR.length; ++j) { if (topBits.shiftRight(j).and(1).equals(1)) { checksum = checksum.xor(GENERATOR[j]); } } } return checksum.xor(1); } /** * Verify that the payload has not been corrupted by checking that the * checksum is valid. * * @private * @param {string} prefix Cash address prefix. E.g.: 'ecash'. * @param {Uint8Array} payload Array of 5-bit integers containing the address' payload. * @returns {boolean} */ function validChecksum(prefix, payload) { var prefixData = concat(prefixToUint5Array(prefix), new Uint8Array(1)); var checksumData = concat(prefixData, payload); return polymod(checksumData).equals(0); } /** * Returns true if, and only if, the given string contains either uppercase * or lowercase letters, but not both. * * @private * @param {string} string Input string. * @returns {boolean} */ function hasSingleCase(string) { return string === string.toLowerCase() || string === string.toUpperCase(); } module.exports = { encode: encode, decode: decode, ValidationError: ValidationError, }; diff --git a/web/ecashaddrjs/src/validation.js b/web/ecashaddrjs/src/validation.js index 782191294..c7665d720 100644 --- a/web/ecashaddrjs/src/validation.js +++ b/web/ecashaddrjs/src/validation.js @@ -1,50 +1,50 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ 'use strict'; /** * Validation utility. * * @module validation */ /** * Error thrown when encoding or decoding fail due to invalid input. * * @constructor ValidationError * @param {string} message Error description. */ function ValidationError(message) { var error = new Error(); this.name = error.name = 'ValidationError'; this.message = error.message = message; this.stack = error.stack; } ValidationError.prototype = Object.create(Error.prototype); /** * Validates a given condition, throwing a {@link ValidationError} if * the given condition does not hold. * * @static * @param {boolean} condition Condition to validate. * @param {string} message Error message in case the condition does not hold. */ function validate(condition, message) { if (!condition) { throw new ValidationError(message); } } module.exports = { ValidationError: ValidationError, validate: validate, }; diff --git a/web/ecashaddrjs/test/base32.js b/web/ecashaddrjs/test/base32.js index a7669f23f..cdfd9065d 100644 --- a/web/ecashaddrjs/test/base32.js +++ b/web/ecashaddrjs/test/base32.js @@ -1,73 +1,73 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ /* global describe it */ const { assert } = require('chai'); const { ValidationError } = require('../src/validation'); const base32 = require('../src/base32'); const { Random, MersenneTwister19937 } = require('random-js'); describe('base32', () => { const random = new Random(MersenneTwister19937.seed(42)); function getRandomData(size) { const data = new Uint8Array(size); for (let i = 0; i < size; ++i) { data[i] = random.integer(0, 31); } return data; } describe('#encode()', () => { it('should fail on invalid input', () => { const INVALID_INPUTS = [ undefined, 'some string', 1234.567, new Uint8Array([100, 2, 3, 4]), ]; for (const input of INVALID_INPUTS) { assert.throws(() => base32.encode(input), ValidationError); } }); it('should encode single digits correctly', () => { for (let i = 0; i < base32.CHARSET; ++i) { assert.equal(base32.CHARSET[i], base32.encode([i])); } }); }); describe('#decode()', () => { it('should fail on invalid input', () => { const INVALID_INPUTS = [undefined, 1234.567, [1, 2, 3, 4], 'b']; for (const input of INVALID_INPUTS) { assert.throws(() => base32.decode(input), ValidationError); } }); it('should decode single digits correctly', () => { for (let i = 0; i < base32.CHARSET; ++i) { assert.equal(i, base32.decode(base32.CHARSET[i])); } }); }); describe('#encode() #decode()', () => { it('should encode and decode random data correctly', () => { const NUM_TESTS = 2000; for (let i = 0; i < NUM_TESTS; ++i) { const data = getRandomData(1000); const x = base32.encode(data); assert.deepEqual(base32.decode(x), data); } }); }); }); diff --git a/web/ecashaddrjs/test/cashaddr.js b/web/ecashaddrjs/test/cashaddr.js index f5cb96629..4cf93e5f8 100644 --- a/web/ecashaddrjs/test/cashaddr.js +++ b/web/ecashaddrjs/test/cashaddr.js @@ -1,277 +1,277 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ /* global describe it */ const { assert } = require('chai'); const cashaddr = require('../src/cashaddr'); const { Random, MersenneTwister19937 } = require('random-js'); describe('cashaddr', () => { const NETWORKS = ['ecash', 'ectest', 'etoken']; const ADDRESS_TYPES = ['P2PKH', 'P2SH']; const VALID_SIZES = [20, 24, 28, 32, 40, 48, 56, 64]; const TEST_HASHES = [ new Uint8Array([ 118, 160, 64, 83, 189, 160, 168, 139, 218, 81, 119, 184, 106, 21, 195, 178, 159, 85, 152, 115, ]), new Uint8Array([ 203, 72, 18, 50, 41, 156, 213, 116, 49, 81, 172, 75, 45, 99, 174, 25, 142, 123, 176, 169, ]), new Uint8Array([ 1, 31, 40, 228, 115, 201, 95, 64, 19, 215, 213, 62, 197, 251, 195, 180, 45, 248, 237, 16, ]), ]; const EXPECTED_P2PKH_OUTPUTS = [ 'ecash:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2', 'ecash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4ykdcjcn6n', 'ecash:qqq3728yw0y47sqn6l2na30mcw6zm78dzq653y7pv5', ]; const EXPECTED_P2SH_OUTPUTS = [ 'ecash:ppm2qsznhks23z7629mms6s4cwef74vcwv2zrv3l8h', 'ecash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4ypg9alspw', 'ecash:pqq3728yw0y47sqn6l2na30mcw6zm78dzqd3vtezhf', ]; const EXPECTED_P2PKH_OUTPUTS_TESTNET = [ 'ectest:qpm2qsznhks23z7629mms6s4cwef74vcwvmvqr33lm', 'ectest:qr95sy3j9xwd2ap32xkykttr4cvcu7as4ysxxjl7ez', 'ectest:qqq3728yw0y47sqn6l2na30mcw6zm78dzqul0yev09', ]; const EXPECTED_P2SH_OUTPUTS_TESTNET = [ 'ectest:ppm2qsznhks23z7629mms6s4cwef74vcwvvfavkjyx', 'ectest:pr95sy3j9xwd2ap32xkykttr4cvcu7as4y8rmacazl', 'ectest:pqq3728yw0y47sqn6l2na30mcw6zm78dzqt6jt705c', ]; const random = new Random(MersenneTwister19937.seed(42)); function getRandomHash(size) { const hash = new Uint8Array(size); for (let i = 0; i < size; ++i) { hash[i] = random.integer(0, 255); } return hash; } describe('#encode()', () => { it('should fail on an invalid prefix', () => { assert.throws(() => { cashaddr.encode( 'some invalid prefix', ADDRESS_TYPES[0], new Uint8Array([]), ); }, cashaddr.ValidationError); }); it('should fail on a prefix with mixed letter case', () => { assert.throws(() => { cashaddr.encode('EcAsH', ADDRESS_TYPES[0], new Uint8Array([])); }, cashaddr.ValidationError); }); it('should fail on an invalid type', () => { assert.throws(() => { cashaddr.encode( NETWORKS[0], 'some invalid type', new Uint8Array([]), ); }, cashaddr.ValidationError); }); it('should fail on hashes of invalid length', () => { for (const size of VALID_SIZES) { const hash = getRandomHash(size - 1); assert.throws(() => { cashaddr.encode(NETWORKS[0], ADDRESS_TYPES[0], hash); }, cashaddr.ValidationError); } }); it('should encode test hashes on mainnet correctly', () => { for (const index in TEST_HASHES) { assert.equal( cashaddr.encode('ecash', 'P2PKH', TEST_HASHES[index]), EXPECTED_P2PKH_OUTPUTS[index], ); assert.equal( cashaddr.encode('ecash', 'P2SH', TEST_HASHES[index]), EXPECTED_P2SH_OUTPUTS[index], ); } }); it('should encode test hashes on testnet correctly', () => { for (const index in TEST_HASHES) { assert.equal( cashaddr.encode('ectest', 'P2PKH', TEST_HASHES[index]), EXPECTED_P2PKH_OUTPUTS_TESTNET[index], ); assert.equal( cashaddr.encode('ectest', 'P2SH', TEST_HASHES[index]), EXPECTED_P2SH_OUTPUTS_TESTNET[index], ); } }); }); describe('#decode()', () => { it('should fail when the version byte is invalid', () => { assert.throws(() => { cashaddr.decode( 'ecash:zpm2qsznhks23z7629mms6s4cwef74vcwv6ddac6re', ); }, cashaddr.ValidationError); }); it('should fail when given an address with mixed letter case', () => { assert.throws(() => { cashaddr.decode( 'ecash:QPM2QSZNHKS23Z7629MMS6s4cwef74vcwvA87RKUU2', ); }, cashaddr.ValidationError); assert.throws(() => { cashaddr.decode( 'eCASH:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2', ); }, cashaddr.ValidationError); assert.throws(() => { cashaddr.decode( 'Ecash:QPM2QSZNHKS23Z7629MMS6s4cwef74vcwvA87RKUU2', ); }, cashaddr.ValidationError); }); it('should decode a valid address regardless of letter case', () => { assert.deepEqual( cashaddr.decode( 'ecash:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2', ).hash, cashaddr.decode( 'ECASH:QPM2QSZNHKS23Z7629MMS6S4CWEF74VCWVA87RKUU2', ).hash, ); }); it('should accept prefixless input if checksum is valid', () => { assert.deepEqual( cashaddr.decode('qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2') .hash, cashaddr.decode( 'ecash:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2', ).hash, ); }); it('should reject prefixless input if checksum is invalid', () => { assert.throws(() => { cashaddr.decode( 'qpm2qsznhks23z7629mms6s4cwef74vcwvINVALIDCHECKSUM', ); }, cashaddr.ValidationError); }); it('should reject any input that has two prefixes for some reason', () => { assert.throws(() => { cashaddr.decode( 'ecash:bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwva87rkuu2', ); }, cashaddr.ValidationError); }); it('should fail when decoding for a different network', () => { for (const network of NETWORKS) { for (const anotherNetwork of NETWORKS) { if (network !== anotherNetwork) { const hash = getRandomHash(20); assert.throws(() => { const address = cashaddr.encode( network, ADDRESS_TYPES[0], hash, ); const invalidAddress = [ anotherNetwork, address.split(':')[1], ].join(':'); cashaddr.decode(invalidAddress); }, cashaddr.ValidationError); } } } }); }); describe('#encode() #decode()', () => { it('should encode and decode all sizes correctly', () => { for (const size of VALID_SIZES) { const hash = getRandomHash(size); const address = cashaddr.encode( NETWORKS[0], ADDRESS_TYPES[0], hash, ); const { prefix, type, hash: actualHash, } = cashaddr.decode(address); assert.equal(prefix, NETWORKS[0]); assert.equal(type, ADDRESS_TYPES[0]); assert.deepEqual(actualHash, hash); } }); it('should encode and decode all types and networks', () => { for (const type of ADDRESS_TYPES) { for (const network of NETWORKS) { const hash = getRandomHash(20); const address = cashaddr.encode(network, type, hash); const { prefix, type: actualType, hash: actualHash, } = cashaddr.decode(address); assert.equal(prefix, network); assert.equal(actualType, type); assert.deepEqual(actualHash, hash); } } }); it('should encode and decode many random hashes', () => { const NUM_TESTS = 1000; for (let i = 0; i < NUM_TESTS; ++i) { for (const type of ADDRESS_TYPES) { const hash = getRandomHash(20); const address = cashaddr.encode(NETWORKS[0], type, hash); const { prefix, type: actualType, hash: actualHash, } = cashaddr.decode(address); assert.equal(prefix, NETWORKS[0]); assert.equal(actualType, type); assert.deepEqual(actualHash, hash); } } }); }); }); diff --git a/web/ecashaddrjs/test/convertBits.js b/web/ecashaddrjs/test/convertBits.js index 524dc524b..81a8368e9 100644 --- a/web/ecashaddrjs/test/convertBits.js +++ b/web/ecashaddrjs/test/convertBits.js @@ -1,47 +1,47 @@ /** * @license * https://reviews.bitcoinabc.org - * Copyright (c) 2023 Bitcoin ABC * Copyright (c) 2017-2020 Emilio Almansi + * Copyright (c) 2023 Bitcoin ABC * Distributed under the MIT software license, see the accompanying * file LICENSE or http://www.opensource.org/licenses/mit-license.php. */ /* global describe it */ const { assert } = require('chai'); const { ValidationError } = require('../src/validation'); const convertBits = require('../src/convertBits'); const { Random, MersenneTwister19937 } = require('random-js'); describe('#convertBits()', () => { const random = new Random(MersenneTwister19937.seed(42)); function getRandomData(size, max) { const data = new Uint8Array(size); for (let i = 0; i < size; ++i) { data[i] = random.integer(0, max); } return data; } it('should fail if it receives an invalid value', () => { assert.throws(() => { convertBits([100], 5, 8); }, ValidationError); }); it('should fail in strict mode if padding is needed', () => { const data = getRandomData(10, 31); assert.throws(() => { convertBits(data, 5, 8, true); }, ValidationError); }); it('should convert both ways successfully', () => { const data1 = getRandomData(80, 31); assert.deepEqual(convertBits(convertBits(data1, 5, 8), 8, 5), data1); const data2 = getRandomData(80, 255); assert.deepEqual(convertBits(convertBits(data2, 8, 5), 5, 8), data2); }); });