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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"}
|
|
||||||
-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
-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
|
|
||||||
*/
|
|
||||||
-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);
|
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);
|
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) {
|
function is_list(x) {
|
||||||
return (x instanceof Array);
|
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: {
|
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;
|
encoded: Uint8Array;
|
||||||
}[];
|
}[];
|
||||||
export declare const base58: {
|
export declare const base58: {
|
||||||
+1
-1
@@ -379,4 +379,4 @@ function is_binary(x) {
|
|||||||
function is_list(x) {
|
function is_list(x) {
|
||||||
return (x instanceof Array);
|
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 = {
|
declare type rlpcases = {
|
||||||
decoded: rlp.decoded_data;
|
decoded: rlp.decoded_data;
|
||||||
encoded: Uint8Array;
|
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};
|
type rlpcases = {decoded: rlp.decoded_data, encoded: Uint8Array};
|
||||||
|
|
||||||
Vendored
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
// test cases
|
// 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
|
// vanillae libs
|
||||||
import * as b64 from './jex_include/local-vanillae-0.1.0/dist/b64.js';
|
import * as b64 from './jex_include/local-vdk_base64-0.1.0/dist/vdk_base64.js';
|
||||||
import * as b58 from './jex_include/local-vanillae-0.1.0/dist/b58.js';
|
import * as b58 from './jex_include/local-vdk_base58-0.1.0/dist/vdk_base58.js';
|
||||||
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';
|
||||||
// TODO: move this to rlp library
|
// TODO: move this to rlp library
|
||||||
function deepeq(dd1, dd2) {
|
function deepeq(dd1, dd2) {
|
||||||
// if both are Uint8Arrays
|
// if both are Uint8Arrays
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -2,5 +2,7 @@
|
|||||||
{realm, local}.
|
{realm, local}.
|
||||||
{name, vdk_tests}.
|
{name, vdk_tests}.
|
||||||
{version, "0.1.0"}.
|
{version, "0.1.0"}.
|
||||||
{deps, ["local-vdk-0.1.0",
|
{deps, ["local-vdk_base58-0.1.0",
|
||||||
"local-vdk_cases-0.1.0"]}.
|
"local-vdk_base64-0.1.0",
|
||||||
|
"local-vdk_rlp-0.1.0",
|
||||||
|
"local-vdk_tests_cases-0.1.0"]}.
|
||||||
|
|||||||
@@ -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;
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -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;
|
|
||||||
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,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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
-507
@@ -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
|
|
||||||
-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);
|
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user