[wip] code cleanups, making style uniform in vanillae ts
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
done:
|
done:
|
||||||
- base64/base58 encoding/decoding
|
- base64/base58 encoding/decoding
|
||||||
|
- rlp
|
||||||
|
|
||||||
|
today:
|
||||||
|
- pull apart data
|
||||||
|
|
||||||
tomorrow:
|
tomorrow:
|
||||||
|
|
||||||
- rlp
|
|
||||||
- pull apart data
|
|
||||||
- basic easy serialization/deserialization
|
- basic easy serialization/deserialization
|
||||||
- serialize/deserialize examples
|
- serialize/deserialize examples
|
||||||
- (maybe) pull in awcp/sidekick
|
- (maybe) pull in awcp/sidekick
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
const OTAG_SIGNED_TX = 11n;
|
||||||
|
const OTAG_SPEND_TX = 12n;
|
||||||
|
const OTAG_CONTRACT_CREATE_TX = 42n;
|
||||||
|
const OTAG_CONTRACT_CALL_TX = 43n;
|
||||||
|
|
||||||
|
type otag = 11n | 12n | 42n | 43n;
|
||||||
|
|
||||||
|
const IDTAG_ACCOUNT = 1n;
|
||||||
|
const IDTAG_NAME = 2n;
|
||||||
|
const IDTAG_CONTRACT = 5n;
|
||||||
|
|
||||||
|
type idtag = 1n | 2n | 5n;
|
||||||
|
|
||||||
|
|
||||||
|
type id =
|
||||||
|
{tag : idtag,
|
||||||
|
hash : Uint8Array};
|
||||||
|
|
||||||
|
type SignedTx =
|
||||||
|
{signatures : Array<Uint8Array>,
|
||||||
|
transaction : Uint8Array};
|
||||||
|
|
||||||
|
type SpendTx =
|
||||||
|
{sender : id,
|
||||||
|
recipient : id,
|
||||||
|
amount : bigint,
|
||||||
|
fee : bigint,
|
||||||
|
ttl : bigint,
|
||||||
|
nonce : bigint,
|
||||||
|
payload : Uint8Array};
|
||||||
|
|
||||||
|
type ContractCreateTx =
|
||||||
|
{owner : id,
|
||||||
|
nonce : bigint,
|
||||||
|
code : Uint8Array,
|
||||||
|
ct_version : bigint,
|
||||||
|
fee : bigint,
|
||||||
|
ttl : bigint,
|
||||||
|
deposit : bigint,
|
||||||
|
amount : bigint,
|
||||||
|
gas : bigint,
|
||||||
|
gas_price : bigint,
|
||||||
|
call_data : Uint8Array};
|
||||||
|
|
||||||
|
type ContractCallTx =
|
||||||
|
{caller : id,
|
||||||
|
nonce : bigint,
|
||||||
|
contract : id,
|
||||||
|
abi_version : bigint,
|
||||||
|
fee : bigint,
|
||||||
|
ttl : bigint,
|
||||||
|
amount : bigint,
|
||||||
|
gas : bigint,
|
||||||
|
gas_price : bigint,
|
||||||
|
call_data : Uint8Array};
|
||||||
|
|
||||||
|
type tx = SignedTx | SpendTx | ContractCreateTx | ContractCallTx;
|
||||||
|
|
||||||
|
type decoded_tx =
|
||||||
|
{tag : otag,
|
||||||
|
version : Uint8Array,
|
||||||
|
tx : tx};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a `tx_Base64` string
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
decode_tx(tx_str : string): decoded_tx {
|
||||||
|
let base64_stuff : string = tx_str.slice(3); // tx_[...] -> [...]
|
||||||
|
let stuff : Uint8Array = b64.decode(base64_stuff); // <<Bin/binary, DoubleSha:4>>
|
||||||
|
let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <<Bin/binary>>
|
||||||
|
let decoded_datas : Array<rlp.decoded_data> = rlp.decode(rlp_stuff).decoded_data as Array<rlp.decoded_data>; // decoded_data : list(rlp.decoded_data() :: binary() | list(decoded_data()))
|
||||||
|
// tag, vsn
|
||||||
|
let tag_bytes : Uint8Array = decoded_datas[0] as Uint8Array; // [tag, vsn, fields] -> tag
|
||||||
|
let tag : bigint = bytes_to_bigint(tag_bytes); // <<Tag:(byte_size(TagBytes))>> = TagBytes
|
||||||
|
let vsn : Uint8Array = decoded_datas[1] as Uint8Array;
|
||||||
|
// tx fields
|
||||||
|
let tx_fields : Array<rlp.decoded_data> = decoded_datas.slice(2);
|
||||||
|
let tx : tx = decode_fields(tag, tx_fields);
|
||||||
|
return {tag: tag as otag, version: vsn, tx: tx};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a transaction given the raw fields
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
decode_fields(tag: bigint, fields: Array<rlp.decoded_data>): tx {
|
||||||
|
switch (tag) {
|
||||||
|
case 11n: return decode_fields_SignedTx(fields);
|
||||||
|
case 12n: return decode_fields_SpendTx(fields);
|
||||||
|
case 42n: return decode_fields_ContractCreateTx(fields);
|
||||||
|
case 43n: return decode_fields_ContractCallTx(fields);
|
||||||
|
default : throw new Error("invalid object tag: " + tag);
|
||||||
|
}
|
||||||
|
console.log('fields: ', fields);
|
||||||
|
throw new Error("nyi");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a SignedTx
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
decode_fields_SignedTx(fields: Array<rlp.decoded_data>): SignedTx {
|
||||||
|
throw new Error('nyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a SpendTx
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
decode_fields_SpendTx(fields: Array<rlp.decoded_data>): SpendTx {
|
||||||
|
// [<sender> :: id(),
|
||||||
|
// <recipient> :: id(),
|
||||||
|
// <amount> :: int(),
|
||||||
|
// <fee> :: int(),
|
||||||
|
// <ttl> :: int(),
|
||||||
|
// <nonce> :: int(),
|
||||||
|
// <payload> :: binary()]
|
||||||
|
let sender : id = decode_id(fields[0] as Uint8Array);
|
||||||
|
let recipient : id = decode_id(fields[1] as Uint8Array);
|
||||||
|
let amount : bigint = bytes_to_bigint(fields[2] as Uint8Array);
|
||||||
|
let fee : bigint = bytes_to_bigint(fields[3] as Uint8Array);
|
||||||
|
let ttl : bigint = bytes_to_bigint(fields[4] as Uint8Array);
|
||||||
|
let nonce : bigint = bytes_to_bigint(fields[5] as Uint8Array);
|
||||||
|
let payload : Uint8Array = fields[6] as Uint8Array;
|
||||||
|
return {sender : sender,
|
||||||
|
recipient : recipient,
|
||||||
|
amount : amount,
|
||||||
|
fee : fee,
|
||||||
|
ttl : ttl,
|
||||||
|
nonce : nonce,
|
||||||
|
payload : payload};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
decode_fields_ContractCreateTx(fields: Array<rlp.decoded_data>): ContractCreateTx {
|
||||||
|
throw new Error('nyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
decode_fields_ContractCallTx(fields: Array<rlp.decoded_data>): ContractCallTx {
|
||||||
|
throw new Error('nyi');
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
decode_id(id: Uint8Array): id {
|
||||||
|
let idtag : idtag = BigInt(id[0]) as idtag;
|
||||||
|
return {tag: idtag, hash: id.slice(1)};
|
||||||
|
}
|
||||||
+117
-189
@@ -1,14 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Humanization/dehumanization of data
|
* Node API constructor/deconstructor
|
||||||
*
|
*
|
||||||
* This is similar to serialization/deserialization, but not quite the same thing
|
* This is similar to serialization/deserialization, but not quite the same
|
||||||
|
* thing. It converts back and forth between different forms of
|
||||||
|
* "api-serialized" data.
|
||||||
*
|
*
|
||||||
* This is for taking "api-encoded" data, and pulling it apart and seeing what
|
* References:
|
||||||
* is in it. See example below.
|
* 1. https://github.com/aeternity/protocol/blob/master/serializations.md
|
||||||
*
|
* 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md
|
||||||
* This is not exhaustive.
|
|
||||||
*
|
|
||||||
* Reference: https://github.com/aeternity/protocol/blob/master/serializations.md
|
|
||||||
*
|
*
|
||||||
* ## General type rules
|
* ## General type rules
|
||||||
*
|
*
|
||||||
@@ -118,7 +117,7 @@
|
|||||||
*
|
*
|
||||||
* The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction)
|
* 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"
|
* [ <sender> :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE"
|
||||||
* , <recipient> :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
* , <recipient> :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv"
|
||||||
* , <amount> :: int() % <<"\n">> "=" 10
|
* , <amount> :: int() % <<"\n">> "=" 10
|
||||||
@@ -131,207 +130,136 @@
|
|||||||
*
|
*
|
||||||
* Our task here is to be able to pull apart the "tx_..." string into its fields.
|
* Our task here is to be able to pull apart the "tx_..." string into its fields.
|
||||||
*
|
*
|
||||||
* Need to think about this
|
* 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
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export {
|
||||||
OTAG_SIGNED_TX,
|
// types
|
||||||
OTAG_SPEND_TX,
|
tx_str,
|
||||||
OTAG_CONTRACT_CREATE_TX,
|
deconstructed_tx,
|
||||||
OTAG_CONTRACT_CALL_TX,
|
// functions
|
||||||
IDTAG_ACCOUNT,
|
deconstruct_tx
|
||||||
IDTAG_NAME,
|
|
||||||
IDTAG_CONTRACT,
|
|
||||||
id,
|
|
||||||
SignedTx,
|
|
||||||
SpendTx,
|
|
||||||
ContractCreateTx,
|
|
||||||
ContractCallTx,
|
|
||||||
decode_tx
|
|
||||||
};
|
};
|
||||||
|
|
||||||
import * as b64 from './b64.js'
|
import * as b64 from './b64.js'
|
||||||
|
import * as bin from './bin.js'
|
||||||
import * as rlp from './rlp.js'
|
import * as rlp from './rlp.js'
|
||||||
|
|
||||||
const OTAG_SIGNED_TX = 11n;
|
|
||||||
const OTAG_SPEND_TX = 12n;
|
|
||||||
const OTAG_CONTRACT_CREATE_TX = 42n;
|
|
||||||
const OTAG_CONTRACT_CALL_TX = 43n;
|
|
||||||
|
|
||||||
type otag = 11n | 12n | 42n | 43n;
|
|
||||||
|
|
||||||
const IDTAG_ACCOUNT = 1n;
|
|
||||||
const IDTAG_NAME = 2n;
|
|
||||||
const IDTAG_CONTRACT = 5n;
|
|
||||||
|
|
||||||
type idtag = 1n | 2n | 5n;
|
|
||||||
|
|
||||||
|
|
||||||
type id =
|
|
||||||
{tag : idtag,
|
|
||||||
hash : Uint8Array};
|
|
||||||
|
|
||||||
type SignedTx =
|
|
||||||
{signatures : Array<Uint8Array>,
|
|
||||||
transaction : Uint8Array};
|
|
||||||
|
|
||||||
type SpendTx =
|
|
||||||
{sender : id,
|
|
||||||
recipient : id,
|
|
||||||
amount : bigint,
|
|
||||||
fee : bigint,
|
|
||||||
ttl : bigint,
|
|
||||||
nonce : bigint,
|
|
||||||
payload : Uint8Array};
|
|
||||||
|
|
||||||
type ContractCreateTx =
|
|
||||||
{owner : id,
|
|
||||||
nonce : bigint,
|
|
||||||
code : Uint8Array,
|
|
||||||
ct_version : bigint,
|
|
||||||
fee : bigint,
|
|
||||||
ttl : bigint,
|
|
||||||
deposit : bigint,
|
|
||||||
amount : bigint,
|
|
||||||
gas : bigint,
|
|
||||||
gas_price : bigint,
|
|
||||||
call_data : Uint8Array};
|
|
||||||
|
|
||||||
type ContractCallTx =
|
|
||||||
{caller : id,
|
|
||||||
nonce : bigint,
|
|
||||||
contract : id,
|
|
||||||
abi_version : bigint,
|
|
||||||
fee : bigint,
|
|
||||||
ttl : bigint,
|
|
||||||
amount : bigint,
|
|
||||||
gas : bigint,
|
|
||||||
gas_price : bigint,
|
|
||||||
call_data : Uint8Array};
|
|
||||||
|
|
||||||
type tx = SignedTx | SpendTx | ContractCreateTx | ContractCallTx;
|
|
||||||
|
|
||||||
type decoded_tx =
|
|
||||||
{tag : otag,
|
|
||||||
version : Uint8Array,
|
|
||||||
tx : tx};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a `tx_Base64` string
|
* Alias type for a `tx_...` string
|
||||||
|
*/
|
||||||
|
type tx_str = string;
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deconstruct a Tx
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_tx(tx_str : string): decoded_tx {
|
deconstruct_tx
|
||||||
let base64_stuff : string = tx_str.slice(3); // tx_[...] -> [...]
|
(tx_str: tx_str)
|
||||||
let stuff : Uint8Array = b64.decode(base64_stuff); // <<Bin/binary, DoubleSha:4>>
|
: deconstructed_tx
|
||||||
let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <<Bin/binary>>
|
{
|
||||||
let decoded_datas : Array<rlp.decoded_data> = rlp.decode(rlp_stuff).decoded_data as Array<rlp.decoded_data>; // decoded_data : list(rlp.decoded_data() :: binary() | list(decoded_data()))
|
let b64_str : string = tx_str.slice(3); // tx_[...] -> [...]
|
||||||
// tag, vsn
|
let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes
|
||||||
let tag_bytes : Uint8Array = decoded_datas[0] as Uint8Array; // [tag, vsn, fields] -> tag
|
let tx_data : Array<rlpdata> = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing
|
||||||
let tag : bigint = bytes_to_bigint(tag_bytes); // <<Tag:(byte_size(TagBytes))>> = TagBytes
|
let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint
|
||||||
let vsn : Uint8Array = decoded_datas[1] as Uint8Array;
|
let tx_type_str : tx_type_str = tx_type_str(tx_type);
|
||||||
// tx fields
|
let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]);
|
||||||
let tx_fields : Array<rlp.decoded_data> = decoded_datas.slice(2);
|
let tx_fields : fields = deconstruct_fields(tx_type_str, tx_version, tx_data.slice(2));
|
||||||
let tx : tx = decode_fields(tag, tx_fields);
|
return {type : tx_type_str,
|
||||||
return {tag: tag as otag, version: vsn, tx: tx};
|
version : tx_version,
|
||||||
|
fields : tx_fields};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a byte array to a bigint
|
* 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
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
bytes_to_bigint(bytes: Uint8Array): bigint {
|
shasha_rlp_decode
|
||||||
let n : bigint = 0n;
|
(hashed_bs : Uint8Array)
|
||||||
for (let b of bytes) {
|
: Array<rlpdata>
|
||||||
// move first, then add
|
{
|
||||||
// otherwise it ends on a move
|
|
||||||
// imperative languages are for losers
|
|
||||||
n <<= 8n;
|
|
||||||
n += BigInt(b);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a transaction given the raw fields
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
decode_fields(tag: bigint, fields: Array<rlp.decoded_data>): tx {
|
|
||||||
switch (tag) {
|
|
||||||
case 11n: return decode_fields_SignedTx(fields);
|
|
||||||
case 12n: return decode_fields_SpendTx(fields);
|
|
||||||
case 42n: return decode_fields_ContractCreateTx(fields);
|
|
||||||
case 43n: return decode_fields_ContractCallTx(fields);
|
|
||||||
default : throw new Error("invalid object tag: " + tag);
|
|
||||||
}
|
|
||||||
console.log('fields: ', fields);
|
|
||||||
throw new Error("nyi");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a SignedTx
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
decode_fields_SignedTx(fields: Array<rlp.decoded_data>): SignedTx {
|
|
||||||
throw new Error('nyi');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a SpendTx
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function
|
|
||||||
decode_fields_SpendTx(fields: Array<rlp.decoded_data>): SpendTx {
|
|
||||||
// [<sender> :: id(),
|
|
||||||
// <recipient> :: id(),
|
|
||||||
// <amount> :: int(),
|
|
||||||
// <fee> :: int(),
|
|
||||||
// <ttl> :: int(),
|
|
||||||
// <nonce> :: int(),
|
|
||||||
// <payload> :: binary()]
|
|
||||||
let sender : id = decode_id(fields[0] as Uint8Array);
|
|
||||||
let recipient : id = decode_id(fields[1] as Uint8Array);
|
|
||||||
let amount : bigint = bytes_to_bigint(fields[2] as Uint8Array);
|
|
||||||
let fee : bigint = bytes_to_bigint(fields[3] as Uint8Array);
|
|
||||||
let ttl : bigint = bytes_to_bigint(fields[4] as Uint8Array);
|
|
||||||
let nonce : bigint = bytes_to_bigint(fields[5] as Uint8Array);
|
|
||||||
let payload : Uint8Array = fields[6] as Uint8Array;
|
|
||||||
return {sender : sender,
|
|
||||||
recipient : recipient,
|
|
||||||
amount : amount,
|
|
||||||
fee : fee,
|
|
||||||
ttl : ttl,
|
|
||||||
nonce : nonce,
|
|
||||||
payload : payload};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function
|
|
||||||
decode_fields_ContractCreateTx(fields: Array<rlp.decoded_data>): ContractCreateTx {
|
|
||||||
throw new Error('nyi');
|
|
||||||
}
|
|
||||||
|
|
||||||
function
|
|
||||||
decode_fields_ContractCallTx(fields: Array<rlp.decoded_data>): ContractCallTx {
|
|
||||||
throw new Error('nyi');
|
|
||||||
}
|
|
||||||
|
|
||||||
function
|
|
||||||
decode_id(id: Uint8Array): id {
|
|
||||||
let idtag : idtag = BigInt(id[0]) as idtag;
|
|
||||||
return {tag: idtag, hash: id.slice(1)};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ export {
|
|||||||
* Encode a Uint8Array into base58
|
* Encode a Uint8Array into base58
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode(binary : Uint8Array): string {
|
encode
|
||||||
|
(binary : Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let num_leading_zeros : number = nlz(binary);
|
let num_leading_zeros : number = nlz(binary);
|
||||||
let rest : Uint8Array = binary.slice(num_leading_zeros);
|
let rest : Uint8Array = binary.slice(num_leading_zeros);
|
||||||
let ones : string = encode_zeros(num_leading_zeros);
|
let ones : string = encode_zeros(num_leading_zeros);
|
||||||
@@ -34,13 +37,15 @@ encode(binary : Uint8Array): string {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
nlz(bytes: Uint8Array): number {
|
nlz
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: number
|
||||||
|
{
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for (let this_byte of bytes) {
|
for (let this_byte of bytes)
|
||||||
if (0 === this_byte)
|
{
|
||||||
n++;
|
if (0 === this_byte) { n++; }
|
||||||
else
|
else { break; }
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -53,7 +58,10 @@ nlz(bytes: Uint8Array): number {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_zeros(how_many : number): string {
|
encode_zeros
|
||||||
|
(how_many : number)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let ones : string = '';
|
let ones : string = '';
|
||||||
for (let i = 1;
|
for (let i = 1;
|
||||||
i <= how_many;
|
i <= how_many;
|
||||||
@@ -73,7 +81,10 @@ encode_zeros(how_many : number): string {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_rest(bytes : Uint8Array): string {
|
encode_rest
|
||||||
|
(bytes : Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let bytes_bignum : bigint = bytes_to_bigint(bytes);
|
let bytes_bignum : bigint = bytes_to_bigint(bytes);
|
||||||
let result : string = bignum_to_base58(bytes_bignum);
|
let result : string = bignum_to_base58(bytes_bignum);
|
||||||
return result;
|
return result;
|
||||||
@@ -87,9 +98,13 @@ encode_rest(bytes : Uint8Array): string {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
bytes_to_bigint(bytes: Uint8Array): bigint {
|
bytes_to_bigint
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: bigint
|
||||||
|
{
|
||||||
let acc_bigint : bigint = 0n;
|
let acc_bigint : bigint = 0n;
|
||||||
for(let this_byte of bytes) {
|
for(let this_byte of bytes)
|
||||||
|
{
|
||||||
acc_bigint <<= 8n;
|
acc_bigint <<= 8n;
|
||||||
acc_bigint += BigInt(this_byte);
|
acc_bigint += BigInt(this_byte);
|
||||||
}
|
}
|
||||||
@@ -104,9 +119,13 @@ bytes_to_bigint(bytes: Uint8Array): bigint {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
bignum_to_base58(q: bigint) {
|
bignum_to_base58
|
||||||
|
(q: bigint)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let s = '';
|
let s = '';
|
||||||
while (q !== 0n) {
|
while (q !== 0n)
|
||||||
|
{
|
||||||
let this_n : bigint = q % 58n;
|
let this_n : bigint = q % 58n;
|
||||||
q /= 58n;
|
q /= 58n;
|
||||||
|
|
||||||
@@ -126,7 +145,10 @@ bignum_to_base58(q: bigint) {
|
|||||||
* Decode a Base58 string into a Uint8Array
|
* Decode a Base58 string into a Uint8Array
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode(base58: string): Uint8Array {
|
decode
|
||||||
|
(base58: string)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
let num_leading_ones : number = nlo(base58);
|
let num_leading_ones : number = nlo(base58);
|
||||||
let rest : string = base58.slice(num_leading_ones);
|
let rest : string = base58.slice(num_leading_ones);
|
||||||
let zeros : Array<number> = decode_ones(num_leading_ones);
|
let zeros : Array<number> = decode_ones(num_leading_ones);
|
||||||
@@ -143,13 +165,15 @@ decode(base58: string): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
nlo(base58: string): number {
|
nlo
|
||||||
|
(base58: string)
|
||||||
|
: number
|
||||||
|
{
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for (let this_char of base58) {
|
for (let this_char of base58)
|
||||||
if ('1' === this_char)
|
{
|
||||||
n++;
|
if ('1' === this_char) { n++; }
|
||||||
else
|
else { break; }
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -162,7 +186,10 @@ nlo(base58: string): number {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_ones(how_many : number): Array<number> {
|
decode_ones
|
||||||
|
(how_many : number)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
let zeros : Array<number> = [];
|
let zeros : Array<number> = [];
|
||||||
for (let i = 1;
|
for (let i = 1;
|
||||||
i <= how_many;
|
i <= how_many;
|
||||||
@@ -182,7 +209,10 @@ decode_ones(how_many : number): Array<number> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_rest(base58: string): Array<number> {
|
decode_rest
|
||||||
|
(base58: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
let result_bignum : bigint = base58_to_bigint(base58);
|
let result_bignum : bigint = base58_to_bigint(base58);
|
||||||
let result : Array<number> = bigint_to_base256(result_bignum);
|
let result : Array<number> = bigint_to_base256(result_bignum);
|
||||||
return result;
|
return result;
|
||||||
@@ -196,9 +226,13 @@ decode_rest(base58: string): Array<number> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
base58_to_bigint(base58: string): bigint {
|
base58_to_bigint
|
||||||
|
(base58: string)
|
||||||
|
: bigint
|
||||||
|
{
|
||||||
let acc_bigint : bigint = 0n;
|
let acc_bigint : bigint = 0n;
|
||||||
for(let this_char of base58) {
|
for(let this_char of base58)
|
||||||
|
{
|
||||||
acc_bigint *= 58n;
|
acc_bigint *= 58n;
|
||||||
acc_bigint += char_to_bigint(this_char);
|
acc_bigint += char_to_bigint(this_char);
|
||||||
}
|
}
|
||||||
@@ -212,10 +246,14 @@ base58_to_bigint(base58: string): bigint {
|
|||||||
* @end
|
* @end
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
bigint_to_base256(q: bigint): Array<number> {
|
bigint_to_base256
|
||||||
|
(q: bigint)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
let arr_reverse = [];
|
let arr_reverse = [];
|
||||||
while(q !== 0n) {
|
while(q !== 0n)
|
||||||
let r = Number(q % 256n);
|
{
|
||||||
|
let r: number = Number(q % 256n);
|
||||||
q /= 256n;
|
q /= 256n;
|
||||||
arr_reverse.push(r);
|
arr_reverse.push(r);
|
||||||
}
|
}
|
||||||
@@ -228,8 +266,17 @@ bigint_to_base256(q: bigint): Array<number> {
|
|||||||
// TRANSLATION TABLES
|
// TRANSLATION TABLES
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base58 integer -> character conversion table
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
function
|
function
|
||||||
bigint_to_char(n: bigint): string {
|
bigint_to_char
|
||||||
|
(n: bigint)
|
||||||
|
: string
|
||||||
|
{
|
||||||
switch(n) {
|
switch(n) {
|
||||||
case 0n: return '1';
|
case 0n: return '1';
|
||||||
case 1n: return '2';
|
case 1n: return '2';
|
||||||
@@ -295,8 +342,17 @@ bigint_to_char(n: bigint): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base58 character -> integer conversion table
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
function
|
function
|
||||||
char_to_bigint(s: string): bigint {
|
char_to_bigint
|
||||||
|
(s: string)
|
||||||
|
: bigint
|
||||||
|
{
|
||||||
switch(s) {
|
switch(s) {
|
||||||
case '1': return 0n;
|
case '1': return 0n;
|
||||||
case '2': return 1n;
|
case '2': return 1n;
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ export {
|
|||||||
* Encode an array of bytes as a Uint8Array in base64 notation.
|
* Encode an array of bytes as a Uint8Array in base64 notation.
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode(bytes: Uint8Array): string {
|
encode
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
// slice the array
|
// slice the array
|
||||||
// length of head is a multiple of 3
|
// length of head is a multiple of 3
|
||||||
// treat the tail as a special case
|
// treat the tail as a special case
|
||||||
@@ -25,12 +28,11 @@ encode(bytes: Uint8Array): string {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
type slice3k = {head : Uint8Array,
|
type slice3k
|
||||||
|
= {head : Uint8Array,
|
||||||
tail : Uint8Array,
|
tail : Uint8Array,
|
||||||
tail_len : number};
|
tail_len : number};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and
|
* 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
|
* the remaining 0,1, or 2 bytes, put them in tail
|
||||||
@@ -38,7 +40,10 @@ type slice3k = {head : Uint8Array,
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
slice3k(bytes: Uint8Array): slice3k {
|
slice3k
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: slice3k
|
||||||
|
{
|
||||||
let len : number = bytes.length;
|
let len : number = bytes.length;
|
||||||
// too lazy to look up how to do integer division in js so this will do
|
// too lazy to look up how to do integer division in js so this will do
|
||||||
let tail_len : number = len % 3;
|
let tail_len : number = len % 3;
|
||||||
@@ -62,7 +67,10 @@ slice3k(bytes: Uint8Array): slice3k {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_head(head_bytes: Uint8Array): string {
|
encode_head
|
||||||
|
(head_bytes: Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
// can assume length of bytes is a multiple of 3
|
// can assume length of bytes is a multiple of 3
|
||||||
// start index at 0
|
// start index at 0
|
||||||
// increment by 3
|
// increment by 3
|
||||||
@@ -90,7 +98,10 @@ encode_head(head_bytes: Uint8Array): string {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode3(bytes: Uint8Array) {
|
encode3
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let b0 : number = bytes[0];
|
let b0 : number = bytes[0];
|
||||||
let b1 : number = bytes[1];
|
let b1 : number = bytes[1];
|
||||||
let b2 : number = bytes[2];
|
let b2 : number = bytes[2];
|
||||||
@@ -139,9 +150,11 @@ encode3(bytes: Uint8Array) {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_tail(tail_bytes : Uint8Array,
|
encode_tail
|
||||||
|
(tail_bytes : Uint8Array,
|
||||||
tail_len : number)
|
tail_len : number)
|
||||||
: string {
|
: string
|
||||||
|
{
|
||||||
switch(tail_len) {
|
switch(tail_len) {
|
||||||
case 0: return '';
|
case 0: return '';
|
||||||
case 1: return encode1(tail_bytes);
|
case 1: return encode1(tail_bytes);
|
||||||
@@ -159,7 +172,10 @@ encode_tail(tail_bytes : Uint8Array,
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode1(bytes: Uint8Array): string {
|
encode1
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let b0 : number = bytes[0];
|
let b0 : number = bytes[0];
|
||||||
// n0 = __ABCDEF
|
// n0 = __ABCDEF
|
||||||
// b0 = ABCDEFGH
|
// b0 = ABCDEFGH
|
||||||
@@ -183,7 +199,10 @@ encode1(bytes: Uint8Array): string {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode2(bytes: Uint8Array): string {
|
encode2
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: string
|
||||||
|
{
|
||||||
let b0 : number = bytes[0];
|
let b0 : number = bytes[0];
|
||||||
let b1 : number = bytes[1];
|
let b1 : number = bytes[1];
|
||||||
|
|
||||||
@@ -222,7 +241,10 @@ encode2(bytes: Uint8Array): string {
|
|||||||
* Decode a base64-encoded string
|
* Decode a base64-encoded string
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode(base64_str : string): Uint8Array {
|
decode
|
||||||
|
(base64_str : string)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
// length of the string is guaranteed to be a multiple of 4
|
// length of the string is guaranteed to be a multiple of 4
|
||||||
// if the string is empty, return the empty array
|
// if the string is empty, return the empty array
|
||||||
let len = base64_str.length;
|
let len = base64_str.length;
|
||||||
@@ -280,7 +302,10 @@ decode(base64_str : string): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_head(s: string): Array<number> {
|
decode_head
|
||||||
|
(s: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
// go 4 characters at a time
|
// go 4 characters at a time
|
||||||
let max_i0 : number = s.length - 1;
|
let max_i0 : number = s.length - 1;
|
||||||
let decoded_acc : Array<number> = [];
|
let decoded_acc : Array<number> = [];
|
||||||
@@ -304,7 +329,10 @@ decode_head(s: string): Array<number> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_tail(s: string): Array<number> {
|
decode_tail
|
||||||
|
(s: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
// all that matters right now is the last 2 chars
|
// all that matters right now is the last 2 chars
|
||||||
// s0, s1, s2, s3
|
// s0, s1, s2, s3
|
||||||
// 0 based indexing is so annoying
|
// 0 based indexing is so annoying
|
||||||
@@ -333,7 +361,11 @@ decode_tail(s: string): Array<number> {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function decode3(s: string): Array<number> {
|
function
|
||||||
|
decode3
|
||||||
|
(s: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
// pull out strings
|
// pull out strings
|
||||||
let s0 : string = s[0];
|
let s0 : string = s[0];
|
||||||
let s1 : string = s[1];
|
let s1 : string = s[1];
|
||||||
@@ -382,7 +414,11 @@ function decode3(s: string): Array<number> {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function decode2(s: string): Array<number> {
|
function
|
||||||
|
decode2
|
||||||
|
(s: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
// xyz=
|
// xyz=
|
||||||
// pull out strings
|
// pull out strings
|
||||||
let s0 : string = s[0];
|
let s0 : string = s[0];
|
||||||
@@ -424,7 +460,11 @@ function decode2(s: string): Array<number> {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function decode1(s: string): Array<number> {
|
function
|
||||||
|
decode1
|
||||||
|
(s: string)
|
||||||
|
: Array<number>
|
||||||
|
{
|
||||||
// xy==
|
// xy==
|
||||||
// pull out strings
|
// pull out strings
|
||||||
let s0 : string = s[0];
|
let s0 : string = s[0];
|
||||||
@@ -460,7 +500,9 @@ function decode1(s: string): Array<number> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
int2char(n: number): string
|
int2char
|
||||||
|
(n: number)
|
||||||
|
: string
|
||||||
{
|
{
|
||||||
switch(n) {
|
switch(n) {
|
||||||
case 0: return 'A';
|
case 0: return 'A';
|
||||||
@@ -539,7 +581,9 @@ int2char(n: number): string
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
char2int(s: string): number
|
char2int
|
||||||
|
(s: string)
|
||||||
|
: number
|
||||||
{
|
{
|
||||||
switch(s) {
|
switch(s) {
|
||||||
case 'A': return 0;
|
case 'A': return 0;
|
||||||
@@ -609,5 +653,3 @@ char2int(s: string): number
|
|||||||
default: throw new Error("invalid base64 character: " + s);
|
default: throw new Error("invalid base64 character: " + s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,56 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export {
|
||||||
encode_unsigned,
|
bytes_to_bigint,
|
||||||
decode_unsigned
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ type decode_result
|
|||||||
* whatever wasn't consumed)
|
* whatever wasn't consumed)
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode(bytes: Uint8Array): decode_result {
|
decode
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: decode_result
|
||||||
|
{
|
||||||
// check the first byte
|
// check the first byte
|
||||||
let first_byte: number = bytes[0];
|
let first_byte: number = bytes[0];
|
||||||
let rest : Uint8Array = bytes.slice(1);
|
let rest : Uint8Array = bytes.slice(1);
|
||||||
@@ -118,7 +121,10 @@ decode(bytes: Uint8Array): decode_result {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
decode_list(bytes: Uint8Array): Array<decoded_data> {
|
decode_list
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: Array<decoded_data>
|
||||||
|
{
|
||||||
let arr : Array<decoded_data> = [];
|
let arr : Array<decoded_data> = [];
|
||||||
while (bytes.length > 0) {
|
while (bytes.length > 0) {
|
||||||
// grab an item off the bytes
|
// grab an item off the bytes
|
||||||
@@ -139,9 +145,13 @@ decode_list(bytes: Uint8Array): Array<decoded_data> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
bytes_to_number(bytes: Uint8Array): number {
|
bytes_to_number
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: number
|
||||||
|
{
|
||||||
let n : number = 0;
|
let n : number = 0;
|
||||||
for (let b of bytes) {
|
for (let b of bytes)
|
||||||
|
{
|
||||||
n <<= 8;
|
n <<= 8;
|
||||||
n += b;
|
n += b;
|
||||||
}
|
}
|
||||||
@@ -156,8 +166,13 @@ bytes_to_number(bytes: Uint8Array): number {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
dr(x : decoded_data, y : Uint8Array): decode_result {
|
dr
|
||||||
return {decoded_data: x, remainder: y};
|
(x : decoded_data,
|
||||||
|
y : Uint8Array)
|
||||||
|
: decode_result
|
||||||
|
{
|
||||||
|
return {decoded_data : x,
|
||||||
|
remainder : y};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -172,12 +187,17 @@ dr(x : decoded_data, y : Uint8Array): decode_result {
|
|||||||
* Encode some decoded data
|
* Encode some decoded data
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode(data: decoded_data): Uint8Array {
|
encode
|
||||||
|
(data: decoded_data)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
// is it an array or data
|
// is it an array or data
|
||||||
if (is_binary(data)) {
|
if
|
||||||
|
(is_binary(data)) {
|
||||||
return encode_binary(data as Uint8Array);
|
return encode_binary(data as Uint8Array);
|
||||||
}
|
}
|
||||||
else if (is_list(data)) {
|
else if
|
||||||
|
(is_list(data)) {
|
||||||
return encode_list(data as Array<decoded_data>);
|
return encode_list(data as Array<decoded_data>);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -193,7 +213,10 @@ encode(data: decoded_data): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_binary(bytes: Uint8Array): Uint8Array {
|
encode_binary
|
||||||
|
(bytes: Uint8Array)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
let len: number = bytes.length;
|
let len: number = bytes.length;
|
||||||
// single byte case when the byte is between 0..127
|
// single byte case when the byte is between 0..127
|
||||||
// result is the bytestring containing the byte itself
|
// result is the bytestring containing the byte itself
|
||||||
@@ -273,7 +296,10 @@ encode_binary(bytes: Uint8Array): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_list(list: Array<decoded_data>): Uint8Array {
|
encode_list
|
||||||
|
(list: Array<decoded_data>)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
// first encode every element in the list, then branch
|
// first encode every element in the list, then branch
|
||||||
let payloads : Array<Uint8Array> = list.map(encode);
|
let payloads : Array<Uint8Array> = list.map(encode);
|
||||||
let payload : Uint8Array = uint8arr_concat(payloads);
|
let payload : Uint8Array = uint8arr_concat(payloads);
|
||||||
@@ -361,7 +387,10 @@ encode_list(list: Array<decoded_data>): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
encode_unsigned(n: number): Uint8Array {
|
encode_unsigned
|
||||||
|
(n: number)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
// can assume that n initially is greater than 55
|
// can assume that n initially is greater than 55
|
||||||
// have to encode it in reverse order
|
// have to encode it in reverse order
|
||||||
let arr : Array<number> = [];
|
let arr : Array<number> = [];
|
||||||
@@ -431,7 +460,10 @@ encode_unsigned(n: number): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
uint8arr_concat(arrs: Array<Uint8Array>): Uint8Array {
|
uint8arr_concat
|
||||||
|
(arrs: Array<Uint8Array>)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
// total length
|
// total length
|
||||||
let total_len = arrs.reduce(// fold
|
let total_len = arrs.reduce(// fold
|
||||||
function (acc_len: number, this_uint8array: Uint8Array): number {
|
function (acc_len: number, this_uint8array: Uint8Array): number {
|
||||||
@@ -463,7 +495,10 @@ uint8arr_concat(arrs: Array<Uint8Array>): Uint8Array {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
is_binary(x: any): boolean {
|
is_binary
|
||||||
|
(x: any)
|
||||||
|
: boolean
|
||||||
|
{
|
||||||
return (x instanceof Uint8Array);
|
return (x instanceof Uint8Array);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,6 +510,9 @@ is_binary(x: any): boolean {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function
|
function
|
||||||
is_list(x: any): boolean {
|
is_list
|
||||||
|
(x: any)
|
||||||
|
: boolean
|
||||||
|
{
|
||||||
return (x instanceof Array);
|
return (x instanceof Array);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user