Files
grin-web-wallet/scripts/base58.js
2024-12-20 18:08:44 -08:00

267 lines
6.7 KiB
JavaScript
Executable File

// Use strict
"use strict";
// Classes
// Base58 class
class Base58 {
// Public
// Encode
static encode(byteArray) {
// Go through all leading zeros in the byte array
var numberOfLeadingZeros = 0;
while(numberOfLeadingZeros < byteArray["length"] && byteArray[numberOfLeadingZeros] === 0) {
// Increment number of leading zeros
++numberOfLeadingZeros;
}
// Get buffer size
var bufferSize = Math.ceil((byteArray["length"] - numberOfLeadingZeros) * Base58.BYTES_PER_LENGTH_IN_NEW_BASE);
// Create buffer
var buffer = (new Uint8Array(bufferSize)).fill(0);
// Go through all bytes in the byte array after the leading zeros
var length = 0;
for(var i = numberOfLeadingZeros; i < byteArray["length"]; ++i) {
// Get byte
var byte = byteArray[i];
// Go through all base 58 components of the byte
for(var j = 0, k = bufferSize - 1; (byte !== 0 || j < length) && k >= 0; ++j, --k) {
// Include the current buffer value in the byte
byte += (Common.BYTE_MAX_VALUE + 1) * buffer[k];
// Set value in the buffer
buffer[k] = byte % Base58.NUMBER_BASE;
// Update the byte
byte = Math.floor(byte / Base58.NUMBER_BASE);
}
// Update length
length = j;
}
// Go through all leading zeros in the buffer
var bufferIndex = bufferSize - length;
while(bufferIndex < buffer["length"] && buffer[bufferIndex] === 0) {
// Increment buffer index
++bufferIndex;
}
// Set result to start with the number of leading zeros in base58
var result = Base58.CHARACTERS[0].repeat(numberOfLeadingZeros);
// Go through all bytes in the buffer after the leading zeros
for(; bufferIndex < buffer["length"]; ++bufferIndex) {
// Append buffer's value in base58 to the result
result += Base58.CHARACTERS[buffer[bufferIndex]];
}
// Return result
return result;
}
// Encode with checksum
static encodeWithChecksum(byteArray) {
// Get the checksum of the byte array
var checksum = Base58.getChecksum(byteArray);
// Return encoding the byte array with the checksum
return Base58.encode(Common.mergeArrays([
// Byte array
byteArray,
// Checksum
checksum
]));
}
// Decode
static decode(base58String) {
// Go through all leading zeros in base58 in the string
var numberOfLeadingZeros = 0;
while(numberOfLeadingZeros < base58String["length"] && base58String[numberOfLeadingZeros] === Base58.CHARACTERS[0]) {
// Increment number of leading zeros
++numberOfLeadingZeros;
}
// Get buffer size
var bufferSize = Math.ceil((base58String["length"] - numberOfLeadingZeros) * Base58.BYTES_PER_LENGTH_IN_OLD_BASE);
// Crete buffer
var buffer = (new Uint8Array(bufferSize)).fill(0);
// Go through all characters in the string after the leading zeros in base58
var length = 0;
for(var i = numberOfLeadingZeros; i < base58String["length"]; ++i) {
// Get character as byte
var byte = Base58.CHARACTERS.indexOf(base58String[i]);
// Check if byte is invalid
if(byte === Common.INDEX_NOT_FOUND) {
// Throw error
throw "Invalid base58 string.";
}
// Go through all base 58 components of the byte
for(var j = 0, k = bufferSize - 1; (byte !== 0 || j < length) && k >= 0; ++j, --k) {
// Include the current buffer value in the byte
byte += Base58.NUMBER_BASE * buffer[k];
// Set value in the buffer
buffer[k] = byte % (Common.BYTE_MAX_VALUE + 1);
// Update the byte
byte = Math.floor(byte / (Common.BYTE_MAX_VALUE + 1));
}
// Update length
length = j;
}
// Go through all leading zeros in the buffer
var bufferIndex = bufferSize - length;
while(bufferIndex < buffer["length"] && buffer[bufferIndex] === 0) {
// Increment buffer index
++bufferIndex;
}
// Set result to start with the number of leading zeros
var result = (new Uint8Array(numberOfLeadingZeros + bufferSize - bufferIndex)).fill(0);
// Go through all bytes in the buffer after the leading zeros
for(var i = 0; bufferIndex < buffer["length"]; ++i, ++bufferIndex) {
// Append buffer's value to the result
result[i + numberOfLeadingZeros] += buffer[bufferIndex];
}
// Return result
return result;
}
// Decode with checksum
static decodeWithChecksum(base58String) {
// Try
try {
// Decode the string
var byteArray = Base58.decode(base58String);
}
// Catch errors
catch(error) {
// Throw error
throw error;
}
// Check if the byte array doesn't include a checksum
if(byteArray["length"] < Base58.CHECKSUM_LENGTH) {
// Throw error
throw "No checksum exists.";
}
// Otherwise
else {
// Get the checksum of the byte array without its checksum
var checksum = Base58.getChecksum(byteArray.subarray(0, byteArray["length"] - Base58.CHECKSUM_LENGTH));
// Get the provided checksum from the byte array
var providedChecksum = byteArray.subarray(byteArray["length"] - Base58.CHECKSUM_LENGTH);
// Check if checksums don't match
if(Common.arraysAreEqual(checksum, providedChecksum) === false) {
// Throw error
throw "Invalid checksum.";
}
// Otherwise
else {
// Return byte array without the checksum
return byteArray.subarray(0, byteArray["length"] - Base58.CHECKSUM_LENGTH);
}
}
}
// Private
// Get checksum
static getChecksum(byteArray) {
// Get a hash of the hash of the byte array
var hash = new Uint8Array(sha256.arrayBuffer(new Uint8Array(sha256.arrayBuffer(byteArray))));
// Get the checksum from the hash
var checksum = hash.subarray(0, Base58.CHECKSUM_LENGTH);
// Return checksum
return checksum;
}
// Characters
static get CHARACTERS() {
// Return characters
return "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
}
// Number base
static get NUMBER_BASE() {
// Return number base
return 58;
}
// Bytes per length in new base
static get BYTES_PER_LENGTH_IN_NEW_BASE() {
// Return bytes per length in new base
return Math.log(Common.BYTE_MAX_VALUE + 1) / Math.log(Base58.NUMBER_BASE);
}
// Bytes per length in old base
static get BYTES_PER_LENGTH_IN_OLD_BASE() {
// Return bytes per length in old base
return Math.log(Base58.NUMBER_BASE) / Math.log(Common.BYTE_MAX_VALUE + 1);
}
// Checksum length
static get CHECKSUM_LENGTH() {
// Return checksum length
return 4;
}
}
// Main function
// Set global object's base58
globalThis["Base58"] = Base58;