vdk_rlp appears done

This commit is contained in:
2023-05-28 13:08:45 -06:00
parent 7b9df16196
commit 668fe623cd
7 changed files with 1 additions and 1962 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{type, library}. {type, library}.
{realm, local}. {realm, local}.
{name, vdk}. {name, vdk_rlp}.
{version, "0.1.0"}. {version, "0.1.0"}.
{deps, []}. {deps, []}.
-418
View File
@@ -1,418 +0,0 @@
/**
* Base58 encoding/decoding
*/
export {
encode,
decode
}
//=============================================================================
// ENCODING
//=============================================================================
/**
* Encode a Uint8Array into base58
*/
function
encode
(binary : Uint8Array)
: string
{
let num_leading_zeros : number = nlz(binary);
let rest : Uint8Array = binary.slice(num_leading_zeros);
let ones : string = encode_zeros(num_leading_zeros);
let rest_b58 : string = encode_rest(rest);
let result : string = ones + rest_b58;
return result;
}
/**
* count the number of leading zeros in a uint8array
*
* @internal
*/
function
nlz
(bytes: Uint8Array)
: number
{
let n = 0;
for (let this_byte of bytes)
{
if (0 === this_byte) { n++; }
else { break; }
}
return n;
}
/**
* Generate a bunch of '1's for however many leading zeros there are
*
* @internal
*/
function
encode_zeros
(how_many : number)
: string
{
let ones : string = '';
for (let i = 1;
i <= how_many;
i++)
{
ones += '1';
}
return ones;
}
/**
* Encode a Uint8Array that has no leading zeros
*
* @internal
*/
function
encode_rest
(bytes : Uint8Array)
: string
{
let bytes_bignum : bigint = bytes_to_bigint(bytes);
let result : string = bignum_to_base58(bytes_bignum);
return result;
}
/**
* Convert a bytestring to a bignum
*
* @internal
*/
function
bytes_to_bigint
(bytes: Uint8Array)
: bigint
{
let acc_bigint : bigint = 0n;
for(let this_byte of bytes)
{
acc_bigint <<= 8n;
acc_bigint += BigInt(this_byte);
}
return acc_bigint;
}
/**
* Convert a BigInt to Base58
*
* @internal
*/
function
bignum_to_base58
(q: bigint)
: string
{
let s = '';
while (q !== 0n)
{
let this_n : bigint = q % 58n;
q /= 58n;
let this_b58_char : string = bigint_to_char(this_n);
s = this_b58_char + s;
}
return s;
}
//=============================================================================
// DECODING
//=============================================================================
/**
* Decode a Base58 string into a Uint8Array
*/
function
decode
(base58: string)
: Uint8Array
{
let num_leading_ones : number = nlo(base58);
let rest : string = base58.slice(num_leading_ones);
let zeros : Array<number> = decode_ones(num_leading_ones);
let rest_arr : Array<number> = decode_rest(rest);
let pre_result : Array<number> = zeros.concat(rest_arr);
return new Uint8Array(pre_result);
}
/**
* count the number of leading 1 characters in a uint8array
*
* @internal
*/
function
nlo
(base58: string)
: number
{
let n = 0;
for (let this_char of base58)
{
if ('1' === this_char) { n++; }
else { break; }
}
return n;
}
/**
* Generate a bunch of '0's for however many leading ones there are
*
* @internal
*/
function
decode_ones
(how_many : number)
: Array<number>
{
let zeros : Array<number> = [];
for (let i = 1;
i <= how_many;
i++)
{
zeros.push(0);
}
return zeros;
}
/**
* Decode a string that has no leading 1s
*
* @internal
*/
function
decode_rest
(base58: string)
: Array<number>
{
let result_bignum : bigint = base58_to_bigint(base58);
let result : Array<number> = bigint_to_base256(result_bignum);
return result;
}
/**
* Convert a base58 string to a bignum
*
* @internal
*/
function
base58_to_bigint
(base58: string)
: bigint
{
let acc_bigint : bigint = 0n;
for(let this_char of base58)
{
acc_bigint *= 58n;
acc_bigint += char_to_bigint(this_char);
}
return acc_bigint;
}
/**
* convert a bignum into a byte array
*
* @end
*/
function
bigint_to_base256
(q: bigint)
: Array<number>
{
let arr_reverse = [];
while(q !== 0n)
{
let r: number = Number(q % 256n);
q /= 256n;
arr_reverse.push(r);
}
arr_reverse.reverse();
return arr_reverse;
}
//=============================================================================
// TRANSLATION TABLES
//=============================================================================
/**
* Base58 integer -> character conversion table
*
* @internal
*/
function
bigint_to_char
(n: bigint)
: string
{
switch(n) {
case 0n: return '1';
case 1n: return '2';
case 2n: return '3';
case 3n: return '4';
case 4n: return '5';
case 5n: return '6';
case 6n: return '7';
case 7n: return '8';
case 8n: return '9';
case 9n: return 'A';
case 10n: return 'B';
case 11n: return 'C';
case 12n: return 'D';
case 13n: return 'E';
case 14n: return 'F';
case 15n: return 'G';
case 16n: return 'H';
case 17n: return 'J';
case 18n: return 'K';
case 19n: return 'L';
case 20n: return 'M';
case 21n: return 'N';
case 22n: return 'P';
case 23n: return 'Q';
case 24n: return 'R';
case 25n: return 'S';
case 26n: return 'T';
case 27n: return 'U';
case 28n: return 'V';
case 29n: return 'W';
case 30n: return 'X';
case 31n: return 'Y';
case 32n: return 'Z';
case 33n: return 'a';
case 34n: return 'b';
case 35n: return 'c';
case 36n: return 'd';
case 37n: return 'e';
case 38n: return 'f';
case 39n: return 'g';
case 40n: return 'h';
case 41n: return 'i';
case 42n: return 'j';
case 43n: return 'k';
case 44n: return 'm';
case 45n: return 'n';
case 46n: return 'o';
case 47n: return 'p';
case 48n: return 'q';
case 49n: return 'r';
case 50n: return 's';
case 51n: return 't';
case 52n: return 'u';
case 53n: return 'v';
case 54n: return 'w';
case 55n: return 'x';
case 56n: return 'y';
case 57n: return 'z';
default:
throw new Error('invalid base58 bigint: ' + n);
}
}
/**
* Base58 character -> integer conversion table
*
* @internal
*/
function
char_to_bigint
(s: string)
: bigint
{
switch(s) {
case '1': return 0n;
case '2': return 1n;
case '3': return 2n;
case '4': return 3n;
case '5': return 4n;
case '6': return 5n;
case '7': return 6n;
case '8': return 7n;
case '9': return 8n;
case 'A': return 9n;
case 'B': return 10n;
case 'C': return 11n;
case 'D': return 12n;
case 'E': return 13n;
case 'F': return 14n;
case 'G': return 15n;
case 'H': return 16n;
case 'J': return 17n;
case 'K': return 18n;
case 'L': return 19n;
case 'M': return 20n;
case 'N': return 21n;
case 'P': return 22n;
case 'Q': return 23n;
case 'R': return 24n;
case 'S': return 25n;
case 'T': return 26n;
case 'U': return 27n;
case 'V': return 28n;
case 'W': return 29n;
case 'X': return 30n;
case 'Y': return 31n;
case 'Z': return 32n;
case 'a': return 33n;
case 'b': return 34n;
case 'c': return 35n;
case 'd': return 36n;
case 'e': return 37n;
case 'f': return 38n;
case 'g': return 39n;
case 'h': return 40n;
case 'i': return 41n;
case 'j': return 42n;
case 'k': return 43n;
case 'm': return 44n;
case 'n': return 45n;
case 'o': return 46n;
case 'p': return 47n;
case 'q': return 48n;
case 'r': return 49n;
case 's': return 50n;
case 't': return 51n;
case 'u': return 52n;
case 'v': return 53n;
case 'w': return 54n;
case 'x': return 55n;
case 'y': return 56n;
case 'z': return 57n;
default:
throw new Error('invalid base58 char: ' + s);
}
}
-655
View File
@@ -1,655 +0,0 @@
/**
* Base64 Utility Functions in TypeScript
*/
export {
encode,
decode
}
/**
* Encode an array of bytes as a Uint8Array in base64 notation.
*/
function
encode
(bytes: Uint8Array)
: string
{
// slice the array
// length of head is a multiple of 3
// treat the tail as a special case
let {head, tail, tail_len} = slice3k(bytes);
let head_str : string = encode_head(head);
let tail_str : string = encode_tail(tail, tail_len);
return head_str + tail_str;
}
type slice3k
= {head : Uint8Array,
tail : Uint8Array,
tail_len : number};
/**
* Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and
* the remaining 0,1, or 2 bytes, put them in tail
*
* @internal
*/
function
slice3k
(bytes: Uint8Array)
: slice3k
{
let len : number = bytes.length;
// too lazy to look up how to do integer division in js so this will do
let tail_len : number = len % 3;
let head_len : number = len - tail_len;
// for slice:
// first argument is the 0-index of the start
// second - first is the length of the slice
let head : Uint8Array = bytes.slice(0, head_len);
// empty second argument means go to the end
let tail : Uint8Array = bytes.slice(head_len);
return {head : head,
tail : tail,
tail_len : tail_len};
}
/**
* Encode a Uint8Array whose length is known to be a multiple of 3
*
* @internal
*/
function
encode_head
(head_bytes: Uint8Array)
: string
{
// can assume length of bytes is a multiple of 3
// start index at 0
// increment by 3
let head_bytes_len : number = head_bytes.length;
let max_idx0 : number = head_bytes_len - 1;
let head_str_acc : string = '';
for(let this_3slice_start_idx0 = 0;
this_3slice_start_idx0 <= max_idx0;
this_3slice_start_idx0 += 3)
{
let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3);
let this_3slice_str : string = encode3(this_3slice_bytes);
head_str_acc += this_3slice_str;
}
return head_str_acc;
}
/**
* Encode a 3 bytes into base64 notation
*
* @internal
*/
function
encode3
(bytes: Uint8Array)
: string
{
let b0 : number = bytes[0];
let b1 : number = bytes[1];
let b2 : number = bytes[2];
// ABCDEFGH 12345678 abcdefgh
// b0 b1 b2
// ABCDEF GH1234 5678ab cdefgh
// n0 n1 n2 n3
let n0 : number = b0 >> 2;
// b0 = ABCDEFGH
// 4 = _____1__
// b0 % 4 = ______GH
// (b0 % 4) << 4 = __GH____
// b1 = 12345678
// b1 >> 4 = ____1234
// n1 = __GH1234
let n1 : number = ((b0 % 4) << 4) + (b1 >> 4);
// b1 = 12345678
// 16 = ___1____
// b1 % 16 = ____5678
// (b1 % 16) << 2 = __5678__
// b2 = abcdefgh
// b2 >> 6 = ______ab
// n2 = __5678ab
let n2 : number = ((b1 % 16) << 2) + (b2 >> 6);
// b2 = abcdefgh
// 64 = _1______
// n3 = __cdefgh
let n3 : number = b2 % 64;
// convert to chars
let s0 : string = int2char(n0);
let s1 : string = int2char(n1);
let s2 : string = int2char(n2);
let s3 : string = int2char(n3);
// retrvn
return s0 + s1 + s2 + s3;
}
/**
* Encode the final 0, 1, or 2 bytes
*
* @internal
*/
function
encode_tail
(tail_bytes : Uint8Array,
tail_len : number)
: string
{
switch(tail_len) {
case 0: return '';
case 1: return encode1(tail_bytes);
case 2: return encode2(tail_bytes);
default:
throw new Error('encode_tail with tail_len = ' + tail_len);
}
}
/**
* Encode a single byte
*
* @internal
*/
function
encode1
(bytes: Uint8Array)
: string
{
let b0 : number = bytes[0];
// n0 = __ABCDEF
// b0 = ABCDEFGH
// b0 >> 2 = __ABCDEF
let n0 : number = b0 >> 2;
// n1 = __GH____
// b0 = ABCDEFGH
// 4 = _____1__
// b0 % 4 = ______GH
// (b0 % 4) << 4 = __GH____
let n1 : number = (b0 % 4) << 4;
return int2char(n0) + int2char(n1) + '==';
}
/**
* Encode two bytes
*
* @internal
*/
function
encode2
(bytes: Uint8Array)
: string
{
let b0 : number = bytes[0];
let b1 : number = bytes[1];
// ABCDEFGH 12345678
// b0 b1
// ABCDEF GH1234 5678__
// n0 n1 n2
let n0 : number = b0 >> 2;
// b0 = ABCDEFGH
// 4 = _____1__
// b0 % 4 = ______GH
// (b0 % 4) << 4 = __GH____
// b1 = 12345678
// b1 >> 4 = ____1234
// n1 = __GH1234
let n1 : number = ((b0 % 4) << 4) + (b1 >> 4);
// b1 = 12345678
// 16 = ___1____
// b1 % 16 = ____5678
// (b1 % 16) << 2 = __5678__
// n2 = __5678__
let n2 : number = (b1 % 16) << 2;
// convert to chars
let s0 : string = int2char(n0);
let s1 : string = int2char(n1);
let s2 : string = int2char(n2);
// retrvn
return s0 + s1 + s2 + '=';
}
/**
* Decode a base64-encoded string
*/
function
decode
(base64_str : string)
: Uint8Array
{
// length of the string is guaranteed to be a multiple of 4
// if the string is empty, return the empty array
let len = base64_str.length;
// this branching contains the implicit assertion that the length is a
// multiple of 4. If this is not true, the bottom branch is triggered.
// general case goes first because speeeeeed
if ( (4 < len)
&& (0 === (len % 4)))
{
// split the head and tail
let tail_start_idx0 : number = len - 4;
let head_s : string = base64_str.slice(0, tail_start_idx0);
let tail_s : string = base64_str.slice(tail_start_idx0);
// Using arrays because Uint8Arrays don't have a concat operation
let head_arr : Array<number> = decode_head(head_s);
let tail_arr : Array<number> = decode_tail(tail_s);
// silly to put these in variables but this is exactly the type of
// situation where JS type insanity shows up
//
// see: i forgot
// > [1,2,3] + [4,5,6]
// '1,2,34,5,6'
//
// Originally, I used + like some sort of moron who codes in a sane
// language
//
// seriously what is this language
//
// this is some clown behavior
let total_arr : Array<number> = head_arr.concat(tail_arr);
return new Uint8Array(total_arr);
}
// special case if the length is exactly 4
else if (4 === len)
{
// it's just a tail
return new Uint8Array(decode_tail(base64_str));
}
// empty string
else if (0 === len)
{
return new Uint8Array([]);
}
else
{
throw new Error('base64 decode: invalid string length: ' + len);
}
}
/**
* Decode a string known to not have any padding
*
* @internal
*/
function
decode_head
(s: string)
: Array<number>
{
// go 4 characters at a time
let max_i0 : number = s.length - 1;
let decoded_acc : Array<number> = [];
for(let i0 = 0;
i0 <= max_i0;
i0 += 4)
{
let this_slice_s : string = s.slice(i0, i0 + 4);
let this_slice_arr : Array<number> = decode3(this_slice_s);
// update accumulator
decoded_acc = decoded_acc.concat(this_slice_arr);
}
return decoded_acc;
}
/**
* Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte
*
* @internal
*/
function
decode_tail
(s: string)
: Array<number>
{
// all that matters right now is the last 2 chars
// s0, s1, s2, s3
// 0 based indexing is so annoying
let s2 = s[2];
let s3 = s[3];
// braaaaaaaaaaaaaaaaaench
// two equals signs means 1 byte
if (('=' === s3) && ('=' === s2)) {
return decode1(s);
}
// one equals sign means 2 bytes
else if (('=' === s3)) {
return decode2(s);
}
// 0 equals signs means 3 bytes
else {
return decode3(s);
}
}
/**
* Decode a 4-character long base64 string corresponding to 3 bytes
*
* @internal
*/
function
decode3
(s: string)
: Array<number>
{
// pull out strings
let s0 : string = s[0];
let s1 : string = s[1];
let s2 : string = s[2];
let s3 : string = s[3];
// convert to numbers
let n0 : number = char2int(s0);
let n1 : number = char2int(s1);
let n2 : number = char2int(s2);
let n3 : number = char2int(s3);
// abcdef gh1234 5678ab cdefgh
// n0 n1 n2 n3
// abcdefgh 12345678 abcdefgh
// b0 b1 b2
// n0 = __abcdef
// n1 = __gh1234
// n0 << 2 = abcdef__
// n1 >> 4 = ______gh
// b0 = abcdefgh
let b0 : number = (n0 << 2) + (n1 >> 4);
// n1 = __gh1234
// 16 = ___1____
// n1 % 16 = ____1234
// (n1 % 16) << 4 = 1234____
// n2 = __5678ab
// n2 >> 2 = ____5678
// b1 = 12345678
let b1 : number = ((n1 % 16) << 4) + (n2 >> 2);
// n2 = __5678ab
// 4 = _____1__
// n2 % 4 = ______ab
// (n2 % 4) << 6 = ab______
// n3 = __cdefgh
let b2 : number = ((n2 % 4) << 6) + n3;
return [b0, b1, b2];
}
/**
* Decode a 4-character long base64 string corresponding to 2 bytes
*
* @internal
*/
function
decode2
(s: string)
: Array<number>
{
// xyz=
// pull out strings
let s0 : string = s[0];
let s1 : string = s[1];
let s2 : string = s[2];
// convert to numbers
let n0 : number = char2int(s0);
let n1 : number = char2int(s1);
let n2 : number = char2int(s2);
// abcdef gh1234 5678__
// n0 n1 n2
// abcdefgh 12345678
// b0 b1
// n0 = __abcdef
// n1 = __gh1234
// n0 << 2 = abcdef__
// n1 >> 4 = ______gh
// b0 = abcdefgh
let b0 : number = (n0 << 2) + (n1 >> 4);
// n1 = __gh1234
// 16 = ___1____
// n1 % 16 = ____1234
// (n1 % 16) << 4 = 1234____
// n2 = __5678__
// n2 >> 2 = ____5678
// b1 = 12345678
let b1 : number = ((n1 % 16) << 4) + (n2 >> 2);
return [b0, b1];
}
/**
* Decode a 4-character long base64 string corresponding to 2 bytes
*
* @internal
*/
function
decode1
(s: string)
: Array<number>
{
// xy==
// pull out strings
let s0 : string = s[0];
let s1 : string = s[1];
// convert to numbers
let n0 : number = char2int(s0);
let n1 : number = char2int(s1);
// abcdef gh____
// n0 n1
// abcdefgh
// b0
// n0 = __abcdef
// n1 = __gh____
// n0 << 2 = abcdef__
// n1 >> 4 = ______gh
// b0 = abcdefgh
let b0 : number = (n0 << 2) + (n1 >> 4);
return [b0];
}
// FIXME: these tables would *probably* be faster if they were made into objects
/**
* Conversion table for base64 encode
*
* @internal
*/
function
int2char
(n: number)
: string
{
switch(n) {
case 0: return 'A';
case 1: return 'B';
case 2: return 'C';
case 3: return 'D';
case 4: return 'E';
case 5: return 'F';
case 6: return 'G';
case 7: return 'H';
case 8: return 'I';
case 9: return 'J';
case 10: return 'K';
case 11: return 'L';
case 12: return 'M';
case 13: return 'N';
case 14: return 'O';
case 15: return 'P';
case 16: return 'Q';
case 17: return 'R';
case 18: return 'S';
case 19: return 'T';
case 20: return 'U';
case 21: return 'V';
case 22: return 'W';
case 23: return 'X';
case 24: return 'Y';
case 25: return 'Z';
case 26: return 'a';
case 27: return 'b';
case 28: return 'c';
case 29: return 'd';
case 30: return 'e';
case 31: return 'f';
case 32: return 'g';
case 33: return 'h';
case 34: return 'i';
case 35: return 'j';
case 36: return 'k';
case 37: return 'l';
case 38: return 'm';
case 39: return 'n';
case 40: return 'o';
case 41: return 'p';
case 42: return 'q';
case 43: return 'r';
case 44: return 's';
case 45: return 't';
case 46: return 'u';
case 47: return 'v';
case 48: return 'w';
case 49: return 'x';
case 50: return 'y';
case 51: return 'z';
case 52: return '0';
case 53: return '1';
case 54: return '2';
case 55: return '3';
case 56: return '4';
case 57: return '5';
case 58: return '6';
case 59: return '7';
case 60: return '8';
case 61: return '9';
case 62: return '+';
case 63: return '/';
default: throw new Error("invalid base64 encode byte: " + n);
}
}
/**
* Conversion table for base64 decode
*
* @internal
*/
function
char2int
(s: string)
: number
{
switch(s) {
case 'A': return 0;
case 'B': return 1;
case 'C': return 2;
case 'D': return 3;
case 'E': return 4;
case 'F': return 5;
case 'G': return 6;
case 'H': return 7;
case 'I': return 8;
case 'J': return 9;
case 'K': return 10;
case 'L': return 11;
case 'M': return 12;
case 'N': return 13;
case 'O': return 14;
case 'P': return 15;
case 'Q': return 16;
case 'R': return 17;
case 'S': return 18;
case 'T': return 19;
case 'U': return 20;
case 'V': return 21;
case 'W': return 22;
case 'X': return 23;
case 'Y': return 24;
case 'Z': return 25;
case 'a': return 26;
case 'b': return 27;
case 'c': return 28;
case 'd': return 29;
case 'e': return 30;
case 'f': return 31;
case 'g': return 32;
case 'h': return 33;
case 'i': return 34;
case 'j': return 35;
case 'k': return 36;
case 'l': return 37;
case 'm': return 38;
case 'n': return 39;
case 'o': return 40;
case 'p': return 41;
case 'q': return 42;
case 'r': return 43;
case 's': return 44;
case 't': return 45;
case 'u': return 46;
case 'v': return 47;
case 'w': return 48;
case 'x': return 49;
case 'y': return 50;
case 'z': return 51;
case '0': return 52;
case '1': return 53;
case '2': return 54;
case '3': return 55;
case '4': return 56;
case '5': return 57;
case '6': return 58;
case '7': return 59;
case '8': return 60;
case '9': return 61;
case '+': return 62;
case '/': return 63;
default: throw new Error("invalid base64 character: " + s);
}
}
-377
View File
@@ -1,377 +0,0 @@
/**
* Miscellaneous binary utility functions
*
* @module
*/
export {
bytes_to_bigint,
bigint_to_bytes,
concat,
strong_rand_bytes
};
/**
* Concatenate two arrays
*/
function
concat
(arr1 : Uint8Array,
arr2 : Uint8Array)
: Uint8Array
{
let len1 : number = arr1.length;
let len2 : number = arr2.length;
let arr1_idx0_offset : number = 0;
let arr2_idx0_offset : number = len1;
let result_len : number = len1 + len2;
let result : Uint8Array = new Uint8Array(result_len);
// copy first array into result
for (let arr1_idx0 = 0;
arr1_idx0 < len1;
arr1_idx0++)
{
// no offset here
let result_idx0 : number = arr1_idx0 + arr1_idx0_offset;
result[result_idx0] = arr1[arr1_idx0];
}
// copy second array into result
for (let arr2_idx0 = 0;
arr2_idx0 < len2;
arr2_idx0++)
{
// offset by the length of the first array
let result_idx0 : number = arr2_idx0 + arr2_idx0_offset;
result[result_idx0] = arr2[arr2_idx0];
}
return result;
}
/**
* Cryptographically random bytes
*/
function
strong_rand_bytes
(how_many : number)
: Uint8Array
{
let arr = new Uint8Array(how_many);
(new Crypto()).getRandomValues(arr);
return arr;
}
/**
* Convert a byte array to a bigint
*
* Equivalent to `binary:decode_unsigned/1` from Erlang
*/
function
bytes_to_bigint
(bytes: Uint8Array)
: bigint
{
let n : bigint = 0n;
for (let b of bytes) {
// move first, then add
// otherwise it ends on a move
// imperative languages are for losers
n <<= 8n;
n += BigInt(b);
}
return n;
}
/**
* Convert a bigint to a byte array
*
* Equivalent to `binary:encode_unsigned/1` from Erlang
*
* Requires input to be positive
*/
function
bigint_to_bytes
(q: bigint)
: Uint8Array
{
if (q < 0n) {
throw new Error('q < 0n: ' + q);
}
let arr_reverse = [];
while (q > 0n) {
let r = Number(q % 256n);
q /= 256n;
arr_reverse.push(r);
}
arr_reverse.reverse();
return new Uint8Array(arr_reverse);
}
/**
* Oh no, bitstrings in a language that only has bytestrings
*
* By convention these are `Uint8Array`s with byte length `ceil(bit_length /
* 8)`, and all trailing bits are zero.
*/
type bits =
{bit_length : number,
bytes : Uint8Array};
/**
* Get an uninitialized bitstring
*
* @internal
*/
function
bits_null
(bit_length : number)
: bits
{
let byte_length : number = Math.ceil(bit_length / 8);
let result : Uint8Array = new Uint8Array(byte_length);
return {bit_length : bit_length,
bytes : result};
}
/**
* Get a bitstring of a given length where every value is 0.
*/
function
bits_zeros
(bit_length : number)
: bits
{
let byte_length : number = Math.ceil(bit_length / 8);
let result : Uint8Array = new Uint8Array(byte_length);
for (let i0 = 0;
i0 < byte_length;
i0++)
{
result[i0] = 0;
}
return {bit_length : bit_length,
bytes : result};
}
/**
* Get a bitstring of a given length where every value is 1.
*/
function
bits_ones
(bit_length : number)
: bits
{
let byte_length : number = Math.ceil(bit_length / 8);
let result : Uint8Array = new Uint8Array(byte_length);
// fill everything except the last byte with 255s
for (let i0 = 0;
i0 < (byte_length - 1);
i0++)
{
result[i0] = 255;
}
// alright so the last byte
// ok so the number of leading 0s is
// 8 - (bit_length % 8)
let num_trailing_zero_bits : number = 8 - (bit_length % 8);
// the trailing byte is 255 << that
// e.g. 3 trailing 0s
// 1111_1111 -> 1111_1000
let last_byte : number = 255 << num_trailing_zero_bits;
let last_byte_idx0 : number = byte_length - 1;
result[last_byte_idx0] = last_byte;
return {bit_length : bit_length,
bytes : result};
}
/**
* Get the bit at a given 0-index
*/
function
bits_i0th
(bit_idx0 : number,
bits : bits)
: number
{
// first task is figuring out what byte we're at
// for instance if we want bit 27
// 3*8 = 24 =< 27 < 4*8
// so it's Math.floor(bit_idx0 / 8)
let byte_idx0 : number = Math.floor(bit_idx0 / 8);
// let's fetch the byte and work with that
let the_byte : number = bits.bytes[byte_idx0];
// ok so let's go with 27 again
// 27 = 3 mod 8
// so we bitshift right by (8 - 3)
// and then take the remainder dividing by 2
// --B-_---- -> ----_---B -> 0000_000B
let bsr : number = 8 - (bit_idx0 % 8);
return (the_byte >> bsr) % 2;
}
/**
* Concatenate two bitstrings
*/
function
bits_concat
(bits1 : bits,
bits2 : bits)
: bits
{
let result_bit_length : number = bits1.bit_length + bits2.bit_length;
let bytes1 : Uint8Array = bits1.bytes;
let bytes2 : Uint8Array = bits2.bytes;
// using zeros here because of our xor trick in a minute
let result_bits : bits = bits_null(result_bit_length);
let result_bytes : Uint8Array = result_bits.bytes;
// go along each byte in result, and compute the byte boundary
for (let i = 0;
i < result_bytes.length;
i++)
{
// ABCD_EFGH _
// 0123_4567 8
// this is the bit index of the leftmost bit in this byte
let start_bit_bi0 : number = i * 8;
let next_start_bit_bi0 : number = start_bit_bi0 + 8;
// does the bit at the beginning of this byte correspond to the first array?
// strict comparison:
// suppose i = 0,
// suppose bit_length1 is 0
// then this says no, go to second array
// suppose bl1 = 1,
// this says start at first array
let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length;
// weak comparison:
// suppose bit_length1 = 8
// ABCD_EFGH _
// 0123_4567 8
// ^
// start_bit_bi0 ^ next_start_bit_bi0
let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length;
// is this a bytes1 byte
let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array;
let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array;
// need to work out the ping_pong bs up here because js is dumb and I
// can't put lets between elseifs
// alright, now we're in the case of only copying from the second array
// we have two cases:
// ping-pong:
// ABCD_EFGH 1234_5678
// - ---- ---
// copying these bits
// ping:
// ABCD_EFGH <end>
// - ---- 000
//
// how to distinguish between these two??
//
// we're in the ping case when the start_bit_bi0 corresponds to the
// final byte of the second array
//
// ok
//
// we need to compute the bit address in the second array that
// corresponds to the bit address at the beginning of this byte in the
// result array
let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length;
// I think the variable is
// bits_left_to_copy = bits2.bit_length - bit_addr2_bi0
// no + 1 because the current bit is uncopied,
// so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we
// have the last bit to copy
// cases:
// bits_left_to_copy <= 0 ->
// this would mean we have more bits to copy, but are out of
// source bits. should be impossible if bits_null is correct
// bits_left_to_copy <= 8 ->
// this would mean we are going to fill this last byte in the
// result array with the correct bits from array2, but how we
// do this will depend on how those are arranged in bytes2
// (whether we grab one or two bytes)
//
// this is the tricky case
//
// I think what matters here is the byte address in the second array
// if we're on the last byte
// 8 < bits_left_to_copy ->
// this means we can safely grab two bytes from bytes2, and do
// our bitshifting to make it correct
//
// FIXME
let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0;
// no the variable that matters is which byte we're on in the result
let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8);
let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1;
let bitshift_amt : number = bits1.bit_length % 8;
let ping : boolean = next_bytes2_addr_i0 === bytes2.length;
// simple case: this is a byte from the first array
// just copy it
if (is_bytes1_byte)
result_bytes[i] = bytes1[i];
// this is a boundary byte
// further cases:
// second bit length is 0 ->
// ABCD_EF-- <empty>
// ^ starting here
// just copy first byte and move along
else if (is_boundary_byte && (bits2.bit_length === 0))
result_bytes[i] = bytes1[i];
// boundary byte, and there is at least one byte in the second array
else if (is_boundary_byte)
{
// copy over the first byte
result_bytes[i] = bytes1[i];
// take the first byte from the second array
let first_byte_of_second_array : number = bytes2[0];
// bytes1:
// ABCD_EF00
// 6
// bytes2:
// 1234_5678
// 0000_0012
// and bitshift it right by that amount
let bitshift_amt : number = bits1.bit_length % 8;
result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt;
}
// last byte of second array
else if (ping)
result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt);
// ping-pong: not last byte of second array
else
result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt);
}
return {bit_length : result_bit_length,
bytes : result_bytes};
}
-399
View File
@@ -1,399 +0,0 @@
/**
* FÆRT: Fast Æternity Recovery Text
*
* Reference: https://gitlab.com/zxq9/passgas/-/blob/83607fedb08be5dfd03210e331f9c76125bb3467/fullofbeans
*
* @module
*/
export {
encode,
decode,
byte_of_word,
check_word,
check_byte,
words
}
import * as safe from './safe.js';
/**
* Encode a bytestring into FÆRT
*/
function
encode
(bytes : Uint8Array)
: string
{
// initial accumulator
let words_acc : Array<string> = [];
// go along each byte and look up the word
// add it to the accumulator
for (let this_byte of bytes)
{
let this_word = words[this_byte];
words_acc.push(this_word);
}
// prepend check word to phrase
// yes craig it would be faster to do the check byte inline
// the usage case here is 64 byte strings
// double pass is not a big deal
return check_word(bytes) + ' ' + words_acc.join(' ');
}
/**
* Decode a FAERT string into bytes
*/
function
decode
(faert : string)
: safe.Safe<Uint8Array, string>
{
let all_words : Array<string> = faert.split(' ');
let input_check_word : string = all_words[0];
let input_words : Array<string> = all_words.slice(1)
// accumulator
let computed_bytes : Array<number> = [];
// loop over words and figure out accumulator
for (let this_input_word of input_words)
{
let maybe_this_byte : safe.Safe<number, string> = byte_of_word(this_input_word);
// if the word is an allowable word, add it to the accumulator
if (maybe_this_byte.ok)
{
let this_byte : number = maybe_this_byte.result;
computed_bytes.push(this_byte);
}
// error case, propagate the error up the call chain
else
return maybe_this_byte;
}
// at this point, we can assume we correctly decoded all words
// compute the check byte
let computed_bytes_u8s : Uint8Array = new Uint8Array(computed_bytes);
let computed_check_word : string = check_word(computed_bytes_u8s);
// check if it is correct
// if so, return the computed bytes
if (computed_check_word === input_check_word)
return safe.ok(computed_bytes_u8s);
// otherwise, return an error
else
return safe.error('checksum failure! computed check word: ' + computed_check_word + '; input check word: ' + input_check_word);
}
/**
* given a word, find its index
*/
function
byte_of_word
(word: string)
: safe.Safe<number, string>
{
for (let i=0; i<=255; i++)
{
if (word === words[i])
return safe.ok(i);
}
return safe.error('invalid word: ' + word);
}
/**
* compute the check word of an array
*/
function
check_word
(bytes: Uint8Array)
: string
{
return words[check_byte(bytes)];
}
/**
* given an array, compute the xor of all the bytes in the array
*/
function
check_byte
(bytes: Uint8Array)
: number
{
let check_byte = 0;
for (let this_byte of bytes)
check_byte ^= this_byte;
return check_byte;
}
let words = [
"able",
"abuse",
"acquire",
"adjust",
"agent",
"air",
"alert",
"alpha",
"anger",
"answer",
"any",
"argue",
"around",
"assume",
"asthma",
"aunt",
"awkward",
"balcony",
"barrel",
"because",
"behave",
"bicycle",
"bike",
"blind",
"blouse",
"bonus",
"bottom",
"breeze",
"brother",
"bubble",
"burger",
"butter",
"can",
"cannon",
"cargo",
"catalog",
"caught",
"century",
"chaos",
"chicken",
"churn",
"cinnamon",
"clever",
"clock",
"clown",
"collect",
"conduct",
"convince",
"correct",
"cradle",
"crane",
"crime",
"cross",
"crystal",
"curve",
"daughter",
"debris",
"decrease",
"deny",
"deputy",
"despair",
"diesel",
"dinner",
"distance",
"document",
"donate",
"drama",
"drink",
"dutch",
"earth",
"educate",
"elbow",
"employ",
"endless",
"enlist",
"enter",
"equal",
"eternal",
"example",
"exhibit",
"exotic",
"faculty",
"famous",
"fault",
"feature",
"fiber",
"filter",
"firm",
"flame",
"flush",
"follow",
"forward",
"frame",
"fringe",
"future",
"game",
"gate",
"gift",
"glare",
"glow",
"goat",
"grab",
"grief",
"guilt",
"hand",
"hawk",
"health",
"hen",
"hire",
"honey",
"hover",
"hurry",
"hybrid",
"impose",
"inch",
"inherit",
"injury",
"install",
"iron",
"jazz",
"joke",
"just",
"kit",
"kitchen",
"language",
"laundry",
"leader",
"lend",
"leopard",
"license",
"live",
"lobster",
"lounge",
"magnet",
"mail",
"mansion",
"mass",
"math",
"media",
"message",
"metal",
"misery",
"mix",
"more",
"mountain",
"museum",
"mutual",
"narrow",
"nerve",
"next",
"note",
"obey",
"obtain",
"offer",
"once",
"orange",
"ostrich",
"over",
"owner",
"palace",
"patch",
"pave",
"pen",
"phrase",
"piece",
"pizza",
"plunge",
"polar",
"pool",
"power",
"pretty",
"private",
"protect",
"pulp",
"purity",
"quit",
"quote",
"raise",
"razor",
"recall",
"region",
"relief",
"rent",
"rescue",
"review",
"ride",
"risk",
"roof",
"rough",
"saddle",
"salmon",
"sand",
"scene",
"scrub",
"season",
"seek",
"series",
"shed",
"shoe",
"sick",
"silent",
"situate",
"skill",
"slide",
"slush",
"snack",
"solar",
"soul",
"special",
"sphere",
"spot",
"spy",
"stairs",
"stereo",
"strategy",
"stuff",
"success",
"sunny",
"surge",
"swear",
"symptom",
"tail",
"ten",
"tent",
"theme",
"thumb",
"tiny",
"toe",
"tooth",
"topic",
"trade",
"trash",
"trophy",
"truly",
"tumble",
"typical",
"uncle",
"unfair",
"until",
"upper",
"useless",
"van",
"venue",
"video",
"visa",
"vocal",
"walk",
"waste",
"wedding",
"weekend",
"wide",
"winner",
"wise",
"wood",
"wrist",
"zero"
]
-112
View File
@@ -1,112 +0,0 @@
/**
* "Safe" error handling
*
* The idea here is that there are situations where it is known
*
* ```typescript
* type Safe<ok_t, err_t>
* = Ok<ok_t>
* | Error<err_t>;
*
* type Ok<ok_t>
* = {ok : true,
* result : ok_t};
*
* type Error<err_t>
* = {ok : false,
* error : err_t};
* ```
*
* 1. a given function call is likely to fail
* 2. the likely errors can be enumerated
*
* These are called "positive errors". An example would be a page script
* asking a browser wallet extension to sign a transaction. The following
* errors, among others, are likely:
*
* - the user does not have a wallet installed
* - the user has a wallet but does not have the correct signing key
* - the user rejects the transaction
* - the sign request timed out
*
* These errors should not generate exceptions, as these behaviors are to some
* degree "expected".
*/
export {
Safe,
Ok,
Error,
ok,
error,
unsafe
}
/**
* Type that catches positive errors
*/
type Safe<ok_t, err_t>
= Ok<ok_t>
| Error<err_t>;
/**
* Ok type
*/
type Ok<ok_t>
= {ok : true,
result : ok_t};
/**
* Error type
*/
type Error<err_t>
= {ok : false,
error : err_t};
/**
* Constructs an `Ok` value from a pure value
*/
function
ok
<ok_t>
(x : ok_t)
: Ok<ok_t>
{
return {ok: true, result: x};
}
/**
* Constructs an `Error` value from a pure value
*/
function
error
<err_t>
(x: err_t)
: Error<err_t>
{
return {ok: false, error: x};
}
/**
* Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the
* `err_t`
*/
function
unsafe
<ok_t, err_t>
(x: Safe<ok_t, err_t>)
: ok_t
{
if (x.ok)
return x.result;
else
throw x.error;
}