vdk_base58 appears done
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{type, library}.
|
{type, library}.
|
||||||
{realm, local}.
|
{realm, local}.
|
||||||
{name, vdk}.
|
{name, vdk_base58}.
|
||||||
{version, "0.1.0"}.
|
{version, "0.1.0"}.
|
||||||
{deps, []}.
|
{deps, []}.
|
||||||
|
|||||||
@@ -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,377 +0,0 @@
|
|||||||
/**
|
|
||||||
* Miscellaneous binary utility functions
|
|
||||||
*
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
export {
|
|
||||||
bytes_to_bigint,
|
|
||||||
bigint_to_bytes,
|
|
||||||
concat,
|
|
||||||
strong_rand_bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenate two arrays
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
concat
|
|
||||||
(arr1 : Uint8Array,
|
|
||||||
arr2 : Uint8Array)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
let len1 : number = arr1.length;
|
|
||||||
let len2 : number = arr2.length;
|
|
||||||
let arr1_idx0_offset : number = 0;
|
|
||||||
let arr2_idx0_offset : number = len1;
|
|
||||||
let result_len : number = len1 + len2;
|
|
||||||
let result : Uint8Array = new Uint8Array(result_len);
|
|
||||||
// copy first array into result
|
|
||||||
for (let arr1_idx0 = 0;
|
|
||||||
arr1_idx0 < len1;
|
|
||||||
arr1_idx0++)
|
|
||||||
{
|
|
||||||
// no offset here
|
|
||||||
let result_idx0 : number = arr1_idx0 + arr1_idx0_offset;
|
|
||||||
result[result_idx0] = arr1[arr1_idx0];
|
|
||||||
}
|
|
||||||
// copy second array into result
|
|
||||||
for (let arr2_idx0 = 0;
|
|
||||||
arr2_idx0 < len2;
|
|
||||||
arr2_idx0++)
|
|
||||||
{
|
|
||||||
// offset by the length of the first array
|
|
||||||
let result_idx0 : number = arr2_idx0 + arr2_idx0_offset;
|
|
||||||
result[result_idx0] = arr2[arr2_idx0];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cryptographically random bytes
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
strong_rand_bytes
|
|
||||||
(how_many : number)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
let arr = new Uint8Array(how_many);
|
|
||||||
(new Crypto()).getRandomValues(arr);
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a byte array to a bigint
|
|
||||||
*
|
|
||||||
* Equivalent to `binary:decode_unsigned/1` from Erlang
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bytes_to_bigint
|
|
||||||
(bytes: Uint8Array)
|
|
||||||
: bigint
|
|
||||||
{
|
|
||||||
let n : bigint = 0n;
|
|
||||||
for (let b of bytes) {
|
|
||||||
// move first, then add
|
|
||||||
// otherwise it ends on a move
|
|
||||||
// imperative languages are for losers
|
|
||||||
n <<= 8n;
|
|
||||||
n += BigInt(b);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a bigint to a byte array
|
|
||||||
*
|
|
||||||
* Equivalent to `binary:encode_unsigned/1` from Erlang
|
|
||||||
*
|
|
||||||
* Requires input to be positive
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bigint_to_bytes
|
|
||||||
(q: bigint)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
if (q < 0n) {
|
|
||||||
throw new Error('q < 0n: ' + q);
|
|
||||||
}
|
|
||||||
|
|
||||||
let arr_reverse = [];
|
|
||||||
while (q > 0n) {
|
|
||||||
let r = Number(q % 256n);
|
|
||||||
q /= 256n;
|
|
||||||
arr_reverse.push(r);
|
|
||||||
}
|
|
||||||
arr_reverse.reverse();
|
|
||||||
return new Uint8Array(arr_reverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Oh no, bitstrings in a language that only has bytestrings
|
|
||||||
*
|
|
||||||
* By convention these are `Uint8Array`s with byte length `ceil(bit_length /
|
|
||||||
* 8)`, and all trailing bits are zero.
|
|
||||||
*/
|
|
||||||
type bits =
|
|
||||||
{bit_length : number,
|
|
||||||
bytes : Uint8Array};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an uninitialized bitstring
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bits_null
|
|
||||||
(bit_length : number)
|
|
||||||
: bits
|
|
||||||
{
|
|
||||||
let byte_length : number = Math.ceil(bit_length / 8);
|
|
||||||
let result : Uint8Array = new Uint8Array(byte_length);
|
|
||||||
return {bit_length : bit_length,
|
|
||||||
bytes : result};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a bitstring of a given length where every value is 0.
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bits_zeros
|
|
||||||
(bit_length : number)
|
|
||||||
: bits
|
|
||||||
{
|
|
||||||
let byte_length : number = Math.ceil(bit_length / 8);
|
|
||||||
let result : Uint8Array = new Uint8Array(byte_length);
|
|
||||||
for (let i0 = 0;
|
|
||||||
i0 < byte_length;
|
|
||||||
i0++)
|
|
||||||
{
|
|
||||||
result[i0] = 0;
|
|
||||||
}
|
|
||||||
return {bit_length : bit_length,
|
|
||||||
bytes : result};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a bitstring of a given length where every value is 1.
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bits_ones
|
|
||||||
(bit_length : number)
|
|
||||||
: bits
|
|
||||||
{
|
|
||||||
let byte_length : number = Math.ceil(bit_length / 8);
|
|
||||||
let result : Uint8Array = new Uint8Array(byte_length);
|
|
||||||
|
|
||||||
// fill everything except the last byte with 255s
|
|
||||||
for (let i0 = 0;
|
|
||||||
i0 < (byte_length - 1);
|
|
||||||
i0++)
|
|
||||||
{
|
|
||||||
result[i0] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
// alright so the last byte
|
|
||||||
// ok so the number of leading 0s is
|
|
||||||
// 8 - (bit_length % 8)
|
|
||||||
let num_trailing_zero_bits : number = 8 - (bit_length % 8);
|
|
||||||
// the trailing byte is 255 << that
|
|
||||||
// e.g. 3 trailing 0s
|
|
||||||
// 1111_1111 -> 1111_1000
|
|
||||||
let last_byte : number = 255 << num_trailing_zero_bits;
|
|
||||||
let last_byte_idx0 : number = byte_length - 1;
|
|
||||||
result[last_byte_idx0] = last_byte;
|
|
||||||
|
|
||||||
return {bit_length : bit_length,
|
|
||||||
bytes : result};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the bit at a given 0-index
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bits_i0th
|
|
||||||
(bit_idx0 : number,
|
|
||||||
bits : bits)
|
|
||||||
: number
|
|
||||||
{
|
|
||||||
// first task is figuring out what byte we're at
|
|
||||||
// for instance if we want bit 27
|
|
||||||
// 3*8 = 24 =< 27 < 4*8
|
|
||||||
// so it's Math.floor(bit_idx0 / 8)
|
|
||||||
let byte_idx0 : number = Math.floor(bit_idx0 / 8);
|
|
||||||
// let's fetch the byte and work with that
|
|
||||||
let the_byte : number = bits.bytes[byte_idx0];
|
|
||||||
|
|
||||||
// ok so let's go with 27 again
|
|
||||||
// 27 = 3 mod 8
|
|
||||||
// so we bitshift right by (8 - 3)
|
|
||||||
// and then take the remainder dividing by 2
|
|
||||||
// --B-_---- -> ----_---B -> 0000_000B
|
|
||||||
let bsr : number = 8 - (bit_idx0 % 8);
|
|
||||||
return (the_byte >> bsr) % 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenate two bitstrings
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bits_concat
|
|
||||||
(bits1 : bits,
|
|
||||||
bits2 : bits)
|
|
||||||
: bits
|
|
||||||
{
|
|
||||||
let result_bit_length : number = bits1.bit_length + bits2.bit_length;
|
|
||||||
let bytes1 : Uint8Array = bits1.bytes;
|
|
||||||
let bytes2 : Uint8Array = bits2.bytes;
|
|
||||||
// using zeros here because of our xor trick in a minute
|
|
||||||
let result_bits : bits = bits_null(result_bit_length);
|
|
||||||
let result_bytes : Uint8Array = result_bits.bytes;
|
|
||||||
|
|
||||||
// go along each byte in result, and compute the byte boundary
|
|
||||||
for (let i = 0;
|
|
||||||
i < result_bytes.length;
|
|
||||||
i++)
|
|
||||||
{
|
|
||||||
// ABCD_EFGH _
|
|
||||||
// 0123_4567 8
|
|
||||||
// this is the bit index of the leftmost bit in this byte
|
|
||||||
let start_bit_bi0 : number = i * 8;
|
|
||||||
let next_start_bit_bi0 : number = start_bit_bi0 + 8;
|
|
||||||
// does the bit at the beginning of this byte correspond to the first array?
|
|
||||||
// strict comparison:
|
|
||||||
// suppose i = 0,
|
|
||||||
// suppose bit_length1 is 0
|
|
||||||
// then this says no, go to second array
|
|
||||||
// suppose bl1 = 1,
|
|
||||||
// this says start at first array
|
|
||||||
let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length;
|
|
||||||
// weak comparison:
|
|
||||||
// suppose bit_length1 = 8
|
|
||||||
// ABCD_EFGH _
|
|
||||||
// 0123_4567 8
|
|
||||||
// ^
|
|
||||||
// start_bit_bi0 ^ next_start_bit_bi0
|
|
||||||
let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length;
|
|
||||||
// is this a bytes1 byte
|
|
||||||
let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array;
|
|
||||||
let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array;
|
|
||||||
|
|
||||||
// need to work out the ping_pong bs up here because js is dumb and I
|
|
||||||
// can't put lets between elseifs
|
|
||||||
// alright, now we're in the case of only copying from the second array
|
|
||||||
// we have two cases:
|
|
||||||
// ping-pong:
|
|
||||||
// ABCD_EFGH 1234_5678
|
|
||||||
// - ---- ---
|
|
||||||
// copying these bits
|
|
||||||
// ping:
|
|
||||||
// ABCD_EFGH <end>
|
|
||||||
// - ---- 000
|
|
||||||
//
|
|
||||||
// how to distinguish between these two??
|
|
||||||
//
|
|
||||||
// we're in the ping case when the start_bit_bi0 corresponds to the
|
|
||||||
// final byte of the second array
|
|
||||||
//
|
|
||||||
// ok
|
|
||||||
//
|
|
||||||
// we need to compute the bit address in the second array that
|
|
||||||
// corresponds to the bit address at the beginning of this byte in the
|
|
||||||
// result array
|
|
||||||
let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length;
|
|
||||||
// I think the variable is
|
|
||||||
// bits_left_to_copy = bits2.bit_length - bit_addr2_bi0
|
|
||||||
// no + 1 because the current bit is uncopied,
|
|
||||||
// so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we
|
|
||||||
// have the last bit to copy
|
|
||||||
// cases:
|
|
||||||
// bits_left_to_copy <= 0 ->
|
|
||||||
// this would mean we have more bits to copy, but are out of
|
|
||||||
// source bits. should be impossible if bits_null is correct
|
|
||||||
// bits_left_to_copy <= 8 ->
|
|
||||||
// this would mean we are going to fill this last byte in the
|
|
||||||
// result array with the correct bits from array2, but how we
|
|
||||||
// do this will depend on how those are arranged in bytes2
|
|
||||||
// (whether we grab one or two bytes)
|
|
||||||
//
|
|
||||||
// this is the tricky case
|
|
||||||
//
|
|
||||||
// I think what matters here is the byte address in the second array
|
|
||||||
// if we're on the last byte
|
|
||||||
// 8 < bits_left_to_copy ->
|
|
||||||
// this means we can safely grab two bytes from bytes2, and do
|
|
||||||
// our bitshifting to make it correct
|
|
||||||
//
|
|
||||||
// FIXME
|
|
||||||
let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0;
|
|
||||||
// no the variable that matters is which byte we're on in the result
|
|
||||||
let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8);
|
|
||||||
let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1;
|
|
||||||
let bitshift_amt : number = bits1.bit_length % 8;
|
|
||||||
|
|
||||||
let ping : boolean = next_bytes2_addr_i0 === bytes2.length;
|
|
||||||
|
|
||||||
|
|
||||||
// simple case: this is a byte from the first array
|
|
||||||
// just copy it
|
|
||||||
if (is_bytes1_byte)
|
|
||||||
result_bytes[i] = bytes1[i];
|
|
||||||
// this is a boundary byte
|
|
||||||
// further cases:
|
|
||||||
// second bit length is 0 ->
|
|
||||||
// ABCD_EF-- <empty>
|
|
||||||
// ^ starting here
|
|
||||||
// just copy first byte and move along
|
|
||||||
else if (is_boundary_byte && (bits2.bit_length === 0))
|
|
||||||
result_bytes[i] = bytes1[i];
|
|
||||||
// boundary byte, and there is at least one byte in the second array
|
|
||||||
else if (is_boundary_byte)
|
|
||||||
{
|
|
||||||
// copy over the first byte
|
|
||||||
result_bytes[i] = bytes1[i];
|
|
||||||
// take the first byte from the second array
|
|
||||||
let first_byte_of_second_array : number = bytes2[0];
|
|
||||||
// bytes1:
|
|
||||||
// ABCD_EF00
|
|
||||||
// 6
|
|
||||||
// bytes2:
|
|
||||||
// 1234_5678
|
|
||||||
// 0000_0012
|
|
||||||
// and bitshift it right by that amount
|
|
||||||
let bitshift_amt : number = bits1.bit_length % 8;
|
|
||||||
result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt;
|
|
||||||
}
|
|
||||||
// last byte of second array
|
|
||||||
else if (ping)
|
|
||||||
result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt);
|
|
||||||
// ping-pong: not last byte of second array
|
|
||||||
else
|
|
||||||
result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {bit_length : result_bit_length,
|
|
||||||
bytes : result_bytes};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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,521 +0,0 @@
|
|||||||
/**
|
|
||||||
* Ethereum Recursive-Length Prefix Encoding implementation
|
|
||||||
*
|
|
||||||
* Two reference implementations:
|
|
||||||
*
|
|
||||||
* 1. Ethereum Python implementation
|
|
||||||
* 2. My Erlang implementation (agrees with Ethereum Python in randomized
|
|
||||||
* tests)
|
|
||||||
*
|
|
||||||
* This work can be found in ../test/testgen/
|
|
||||||
*
|
|
||||||
* FIXME: need to have Safe assertions for "i am decoding a list", "i am
|
|
||||||
* decoding a binary", "I am decoding something with no remainder", etc
|
|
||||||
*
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
export {
|
|
||||||
decoded_data,
|
|
||||||
decode_result,
|
|
||||||
decode,
|
|
||||||
encode
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
//=============================================================================
|
|
||||||
// DECODING
|
|
||||||
//=============================================================================
|
|
||||||
//=============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data that RLP can encode. Also the result of decoding.
|
|
||||||
*/
|
|
||||||
type decoded_data
|
|
||||||
= Uint8Array
|
|
||||||
| Array<decoded_data>;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoding can have a remainder
|
|
||||||
*/
|
|
||||||
type decode_result
|
|
||||||
= {decoded_data : decoded_data,
|
|
||||||
remainder : Uint8Array};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode an RLP-encoded bytestring into a `decode_result` (decoded data +
|
|
||||||
* whatever wasn't consumed)
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
decode
|
|
||||||
(bytes: Uint8Array)
|
|
||||||
: decode_result
|
|
||||||
{
|
|
||||||
// check the first byte
|
|
||||||
let first_byte: number = bytes[0];
|
|
||||||
let rest : Uint8Array = bytes.slice(1);
|
|
||||||
// if the first byte is between 0 and 127, that is the data
|
|
||||||
if
|
|
||||||
(first_byte <= 127) {
|
|
||||||
return dr(new Uint8Array([first_byte]), rest);
|
|
||||||
}
|
|
||||||
// if the first byte is between 128 and 183 = 128 + 55, it is a bytestring
|
|
||||||
// and the length is Byte - 128
|
|
||||||
else if
|
|
||||||
(first_byte <= 183) {
|
|
||||||
let payload_byte_length : number = first_byte - 128;
|
|
||||||
let payload : Uint8Array = rest.slice(0, payload_byte_length);
|
|
||||||
let rest2 : Uint8Array = rest.slice(payload_byte_length);
|
|
||||||
return dr(payload, rest2);
|
|
||||||
}
|
|
||||||
// if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a
|
|
||||||
// bytestring. the byte length of bytestring is FirstByte - 183. Then pull
|
|
||||||
// out the actual data
|
|
||||||
else if
|
|
||||||
(first_byte <= 191) {
|
|
||||||
let byte_length_of_byte_length : number = first_byte - 183;
|
|
||||||
let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length);
|
|
||||||
let byte_length : number = bytes_to_number(bytes_of_byte_length);
|
|
||||||
let bytes : Uint8Array = rest.slice(byte_length_of_byte_length,
|
|
||||||
byte_length + byte_length_of_byte_length);
|
|
||||||
let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length);
|
|
||||||
return dr(bytes, rest2);
|
|
||||||
}
|
|
||||||
// If the first byte is between 192 and 247 = 192 + 55, it is a list. The
|
|
||||||
// byte length of the list-payload is FirstByte - 192. Then the list
|
|
||||||
// payload, which needs to be decoded on its own.
|
|
||||||
else if
|
|
||||||
(first_byte <= 247) {
|
|
||||||
let byte_length_of_list : number = first_byte - 192;
|
|
||||||
let list_payload : Uint8Array = rest.slice(0, byte_length_of_list);
|
|
||||||
let list : Array<decoded_data> = decode_list(list_payload);
|
|
||||||
let rest2 : Uint8Array = rest.slice(byte_length_of_list);
|
|
||||||
return dr(list, rest2);
|
|
||||||
}
|
|
||||||
// If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a
|
|
||||||
// list. The byte length of the byte length of the list-payload is
|
|
||||||
// FirstByte - 247. Then the byte length of the list. Then the list
|
|
||||||
// payload, which needs to be decoded on its own.
|
|
||||||
else {
|
|
||||||
let byte_length_of_byte_length : number = first_byte - 247;
|
|
||||||
let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length);
|
|
||||||
let byte_length : number = bytes_to_number(bytes_of_byte_length);
|
|
||||||
let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length,
|
|
||||||
byte_length + byte_length_of_byte_length);
|
|
||||||
let list : Array<decoded_data> = decode_list(list_bytes);
|
|
||||||
let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length);
|
|
||||||
return dr(list, rest2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a list payload (non-prefixed) into an `Array<decoded_data>`.
|
|
||||||
*
|
|
||||||
* Repeatedly decodes an element off the list until remainder is empty.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
decode_list
|
|
||||||
(bytes: Uint8Array)
|
|
||||||
: Array<decoded_data>
|
|
||||||
{
|
|
||||||
let arr : Array<decoded_data> = [];
|
|
||||||
while (bytes.length > 0) {
|
|
||||||
// grab an item off the bytes
|
|
||||||
let {decoded_data, remainder} = decode(bytes);
|
|
||||||
// push it
|
|
||||||
arr.push(decoded_data);
|
|
||||||
// update bytes
|
|
||||||
bytes = remainder;
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert bytestring to number
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
bytes_to_number
|
|
||||||
(bytes: Uint8Array)
|
|
||||||
: number
|
|
||||||
{
|
|
||||||
let n : number = 0;
|
|
||||||
for (let b of bytes)
|
|
||||||
{
|
|
||||||
n <<= 8;
|
|
||||||
n += b;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a `decode_result` containing the two arguments
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
dr
|
|
||||||
(x : decoded_data,
|
|
||||||
y : Uint8Array)
|
|
||||||
: decode_result
|
|
||||||
{
|
|
||||||
return {decoded_data : x,
|
|
||||||
remainder : y};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
//=============================================================================
|
|
||||||
// ENCODING
|
|
||||||
//=============================================================================
|
|
||||||
//=============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode some decoded data
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
encode
|
|
||||||
(data: decoded_data)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
// is it an array or data
|
|
||||||
if
|
|
||||||
(is_binary(data)) {
|
|
||||||
return encode_binary(data as Uint8Array);
|
|
||||||
}
|
|
||||||
else if
|
|
||||||
(is_list(data)) {
|
|
||||||
return encode_list(data as Array<decoded_data>);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('encode told to encode something that is not an array or a binary: ' + data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a binary into RLP
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
encode_binary
|
|
||||||
(bytes: Uint8Array)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
let len: number = bytes.length;
|
|
||||||
// single byte case when the byte is between 0..127
|
|
||||||
// result is the bytestring containing the byte itself
|
|
||||||
if
|
|
||||||
((len === 1) &&
|
|
||||||
(bytes[0] <= 127)){
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
// if the bytestring is 0..55 bytes long, the first byte in the result is
|
|
||||||
// 128 + Length, the rest of the result bytestring is the input string
|
|
||||||
else if
|
|
||||||
(len <= 55) {
|
|
||||||
// construct the result
|
|
||||||
// <<128 + Len, Bytes/binary>>
|
|
||||||
let result : Uint8Array = new Uint8Array(len + 1);
|
|
||||||
// first byte is 128 + length
|
|
||||||
result[0] = 128 + len;
|
|
||||||
// copy input bytes into result
|
|
||||||
for (let input_idx0 = 0;
|
|
||||||
input_idx0 < len;
|
|
||||||
input_idx0++)
|
|
||||||
{
|
|
||||||
let result_idx0 : number = input_idx0 + 1;
|
|
||||||
result[result_idx0] = bytes[input_idx0];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// if the bytestring is more than 55 bytes long, the first byte is 183 +
|
|
||||||
// ByteLengthOfByteLength, followed by the byte length, followed by the
|
|
||||||
// bytes
|
|
||||||
else {
|
|
||||||
let len_bytes : Uint8Array = encode_unsigned(len);
|
|
||||||
let len_bytes_length : number = len_bytes.length;
|
|
||||||
// total array is
|
|
||||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
|
||||||
// 1 byte len_bytes_length bytes len bytes
|
|
||||||
let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len);
|
|
||||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
|
||||||
// 1 byte len_bytes_length bytes len bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
result[0] = 183 + len_bytes_length;
|
|
||||||
// copy len_bytes into result
|
|
||||||
for (let len_bytes_idx0 = 0;
|
|
||||||
len_bytes_idx0 < len_bytes_length;
|
|
||||||
len_bytes_idx0++)
|
|
||||||
{
|
|
||||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
|
||||||
// 1 byte len_bytes_length bytes len bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
let result_idx0: number = len_bytes_idx0 + 1;
|
|
||||||
result[result_idx0] = len_bytes[len_bytes_idx0];
|
|
||||||
}
|
|
||||||
// copy original byte array into result
|
|
||||||
for (let input_idx0 = 0;
|
|
||||||
input_idx0 < len;
|
|
||||||
input_idx0++)
|
|
||||||
{
|
|
||||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
|
||||||
// 1 byte len_bytes_length bytes len bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
let result_idx0: number = input_idx0 + (1 + len_bytes_length);
|
|
||||||
result[result_idx0] = bytes[input_idx0];
|
|
||||||
}
|
|
||||||
// finally, return the result
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a list
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
encode_list
|
|
||||||
(list: Array<decoded_data>)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
// first encode every element in the list, then branch
|
|
||||||
let payloads : Array<Uint8Array> = list.map(encode);
|
|
||||||
let payload : Uint8Array = uint8arr_concat(payloads);
|
|
||||||
let payload_size : number = payload.length;
|
|
||||||
// if the payload size is in 0..55, then the first byte is 192 + payload
|
|
||||||
// size, followed by the payload
|
|
||||||
if
|
|
||||||
(payload_size <= 55) {
|
|
||||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
|
||||||
// 1 byte payload_size bytes
|
|
||||||
let result : Uint8Array = new Uint8Array(1 + payload_size);
|
|
||||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
|
||||||
// 1 byte payload_size bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
result[0] = 192 + payload_size;
|
|
||||||
// copy the rest of the payload into result
|
|
||||||
for (let payload_idx0 = 0;
|
|
||||||
payload_idx0 < payload_size;
|
|
||||||
payload_idx0++)
|
|
||||||
{
|
|
||||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
|
||||||
// 1 byte payload_size bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
let result_idx0: number = payload_idx0 + 1;
|
|
||||||
result[result_idx0] = payload[payload_idx0];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// if the payload size is greater than 55, the first byte is 247 +
|
|
||||||
// size_of_payload_size, followed by the payload size, followed by the
|
|
||||||
// payload
|
|
||||||
else {
|
|
||||||
// compute the payload size size
|
|
||||||
let payload_size_bytes : Uint8Array = encode_unsigned(payload_size);
|
|
||||||
let payload_size_size : number = payload_size_bytes.length;
|
|
||||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
|
||||||
// 1 byte payload_size_size bytes payload_size bytes
|
|
||||||
let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size);
|
|
||||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
|
||||||
// 1 byte payload_size_size bytes payload_size bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
// first byte is 247 + payload_size_size
|
|
||||||
result[0] = 247 + payload_size_size;
|
|
||||||
// copy the payload_size_bytes into result
|
|
||||||
for (let psb_idx0 = 0;
|
|
||||||
psb_idx0 < payload_size_size;
|
|
||||||
psb_idx0++)
|
|
||||||
{
|
|
||||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
|
||||||
// 1 byte payload_size_size bytes payload_size bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
// offset is 1 byte for this
|
|
||||||
let result_idx0 : number = psb_idx0 + 1;
|
|
||||||
result[result_idx0] = payload_size_bytes[psb_idx0];
|
|
||||||
}
|
|
||||||
// copy the payload into result
|
|
||||||
for (let pb_idx0 = 0;
|
|
||||||
pb_idx0 < payload_size;
|
|
||||||
pb_idx0++)
|
|
||||||
{
|
|
||||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
|
||||||
// 1 byte payload_size_size bytes payload_size bytes
|
|
||||||
//
|
|
||||||
// ^ YOU ARE HERE
|
|
||||||
// offset is 1 + payload_size_size bytes for this
|
|
||||||
let result_idx0 : number = pb_idx0 + (1 + payload_size_size);
|
|
||||||
result[result_idx0] = payload[pb_idx0];
|
|
||||||
}
|
|
||||||
// finally, return result
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a number as a bytestring
|
|
||||||
*
|
|
||||||
* Not for general use, this assumes the input number is greater than 55
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
encode_unsigned
|
|
||||||
(n: number)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
// can assume that n initially is greater than 55
|
|
||||||
// have to encode it in reverse order
|
|
||||||
let arr : Array<number> = [];
|
|
||||||
// repeated division by 256 with remainder
|
|
||||||
//
|
|
||||||
// example: suppose bign was 1234 and we were dividing by 10
|
|
||||||
//
|
|
||||||
// iteration 0:
|
|
||||||
// bign = 1234
|
|
||||||
// arr = []
|
|
||||||
// ---
|
|
||||||
// bign / 10 = 123
|
|
||||||
// bign % 10 = 4
|
|
||||||
// ---
|
|
||||||
// bign = 123
|
|
||||||
// arr = [4]
|
|
||||||
//
|
|
||||||
// iteration 1:
|
|
||||||
// bign = 123
|
|
||||||
// arr = [4]
|
|
||||||
// ---
|
|
||||||
// bign / 10 = 12
|
|
||||||
// bign % 10 = 3
|
|
||||||
// ---
|
|
||||||
// bign = 12
|
|
||||||
// arr = [4, 3]
|
|
||||||
//
|
|
||||||
// iteration 2:
|
|
||||||
// bign = 12
|
|
||||||
// arr = [4, 3]
|
|
||||||
// ---
|
|
||||||
// bign / 10 = 1
|
|
||||||
// bign % 10 = 2
|
|
||||||
// ---
|
|
||||||
// bign = 1
|
|
||||||
// arr = [4, 3, 2]
|
|
||||||
//
|
|
||||||
// iteration 3:
|
|
||||||
// bign = 1
|
|
||||||
// arr = [4, 3, 2]
|
|
||||||
// ---
|
|
||||||
// bign / 10 = 0
|
|
||||||
// bign % 10 = 1
|
|
||||||
// ---
|
|
||||||
// bign = 0
|
|
||||||
// arr = [4, 3, 2, 1]
|
|
||||||
//
|
|
||||||
// iteration 4:
|
|
||||||
// bign = 0
|
|
||||||
// arr = [4, 3, 2, 1]
|
|
||||||
// ---
|
|
||||||
// DONE
|
|
||||||
while (n > 0) {
|
|
||||||
arr.push(n % 256);
|
|
||||||
n >>= 8;
|
|
||||||
}
|
|
||||||
// reverse and make into Uint8Array
|
|
||||||
arr.reverse();
|
|
||||||
return new Uint8Array(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* concatenate an array of `Uint8Array`s into a single Uint8Array
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
uint8arr_concat
|
|
||||||
(arrs: Array<Uint8Array>)
|
|
||||||
: Uint8Array
|
|
||||||
{
|
|
||||||
// total length
|
|
||||||
let total_len = arrs.reduce(// fold
|
|
||||||
function (acc_len: number, this_uint8array: Uint8Array): number {
|
|
||||||
return acc_len + this_uint8array.length;
|
|
||||||
},
|
|
||||||
// initial accumulator
|
|
||||||
0);
|
|
||||||
// start up result
|
|
||||||
let result : Uint8Array = new Uint8Array(total_len);
|
|
||||||
let result_idx0 : number = 0;
|
|
||||||
// concatenate
|
|
||||||
for (let this_uint8arr of arrs) {
|
|
||||||
// bytewise copy of this uint8array
|
|
||||||
for (let this_uint8arr_idx0 = 0;
|
|
||||||
this_uint8arr_idx0 < this_uint8arr.length;
|
|
||||||
this_uint8arr_idx0++)
|
|
||||||
{
|
|
||||||
// copy byte and increment result idx0
|
|
||||||
result[result_idx0] = this_uint8arr[this_uint8arr_idx0];
|
|
||||||
result_idx0++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns true if input is `instanceof Uint8Array`
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
is_binary
|
|
||||||
(x: any)
|
|
||||||
: boolean
|
|
||||||
{
|
|
||||||
return (x instanceof Uint8Array);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns true if input is `instanceof Array`
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
is_list
|
|
||||||
(x: any)
|
|
||||||
: boolean
|
|
||||||
{
|
|
||||||
return (x instanceof Array);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user