all the tests pass
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,507 +0,0 @@
|
||||
/**
|
||||
* Base64 Utility Functions in TypeScript
|
||||
*/
|
||||
export { encode, decode };
|
||||
/**
|
||||
* Encode an array of bytes as a Uint8Array in base64 notation.
|
||||
*/
|
||||
function encode(bytes) {
|
||||
// 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 = encode_head(head);
|
||||
let tail_str = encode_tail(tail, tail_len);
|
||||
return head_str + tail_str;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
let len = bytes.length;
|
||||
// too lazy to look up how to do integer division in js so this will do
|
||||
let tail_len = len % 3;
|
||||
let head_len = len - tail_len;
|
||||
// for slice:
|
||||
// first argument is the 0-index of the start
|
||||
// second - first is the length of the slice
|
||||
let head = bytes.slice(0, head_len);
|
||||
// empty second argument means go to the end
|
||||
let tail = 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) {
|
||||
// can assume length of bytes is a multiple of 3
|
||||
// start index at 0
|
||||
// increment by 3
|
||||
let head_bytes_len = head_bytes.length;
|
||||
let max_idx0 = head_bytes_len - 1;
|
||||
let head_str_acc = '';
|
||||
for (let this_3slice_start_idx0 = 0; this_3slice_start_idx0 <= max_idx0; this_3slice_start_idx0 += 3) {
|
||||
let this_3slice_bytes = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3);
|
||||
let this_3slice_str = 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) {
|
||||
let b0 = bytes[0];
|
||||
let b1 = bytes[1];
|
||||
let b2 = bytes[2];
|
||||
// ABCDEFGH 12345678 abcdefgh
|
||||
// b0 b1 b2
|
||||
// ABCDEF GH1234 5678ab cdefgh
|
||||
// n0 n1 n2 n3
|
||||
let n0 = b0 >> 2;
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
// b1 = 12345678
|
||||
// b1 >> 4 = ____1234
|
||||
// n1 = __GH1234
|
||||
let n1 = ((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 = ((b1 % 16) << 2) + (b2 >> 6);
|
||||
// b2 = abcdefgh
|
||||
// 64 = _1______
|
||||
// n3 = __cdefgh
|
||||
let n3 = b2 % 64;
|
||||
// convert to chars
|
||||
let s0 = int2char(n0);
|
||||
let s1 = int2char(n1);
|
||||
let s2 = int2char(n2);
|
||||
let s3 = int2char(n3);
|
||||
// retrvn
|
||||
return s0 + s1 + s2 + s3;
|
||||
}
|
||||
/**
|
||||
* Encode the final 0, 1, or 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function encode_tail(tail_bytes, tail_len) {
|
||||
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) {
|
||||
let b0 = bytes[0];
|
||||
// n0 = __ABCDEF
|
||||
// b0 = ABCDEFGH
|
||||
// b0 >> 2 = __ABCDEF
|
||||
let n0 = b0 >> 2;
|
||||
// n1 = __GH____
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
let n1 = (b0 % 4) << 4;
|
||||
return int2char(n0) + int2char(n1) + '==';
|
||||
}
|
||||
/**
|
||||
* Encode two bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function encode2(bytes) {
|
||||
let b0 = bytes[0];
|
||||
let b1 = bytes[1];
|
||||
// ABCDEFGH 12345678
|
||||
// b0 b1
|
||||
// ABCDEF GH1234 5678__
|
||||
// n0 n1 n2
|
||||
let n0 = b0 >> 2;
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
// b1 = 12345678
|
||||
// b1 >> 4 = ____1234
|
||||
// n1 = __GH1234
|
||||
let n1 = ((b0 % 4) << 4) + (b1 >> 4);
|
||||
// b1 = 12345678
|
||||
// 16 = ___1____
|
||||
// b1 % 16 = ____5678
|
||||
// (b1 % 16) << 2 = __5678__
|
||||
// n2 = __5678__
|
||||
let n2 = (b1 % 16) << 2;
|
||||
// convert to chars
|
||||
let s0 = int2char(n0);
|
||||
let s1 = int2char(n1);
|
||||
let s2 = int2char(n2);
|
||||
// retrvn
|
||||
return s0 + s1 + s2 + '=';
|
||||
}
|
||||
/**
|
||||
* Decode a base64-encoded string
|
||||
*/
|
||||
function decode(base64_str) {
|
||||
// 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 = len - 4;
|
||||
let head_s = base64_str.slice(0, tail_start_idx0);
|
||||
let tail_s = base64_str.slice(tail_start_idx0);
|
||||
// Using arrays because Uint8Arrays don't have a concat operation
|
||||
let head_arr = decode_head(head_s);
|
||||
let tail_arr = 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 = 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) {
|
||||
// go 4 characters at a time
|
||||
let max_i0 = s.length - 1;
|
||||
let decoded_acc = [];
|
||||
for (let i0 = 0; i0 <= max_i0; i0 += 4) {
|
||||
let this_slice_s = s.slice(i0, i0 + 4);
|
||||
let this_slice_arr = 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) {
|
||||
// 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) {
|
||||
// pull out strings
|
||||
let s0 = s[0];
|
||||
let s1 = s[1];
|
||||
let s2 = s[2];
|
||||
let s3 = s[3];
|
||||
// convert to numbers
|
||||
let n0 = char2int(s0);
|
||||
let n1 = char2int(s1);
|
||||
let n2 = char2int(s2);
|
||||
let n3 = 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 = (n0 << 2) + (n1 >> 4);
|
||||
// n1 = __gh1234
|
||||
// 16 = ___1____
|
||||
// n1 % 16 = ____1234
|
||||
// (n1 % 16) << 4 = 1234____
|
||||
// n2 = __5678ab
|
||||
// n2 >> 2 = ____5678
|
||||
// b1 = 12345678
|
||||
let b1 = ((n1 % 16) << 4) + (n2 >> 2);
|
||||
// n2 = __5678ab
|
||||
// 4 = _____1__
|
||||
// n2 % 4 = ______ab
|
||||
// (n2 % 4) << 6 = ab______
|
||||
// n3 = __cdefgh
|
||||
let b2 = ((n2 % 4) << 6) + n3;
|
||||
return [b0, b1, b2];
|
||||
}
|
||||
/**
|
||||
* Decode a 4-character long base64 string corresponding to 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function decode2(s) {
|
||||
// xyz=
|
||||
// pull out strings
|
||||
let s0 = s[0];
|
||||
let s1 = s[1];
|
||||
let s2 = s[2];
|
||||
// convert to numbers
|
||||
let n0 = char2int(s0);
|
||||
let n1 = char2int(s1);
|
||||
let n2 = 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 = (n0 << 2) + (n1 >> 4);
|
||||
// n1 = __gh1234
|
||||
// 16 = ___1____
|
||||
// n1 % 16 = ____1234
|
||||
// (n1 % 16) << 4 = 1234____
|
||||
// n2 = __5678__
|
||||
// n2 >> 2 = ____5678
|
||||
// b1 = 12345678
|
||||
let b1 = ((n1 % 16) << 4) + (n2 >> 2);
|
||||
return [b0, b1];
|
||||
}
|
||||
/**
|
||||
* Decode a 4-character long base64 string corresponding to 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function decode1(s) {
|
||||
// xy==
|
||||
// pull out strings
|
||||
let s0 = s[0];
|
||||
let s1 = s[1];
|
||||
// convert to numbers
|
||||
let n0 = char2int(s0);
|
||||
let n1 = char2int(s1);
|
||||
// abcdef gh____
|
||||
// n0 n1
|
||||
// abcdefgh
|
||||
// b0
|
||||
// n0 = __abcdef
|
||||
// n1 = __gh____
|
||||
// n0 << 2 = abcdef__
|
||||
// n1 >> 4 = ______gh
|
||||
// b0 = abcdefgh
|
||||
let b0 = (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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=b64.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Miscellaneous binary utility functions
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export { bytes_to_bigint, bigint_to_bytes, concat, strong_rand_bytes };
|
||||
/**
|
||||
* Concatenate two arrays
|
||||
*/
|
||||
declare function concat(arr1: Uint8Array, arr2: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* Cryptographically random bytes
|
||||
*/
|
||||
declare function strong_rand_bytes(how_many: number): Uint8Array;
|
||||
/**
|
||||
* Convert a byte array to a bigint
|
||||
*
|
||||
* Equivalent to `binary:decode_unsigned/1` from Erlang
|
||||
*/
|
||||
declare function bytes_to_bigint(bytes: Uint8Array): bigint;
|
||||
/**
|
||||
* Convert a bigint to a byte array
|
||||
*
|
||||
* Equivalent to `binary:encode_unsigned/1` from Erlang
|
||||
*
|
||||
* Requires input to be positive
|
||||
*/
|
||||
declare function bigint_to_bytes(q: bigint): Uint8Array;
|
||||
@@ -1,232 +0,0 @@
|
||||
/**
|
||||
* Miscellaneous binary utility functions
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export { bytes_to_bigint, bigint_to_bytes, concat, strong_rand_bytes };
|
||||
/**
|
||||
* Concatenate two arrays
|
||||
*/
|
||||
function concat(arr1, arr2) {
|
||||
let len1 = arr1.length;
|
||||
let len2 = arr2.length;
|
||||
let arr1_idx0_offset = 0;
|
||||
let arr2_idx0_offset = len1;
|
||||
let result_len = len1 + len2;
|
||||
let result = 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 = 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 = arr2_idx0 + arr2_idx0_offset;
|
||||
result[result_idx0] = arr2[arr2_idx0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Cryptographically random bytes
|
||||
*/
|
||||
function strong_rand_bytes(how_many) {
|
||||
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) {
|
||||
let n = 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) {
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Get an uninitialized bitstring
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function bits_null(bit_length) {
|
||||
let byte_length = Math.ceil(bit_length / 8);
|
||||
let result = 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) {
|
||||
let byte_length = Math.ceil(bit_length / 8);
|
||||
let result = 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) {
|
||||
let byte_length = Math.ceil(bit_length / 8);
|
||||
let result = 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 = 8 - (bit_length % 8);
|
||||
// the trailing byte is 255 << that
|
||||
// e.g. 3 trailing 0s
|
||||
// 1111_1111 -> 1111_1000
|
||||
let last_byte = 255 << num_trailing_zero_bits;
|
||||
let last_byte_idx0 = 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, bits) {
|
||||
// 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 = Math.floor(bit_idx0 / 8);
|
||||
// let's fetch the byte and work with that
|
||||
let the_byte = 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 = 8 - (bit_idx0 % 8);
|
||||
return (the_byte >> bsr) % 2;
|
||||
}
|
||||
/**
|
||||
* Concatenate two bitstrings
|
||||
*/
|
||||
function bits_concat(bits1, bits2) {
|
||||
let result_bit_length = bits1.bit_length + bits2.bit_length;
|
||||
let bytes1 = bits1.bytes;
|
||||
let bytes2 = bits2.bytes;
|
||||
// using zeros here because of our xor trick in a minute
|
||||
let result_bits = bits_zeros(result_bit_length);
|
||||
let result_bytes = result_bits.bytes;
|
||||
// alright so
|
||||
// we can start by copying the first bytes into result bytes
|
||||
for (let bytes1_idx0 = 0; bytes1_idx0 < bytes1.length; bytes1_idx0++) {
|
||||
result_bytes[bytes1_idx0] = bytes1[bytes1_idx0];
|
||||
}
|
||||
// next
|
||||
// we need to calculate the left-shift offset
|
||||
// this will be 8 - (bytes1.bit_length % 8)
|
||||
let num_trailing_zeros_in_first_array = 8 - (bits1.bit_length % 8);
|
||||
// so
|
||||
// bytes1: ABCD_EF00
|
||||
// bytes2: GH12_3000
|
||||
// result: ABCD_EFGH 1230_0000
|
||||
// ah ok, so we need to for each byte in the second array
|
||||
// take the first however many bits, xor it with the existing byte
|
||||
// then take the last however many bits and place them into the next byte
|
||||
// this is super confusing but
|
||||
// ABCD_EF00
|
||||
// GH12_3456
|
||||
// operation:
|
||||
// ABCD_EF00
|
||||
// xor 0000_00GH
|
||||
// = ABCD_EFGH 1234_5600
|
||||
//
|
||||
// then on the next iteration
|
||||
// 1234_5600
|
||||
// abcd_efgh
|
||||
// ->
|
||||
// 1234_56ab cdef_gh00
|
||||
//
|
||||
// ah so there's a pattern
|
||||
// however many trailing 0s there are in the first array
|
||||
// say there's 2
|
||||
// we take the first 2 bits of the upcoming byte
|
||||
// xor that against the current byte
|
||||
// take the last 6 bits of the upcoming byte
|
||||
// set the next byte to that
|
||||
//
|
||||
// have to think about edge behavior
|
||||
// this is ripe for off-by-1 errors
|
||||
// but i think the general idea is right
|
||||
//
|
||||
// so we start the iteration
|
||||
// on the last byte of the first array
|
||||
let last_byte_of_first_array_idx0 = bytes1.length - 1;
|
||||
// and we end
|
||||
// on the second-to-last-byte of the result array
|
||||
let second_to_last_byte_of_result_array_idx0 = result_bytes.length - 2;
|
||||
// the reason we do that is because we're doing this is because we are
|
||||
// going along, xoring against the current byte and then setting the next
|
||||
// byte
|
||||
//
|
||||
// ok so
|
||||
for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; this_result_byte_idx0++) {
|
||||
let this_result_byte = result_bytes[this_result_byte_idx0];
|
||||
// ok here we need to fish out the relevant byte of the second array
|
||||
// gaaah
|
||||
// so this will be 0 at the start of the loop
|
||||
let relevant_byte_of_second_array_idx0 = this_result_byte_idx0 - last_byte_of_first_array_idx0;
|
||||
let relevant_byte_of_second_array = bytes2[relevant_byte_of_second_array_idx0];
|
||||
// ok so let's fish out the leading digits
|
||||
// the number of leading digits is the number of trailing 0s in the first array
|
||||
let num_leading_digits = num_trailing_zeros_in_first_array;
|
||||
let num_trailing_digits = 8 - num_leading_digits;
|
||||
// suppose there are 2 leading digits and 6 trailing digits
|
||||
// ABCD_EFGH
|
||||
// leading digits are
|
||||
// ABCD_EFGH >> 6 = 0000_00AB
|
||||
// trailing digits are
|
||||
// (ABCD_EFGH << 2) % 255 = CDEF_GH00
|
||||
let leading_digits = relevant_byte_of_second_array >> num_trailing_digits;
|
||||
let trailing_digits = (relevant_byte_of_second_array << num_leading_digits) % 255;
|
||||
// xor the current byte against the leading digits
|
||||
let new_this_result_byte = this_result_byte ^ leading_digits;
|
||||
result_bytes[this_result_byte_idx0] = new_this_result_byte;
|
||||
// set the next byte to the trailing digits
|
||||
result_bytes[this_result_byte_idx0 + 1] = trailing_digits;
|
||||
}
|
||||
// i think we're done
|
||||
return { bit_length: result_bit_length,
|
||||
bytes: result_bytes };
|
||||
}
|
||||
//# sourceMappingURL=bin.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,30 +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
|
||||
*/
|
||||
declare function encode(bytes: Uint8Array): string;
|
||||
/**
|
||||
* Decode a FAERT string into bytes
|
||||
*/
|
||||
declare function decode(faert: string): safe.Safe<Uint8Array, string>;
|
||||
/**
|
||||
* given a word, find its index
|
||||
*/
|
||||
declare function byte_of_word(word: string): safe.Safe<number, string>;
|
||||
/**
|
||||
* compute the check word of an array
|
||||
*/
|
||||
declare function check_word(bytes: Uint8Array): string;
|
||||
/**
|
||||
* given an array, compute the xor of all the bytes in the array
|
||||
*/
|
||||
declare function check_byte(bytes: Uint8Array): number;
|
||||
declare let words: string[];
|
||||
@@ -1,344 +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) {
|
||||
// initial accumulator
|
||||
let words_acc = [];
|
||||
// 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) {
|
||||
let all_words = faert.split(' ');
|
||||
let input_check_word = all_words[0];
|
||||
let input_words = all_words.slice(1);
|
||||
// accumulator
|
||||
let computed_bytes = [];
|
||||
// loop over words and figure out accumulator
|
||||
for (let this_input_word of input_words) {
|
||||
let maybe_this_byte = 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 = 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 = new Uint8Array(computed_bytes);
|
||||
let computed_check_word = 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) {
|
||||
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) {
|
||||
return words[check_byte(bytes)];
|
||||
}
|
||||
/**
|
||||
* given an array, compute the xor of all the bytes in the array
|
||||
*/
|
||||
function check_byte(bytes) {
|
||||
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"
|
||||
];
|
||||
//# sourceMappingURL=faert.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"faert.js","sourceRoot":"","sources":["../src/faert.ts"],"names":[],"mappings":"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"}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,66 +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
|
||||
*/
|
||||
declare type Safe<ok_t, err_t> = Ok<ok_t> | Error<err_t>;
|
||||
/**
|
||||
* Ok type
|
||||
*/
|
||||
declare type Ok<ok_t> = {
|
||||
ok: true;
|
||||
result: ok_t;
|
||||
};
|
||||
/**
|
||||
* Error type
|
||||
*/
|
||||
declare type Error<err_t> = {
|
||||
ok: false;
|
||||
error: err_t;
|
||||
};
|
||||
/**
|
||||
* Constructs an `Ok` value from a pure value
|
||||
*/
|
||||
declare function ok<ok_t>(x: ok_t): Ok<ok_t>;
|
||||
/**
|
||||
* Constructs an `Error` value from a pure value
|
||||
*/
|
||||
declare function error<err_t>(x: err_t): Error<err_t>;
|
||||
/**
|
||||
* Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the
|
||||
* `err_t`
|
||||
*/
|
||||
declare function unsafe<ok_t, err_t>(x: Safe<ok_t, err_t>): ok_t;
|
||||
@@ -1,58 +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 { ok, error, unsafe };
|
||||
/**
|
||||
* Constructs an `Ok` value from a pure value
|
||||
*/
|
||||
function ok(x) {
|
||||
return { ok: true, result: x };
|
||||
}
|
||||
/**
|
||||
* Constructs an `Error` value from a pure value
|
||||
*/
|
||||
function error(x) {
|
||||
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(x) {
|
||||
if (x.ok)
|
||||
return x.result;
|
||||
else
|
||||
throw x.error;
|
||||
}
|
||||
//# sourceMappingURL=safe.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"safe.js","sourceRoot":"","sources":["../src/safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAIH,EAAE,EACF,KAAK,EACL,MAAM,EACT,CAAA;AA2BD;;GAEG;AACH,SACA,EAAE,CAEG,CAAQ;IAGT,OAAO,EAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAC,CAAC;AACjC,CAAC;AAID;;GAEG;AACH,SACA,KAAK,CAEA,CAAQ;IAGT,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAC,CAAC;AACjC,CAAC;AAID;;;GAGG;AACH,SACA,MAAM,CAED,CAAoB;IAGrB,IAAI,CAAC,CAAC,EAAE;QACJ,OAAO,CAAC,CAAC,MAAM,CAAC;;QAEhB,MAAM,CAAC,CAAC,KAAK,CAAC;AACtB,CAAC"}
|
||||
@@ -1,346 +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_zeros(result_bit_length);
|
||||
let result_bytes : Uint8Array = result_bits.bytes;
|
||||
|
||||
// alright so
|
||||
// we can start by copying the first bytes into result bytes
|
||||
for (let bytes1_idx0 = 0;
|
||||
bytes1_idx0 < bytes1.length;
|
||||
bytes1_idx0++)
|
||||
{
|
||||
result_bytes[bytes1_idx0] = bytes1[bytes1_idx0];
|
||||
}
|
||||
|
||||
// next
|
||||
// we need to calculate the left-shift offset
|
||||
// this will be 8 - (bytes1.bit_length % 8)
|
||||
let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8);
|
||||
// so
|
||||
// bytes1: ABCD_EF00
|
||||
// bytes2: GH12_3000
|
||||
// result: ABCD_EFGH 1230_0000
|
||||
// ah ok, so we need to for each byte in the second array
|
||||
// take the first however many bits, xor it with the existing byte
|
||||
// then take the last however many bits and place them into the next byte
|
||||
// this is super confusing but
|
||||
// ABCD_EF00
|
||||
// GH12_3456
|
||||
// operation:
|
||||
// ABCD_EF00
|
||||
// xor 0000_00GH
|
||||
// = ABCD_EFGH 1234_5600
|
||||
//
|
||||
// then on the next iteration
|
||||
// 1234_5600
|
||||
// abcd_efgh
|
||||
// ->
|
||||
// 1234_56ab cdef_gh00
|
||||
//
|
||||
// ah so there's a pattern
|
||||
// however many trailing 0s there are in the first array
|
||||
// say there's 2
|
||||
// we take the first 2 bits of the upcoming byte
|
||||
// xor that against the current byte
|
||||
// take the last 6 bits of the upcoming byte
|
||||
// set the next byte to that
|
||||
//
|
||||
// have to think about edge behavior
|
||||
// this is ripe for off-by-1 errors
|
||||
// but i think the general idea is right
|
||||
//
|
||||
// so we start the iteration
|
||||
// on the last byte of the first array
|
||||
let last_byte_of_first_array_idx0 : number = bytes1.length - 1;
|
||||
// and we end
|
||||
// on the second-to-last-byte of the result array
|
||||
let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2;
|
||||
// the reason we do that is because we're doing this is because we are
|
||||
// going along, xoring against the current byte and then setting the next
|
||||
// byte
|
||||
//
|
||||
// ok so
|
||||
for (let this_result_byte_idx0 = last_byte_of_first_array_idx0;
|
||||
this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0;
|
||||
this_result_byte_idx0++)
|
||||
{
|
||||
let this_result_byte : number = result_bytes[this_result_byte_idx0];
|
||||
|
||||
// ok here we need to fish out the relevant byte of the second array
|
||||
// gaaah
|
||||
// so this will be 0 at the start of the loop
|
||||
let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0;
|
||||
let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0];
|
||||
|
||||
// ok so let's fish out the leading digits
|
||||
// the number of leading digits is the number of trailing 0s in the first array
|
||||
let num_leading_digits : number = num_trailing_zeros_in_first_array;
|
||||
let num_trailing_digits : number = 8 - num_leading_digits;
|
||||
|
||||
// suppose there are 2 leading digits and 6 trailing digits
|
||||
// ABCD_EFGH
|
||||
// leading digits are
|
||||
// ABCD_EFGH >> 6 = 0000_00AB
|
||||
// trailing digits are
|
||||
// (ABCD_EFGH << 2) % 255 = CDEF_GH00
|
||||
let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits;
|
||||
let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255;
|
||||
|
||||
// xor the current byte against the leading digits
|
||||
let new_this_result_byte : number = this_result_byte ^ leading_digits;
|
||||
result_bytes[this_result_byte_idx0] = new_this_result_byte;
|
||||
|
||||
// set the next byte to the trailing digits
|
||||
result_bytes[this_result_byte_idx0 + 1] = trailing_digits;
|
||||
}
|
||||
|
||||
// i think we're done
|
||||
return {bit_length : result_bit_length,
|
||||
bytes : result_bytes};
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
@@ -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;
|
||||
}
|
||||
-287
@@ -1,287 +0,0 @@
|
||||
/**
|
||||
* Node API constructor/deconstructor
|
||||
*
|
||||
* This is similar to serialization/deserialization, but not quite the same
|
||||
* thing. It converts back and forth between different forms of
|
||||
* "api-serialized" data.
|
||||
*
|
||||
* References:
|
||||
* 1. https://github.com/aeternity/protocol/blob/master/serializations.md
|
||||
* 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md
|
||||
*
|
||||
* ## General type rules
|
||||
*
|
||||
* ```
|
||||
* ERLANG TYPE | JS TYPE
|
||||
* -------------------------------
|
||||
* integer | bigint
|
||||
* list | Array
|
||||
* binary | Uint8Array
|
||||
* ```
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`.
|
||||
*
|
||||
* We can tell by the `tx_` prefix that this represents transaction data of
|
||||
* some sort. But the rest of the data is totally opaque. The task of this
|
||||
* module is to "humanize" that `tx_...` string and show what data is contained
|
||||
* in the rest of it.
|
||||
*
|
||||
* The remainder of the string is a base64-encoded bytestring
|
||||
*
|
||||
* ```erlang
|
||||
* 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]).
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>>
|
||||
* ```
|
||||
*
|
||||
* That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I
|
||||
* wrote an RLP decoder. RLP has two types of data: binaries, and
|
||||
* arbitrary-depth (possibly empty) lists of binaries.
|
||||
*
|
||||
* ```erlang
|
||||
* -type decoded_data() :: binary() | [decoded_data()].
|
||||
*
|
||||
* -spec decode(RLP) -> {Data, Rest}
|
||||
* when RLP :: binary(),
|
||||
* Data :: decoded_data(),
|
||||
* Rest :: binary().
|
||||
* ```
|
||||
*
|
||||
* ```erlang
|
||||
* 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)).
|
||||
* {[<<"\f">>,
|
||||
* <<1>>,
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,...>>,
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,...>>,
|
||||
* <<"\n">>,
|
||||
* <<15,38,245,97,200,0>>,
|
||||
* <<0>>,
|
||||
* <<"\t">>,<<"hainana">>],
|
||||
* <<3,182,66,215>>}
|
||||
* ```
|
||||
*
|
||||
* As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning
|
||||
*
|
||||
* ```erlang
|
||||
* 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>).
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,
|
||||
* 127,198,64,232,35,118,68,68,244,48,85,218,93,...>>
|
||||
* 4> SizeX = byte_size(X).
|
||||
* 94
|
||||
* 6> <<RLPEncodedData:(SizeX - 4)/binary, Hash/binary>> = X.
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,
|
||||
* 127,198,64,232,35,118,68,68,244,48,85,218,93,...>>
|
||||
* 10> <<Check:4/binary, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)).
|
||||
* <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81,
|
||||
* 189,184,82,207,164,68,128,43,11,174,236,59,77,...>>
|
||||
* 11> Hash.
|
||||
* <<3,182,66,215>>
|
||||
* 12> Check.
|
||||
* <<3,182,66,215>>
|
||||
* ```
|
||||
*
|
||||
* What we're really interested in is `Data`
|
||||
*
|
||||
* ```erlang
|
||||
* 14> {Data, _} = rlp:decode(X).
|
||||
* {[<<"\f">>,
|
||||
* <<1>>,
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,...>>,
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,...>>,
|
||||
* <<"\n">>,
|
||||
* <<15,38,245,97,200,0>>,
|
||||
* <<0>>,
|
||||
* <<"\t">>,<<"hainana">>],
|
||||
* <<3,182,66,215>>}
|
||||
* ```
|
||||
*
|
||||
* `Data` is a list. The first field `<<"\f">>` is meant to be an integer which
|
||||
* tells us what type of data this is.
|
||||
*
|
||||
* ```erlang
|
||||
* 16> $\f.
|
||||
* 12
|
||||
* ```
|
||||
*
|
||||
* If we look at our table
|
||||
* (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags),
|
||||
* we see that a value of `12` is a spend transaction.
|
||||
*
|
||||
* The second field `<<1>>` tells us the "version" of the field orderings,
|
||||
* which we can ignore for now.
|
||||
*
|
||||
* The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction)
|
||||
*
|
||||
* ```erlang
|
||||
* [ <sender> :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE"
|
||||
* , <recipient> :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
||||
* , <amount> :: int() % <<"\n">> "=" 10
|
||||
* , <fee> :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000
|
||||
* , <ttl> :: int() % <<0>> "=" 0
|
||||
* , <nonce> :: int() % <<"\t">> "=" 9
|
||||
* , <payload> :: binary() % <<"hainana">> "=" "hainana"
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Our task here is to be able to pull apart the "tx_..." string into its fields.
|
||||
*
|
||||
* Converting the binaries to integers is pretty trivial. The only mildly
|
||||
* annoying thing is the `id` type.
|
||||
*
|
||||
* `id`s have two fields: a single-byte prefix which says which type of ID it
|
||||
* is. In this case, both `id`s have a prefix of `1`, which means they are both
|
||||
* normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other
|
||||
* options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or
|
||||
* names (prefix `2`/`nm_`)
|
||||
*
|
||||
* To "api-encode" the name, we first pick the appropriate prefix based on the
|
||||
* first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then
|
||||
* double-SHA'd to get the 4-byte check suffix
|
||||
*
|
||||
* ```erlang
|
||||
* 30> SenderBytes = lists:nth(3, Data).
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,120,164,154,55,...>>
|
||||
* 31> <<1, SenderAddrBytes/binary>> = SenderBytes.
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,120,164,154,55,...>>
|
||||
* 32> DoubleSha = fun(Bytes) -> <<Foo:4/binary, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end.
|
||||
* #Fun<erl_eval.44.97283095>
|
||||
* 33> "ak_" ++ b58:enc(<<SenderAddrBytes/binary, (DoubleSha(SenderAddrBytes))/binary>>).
|
||||
* "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE"
|
||||
* 34> RecipBytes = lists:nth(4, Data).
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,7,3,234,139,205,...>>
|
||||
* 35> <<1, RecipAddrBytes/binary>> = RecipBytes.
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,7,3,234,139,205,...>>
|
||||
* 36> "ak_" ++ b58:enc(<<RecipAddrBytes/binary, (DoubleSha(RecipAddrBytes))/binary>>).
|
||||
* "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==")
|
||||
* {tag : 'SpendTx',
|
||||
* version : 1n,
|
||||
* fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE",
|
||||
* recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv",
|
||||
* amount : 10n,
|
||||
* fee : 16660000000000n,
|
||||
* ttl : 0n,
|
||||
* nonce : 9n,
|
||||
* payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}}
|
||||
* ```
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export {
|
||||
// functions
|
||||
deconstruct_tx };
|
||||
import * as b64 from './b64.js';
|
||||
import * as bin from './bin.js';
|
||||
import * as rlp from './rlp.js';
|
||||
/**
|
||||
* Deconstruct a Tx
|
||||
*/
|
||||
function deconstruct_tx(tx_str) {
|
||||
let b64_str = tx_str.slice(3); // tx_[...] -> [...]
|
||||
let tx_rlp_encoded = b64.decode(b64_str); // [...] -> bytes
|
||||
let tx_data = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing
|
||||
let tx_type = bin.bytes_to_bigint(tx_data[0]); // get a bigint
|
||||
let tts = tx_type_str(tx_type);
|
||||
let tx_version = bin.bytes_to_bigint(tx_data[1]);
|
||||
let tx_fields = deconstruct_fields(tts, tx_version, tx_data.slice(2));
|
||||
return { type: tts,
|
||||
version: tx_version,
|
||||
fields: tx_fields };
|
||||
}
|
||||
/**
|
||||
* Data that's "api-encoded" goes through the following stages:
|
||||
*
|
||||
* 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings)
|
||||
* 2. rlp decode data -> bytestring
|
||||
* 3. bytestring -> <<Bytestring/binary, Hash:4/binary>>
|
||||
* 4. HashedBytestring -> base64/base58 string encoding
|
||||
* 5. Add string prefix
|
||||
*
|
||||
* This function undoes step 3 and step 2, returns back the rlp decode data
|
||||
*
|
||||
* FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures
|
||||
* FIXME: No good way to handle failure cases
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function shasha_rlp_decode_list(hashed_bs) {
|
||||
let len = hashed_bs.length;
|
||||
let bytes = hashed_bs.slice(0, len - 4);
|
||||
let { decoded_data } = rlp.decode(bytes);
|
||||
return decoded_data;
|
||||
}
|
||||
/**
|
||||
* Convert an object tag that's a type of transaction to the type string
|
||||
*
|
||||
* See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function tx_type_str(tx_type_int) {
|
||||
switch (tx_type_int) {
|
||||
case 11n: return 'SignedTx';
|
||||
case 12n: return 'SpendTx';
|
||||
case 42n: return 'ContractCreateTx';
|
||||
case 43n: return 'ContractCallTx';
|
||||
default: throw new Error('invalid transaction type: ' + tx_type_int);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Given an array of data decoded from RLP, convert it to the fields, as
|
||||
* appropriate as given by the tx type string and the version
|
||||
*/
|
||||
function deconstruct_fields(tx_type_str, tx_version, tx_rawfields) {
|
||||
switch (tx_type_str) {
|
||||
// case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields);
|
||||
case 'SpendTx': return deconstruct_fields_SpendTx(tx_rawfields);
|
||||
// case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields);
|
||||
// case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields);
|
||||
default: throw new Error('invalid tx type str: ' + tx_type_str);
|
||||
}
|
||||
}
|
||||
// TODO: do all this in Erlang
|
||||
function deconstruct_fields_SpendTx(fields) {
|
||||
let sender_bytes = fields[0];
|
||||
let recip_bytes = fields[1];
|
||||
let amount_bytes = fields[2];
|
||||
let fee_bytes = fields[3];
|
||||
let ttl_bytes = fields[4];
|
||||
let nonce_bytes = fields[5];
|
||||
let payload_bytes = fields[6];
|
||||
return { sender: encode_id(sender_bytes),
|
||||
recipient: encode_id(sender_bytes),
|
||||
amount: bin.bytes_to_bigint(amount_bytes),
|
||||
fee: bin.bytes_to_bigint(fee_bytes),
|
||||
ttl: bin.bytes_to_bigint(ttl_bytes),
|
||||
nonce: bin.bytes_to_bigint(nonce_bytes),
|
||||
payload: bin.bytes_to_bigint(payload_bytes) };
|
||||
}
|
||||
/**
|
||||
* Convert a binary account/name/etc binary id into the appropriate type of string
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function encode_id(id) {
|
||||
throw new Error('nyi');
|
||||
}
|
||||
/*
|
||||
FIXME:
|
||||
1. work out all this in Erlang to clear conceptual goo
|
||||
2. think about how i want type safety etc to work
|
||||
3. think about a language to assert that the data has the correct shape to it
|
||||
4. get some examples working in Erlang
|
||||
5. convert erlang code back to ts
|
||||
*/
|
||||
//# sourceMappingURL=anth.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"anth.js","sourceRoot":"","sources":["../src/anth.ts"],"names":[],"mappings":"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"}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Base58 encoding/decoding
|
||||
*/
|
||||
export { encode, decode };
|
||||
/**
|
||||
* Encode a Uint8Array into base58
|
||||
*/
|
||||
declare function encode(binary: Uint8Array): string;
|
||||
/**
|
||||
* Decode a Base58 string into a Uint8Array
|
||||
*/
|
||||
declare function decode(base58: string): Uint8Array;
|
||||
-308
@@ -1,308 +0,0 @@
|
||||
/**
|
||||
* Base58 encoding/decoding
|
||||
*/
|
||||
export { encode, decode };
|
||||
//=============================================================================
|
||||
// ENCODING
|
||||
//=============================================================================
|
||||
/**
|
||||
* Encode a Uint8Array into base58
|
||||
*/
|
||||
function encode(binary) {
|
||||
let num_leading_zeros = nlz(binary);
|
||||
let rest = binary.slice(num_leading_zeros);
|
||||
let ones = encode_zeros(num_leading_zeros);
|
||||
let rest_b58 = encode_rest(rest);
|
||||
let result = ones + rest_b58;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* count the number of leading zeros in a uint8array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function nlz(bytes) {
|
||||
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) {
|
||||
let ones = '';
|
||||
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) {
|
||||
let bytes_bignum = bytes_to_bigint(bytes);
|
||||
let result = bignum_to_base58(bytes_bignum);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Convert a bytestring to a bignum
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function bytes_to_bigint(bytes) {
|
||||
let acc_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) {
|
||||
let s = '';
|
||||
while (q !== 0n) {
|
||||
let this_n = q % 58n;
|
||||
q /= 58n;
|
||||
let this_b58_char = bigint_to_char(this_n);
|
||||
s = this_b58_char + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
//=============================================================================
|
||||
// DECODING
|
||||
//=============================================================================
|
||||
/**
|
||||
* Decode a Base58 string into a Uint8Array
|
||||
*/
|
||||
function decode(base58) {
|
||||
let num_leading_ones = nlo(base58);
|
||||
let rest = base58.slice(num_leading_ones);
|
||||
let zeros = decode_ones(num_leading_ones);
|
||||
let rest_arr = decode_rest(rest);
|
||||
let pre_result = zeros.concat(rest_arr);
|
||||
return new Uint8Array(pre_result);
|
||||
}
|
||||
/**
|
||||
* count the number of leading 1 characters in a uint8array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function nlo(base58) {
|
||||
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) {
|
||||
let zeros = [];
|
||||
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) {
|
||||
let result_bignum = base58_to_bigint(base58);
|
||||
let result = bigint_to_base256(result_bignum);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Convert a base58 string to a bignum
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function base58_to_bigint(base58) {
|
||||
let acc_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) {
|
||||
let arr_reverse = [];
|
||||
while (q !== 0n) {
|
||||
let r = 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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=b58.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-12
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Base64 Utility Functions in TypeScript
|
||||
*/
|
||||
export { encode, decode };
|
||||
/**
|
||||
* Encode an array of bytes as a Uint8Array in base64 notation.
|
||||
*/
|
||||
declare function encode(bytes: Uint8Array): string;
|
||||
/**
|
||||
* Decode a base64-encoded string
|
||||
*/
|
||||
declare function decode(base64_str: string): Uint8Array;
|
||||
-1
File diff suppressed because one or more lines are too long
-20
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Binary utilities
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export { bytes_to_bigint, bigint_to_bytes };
|
||||
/**
|
||||
* Convert a byte array to a bigint
|
||||
*
|
||||
* Equivalent to `binary:decode_unsigned/1` from Erlang
|
||||
*/
|
||||
declare function bytes_to_bigint(bytes: Uint8Array): bigint;
|
||||
/**
|
||||
* Convert a bigint to a byte array
|
||||
*
|
||||
* Equivalent to `binary:encode_unsigned/1` from Erlang
|
||||
*
|
||||
* Requires input to be positive
|
||||
*/
|
||||
declare function bigint_to_bytes(q: bigint): Uint8Array;
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Binary utilities
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export { bytes_to_bigint, bigint_to_bytes };
|
||||
/**
|
||||
* Convert a byte array to a bigint
|
||||
*
|
||||
* Equivalent to `binary:decode_unsigned/1` from Erlang
|
||||
*/
|
||||
function bytes_to_bigint(bytes) {
|
||||
let n = 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) {
|
||||
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);
|
||||
}
|
||||
//# sourceMappingURL=bin.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,eAAe,EACf,eAAe,EAClB,CAAC;AAGF;;;;GAIG;AACH,SACA,eAAe,CACV,KAAiB;IAGlB,IAAI,CAAC,GAAY,EAAE,CAAC;IACpB,KAAK,IAAI,CAAC,IAAI,KAAK,EAAE;QACjB,uBAAuB;QACvB,8BAA8B;QAC9B,sCAAsC;QACtC,CAAC,KAAK,EAAE,CAAC;QACT,CAAC,IAAK,MAAM,CAAC,CAAC,CAAC,CAAC;KACnB;IACD,OAAO,CAAC,CAAC;AACb,CAAC;AAID;;;;;;GAMG;AACH,SACA,eAAe,CACV,CAAS;IAGV,IAAI,CAAC,GAAG,EAAE,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;KACnC;IAED,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,OAAO,CAAC,GAAG,EAAE,EAAE;QACX,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACzB,CAAC,IAAI,IAAI,CAAC;QACV,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;KACvB;IACD,WAAW,CAAC,OAAO,EAAE,CAAC;IACtB,OAAO,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;AACvC,CAAC"}
|
||||
-1
File diff suppressed because one or more lines are too long
-66
@@ -1,66 +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
|
||||
*/
|
||||
declare type Safe<ok_t, err_t> = Ok<ok_t> | Error<err_t>;
|
||||
/**
|
||||
* Ok type
|
||||
*/
|
||||
declare type Ok<ok_t> = {
|
||||
ok: true;
|
||||
result: ok_t;
|
||||
};
|
||||
/**
|
||||
* Error type
|
||||
*/
|
||||
declare type Error<err_t> = {
|
||||
ok: false;
|
||||
error: err_t;
|
||||
};
|
||||
/**
|
||||
* Constructs an `Ok` value from a pure value
|
||||
*/
|
||||
declare function ok<ok_t>(x: ok_t): Ok<ok_t>;
|
||||
/**
|
||||
* Constructs an `Error` value from a pure value
|
||||
*/
|
||||
declare function error<err_t>(x: err_t): Error<err_t>;
|
||||
/**
|
||||
* Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the
|
||||
* `err_t`
|
||||
*/
|
||||
declare function unsafe<ok_t, err_t>(x: Safe<ok_t, err_t>): ok_t;
|
||||
-58
@@ -1,58 +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 { ok, error, unsafe };
|
||||
/**
|
||||
* Constructs an `Ok` value from a pure value
|
||||
*/
|
||||
function ok(x) {
|
||||
return { ok: true, result: x };
|
||||
}
|
||||
/**
|
||||
* Constructs an `Error` value from a pure value
|
||||
*/
|
||||
function error(x) {
|
||||
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(x) {
|
||||
if (x.ok)
|
||||
return x.result;
|
||||
else
|
||||
throw x.error;
|
||||
}
|
||||
//# sourceMappingURL=safe.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"safe.js","sourceRoot":"","sources":["../src/safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAIH,EAAE,EACF,KAAK,EACL,MAAM,EACT,CAAA;AA2BD;;GAEG;AACH,SACA,EAAE,CAEG,CAAQ;IAGT,OAAO,EAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAC,CAAC;AACjC,CAAC;AAID;;GAEG;AACH,SACA,KAAK,CAEA,CAAQ;IAGT,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAC,CAAC;AACjC,CAAC;AAID;;;GAGG;AACH,SACA,MAAM,CAED,CAAoB;IAGrB,IAAI,CAAC,CAAC,EAAE;QACJ,OAAO,CAAC,CAAC,MAAM,CAAC;;QAEhB,MAAM,CAAC,CAAC,KAAK,CAAC;AACtB,CAAC"}
|
||||
-403
@@ -1,403 +0,0 @@
|
||||
/**
|
||||
* Node API constructor/deconstructor
|
||||
*
|
||||
* This is similar to serialization/deserialization, but not quite the same
|
||||
* thing. It converts back and forth between different forms of
|
||||
* "api-serialized" data.
|
||||
*
|
||||
* References:
|
||||
* 1. https://github.com/aeternity/protocol/blob/master/serializations.md
|
||||
* 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md
|
||||
*
|
||||
* ## General type rules
|
||||
*
|
||||
* ```
|
||||
* ERLANG TYPE | JS TYPE
|
||||
* -------------------------------
|
||||
* integer | bigint
|
||||
* list | Array
|
||||
* binary | Uint8Array
|
||||
* ```
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`.
|
||||
*
|
||||
* We can tell by the `tx_` prefix that this represents transaction data of
|
||||
* some sort. But the rest of the data is totally opaque. The task of this
|
||||
* module is to "humanize" that `tx_...` string and show what data is contained
|
||||
* in the rest of it.
|
||||
*
|
||||
* The remainder of the string is a base64-encoded bytestring
|
||||
*
|
||||
* ```erlang
|
||||
* 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]).
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>>
|
||||
* ```
|
||||
*
|
||||
* That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I
|
||||
* wrote an RLP decoder. RLP has two types of data: binaries, and
|
||||
* arbitrary-depth (possibly empty) lists of binaries.
|
||||
*
|
||||
* ```erlang
|
||||
* -type decoded_data() :: binary() | [decoded_data()].
|
||||
*
|
||||
* -spec decode(RLP) -> {Data, Rest}
|
||||
* when RLP :: binary(),
|
||||
* Data :: decoded_data(),
|
||||
* Rest :: binary().
|
||||
* ```
|
||||
*
|
||||
* ```erlang
|
||||
* 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)).
|
||||
* {[<<"\f">>,
|
||||
* <<1>>,
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,...>>,
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,...>>,
|
||||
* <<"\n">>,
|
||||
* <<15,38,245,97,200,0>>,
|
||||
* <<0>>,
|
||||
* <<"\t">>,<<"hainana">>],
|
||||
* <<3,182,66,215>>}
|
||||
* ```
|
||||
*
|
||||
* As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning
|
||||
*
|
||||
* ```erlang
|
||||
* 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>).
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,
|
||||
* 127,198,64,232,35,118,68,68,244,48,85,218,93,...>>
|
||||
* 4> SizeX = byte_size(X).
|
||||
* 94
|
||||
* 6> <<RLPEncodedData:(SizeX - 4)/binary, Hash/binary>> = X.
|
||||
* <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,
|
||||
* 127,198,64,232,35,118,68,68,244,48,85,218,93,...>>
|
||||
* 10> <<Check:4/binary, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)).
|
||||
* <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81,
|
||||
* 189,184,82,207,164,68,128,43,11,174,236,59,77,...>>
|
||||
* 11> Hash.
|
||||
* <<3,182,66,215>>
|
||||
* 12> Check.
|
||||
* <<3,182,66,215>>
|
||||
* ```
|
||||
*
|
||||
* What we're really interested in is `Data`
|
||||
*
|
||||
* ```erlang
|
||||
* 14> {Data, _} = rlp:decode(X).
|
||||
* {[<<"\f">>,
|
||||
* <<1>>,
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,...>>,
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,...>>,
|
||||
* <<"\n">>,
|
||||
* <<15,38,245,97,200,0>>,
|
||||
* <<0>>,
|
||||
* <<"\t">>,<<"hainana">>],
|
||||
* <<3,182,66,215>>}
|
||||
* ```
|
||||
*
|
||||
* `Data` is a list. The first field `<<"\f">>` is meant to be an integer which
|
||||
* tells us what type of data this is.
|
||||
*
|
||||
* ```erlang
|
||||
* 16> $\f.
|
||||
* 12
|
||||
* ```
|
||||
*
|
||||
* If we look at our table
|
||||
* (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags),
|
||||
* we see that a value of `12` is a spend transaction.
|
||||
*
|
||||
* The second field `<<1>>` tells us the "version" of the field orderings,
|
||||
* which we can ignore for now.
|
||||
*
|
||||
* The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction)
|
||||
*
|
||||
* ```erlang
|
||||
* [ <sender> :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE"
|
||||
* , <recipient> :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
||||
* , <amount> :: int() % <<"\n">> "=" 10
|
||||
* , <fee> :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000
|
||||
* , <ttl> :: int() % <<0>> "=" 0
|
||||
* , <nonce> :: int() % <<"\t">> "=" 9
|
||||
* , <payload> :: binary() % <<"hainana">> "=" "hainana"
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Our task here is to be able to pull apart the "tx_..." string into its fields.
|
||||
*
|
||||
* Converting the binaries to integers is pretty trivial. The only mildly
|
||||
* annoying thing is the `id` type.
|
||||
*
|
||||
* `id`s have two fields: a single-byte prefix which says which type of ID it
|
||||
* is. In this case, both `id`s have a prefix of `1`, which means they are both
|
||||
* normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other
|
||||
* options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or
|
||||
* names (prefix `2`/`nm_`)
|
||||
*
|
||||
* To "api-encode" the name, we first pick the appropriate prefix based on the
|
||||
* first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then
|
||||
* double-SHA'd to get the 4-byte check suffix
|
||||
*
|
||||
* ```erlang
|
||||
* 30> SenderBytes = lists:nth(3, Data).
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,120,164,154,55,...>>
|
||||
* 31> <<1, SenderAddrBytes/binary>> = SenderBytes.
|
||||
* <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,
|
||||
* 118,68,68,244,48,85,218,93,85,120,164,154,55,...>>
|
||||
* 32> DoubleSha = fun(Bytes) -> <<Foo:4/binary, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end.
|
||||
* #Fun<erl_eval.44.97283095>
|
||||
* 33> "ak_" ++ b58:enc(<<SenderAddrBytes/binary, (DoubleSha(SenderAddrBytes))/binary>>).
|
||||
* "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE"
|
||||
* 34> RecipBytes = lists:nth(4, Data).
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,7,3,234,139,205,...>>
|
||||
* 35> <<1, RecipAddrBytes/binary>> = RecipBytes.
|
||||
* <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,
|
||||
* 89,203,191,216,108,88,186,7,3,234,139,205,...>>
|
||||
* 36> "ak_" ++ b58:enc(<<RecipAddrBytes/binary, (DoubleSha(RecipAddrBytes))/binary>>).
|
||||
* "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==")
|
||||
* {tag : 'SpendTx',
|
||||
* version : 1n,
|
||||
* fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE",
|
||||
* recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv",
|
||||
* amount : 10n,
|
||||
* fee : 16660000000000n,
|
||||
* ttl : 0n,
|
||||
* nonce : 9n,
|
||||
* payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}}
|
||||
* ```
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
export {
|
||||
// types
|
||||
tx_str,
|
||||
deconstructed_tx,
|
||||
// functions
|
||||
deconstruct_tx
|
||||
};
|
||||
|
||||
import * as b64 from './b64.js'
|
||||
import * as bin from './bin.js'
|
||||
import * as rlp from './rlp.js'
|
||||
|
||||
|
||||
/**
|
||||
* Alias type for a `tx_...` string
|
||||
*/
|
||||
type tx_str = string;
|
||||
|
||||
/**
|
||||
* Alias type for a `sg_...` string
|
||||
*/
|
||||
type sg_str = string;
|
||||
|
||||
/**
|
||||
* types of decoded tx we currently support
|
||||
* @internal
|
||||
*/
|
||||
type tx_type_str
|
||||
= 'SignedTx'
|
||||
| 'SpendTx'
|
||||
| 'ContractCreateTx'
|
||||
| 'ContractCallTx';
|
||||
|
||||
|
||||
/**
|
||||
* Results of deconstruct_tx
|
||||
*/
|
||||
type deconstructed_tx
|
||||
= {type : 'SignedTx',
|
||||
version : bigint,
|
||||
fields : fields_SignedTx}
|
||||
| {type : 'SpendTx',
|
||||
version : bigint,
|
||||
fields : fields_SpendTx}
|
||||
| {type : 'ContractCreateTx',
|
||||
version : bigint,
|
||||
fields : fields_ContractCreateTx}
|
||||
| {type : 'ContractCallTx'
|
||||
version : bigint,
|
||||
fields : fields_ContractCallTx};
|
||||
|
||||
/**
|
||||
* Convenient type alias
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type rlpdata = rlp.decoded_data;
|
||||
|
||||
|
||||
/**
|
||||
* Fields types
|
||||
*/
|
||||
type fields
|
||||
= fields_SignedTx
|
||||
| fields_SpendTx
|
||||
| fields_ContractCreateTx
|
||||
| fields_ContractCallTx;
|
||||
|
||||
type fields_SignedTx
|
||||
= {signatures : Array<sg_str>,
|
||||
transaction : tx_str};
|
||||
|
||||
type fields_SpendTx
|
||||
= {sender : string,
|
||||
recipient : string,
|
||||
amount : bigint,
|
||||
fee : bigint,
|
||||
ttl : bigint,
|
||||
nonce : bigint,
|
||||
payload : Uint8Array};
|
||||
|
||||
/**
|
||||
* Deconstruct a Tx
|
||||
*/
|
||||
function
|
||||
deconstruct_tx
|
||||
(tx_str: tx_str)
|
||||
: deconstructed_tx
|
||||
{
|
||||
let b64_str : string = tx_str.slice(3); // tx_[...] -> [...]
|
||||
let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes
|
||||
let tx_data : Array<rlpdata> = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing
|
||||
let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint
|
||||
let tts : tx_type_str = tx_type_str(tx_type);
|
||||
let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]);
|
||||
let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2));
|
||||
return {type : tts,
|
||||
version : tx_version,
|
||||
fields : tx_fields};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data that's "api-encoded" goes through the following stages:
|
||||
*
|
||||
* 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings)
|
||||
* 2. rlp decode data -> bytestring
|
||||
* 3. bytestring -> <<Bytestring/binary, Hash:4/binary>>
|
||||
* 4. HashedBytestring -> base64/base58 string encoding
|
||||
* 5. Add string prefix
|
||||
*
|
||||
* This function undoes step 3 and step 2, returns back the rlp decode data
|
||||
*
|
||||
* FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures
|
||||
* FIXME: No good way to handle failure cases
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
shasha_rlp_decode_list
|
||||
(hashed_bs : Uint8Array)
|
||||
: Array<rlpdata>
|
||||
{
|
||||
let len = hashed_bs.length;
|
||||
let bytes = hashed_bs.slice(0, len - 4);
|
||||
let {decoded_data} = rlp.decode(bytes);
|
||||
return (decoded_data as Array<rlpdata>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert an object tag that's a type of transaction to the type string
|
||||
*
|
||||
* See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
tx_type_str
|
||||
(tx_type_int : bigint)
|
||||
: tx_type_str
|
||||
{
|
||||
switch (tx_type_int)
|
||||
{
|
||||
case 11n: return 'SignedTx';
|
||||
case 12n: return 'SpendTx';
|
||||
case 42n: return 'ContractCreateTx';
|
||||
case 43n: return 'ContractCallTx';
|
||||
default: throw new Error('invalid transaction type: ' + tx_type_int);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Given an array of data decoded from RLP, convert it to the fields, as
|
||||
* appropriate as given by the tx type string and the version
|
||||
*/
|
||||
function
|
||||
deconstruct_fields
|
||||
(tx_type_str : tx_type_str,
|
||||
tx_version : bigint,
|
||||
tx_rawfields : Array<rlpdata>)
|
||||
: fields
|
||||
{
|
||||
switch (tx_type_str)
|
||||
{
|
||||
// case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields);
|
||||
case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields);
|
||||
// case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields);
|
||||
// case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields);
|
||||
default : throw new Error('invalid tx type str: ' + tx_type_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: do all this in Erlang
|
||||
function
|
||||
deconstruct_fields_SpendTx
|
||||
(fields: Array<rlpdata>)
|
||||
: fields_SpendTx
|
||||
{
|
||||
let sender_bytes = fields[0];
|
||||
let recip_bytes = fields[1];
|
||||
let amount_bytes = fields[2];
|
||||
let fee_bytes = fields[3];
|
||||
let ttl_bytes = fields[4];
|
||||
let nonce_bytes = fields[5];
|
||||
let payload_bytes = fields[6];
|
||||
return {sender : encode_id(sender_bytes),
|
||||
recipient : encode_id(sender_bytes),
|
||||
amount : bin.bytes_to_bigint(amount_bytes),
|
||||
fee : bin.bytes_to_bigint(fee_bytes),
|
||||
ttl : bin.bytes_to_bigint(ttl_bytes),
|
||||
nonce : bin.bytes_to_bigint(nonce_bytes),
|
||||
payload : bin.bytes_to_bigint(payload_bytes)};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a binary account/name/etc binary id into the appropriate type of string
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_id
|
||||
(id: Uint8Array)
|
||||
: string
|
||||
{
|
||||
throw new Error('nyi');
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME:
|
||||
1. work out all this in Erlang to clear conceptual goo
|
||||
2. think about how i want type safety etc to work
|
||||
3. think about a language to assert that the data has the correct shape to it
|
||||
4. get some examples working in Erlang
|
||||
5. convert erlang code back to ts
|
||||
*/
|
||||
-418
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Binary utilities
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
export {
|
||||
bytes_to_bigint,
|
||||
bigint_to_bytes
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
-112
@@ -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;
|
||||
}
|
||||
+1
-1
@@ -305,4 +305,4 @@ function char_to_bigint(s) {
|
||||
throw new Error('invalid base58 char: ' + s);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=b58.js.map
|
||||
//# sourceMappingURL=vdk_base58.js.map
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -504,4 +504,4 @@ function char2int(s) {
|
||||
default: throw new Error("invalid base64 character: " + s);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=b64.js.map
|
||||
//# sourceMappingURL=vdk_base64.js.map
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -379,4 +379,4 @@ function is_binary(x) {
|
||||
function is_list(x) {
|
||||
return (x instanceof Array);
|
||||
}
|
||||
//# sourceMappingURL=rlp.js.map
|
||||
//# sourceMappingURL=vdk_rlp.js.map
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,5 +1,5 @@
|
||||
export declare const rlp: {
|
||||
decoded: import("./jex_include/local-vanillae-0.1.0/dist/rlp.js").decoded_data;
|
||||
decoded: import("./jex_include/local-vdk_rlp-0.1.0/dist/vdk_rlp.js").decoded_data;
|
||||
encoded: Uint8Array;
|
||||
}[];
|
||||
export declare const base58: {
|
||||
+1
-1
@@ -379,4 +379,4 @@ function is_binary(x) {
|
||||
function is_list(x) {
|
||||
return (x instanceof Array);
|
||||
}
|
||||
//# sourceMappingURL=rlp.js.map
|
||||
//# sourceMappingURL=vdk_rlp.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
import * as rlp from './jex_include/local-vanillae-0.1.0/dist/rlp.js';
|
||||
import * as rlp from './jex_include/local-vdk_rlp-0.1.0/dist/vdk_rlp.js';
|
||||
declare type rlpcases = {
|
||||
decoded: rlp.decoded_data;
|
||||
encoded: Uint8Array;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import * as rlp from './jex_include/local-vanillae-0.1.0/dist/rlp.js';
|
||||
import * as rlp from './jex_include/local-vdk_rlp-0.1.0/dist/vdk_rlp.js';
|
||||
|
||||
type rlpcases = {decoded: rlp.decoded_data, encoded: Uint8Array};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// test cases
|
||||
import * as cases from './jex_include/local-vanillae_test_cases-0.1.0/dist/cases.js';
|
||||
import * as cases from './jex_include/local-vdk_tests_cases-0.1.0/dist/cases.js';
|
||||
|
||||
// vanillae libs
|
||||
import * as b64 from './jex_include/local-vanillae-0.1.0/dist/b64.js';
|
||||
import * as b58 from './jex_include/local-vanillae-0.1.0/dist/b58.js';
|
||||
import * as rlp from './jex_include/local-vanillae-0.1.0/dist/rlp.js';
|
||||
import * as b64 from './jex_include/local-vdk_base64-0.1.0/dist/vdk_base64.js';
|
||||
import * as b58 from './jex_include/local-vdk_base58-0.1.0/dist/vdk_base58.js';
|
||||
import * as rlp from './jex_include/local-vdk_rlp-0.1.0/dist/vdk_rlp.js';
|
||||
|
||||
|
||||
// TODO: move this to rlp library
|
||||
|
||||
Reference in New Issue
Block a user