From 822ffa6c9413659159ede0c6ef65f29f2990badd Mon Sep 17 00:00:00 2001 From: Peter Harpending Date: Wed, 5 Oct 2022 02:35:43 -0600 Subject: [PATCH] move stuff in here --- .gitignore | 4 + README.md | 53 + erl/jex/.gitignore | 15 + erl/jex/Emakefile | 1 + erl/jex/LICENSE | 16 + erl/jex/ebin/jex.app | 7 + erl/jex/src/jex.erl | 356 ++++++ erl/jex/zomp.meta | 18 + erl/vlogd | 1 + ts/awcp/.gitignore | 18 + ts/awcp/LICENSE | 16 + ts/awcp/Makefile | 15 + ts/awcp/README.md | 490 ++++++++ ts/awcp/jex.eterms | 5 + ts/awcp/src/awcp.ts | 724 +++++++++++ ts/awcp/tsconfig.json | 16 + ts/parasite/.gitignore | 6 + ts/parasite/LICENSE | 16 + ts/parasite/Makefile | 14 + ts/parasite/README.md | 0 ts/parasite/jex.eterms | 5 + ts/parasite/src/ae_compiler.ts | 96 ++ ts/parasite/src/ae_node.ts | 272 ++++ ts/parasite/src/net.ts | 85 ++ ts/parasite/tsconfig.json | 16 + ts/sidekick/.gitignore | 19 + ts/sidekick/LICENSE | 16 + ts/sidekick/Makefile | 15 + ts/sidekick/README.md | 136 ++ ts/sidekick/icons/sidekick_icon_128.png | Bin 0 -> 2883 bytes ts/sidekick/icons/sidekick_icon_128.xcf | Bin 0 -> 5815 bytes ts/sidekick/jex.eterms | 5 + ts/sidekick/scratch/STYLE_GUIDE.md | 187 +++ ts/sidekick/scratch/TODO.md | 250 ++++ ts/sidekick/scratch/async_tests.html | 9 + ts/sidekick/scratch/async_tests.js | 26 + .../examples_old/contract-examples/LICENSE | 674 ++++++++++ .../contract-examples/base_contract.aes | 96 ++ .../contract-examples/crypto_hamster.aes | 76 ++ .../examples_old/contract-examples/floyd.aes | 11 + .../contract-examples/sales_contract.aes | 200 +++ .../examples_html/do_a_transfer.html | 54 + .../examples_old/examples_html/hello.html | 27 + .../examples_old/examples_html/ide.html | 218 ++++ .../examples_old/examples_html/maerket.html | 24 + .../examples_src_ts/do_a_transfer.ts | 160 +++ .../examples_old/examples_src_ts/ide.ts | 222 ++++ .../examples_src_ts/parasite/ae_compiler.ts | 96 ++ .../examples_src_ts/parasite/ae_node.ts | 272 ++++ .../examples_src_ts/parasite/net.ts | 85 ++ ts/sidekick/scratch/examples_old/favicon.ico | Bin 0 -> 1150 bytes ts/sidekick/scratch/examples_old/index.html | 23 + ts/sidekick/scratch/examples_old/style.css | 34 + .../scratch/examples_old/tsconfig.json | 13 + ts/sidekick/scratch/examples_old/why.html | 8 + ts/sidekick/scratch/logging.ts | 219 ++++ ts/sidekick/scratch/old-makefile | 50 + ts/sidekick/scratch/old-readme.md | 490 ++++++++ ts/sidekick/scratch/scratch.txt | 244 ++++ ts/sidekick/src/sidekick.ts | 1102 +++++++++++++++++ ts/sidekick/tsconfig.json | 16 + ts/sidekick_examples/.gitignore | 6 + ts/sidekick_examples/LICENSE | 16 + ts/sidekick_examples/Makefile | 10 + ts/sidekick_examples/README.md | 90 ++ ts/sidekick_examples/connection.html | 88 ++ ts/sidekick_examples/favicon.ico | Bin 0 -> 4166 bytes ts/sidekick_examples/hello.html | 32 + ts/sidekick_examples/index.html | 25 + ts/sidekick_examples/jex.eterms | 7 + ts/sidekick_examples/scratch/logging.html | 32 + ts/sidekick_examples/scratch/logging.ts | 6 + .../scratch/logging_http.html | 28 + ts/sidekick_examples/scratch/logging_http.ts | 23 + ts/sidekick_examples/src/connection.ts | 219 ++++ ts/sidekick_examples/src/hello.ts | 3 + ts/sidekick_examples/tsconfig.json | 16 + 77 files changed, 7943 insertions(+) create mode 100644 .gitignore create mode 100644 erl/jex/.gitignore create mode 100644 erl/jex/Emakefile create mode 100644 erl/jex/LICENSE create mode 100644 erl/jex/ebin/jex.app create mode 100644 erl/jex/src/jex.erl create mode 100644 erl/jex/zomp.meta create mode 160000 erl/vlogd create mode 100644 ts/awcp/.gitignore create mode 100644 ts/awcp/LICENSE create mode 100644 ts/awcp/Makefile create mode 100644 ts/awcp/README.md create mode 100644 ts/awcp/jex.eterms create mode 100644 ts/awcp/src/awcp.ts create mode 100644 ts/awcp/tsconfig.json create mode 100644 ts/parasite/.gitignore create mode 100644 ts/parasite/LICENSE create mode 100644 ts/parasite/Makefile create mode 100644 ts/parasite/README.md create mode 100644 ts/parasite/jex.eterms create mode 100644 ts/parasite/src/ae_compiler.ts create mode 100644 ts/parasite/src/ae_node.ts create mode 100644 ts/parasite/src/net.ts create mode 100644 ts/parasite/tsconfig.json create mode 100644 ts/sidekick/.gitignore create mode 100644 ts/sidekick/LICENSE create mode 100644 ts/sidekick/Makefile create mode 100644 ts/sidekick/README.md create mode 100644 ts/sidekick/icons/sidekick_icon_128.png create mode 100644 ts/sidekick/icons/sidekick_icon_128.xcf create mode 100644 ts/sidekick/jex.eterms create mode 100644 ts/sidekick/scratch/STYLE_GUIDE.md create mode 100644 ts/sidekick/scratch/TODO.md create mode 100644 ts/sidekick/scratch/async_tests.html create mode 100644 ts/sidekick/scratch/async_tests.js create mode 100644 ts/sidekick/scratch/examples_old/contract-examples/LICENSE create mode 100644 ts/sidekick/scratch/examples_old/contract-examples/base_contract.aes create mode 100644 ts/sidekick/scratch/examples_old/contract-examples/crypto_hamster.aes create mode 100644 ts/sidekick/scratch/examples_old/contract-examples/floyd.aes create mode 100644 ts/sidekick/scratch/examples_old/contract-examples/sales_contract.aes create mode 100644 ts/sidekick/scratch/examples_old/examples_html/do_a_transfer.html create mode 100644 ts/sidekick/scratch/examples_old/examples_html/hello.html create mode 100644 ts/sidekick/scratch/examples_old/examples_html/ide.html create mode 100644 ts/sidekick/scratch/examples_old/examples_html/maerket.html create mode 100644 ts/sidekick/scratch/examples_old/examples_src_ts/do_a_transfer.ts create mode 100644 ts/sidekick/scratch/examples_old/examples_src_ts/ide.ts create mode 100644 ts/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts create mode 100644 ts/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_node.ts create mode 100644 ts/sidekick/scratch/examples_old/examples_src_ts/parasite/net.ts create mode 100644 ts/sidekick/scratch/examples_old/favicon.ico create mode 100644 ts/sidekick/scratch/examples_old/index.html create mode 100644 ts/sidekick/scratch/examples_old/style.css create mode 100644 ts/sidekick/scratch/examples_old/tsconfig.json create mode 100644 ts/sidekick/scratch/examples_old/why.html create mode 100644 ts/sidekick/scratch/logging.ts create mode 100644 ts/sidekick/scratch/old-makefile create mode 100644 ts/sidekick/scratch/old-readme.md create mode 100644 ts/sidekick/scratch/scratch.txt create mode 100644 ts/sidekick/src/sidekick.ts create mode 100644 ts/sidekick/tsconfig.json create mode 100644 ts/sidekick_examples/.gitignore create mode 100644 ts/sidekick_examples/LICENSE create mode 100644 ts/sidekick_examples/Makefile create mode 100644 ts/sidekick_examples/README.md create mode 100644 ts/sidekick_examples/connection.html create mode 100644 ts/sidekick_examples/favicon.ico create mode 100644 ts/sidekick_examples/hello.html create mode 100644 ts/sidekick_examples/index.html create mode 100644 ts/sidekick_examples/jex.eterms create mode 100644 ts/sidekick_examples/scratch/logging.html create mode 100644 ts/sidekick_examples/scratch/logging.ts create mode 100644 ts/sidekick_examples/scratch/logging_http.html create mode 100644 ts/sidekick_examples/scratch/logging_http.ts create mode 100644 ts/sidekick_examples/src/connection.ts create mode 100644 ts/sidekick_examples/src/hello.ts create mode 100644 ts/sidekick_examples/tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a37767b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.swp +*.swo +erl_crash.dump +*.beam diff --git a/README.md b/README.md index 83b30a8..6d82255 100644 --- a/README.md +++ b/README.md @@ -1 +1,54 @@ # Vanillae + +Vanillae is an Aeternity tool suite meant for small business use cases. Focus +is placed on simplicity, code quality, and good documentation. + +## About + +### Flagships Applications + +- Jaeck Russell (Erlang) + + A minimal browser wallet extension. + +- jex (Erlang) + + A TypeScript/JavaScript package manager that avoids a large class of + NPM-related security issues. + +- vlogd (Erlang) + + **NOT FOR PRODUCTION USAGE** + + Simple HTTP server that just logs requests it gets. + + +### Erlang Libraries + +- vanillae + + +### TypeScript Libraries + +- awcp + + Aepp/waellet communication protocol + + This is just type definitions, what data the wallet expects from the Aepp, + and what data the aepp expects from the waellet. + +- libjr + + This is the complement to sidekick. It essentially is sidekick from the + perspective of the waellet. + +- sidekick + + This a TypeScript/JavaScript library for talking to Aeternity browser wallets + (e.g. Superhero, Jaeck Russell) from the perspective of an "aepp" (i.e. your + website's JavaScript). + + +## Documentation + +- base64 versus base58 explainer diff --git a/erl/jex/.gitignore b/erl/jex/.gitignore new file mode 100644 index 0000000..20177b4 --- /dev/null +++ b/erl/jex/.gitignore @@ -0,0 +1,15 @@ +.eunit +deps +*.o +*.beam +*.plt +*.swp +erl_crash.dump +ebin/*.beam +doc/*.html +doc/*.css +doc/edoc-info +doc/erlang.png +rel/example_project +.concrete/DEV_MODE +.rebar diff --git a/erl/jex/Emakefile b/erl/jex/Emakefile new file mode 100644 index 0000000..68c7b67 --- /dev/null +++ b/erl/jex/Emakefile @@ -0,0 +1 @@ +{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}. diff --git a/erl/jex/LICENSE b/erl/jex/LICENSE new file mode 100644 index 0000000..2e52b8d --- /dev/null +++ b/erl/jex/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2022 Peter Harpending + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/erl/jex/ebin/jex.app b/erl/jex/ebin/jex.app new file mode 100644 index 0000000..58b4113 --- /dev/null +++ b/erl/jex/ebin/jex.app @@ -0,0 +1,7 @@ +{application,jex, + [{description,"jex"}, + {registered,[]}, + {included_applications,[]}, + {applications,[stdlib,kernel]}, + {vsn,"0.1.0"}, + {modules,[jex]}]}. diff --git a/erl/jex/src/jex.erl b/erl/jex/src/jex.erl new file mode 100644 index 0000000..4cb7ed6 --- /dev/null +++ b/erl/jex/src/jex.erl @@ -0,0 +1,356 @@ +%%% @doc +%%% jex: jex +%%% +%%% This module is currently named `jex', but you may want to change that. +%%% Remember that changing the name in `-module()' below requires renaming +%%% this file, and it is recommended to run `zx update .app` in the main +%%% project directory to make sure the ebin/jex.app file stays in +%%% sync with the project whenever you add, remove or rename a module. +%%% @end + +-module(jex). +-vsn("0.1.0"). +-license("MIT"). +-export([start/1]). + +-include("$zx_include/zx_logger.hrl"). + + +-spec start(ArgV) -> ok + when ArgV :: [string()]. + +start(ArgV) -> + ok = log(info, "ArgV: ~tp", [ArgV]), + ok = dispatch(ArgV), + zx:silent_stop(). + +help() -> + io:format("~ts~n", [help_screen()]). + + +% TODO: be smart about creating jex_include +% TODO: make mindist tarballs +% TODO: make fulldist tarballs +% TODO: make tsdocs +% TODO: jex pull +% TODO: hints about where to find packages +% TODO: tarball shas +% TODO: tarball signatures +% TODO: tarball signatures + +help_screen() -> + ["welcome to hell\n" + "\n" + "COMMANDS:\n" + " cfgbarf barf out the jex.eterms file (mostly to make sure it parses correctly)\n" + " echo home echo $HOME\n" + " echo jexdir echo $HOME/.jex\n" + " echo devdir echo $HOME/.jex/dev\n" + " echo pkgname name of current package\n" + " echo pkgdir echo $HOME/.jex/dev/realm-name-X.Y.Z\n" + " echo deps list dependencies of current package\n" + " echo pathof PKG list the path to PKG or \n" + " init mkdir -p $HOME/.jex/dev\n" + " build tsc && cp -r ./src/jex_include ./dist/\n" + " -w, --weak continue building even if tsc fails\n" + " -f, --force use cp -rf instead of cp -r\n" + " mindist mkdir jex_mindist && cp -r src jex_mindist && cp -r dist jex_mindist && rm -r jex_mindist/src/jex_include\n" + " -f, --force use cp -rf instead of cp -r\n" + " push rsync -a jex_mindist/ PKGDIR\n" + " ls ls $HOME/.jex/dev\n" + " jextree tree $HOME/.jex/\n" + " rmpkg PKG rm -r $HOME/.jex/dev/PKG\n" + " pull pull each dependency into src/jx_include\n" + ]. + + +dispatch(["cfgbarf"]) -> cfgbarf(); +dispatch(["echo", "home"]) -> echo(home); +dispatch(["echo", "jexdir"]) -> echo(jexdir); +dispatch(["echo", "devdir"]) -> echo(devdir); +dispatch(["echo", "pkgname"]) -> echo(pkgname); +dispatch(["echo", "pkgdir"]) -> echo(pkgdir); +dispatch(["echo", "deps"]) -> echo(deps); +dispatch(["init"]) -> init(); +dispatch(["build" | Opts]) -> build(Opts); +dispatch(["mindist" | Opts]) -> mindist(Opts); +dispatch(["push"]) -> push(); +dispatch(["ls"]) -> ls(); +dispatch(["tree"]) -> tree(); +dispatch(["rmpkg", Pkg]) -> rmpkg(Pkg); +dispatch(["pull"]) -> pull(); +dispatch(_) -> help(). + + + + + + +%%----------------------------------------------------------------------------- +%% jex cfgbarf +%%----------------------------------------------------------------------------- + +cfgbarf() -> + io:format("~tp~n", [file:consult("jex.eterms")]). + + +cfg() -> + file:consult("jex.eterms"). + +%%----------------------------------------------------------------------------- +%% jex echo +%%----------------------------------------------------------------------------- + +echo(home) -> + tell(info, "~ts", [home()]); +echo(jexdir) -> + tell(info, "~ts", [jexdir()]); +echo(devdir) -> + tell(info, "~ts", [devdir()]); +echo(pkgname) -> + tell(info, "~ts", [pkgname()]); +echo(pkgdir) -> + tell(info, "~ts", [pkgdir()]); +echo(deps) -> + PrintDep = + fun(Dep) -> + tell(info, "~ts", [Dep]) + end, + lists:foreach(PrintDep, deps()); +echo({pathof, Pkg}) -> + tell(info, "~ts", [pathof(Pkg)]). + + +home() -> + case os:getenv("HOME") of + false -> error("You are running some retard system that doesn't have $HOME defined. Fuck off"); + HomeD -> HomeD + end. + + +jexdir() -> + filename:join(home(), ".jex"). + +devdir() -> + filename:join(jexdir(), "dev"). + + +pkgname() -> + {ok, Cfg} = cfg(), + Realm = proplists:get_value(realm, Cfg), + Name = proplists:get_value(name, Cfg), + Vsn = proplists:get_value(version, Cfg), + io_lib:format("~tp-~tp-~ts", [Realm, Name, Vsn]). + +pkgdir() -> + filename:join(devdir(), pkgname()). + + +deps() -> + {ok, Cfg} = cfg(), + case proplists:get_value(deps, Cfg) of + undefined -> + error("jex.eterms is missing key `deps`"); + Deps -> + Deps + end. + + +pathof(Pkg) -> + Filename = filename:join(devdir(), Pkg), + case file_exists(Filename) of + true -> Filename; + false -> error({unknown_package, Pkg}) + end. + +file_exists(Filename) -> + case file:read_file_info(Filename) of + {ok, _} -> true; + {error, _} -> false + end. + + + +%%----------------------------------------------------------------------------- +%% jex init +%%----------------------------------------------------------------------------- + +init() -> + DevDir = devdir(), + Cmd = io_lib:format("mkdir -p ~ts", [DevDir]), + _ = cmd(Cmd), + ok. + + + +%%----------------------------------------------------------------------------- +%% jex build +%%----------------------------------------------------------------------------- + +build(Opts) -> + % flags if flag default + OptsConfig = [{["-w", "--weak"], {weak, weak}, {weak, strict}}, + {["-f", "--force"], {force, force}, {force, dont_force}}], + #{force := Force, weak := Weak} = parseopts(OptsConfig, Opts), + _ = cmd("mkdir -p src/jex_include"), + ok = tsc(Weak), + ok = cp_jex_include(Force), + ok. + %ok. + +tsc(strict) -> + "" = cmd("tsc"), + ok; +tsc(weak) -> + _ = cmd("tsc"), + ok. + +cp_jex_include(dont_force) -> + _ = cmd("cp -rv src/jex_include dist"), + ok; +cp_jex_include(force) -> + _ = cmd("cp -rvf src/jex_include dist"), + ok. + + + +%%----------------------------------------------------------------------------- +%% jex mindist +%%----------------------------------------------------------------------------- + +mindist(Opts) -> + % flags if flag default + OptsConfig = [{["-f", "--force"], {force, force}, {force, dont_force}}], + #{force := Force} = parseopts(OptsConfig, Opts), + _ = cmd("mkdir -p jex_mindist"), + _ = mindist_cp(Force), + _ = cmd("rm -r jex_mindist/src/jex_include"), + ok. + +mindist_cp(dont_force) -> + _ = cmd("cp -rv src jex_mindist"), + _ = cmd("cp -rv dist jex_mindist"), + ok; +mindist_cp(force) -> + _ = cmd("cp -rvf src jex_mindist"), + _ = cmd("cp -rvf dist jex_mindist"), + ok. + + + +%%----------------------------------------------------------------------------- +%% jex push +%%----------------------------------------------------------------------------- + +push() -> + _ = cmd(io_lib:format("rsync -avv jex_mindist/ ~ts", [pkgdir()])), + ok. + + +%%----------------------------------------------------------------------------- +%% jex ls +%%----------------------------------------------------------------------------- + +ls() -> + _ = cmd(io_lib:format("ls ~ts", [devdir()])), + ok. + +%%----------------------------------------------------------------------------- +%% jex tree +%%----------------------------------------------------------------------------- + +tree() -> + _ = cmd(io_lib:format("tree ~ts", [jexdir()])), + ok. + +%%----------------------------------------------------------------------------- +%% jex rmpkg Pkg +%%----------------------------------------------------------------------------- + +rmpkg(Pkg) -> + Filename = pathof(Pkg), + _ = cmd(io_lib:format("rm -r ~ts", [Filename])), + ok. + +%%----------------------------------------------------------------------------- +%% jex pull +%%----------------------------------------------------------------------------- + +pull() -> + pull(deps()). + +pull([Dep | Deps]) -> + pull_dep(Dep), + pull(Deps); +pull([]) -> + tell(info, "no more dependencies to pull", []), + ok. + +pull_dep(Dep) -> + Src = pathof(Dep), + Dst = filename:join("./src/jex_include", Dep), + _ = cmd(io_lib:format("mkdir -p ~ts", [Dst])), + %tell(info, "path of ~s: ~s", [Dep, Src]), + _ = cmd(io_lib:format("rsync -avv ~ts/ ~ts", [Src, Dst])), + ok. + +%%----------------------------------------------------------------------------- +%% INTERNALS +%%----------------------------------------------------------------------------- +cmd(Command) -> + ok = tell("$ ~ts", [Command]), + S = os:cmd(Command), + ok = tell("~ts", [S]), + S. + +parseopts(OptsConfig, Opts) -> + log(info, "OptsConfig: ~tp", [OptsConfig]), + log(info, "Opts: ~tp", [Opts]), + DefaultOpts = default_opts(OptsConfig, #{}), + log(info, "DefaultOpts: ~tp", [DefaultOpts]), + Updater = updater(OptsConfig, #{}), + log(info, "Updater: ~tp", [Updater]), + Flags = getflags(Opts, []), + UpdatedFlags = update_flags(Flags, Updater, DefaultOpts), + log(info, "UpdatedFlags: ~tp", [UpdatedFlags]), + UpdatedFlags. + +update_flags([Flag | Rest], Updater, AccFlags) -> + NewAcc = + case maps:find(Flag, Updater) of + {ok, {NewFlag, NewValue}} -> + AccFlags#{NewFlag => NewValue}; + error -> + error(["invalid flag", Flag]) + end, + update_flags(Rest, Updater, NewAcc); +update_flags([], _Updater, FinalAcc) -> + FinalAcc. + +updater([{DashedFlags, IfFlag, _Def} | Rest], Acc) -> + UndashedFlags = getflags(DashedFlags, []), + Flagger = + fun (UndashedFlag, UndashedFlagToOptionMap) -> + UndashedFlagToOptionMap#{UndashedFlag => IfFlag} + end, + NewAcc = lists:foldl(Flagger, Acc, UndashedFlags), + updater(Rest, NewAcc); +updater([], Acc) -> + Acc. + +default_opts([{_X, _Y, {Z, W}} | Rest], Acc) -> + default_opts(Rest, Acc#{Z => W}); +default_opts([], FinalAcc) -> + FinalAcc. + +% --flag (long flag) +getflags(["--"++LongFlag | Flags], Acc) -> + NewAcc = [LongFlag | Acc], + getflags(Flags, NewAcc); +% -xyz (many short flags) +getflags(["-"++ShortFlags | Flags], Acc) -> + NewAcc = lists:map(fun(Char) -> [Char] end, ShortFlags) ++ Acc, + getflags(Flags, NewAcc); +% no more options +getflags([], FinalAcc) -> + FinalAcc. diff --git a/erl/jex/zomp.meta b/erl/jex/zomp.meta new file mode 100644 index 0000000..4d68b22 --- /dev/null +++ b/erl/jex/zomp.meta @@ -0,0 +1,18 @@ +{a_email,[]}. +{author,[]}. +{c_email,[]}. +{copyright,[]}. +{deps,[]}. +{desc,"jex"}. +{file_exts,[]}. +{key_name,none}. +{license,"MIT"}. +{mod,"jex"}. +{modules,[]}. +{name,"jex"}. +{package_id,{"otpr","jex",{0,1,0}}}. +{prefix,none}. +{repo_url,[]}. +{tags,[]}. +{type,cli}. +{ws_url,[]}. diff --git a/erl/vlogd b/erl/vlogd new file mode 160000 index 0000000..a0a6373 --- /dev/null +++ b/erl/vlogd @@ -0,0 +1 @@ +Subproject commit a0a63736b86a0fe8db40e41308780dcb7a6b0fce diff --git a/ts/awcp/.gitignore b/ts/awcp/.gitignore new file mode 100644 index 0000000..f7f9d8a --- /dev/null +++ b/ts/awcp/.gitignore @@ -0,0 +1,18 @@ +*.swp +*.swo +*.beam +dist_js +sidekick_dist +docs +examples/examples_dist_js +www_tree +prose +*.hi +*.o +sidekick_mindist +sidekick_fulldist +dist +jx_mindist +*jx_include* +jex_mindist +erl_crash.dump diff --git a/ts/awcp/LICENSE b/ts/awcp/LICENSE new file mode 100644 index 0000000..2e52b8d --- /dev/null +++ b/ts/awcp/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2022 Peter Harpending + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/ts/awcp/Makefile b/ts/awcp/Makefile new file mode 100644 index 0000000..131d276 --- /dev/null +++ b/ts/awcp/Makefile @@ -0,0 +1,15 @@ +all: prepare build + +deploy: prepare build mindist push + +prepare: + jx pull -f + +build: + jx build -f + +mindist: + jx mindist -f + +push: + jx push -f diff --git a/ts/awcp/README.md b/ts/awcp/README.md new file mode 100644 index 0000000..5257acb --- /dev/null +++ b/ts/awcp/README.md @@ -0,0 +1,490 @@ +# Sidekick + +Sidekick is a simple JavaScript library for talking to an Aeternity +browser wallet extension such as Superhero from the document context +of a webpage. + +| **Thing** | **URL** | +| --- | --- | +| Bug Tracker | https://gitlab.com/DoctorAjayKumar/sidekick/-/issues | +| Documentation | http://orangepill.healthcare/projects/sidekick/current/docs | +| Examples | https://gitlab.com/DoctorAjayKumar/sidekick/-/tree/master/examples | +| Git repository | https://gitlab.com/DoctorAjayKumar/sidekick | +| Homepage | http://orangepill.healthcare/projects/sidekick | +| License | ./LICENSE.txt | +| Maintainer | Dr. Ajay Kumar PHD | +| Releases | http://orangepill.healthcare/projects/sidekick/releases | + + +> One of the most important things for designing a computer, which I +> think most designers don't do, is you study the problem you want to +> solve. And then use what you learn from studying the problem you +> want to solve to put in the mechanisms needed to solve it in the +> computer you're building. No more, no less. + +— Gerald Jay Sussman + +### What sidekick is NOT + +Sidekick is **not** ideal if you operate in the Node/NPM ecosystem. If you are +working in the Node/NPM ecosystem, you probably want the [Aeternity JavaScript +SDK](https://github.com/aeternity/aepp-sdk-js/). Every single +Aeternity-related thing you could ever possibly want to do is possible to +accomplish with the SDK. + +All that Sidekick knows how to do is talk to a browser wallet extension. In an +application, there is a great deal of necessary functionality (e.g. forming +transactions for the wallet to sign) which sidekick assumes your server-side +code has already handled. + +In all software there is a tradeoff between simplicity and number of features. +Sidekick is very simple: 2300 lines of TypeScript, including comments, with no +dependencies. Therefore Sidekick intentionally only has a very limited set of +features. + + +# How to use this library + +To obtain this library, download and unpack a release tarball + + wget http://zxq9.com/projects/vanillae/sidekick/releases/sidekick_dist_X-Y-Z.tar.gz + tar xzvf sidekick_dist_X-Y-Z.tar.gz + +You *should* be able to get all of the functionality you need just +from the exposed functions in the `sidekick` module. (If this is not +the case, please report this as a bug.) + +You probably want to read the documentation of: + + sidekick.js : top-level function calls + awcp/awcp.js : explains the return types of top-level sidekick + functions, explains what sidekick actually does + for you, and explains how all the various + modules fit together + skylight.js : main data structure in sidekick + helpers.js : what it sounds like + +There are examples explained in this document. Their source can be +found in full in the GitLab repository, link above. The examples +exhibit how to interact with the standard release tarball using +TypeScript. + + +## General flow of Sidekick usage + +1. Import the sidekick.js file: + + ```typescript + import * as sk from '/path/to/sidekick_dist_X-Y-Z/dist_js/sidekick.js' + ``` + +2. Create a `Skylight`. This is the main data structure of Sidekick. + + There are two ways to do this + + 1. `start_dwim() : Promise` + + This is probably the one you want. It starts up the Skylight + and does the handshake with the wallet (will pop up the + little window for your user). The effect of this is to + populate all the variables like the user's public key. + + Note that this is an async function. It doesn't return until + the handshake is complete. + + ```typescript + // Needs to be wrapped in an async function in order to use + // await keyword + async function main() : Promise { + let my_skylight = await sk.start_dwim(); + ... + } + + main(); + ``` + + 2. `start : Skylight` + + This is the vanilla option. All it does is create the + Skylight object. + + Beware that this does start up a message queue that passively + listens for messages from the wallet. + + +3. Pass the skylight in to various functions along with additional + calldata generated by your server-side backend code. + + +## Example 1: Hello World + +The effect of this example is to print "hello world" to the JS +console (Ctrl+Shift+C in most browsers). This example is included for +debugging/mental-quicksand-escape purposes. + +```html + + + + + Hello world: Sidekick + + +

Sidekick Example: Hello World

+ +

Check the console

+ + + +``` + +The important lines are + +```html + +``` + +This shows + +- you need to use `type="module"` in order for imports to work +- how to do an import +- how to call functions from an imported module + + +## Example 2: get the user to sign a transaction + +Anything you will want the user to do (e.g. sign smart contracts) is +contained in "get the user to sign a transaction". This example +illustrates the basic things you need to do in order to accomplish +that. + +Sidekick does not provide functionality for forming the transaction +string you want the user to sign. Your backend should be doing that. + +The example here uses a shim library called `parasite`, which you can find in +the GitLab repository in the `examples` directory. Parasite is just a rewrite +layer in front of the `testnet.aeternity.io` JSON interface. It is not suitable +for usage in production. + +This is adapted from +[`examples/examples_src_ts/do_a_transfer.ts`][dat_ts]. The +difference is that `do_a_transfer.ts` is written against +[`examples/examples_html/do_a_transfer.html`][dat_html]. It contains +a lot of unnecessary detail, like doing things in response to buttons +being clicked, and filling in DOM textboxes with the results of things. + +The full example has logic that resembles a register machine, rather +than a functional program. This example is closer to a functional +program. + +[dat_html]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_html/do_a_transfer.html +[dat_ts]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_src_ts/do_a_transfer.ts + + +```typescript +import * as ae_node from './parasite/ae_node.js'; +import * as sk from '../sidekick_dist/dist_js/sidekick.js'; + +async function +main() + : Promise +{ + //--------------------------------------------------------------- + // STEP 1: MAKE A SKYLIGHT + //--------------------------------------------------------------- + let skl : sk.Skylight = + await sk.start_dwim(sk.TIMEOUT_DEF_DETECT, + sk.TIMEOUT_DEF_CONNECT, + sk.TIMEOUT_DEF_ADDRESS); + + + //--------------------------------------------------------------- + // STEP 2: GET THE USER'S ADDRESS + //--------------------------------------------------------------- + let user_addr : string = + await sk.address(skl, sk.TIMEOUT_DEF_ADDRESS); + + // faster/non-async: + // + // let user_addr = skl.waellet_address + // + // the non-commented code + // + // - will crash if the wallet isn't connected + // - will query the wallet for the address if the wallet *is* + // connected but the skl.waellet_address field *is not* + // populated + // - will crash if that times out after the second argument + // number of milliseconds + // + // if you know with 100% certainty that the address field will be + // populated (which we do here, because we used connect_dwim), + // then it's safe to just grab the field from the skylight + // + // The problem is that if the address field isn't populated, the + // commented code defaults to `undefined`, rather than crashing + // with an error message and callstack trace + + + //--------------------------------------------------------------- + // STEP 3: FORM THE TRANSACTION + // + // This will be different for your application, because you're + // not using parasite (right?) + // + // You need to figure out how to make something like `tx_obj` on + // your own. + // + // `tx_obj` is just `{tx: "some_base58_garbage"}`. + //--------------------------------------------------------------- + + let target_addr : string = + 'ak_dvNHMgVvdSgDchLsmcUpuFTbMBGfG3E5V9KZnNjLYPyEhcqnL'; + + // amount is in aettos + let amount : number = 1; + let endpoint : string = ae_node.URL_TESTNET; + let spendtx = {'recipient_id' : target_addr, + 'amount' : amount, + 'fee' : ae_node.MIN_FEE, + 'sender_id' : user_addr, + 'payload' : ""}; + let tx_obj = await ae_node.PostSpend(endpoint, spendtx); + + + //--------------------------------------------------------------- + // STEP 4: HAVE THE USER SIGN THE TRANSACTION + // + // There are two options: + // + // 1. `tx_sign_no_propagate` simply has the wallet sign the + // transaction, and return the signed transaction back to you + // + // 2. `tx_sign_yes_propagate` has the wallet sign the transaction + // and also propagates it into the network. It returns a more + // elaborate data structure, which is documented in somewhere + // the AWCP module documentation. + //--------------------------------------------------------------- + + let result = + await sk.tx_sign_no_propagate(skl, + tx_obj, + sk.NETWORK_ID_TESTNET, + sk.TIMEOUT_DEF_SIGN); + + console.log(result); + +} + +main(); +``` + + +# Known pitfalls + +- See notes in sidekick module about potential state crossups if your + document logic is multi-threaded. + +- Has only been tested against Firefox-on-Linux + +- Have not worked out good user idioms for handling errors + + + + + +# How the release is structured and why + +The idea of the release is that it contains exactly what is needed in +order to drop Sidekick into your project and start using it. No more, +no less. + +A release has the following structure: + +``` +sidekick_dist_X-Y-Z/ + dist_js/............tsc-generated human-readable JS tree + foo.js..............the actual code that is run + foo.d.ts............included so that your TypeScript code can + typecheck against sidekick + foo.js.map..........debug symbols that map foo.js to + locations in the TypeScript source + src_ts/.............included so that the browser's debugger works + foo.ts + README.txt + LICENSE.txt +``` + +We use semantic versioning: `X.Y.Z` + +- A change in `X` means an API-breaking change +- A change in `Y` means an non-breaking API change +- A change in `Z` means no change to the API + +There is no documentation included in the release. There is +autogenerated API documentation linked above, which is generated from +this file and the sources that are included in the release. + + +## What each file does + +``` +src_ts/.....................TypeScript source for sidekick library + awcp/.......................Aepp-Waellet Communication Protocol + awcp.ts.....................Protocol definition + msgq.ts.....................Block-on-raseev implementation + msgr.ts.....................Protocol implementation + helpers.ts..................what it sounds like + sidekick.ts.................top-level module + skylight.ts.................primary data structure +``` + + +## Where is the NPM package or the webpack bundle? + +There isn't one. + + +### Why? + +Sidekick is a library that deals with cryptocurrency. The security +model is based on transparency and trust. A user must be able to +inspect code that is running on his hardware handling his money. + +We don't support NPM because of the security issues that NPM +introduces. Briefly, the code can change at any time under the +developer's nose without the developer knowing. The [leftpad +debacle][lpad] and the [RIAEvangelist debacle][ria] are good examples +of the types of vulnerabilities that package managers like NPM +enable. + +[lpad]: https://archive.ph/Qsh7j +[ria]: https://archive.ph/OF5I9 + +We don't use something like webpack because that introduces an opaque +rewrite using an untrusted tool. Webpack could plausibly alter the +runtime behavior of the program, and we would have no way to detect +that. Even if we trusted webpack, how do we obtain webpack? NPM. +So. + +It is debatable whether or not we should trust the TypeScript +compiler (TSC). On the whole, TSC probably makes Sidekick more +secure, simply by virtue of increasing overall code quality and +eliminating the largest categories of potential bugs. Moreover, the +output that TSC produces is human-readable, and has a very +straightforward mapping to the original source code. A human can +easily read the TSC-generated JavaScript tree, even without the aid +of the source map, and have a high degree of faith that the code is +trustworthy and that TSC is behaving as promised. + + + +# How to obtain the source tree + +The source tree is included in your release. You can clone the +git repository with + +``` +git clone https://gitlab.com/DoctorAjayKumar/sidekick.git +``` + + + +# Repo file tree (non-exhaustive) + + +``` +examples/...................Examples + contract-examples/..........Example Sophia smart contracts + examples_html/..............Low-budget example interfaces + do_a_transfer.html......Send money to an arbitrary address + hello.html..............Hello world + ide.html................Play around with smart contracts + examples_src_ts/............TypeScript source for each example + parasite/...................JavaScript shim that does what + your backend code ordinarily + would do + ae_compiler.ts..............Talk to a remote Sophia + compiler JSON HTTP interface + ae_node.ts..................Talk to a node (for forming + transactions, querying chain, + etc) + net.ts......................Network helper functions + do_a_transfer.ts........Send money to an arbitrary address + ide.ts..................Play around with smart contracts + tsconfig.json...............Examples-specific tsc configuration +src_ts/.....................TypeScript source for sidekick library + awcp/.......................Aepp-Waellet Communication Protocol + awcp.ts.....................Protocol definition + msgq.ts.....................Block-on-raseev implementation + msgr.ts.....................Protocol implementation + helpers.ts..................what it sounds like + sidekick.ts.................top-level module + skylight.ts.................primary data structure +LICENSE.txt.................MIT License +Makefile....................Makefile + make........................Equivalent to `make build` + make build..................Run tsc + make clean..................rm -r dist_js sidekick_dist docs \ + examples/examples_dist_js + make dist...................Build a release tarball directory + make jsdoc..................Build the HTML documentation + make build_examples.........cd examples && tsc + make serve_examples.........cd examples && python3 -m \ + http.server 8001 +README.txt..................This file +STYLE_GUIDE.md..............Explains why the code looks so weird +TODO.md.....................what it sounds like +tsconfig.json...............Configuration file for tsc +``` + + + +# How to build a release + +You will need the TypeScript compiler installed. See prereqs section. + + make dist + + +## Prereqs for building source files + +If you want to edit and rebuild the source files, you need TypeScript +installed, and you should update npm. + + npm install -g typescript + npm install -g npm + +## Protip: avoid using `sudo npm install -g` + +If you want to avoid using sudo + +``` +mkdir ~/.npm-packages +npm config set prefix "${HOME}/.npm-packages" +``` + +Edit `~/.bashrc` or `~/.zshrc` with + +``` +NPM_PACKAGES="${HOME}/.npm-packages" +export PATH=$NPM_PACKAGES/bin:$PATH +``` + +Source: https://github.com/sindresorhus/guides/blob/main/npm-global-without-sudo.md + + +# How to build and view documentation + +``` +make build_docs +make serve_docs +``` + + diff --git a/ts/awcp/jex.eterms b/ts/awcp/jex.eterms new file mode 100644 index 0000000..ddebe0b --- /dev/null +++ b/ts/awcp/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, awcp}. +{version, "0.1.0"}. +{deps, []}. diff --git a/ts/awcp/src/awcp.ts b/ts/awcp/src/awcp.ts new file mode 100644 index 0000000..c1163a1 --- /dev/null +++ b/ts/awcp/src/awcp.ts @@ -0,0 +1,724 @@ +/** + * # AWCP: aepp-waellet communication protocol + * + * Suppose you are the aepp and you want to communicate with a waellet. What + * you do is pick an `EventTarget` (typically `window`), and listen to its + * `MessageEvent`s, via something like + * + * ```typescript + * window.addEventListener('message', my_listener); + * ``` + * + * You then communicate with the wallet by sending messages back over the + * `EventTarget` + * + * This module defines the shape of the messages that are sent. There are several + * layers to the onion, each corresponding to natural branch points in the + * protocol. + * + * 1. The `MessageEvent` layer. This is what is actually sent as an event. + * This is an opaque object that is built into every runtime's standard + * library: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent + * + * The `MessageEvent` has a field called `data`, which corresponds to the + * next layer. + * + * 2. The `EventData` layer. This is what goes in `message_event.data`. The + * structure that goes in here is one of + * + * 1. `EventData_W2A`: waellet-to-aepp + * 2. `EventData_A2W`: aepp-to-waellet + * + * This layer corresponds to the "am I supposed to pay attention to this + * event?" branch point. + * + * Those data structures mentioned above have two fields. + * + * 1. `type` is a string which is either `"to_aepp"` or `"to_waellet"` + * 2. `data` contains the next layer + * + * ```typescript + * type EventData_W2A + * + * = {type : "to_aepp", + * data : t}; + * ``` + * + * ``` typescript + * type EventData_A2W + * + * = {type : "to_waellet", + * data : t}; + * ``` + * + * + * 3. We're at `message_event.data.data`. The idiom here is "JSON RPC", which + * is sort of a poor man's HTTP. + * + * In general, the wallet is the server and the aepp is the client. + * + * If you are the aepp, usually you are handling a response to a request + * you sent to the waellet. For instance, you formed a transaction and + * sent it to the waellet to sign, and the waellet is sending you back + * either the signed transaction or an error (e.g. user rejected the + * transaction). + * + * The exception to this pattern is the wallet notifying you that it + * exists, which is the only time the waellet sends a request (a "cast", or + * a "notification") to the aepp. __In no event does the aepp send a + * response to the waellet.__ + * + * I am not 100% sure what RPC stands for, but it will be helpful to think + * about it as "remote procedure call". More below. This layer roughly + * corresponds to the "given that I am supposed to pay attention to this + * event, what am I supposed to do with this information?" + * + * All requests have a `method` field (a string) and a `params` field (an + * object). There are two types of requests: + * + * 1. "casts" (the RPC standard calls these "notifications"). These do not + * need a response. This is only used for the waellet announcing it + * exists. + * + * 2. "calls". These have an `id` field, and get a response. These are + * used when the aepp is requesting the waellet to do something. The + * response will have the same `id` field and the same `method` field. + * + * 4. So far nothing we've talked about is specific to Aeternity, Vanillae, + * JR, or sidekick. This fourth layer is the actual semantics of the + * messaging protocol between the aepp and the waellet. + * + * By analogy, the first two layers are developing something like TCP. The + * third layer is developing HTTP. And this layer is the actual routing + * table of your website, which carries with it the expected semantics of + * how the website is supposed to behave. + * + * This module DOES __NOT__ exhaustively define all of the communication + * protocol that occurs in the SDK, only the subset that I have encountered + * in practice. + * + * The first layer is defined by the runtime, not here. So we're starting with + * layer 2. + * + * # Notes on JSON RPC 2.0 + * + * I have subtly changed the RPC protocol to + * + * 1. improve it in such a way that it is easier to use in code + * 2. more accurately represent how it is used in practice + * + * The only difference here is that the `params` field of requests is + * non-optional, and must be an object (the RPC standard allows arrays). + * + * ## Requests + * + * I have adapted the verbiage here, borrowing from Erlang, to make a + * distinction between + * + * 1. casts (RPC calls these "notifications"): these + * + * 1. do __NOT__ have an `id` field + * 2. __AND__ do __NOT__ require a response. + * + * 2. calls: these + * + * 1. __DO__ have an `id` field + * 2. __AND__ __DO__ require a response. + * + * The `RpcCall` and `RpcResp` data structures each have an `id_n` type + * parameter. + * + * The purpose of this is to notate (and possibly enforce) at the type level + * the constraint that, given a call with say `id = 7`, the response must also + * have `id = 7`. + * + * # Links + * + * - `MessageEvent`s: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent + * - JSON RPC 2.0 https://www.jsonrpc.org/specification + * + * @module + */ + +// TODO: TS code style guide +// TODO: annotate everything with examples +// TODONE: enumerate RPC errors +// TODO: move Safe in here +// TODO: constants for method names + + +//============================================================================= +// IMPORTS +//============================================================================= + +//import type { +// ERROR_TYPE_RpcInvalidTransactionError, +// ERROR_TYPE_RpcBroadcastError, +// ERROR_TYPE_RpcRejectedByUserError, +// ERROR_TYPE_RpcUnsupportedProtocolError, +// ERROR_TYPE_RpcConnectionDenyError, +// ERROR_TYPE_RpcNotAuthorizeError, +// ERROR_TYPE_RpcPermissionDenyError, +// ERROR_TYPE_RpcInternalError, +// ERROR_TYPE_RpcMethodNotFoundError, +//} from './errcode.js'; + + + + +//============================================================================= +// EXPORTS +//============================================================================= + + +export { + // error code constants + ERROR_CODE_RpcInvalidTransactionError, + ERROR_CODE_RpcBroadcastError, + ERROR_CODE_RpcRejectedByUserError, + ERROR_CODE_RpcUnsupportedProtocolError, + ERROR_CODE_RpcConnectionDenyError, + ERROR_CODE_RpcNotAuthorizeError, + ERROR_CODE_RpcPermissionDenyError, + ERROR_CODE_RpcInternalError, + ERROR_CODE_RpcMethodNotFoundError, + // Layer 2: events + EventData_W2A, + EventData_A2W, + // Layer 3: RPC + RpcError, + RpcCast, + RpcCall, + RpcResp_error, + RpcResp_ok, + RpcResp, + RpcResp_Any, + // Layer 4: specific semantics + // connection.announcePresence + Params_W2A_connection_announcePresence, + RpcCast_W2A_connection_announcePresence, + EventData_W2A_connection_announcePresence, + // connection.open + Params_A2W_connection_open, + Result_W2A_connection_open, + RpcCall_A2W_connection_open, + RpcResp_W2A_connection_open, + EventData_A2W_connection_open, + EventData_W2A_connection_open, + // address.subscribe + Params_A2W_address_subscribe, + Result_W2A_address_subscribe, + RpcCall_A2W_address_subscribe, + RpcResp_W2A_address_subscribe, + EventData_A2W_address_subscribe, + EventData_W2A_address_subscribe, + //// transaction.sign (propagate) + //Params_A2W_tx_sign_yesprop, + //Result_W2A_tx_sign_yesprop, + //RpcCall_A2W_tx_sign_yesprop, + //RpcResp_W2A_tx_sign_yesprop, + //EventData_A2W_tx_sign_yesprop, + //EventData_W2A_tx_sign_yesprop, + // transaction.sign (do not propagate) + Params_A2W_tx_sign_noprop, + Result_W2A_tx_sign_noprop, + RpcCall_A2W_tx_sign_noprop, + RpcResp_W2A_tx_sign_noprop, + EventData_A2W_tx_sign_noprop, + EventData_W2A_tx_sign_noprop +}; + + + +//============================================================================= +// LAYER 2: WHO IS THIS MESSAGE FOR +// +// The first layer is defined by the runtime, not here. So we're starting with +// layer 2. +//============================================================================= + +/** + * This is the data that is sent from the wallet to the aepp through the Event + * bus + */ +type EventData_W2A + + = {type : "to_aepp", + data : t}; + + +/** + * This is the data that is sent from the aepp to the wallet through the Event + * bus + */ +type EventData_A2W + + = {type : "to_waellet", + data : t}; + + + + +//============================================================================= +// LAYER 3: JSON RPC 2.0 +// +// It should be noted in general that the id field exists as a type parameter +// so that we can use the type system to denote ID matches in callbacks. +// +// Remember, in TypeScript's type system, values are valid types. +// +// So `(_arg0: Foo<3, string>) => Bar<3, number>` is a valid type +//============================================================================= + +/** + * `const ERROR_CODE_RpcInvalidTransactionError = 2;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L92 + */ +const ERROR_CODE_RpcInvalidTransactionError = 2; + +/** + * `const ERROR_CODE_RpcBroadcastError = 3;` + * + * See + * https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L108 + */ +const ERROR_CODE_RpcBroadcastError = 3; + +/** + * `const ERROR_CODE_RpcRejectedByUserError = 4;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L122 + */ +const ERROR_CODE_RpcRejectedByUserError = 4; + + +/** + * `const ERROR_CODE_RpcUnsupportedProtocolError = 5;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L140 + */ +const ERROR_CODE_RpcUnsupportedProtocolError = 5; + + +/** + * This error occurs when the user rejects your attempt to connect. (I think) + * + * The error name here is ungrammatical but following the lead of the SDK. + * + * `const ERROR_CODE_RpcConnectionDenyError = 9;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L155 + */ +const ERROR_CODE_RpcConnectionDenyError = 9; + +/** + * This error occurs when you are not connected to the wallet. + * + * The error name here is ungrammatical but following the lead of the SDK. + * + * `const ERROR_CODE_RpcNotAuthorizeError = 10;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L171 + */ +const ERROR_CODE_RpcNotAuthorizeError = 10; + + +/** + * This error occurs when you are not `address.subscribe`d to the wallet (I think?) + * + * The error name here is ungrammatical but following the lead of the SDK. + * + * `const ERROR_CODE_RpcPermissionDenyError = 11;` + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L186 + */ +const ERROR_CODE_RpcPermissionDenyError = 11; + +/** + * This is the general "something went wrong, i dunno" negative error. + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L196-L209 + */ +const ERROR_CODE_RpcInternalError = 12; + + +/** + * This is presumably the equivalent of the HTTP 404 error + * + * See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L211-L224 + */ +const ERROR_CODE_RpcMethodNotFoundError = -32601; + + +/** + * Error data inside the `error` field of a RpcResp_Err + */ +type RpcError + = {code : number, + message : string, + data? : any}; + + + + +//----------------------------------------------------------------------------- +// Requests +//----------------------------------------------------------------------------- + +/** + * This type is used for "notifications", i.e. messages sent from the client to + * the server that do not need a response. An example is the wallet announcing + * it exists. + */ +type RpcCast + + = {jsonrpc : "2.0", + method : method_s, + params : params_t}; + + + +/** + * This type is used for requests from the client to the server that require a + * response. + */ +type RpcCall + + = {jsonrpc : "2.0", + id : number | string, + method : method_s, + params : params_t}; + + + + +//----------------------------------------------------------------------------- +// Responses +//----------------------------------------------------------------------------- + +/** + * This is the shape of unsuccessful responses + */ +type RpcResp_error + + = {jsonrpc : "2.0", + id : number | string, + method : method_s, + error : RpcError}; + + + +/** + * This is the shape of successful responses + */ +type RpcResp_ok + + = {jsonrpc : "2.0", + id : number | string, + method : method_s, + result : result_t}; + + + +/** + * This is the shape of generic responses + */ +type RpcResp + + = RpcResp_ok + | RpcResp_error; + + + +/** + * Most generic possible response + */ +type RpcResp_Any = RpcResp; + + + + +//============================================================================= +// LAYER 4: VANILLAE-SPECIFIC MESSAGE PROTOCOL +// +// https://github.com/aeternity/aepp-sdk-js/blob/a435e9df5c94004bcd16326b26c38a9c0b284279/src/aepp-wallet-communication/schema.ts#L32-L42 +// +// Only the request/responses that I have actually encountered in practice are +// enumerated here. There are many more things that the SDK code appears to +// use which I did not encounter in the wild. +//============================================================================= + +// TODO: need to do more experimentation with superhero to see what sorts of +// messages it sends back to the other requests + +//---------------------------------------------------------------------------- +// connection.announcePresence +//---------------------------------------------------------------------------- + +/** + * Waellet-to-aepp parameters of "connection.announcePresence" cast + * + * (layer 4) + */ +type Params_W2A_connection_announcePresence + = {id : string, + name : string, + origin : string, + type : "window" | "extension"}; + + + +/** + * Shape of the waellet-to-aepp "connection.announcePresence" RPC cast + * + * (layer 3) + */ +type RpcCast_W2A_connection_announcePresence + = RpcCast<"connection.announcePresence", + Params_W2A_connection_announcePresence>; + + +/** + * The actual waellet-to-aepp event passed when the wallet announces it exists + * + * (layer 2) + * + * @example + * + * ```json + * { + * "type": "to_aepp", + * "data": { + * "jsonrpc": "2.0", + * "method": "connection.announcePresence", + * "params": { + * "id": "{aee9e933-52b6-410a-8c3f-99c6be596b4e}", + * "name": "Superhero", + * "networkId": "ae_mainnet", + * "origin": "moz-extension://ee425d81-d5b2-44b6-9406-4da31b019e7c", + * "type": "extension" + * } + * } + * } + * ``` + */ +type EventData_W2A_connection_announcePresence + = EventData_W2A + + + +//---------------------------------------------------------------------------- +// connection.open +//---------------------------------------------------------------------------- + +/** + * Parameters of aepp-to-waellet "connection.open" call + * + * (layer 4) + */ +type Params_A2W_connection_open + = {name : string, + version : 1, + networkId? : string}; + + +/** + * Result type of "connection.open" call + * + * Same as `Params_W2A_connection_open` empirically + * + * (layer 4) + */ +type Result_W2A_connection_open + = Params_W2A_connection_announcePresence; + + + +/** + * Shape of aepp-to-waellet "connection.open" RPC call + * + * (layer 3) + */ +type RpcCall_A2W_connection_open + = RpcCall<"connection.open", + Params_A2W_connection_open>; + + + +/** + * Shape of waellet-to-aepp "connection.open" RPC response + * + * (layer 3) + */ +type RpcResp_W2A_connection_open + = RpcResp<"connection.open", + Result_W2A_connection_open>; + + + +/** + * The actual aepp-to-waellet "connection.open" event data passed over the message bus + * + * (layer 2) + */ +type EventData_A2W_connection_open + = EventData_A2W; + + + +/** + * The actual waellet-to-aepp "connection.open" event data passed over the message bus + * + * (layer 2) + */ +type EventData_W2A_connection_open + = EventData_W2A; + + + + +//---------------------------------------------------------------------------- +// address.subscribe +//---------------------------------------------------------------------------- + +/** + * Parameter type of aepp-to-waellet "address.subscribe" call + * + * (layer 4) + */ +type Params_A2W_address_subscribe + = {type : "subscribe", + value : "connected"}; + + + +/** + * Result type of waellet-to-aepp "address.subscribe response + * + * (layer 4) + * + * @example + * ```typescript + * {subscription : ["connected"], + * address : {current : {"ak_2Wsa8iAmAm917evwDEZjouvPUXKx2nUv5Uz8e8oNXTDfDXnMRN": {}}, + * connected : {}}} + * ``` + */ +type Result_W2A_address_subscribe + = {subscription : ["connected"], + address : {current : object, + connected : object}}; + + + +/** + * Shape of aepp-to-waellet "address.subscribe" RPC call + * + * (layer 3) + */ +type RpcCall_A2W_address_subscribe + = RpcCall<"address.subscribe", + Params_A2W_address_subscribe>; + + + +/** + * Result of waellet-to-aepp "address.subscribe" response + * + * (layer 3) + */ +type RpcResp_W2A_address_subscribe + = RpcResp<"address.subscribe", + Result_W2A_address_subscribe>; + + + +/** + * Actual aepp-to-waellet "address.subscribe" event data sent over the message bus + * + * (layer 2) + */ +type EventData_A2W_address_subscribe + = EventData_A2W; + + + +/** + * Actual waellet-to-aepp "address.subscribe" event data sent over the message bus + * + * (layer 2) + */ +type EventData_W2A_address_subscribe + = EventData_W2A; + + + +//---------------------------------------------------------------------------- +// transaction.sign (do not propagate) +//---------------------------------------------------------------------------- + +/** + * Parameters for "transaction.sign" (do not propagate) + * + * (layer 4) + */ +type Params_A2W_tx_sign_noprop + = {tx : string, + returnSigned : true, + networkId : string} + + + +/** + * Success result type for "transaction.sign" (do not propagate) + * + * (layer 4) + */ +type Result_W2A_tx_sign_noprop + = {signedTransaction : string}; + + +/** + * Request type for "transaction.sign" (do not propagate) + * + * (layer 3) + */ +type RpcCall_A2W_tx_sign_noprop + = RpcCall<"transaction.sign", + Params_A2W_tx_sign_noprop>; + + + +/** + * Response type for "transaction.sign" (do not propagate) + * + * (layer 3) + */ +type RpcResp_W2A_tx_sign_noprop + = RpcResp<"transaction.sign", + Result_W2A_tx_sign_noprop>; + + + +/** + * Event data for aepp-to-waellet "transaction.sign" (do not propagate) message + * + * (layer 2) + */ +type EventData_A2W_tx_sign_noprop + = EventData_A2W; + + + +/** + * Event data for aepp-to-waellet "transaction.sign" (do not propagate) response + * + * (layer 2) + */ +type EventData_W2A_tx_sign_noprop + = EventData_W2A; diff --git a/ts/awcp/tsconfig.json b/ts/awcp/tsconfig.json new file mode 100644 index 0000000..c870354 --- /dev/null +++ b/ts/awcp/tsconfig.json @@ -0,0 +1,16 @@ +{"compilerOptions" : {"target" : "es2022", + "strict" : true, + "esModuleInterop" : true, + "skipLibCheck" : true, + "forceConsistentCasingInFileNames" : true, + "noImplicitAny" : true, + "strictNullChecks" : true, + "strictPropertyInitialization" : true, + "sourceMap" : true, + "outDir" : "dist", + "declaration" : true}, + "$schema" : "https://json.schemastore.org/tsconfig", + "display" : "Recommended", + "include" : ["src/**/*"], + "exclude" : ["src/jx_include"], + "composite" : true} diff --git a/ts/parasite/.gitignore b/ts/parasite/.gitignore new file mode 100644 index 0000000..6d324d9 --- /dev/null +++ b/ts/parasite/.gitignore @@ -0,0 +1,6 @@ +*.swp +*.swo +jex_mindist +dist +src/jex_include +erl_crash.dump diff --git a/ts/parasite/LICENSE b/ts/parasite/LICENSE new file mode 100644 index 0000000..2e52b8d --- /dev/null +++ b/ts/parasite/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2022 Peter Harpending + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/ts/parasite/Makefile b/ts/parasite/Makefile new file mode 100644 index 0000000..33e1aec --- /dev/null +++ b/ts/parasite/Makefile @@ -0,0 +1,14 @@ +all: pull build +deploy: pull build mindist push + +pull: + jx pull -f + +build: + jx build -f + +mindist: + jx mindist -f + +push: + jx push -f diff --git a/ts/parasite/README.md b/ts/parasite/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ts/parasite/jex.eterms b/ts/parasite/jex.eterms new file mode 100644 index 0000000..1584815 --- /dev/null +++ b/ts/parasite/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, parasite}. +{version, "0.1.0"}. +{deps, []}. diff --git a/ts/parasite/src/ae_compiler.ts b/ts/parasite/src/ae_compiler.ts new file mode 100644 index 0000000..decf930 --- /dev/null +++ b/ts/parasite/src/ae_compiler.ts @@ -0,0 +1,96 @@ +/******************************************************************* +** Compiler API +** +** The manner in which this module is laid out differs meaningfully +** from the `ae_node` module. +** +** - ae_node basically exposes a subset of the node interface as +** functions. It only black-boxes away the networking aspects. +** +** - part of the reason for this is that the data structures that get +** sent to the node tend to be pretty involved (lots of fields) +** +** - in general, the node interface is significantly more complicated +** than the compiler interface +** +** - by contrast, the data structures that get sent to the compiler +** tend to be pretty simple and only have a small number of fields +** +** - the manner in which this manifests is that ae_node functions +** tend to have a weird esoteric data structure as the input, +** because otherwise there would be too many parameters. +** +** here, there are few parameters, and they are passed directly +** +** - in general, the compiler has fewer things it can do, and the +** things it does tend to depend on less information. so this +** interface is significantly simpler than the node interface +*******************************************************************/ + +import * as net from './net.js'; + +const URL_COMPILER = 'https://compiler.aepps.com'; +// endpoints +const EPT_CompileContract = URL_COMPILER + '/compile'; +const EPT_EncodeCalldata = URL_COMPILER + '/encode-calldata'; + + +//------------------------------------------------------------------- +// HELPERS +//------------------------------------------------------------------- + +// make a CompileOpts data structure +function +compile_options(filename: string) + : object +{ + return {"backend" : "fate", + "file_system" : {}, + "src_file" : filename}; +} + + + +//------------------------------------------------------------------- +// API CALLS +//------------------------------------------------------------------- + +// send back compile response +async function +CompileContract(code : string, + filename : string) + : Promise +{ + let send_obj = + {"code" : code, + "options" : compile_options(filename)}; + + let response = await net.post_json_response(EPT_CompileContract, send_obj); + return response; +} + + + +async function +EncodeCalldata(code : string, + filename : string, + function_name : string, + function_args : Array) + : Promise +{ + let send_obj = + {"source" : code, + "options" : compile_options(filename), + "function" : function_name, + "arguments" : function_args}; + + let response = await net.post_json_response(EPT_EncodeCalldata, send_obj); + return response; +} + + + +export { + CompileContract, + EncodeCalldata +} diff --git a/ts/parasite/src/ae_node.ts b/ts/parasite/src/ae_node.ts new file mode 100644 index 0000000..932d540 --- /dev/null +++ b/ts/parasite/src/ae_node.ts @@ -0,0 +1,272 @@ +/******************************************************************** +** Node API: functions for talking to an Aeternity node +** +** In the future, everything that this module does will be replaced +** by the backend. +** +** Functions should be sorted in alphabetical order. +** +** Names are what they are in the documentation +** +** Useful links: +** +** - HTML API docs : https://api-docs.aeternity.io/ +** - YAML API docs : https://github.com/aeternity/aeternity/blob/master/apps/aehttp/priv/swagger.yaml +** +********************************************************************/ + + +import * as net from './net.js' +import * as ae_compiler from './ae_compiler.js' + +//------------------------------------------------------------------- +// CONSTANTS +//------------------------------------------------------------------- + +export const MIN_FEE = 16660000000000; +export const MIN_CONTRACT_FEE = 79080000000000; +export const MIN_GAS_PRICE = 1000000000; +export const URL_MAINNET = "https://mainnet.aeternity.io/v2"; +export const URL_TESTNET = "https://testnet.aeternity.io/v2"; + + +//------------------------------------------------------------------- +// CANONICAL TYPES ("MODELS") FROM THE DOCUMENTATION +// +// All of these names are as given in the documentation except `Error` +// (which is a reserve term in JS), renamed to `ErrorReason` +//------------------------------------------------------------------- + +// Error +// +// Docs: https://api-docs.aeternity.io/#/definitions/Error +// Docs: https://github.com/aeternity/aeternity/blob/v6.4.0/apps/aehttp/priv/swagger.yaml#L3168-L3172 +type ErrorReason = {reason: string}; + +type Tx = {tx: string}; + + + +//------------------------------------------------------------------- +// FUNCTIONS +// +// The names here follow their names in the documentation +//------------------------------------------------------------------- + + +//------------------------------------------------------------------- +// GetAccountNextNonce: /accounts/{pubkey}/next-nonce +// +// Docs: https://api-docs.aeternity.io/#/account/GetAccountNextNonce +// +// > Get an account's next nonce; This is computed according to +// > whatever is the current account nonce and what transactions are +// > currently present in the transaction pool +//------------------------------------------------------------------- + +type GetAccountNextNonce_params = {pubkey : string, + strategy? : "max" | "continuity"}; + +type GetAccountNextNonce_ret = {next_nonce: string}; + + +async function +GetAccountNextNonce(endpoint_url : string, + params : GetAccountNextNonce_params) + : Promise< GetAccountNextNonce_ret + | ErrorReason> +{ + let pubkey = params.pubkey; + let url = `${endpoint_url}/accounts/${pubkey}/next-nonce`; + + // if the "strategy" field is present, add it as a ?strategy=x + // option + // + // note if the field is absent from `params`, then + // `params.strategy` will be `undefined`, which in js whacko + // world is "falsy" + let strategy = params.strategy; + if (strategy) + { + let addon = `?strategy=${strategy}`; + url += addon; + } + + // irrespective of the response code, this is what we return + // so branching is gay + let ret = await net.get_json(url); + return ret; +} + + + +//------------------------------------------------------------------- +// PostContractCreate +// +// Docs: https://api-docs.aeternity.io/#/contract/PostContractCreate +//------------------------------------------------------------------- + +// > Get a contract_create transaction object + + +type ContractCreateTx = {owner_id : string, + nonce? : number, + code : string, + vm_version : number, + abi_version : number, + deposit : number, + amount : number, + gas : number, + gas_price : number, + fee : number, + ttl? : number, + call_data : string}; + + + +async function +PostContractCreate(endpoint_url : string, + body_obj : ContractCreateTx) + : Promise +{ + // console.log('body_obj', body_obj); + let url = `${endpoint_url}/debug/contracts/create`; + let ret = await net.post_json_response(url, body_obj); + return ret; +} + + + +async function +create_contract(whoami : string, + code : string, + filename : string, + init_args : Array) + : Promise +{ + let code_resp = await ae_compiler.CompileContract(code, filename); + let code_json = await code_resp.json(); + let bytecode = code_json.bytecode; + + let calldata_resp = await ae_compiler.EncodeCalldata(code, filename, "init", init_args); + // assert(calldata_resp.ok); + let calldata_json = await calldata_resp.json(); + let calldata = calldata_json.calldata; + + let cctx: ContractCreateTx = + {owner_id : whoami, + code : bytecode, + vm_version : 7, + abi_version : 3, + deposit : 0, + amount : 0, + gas : 25000, + gas_price : 1*MIN_GAS_PRICE, + fee : 1*MIN_CONTRACT_FEE, + call_data : calldata}; + + let ret = await PostContractCreate(URL_TESTNET, cctx); + + return ret; +} + + + +//------------------------------------------------------------------- +// PostSpend: /debug/transactions/spend +// +// Docs: +// - Input type : https://api-docs.aeternity.io/#/definitions/SpendTx +// - Return type : https://api-docs.aeternity.io/#/definitions/Tx +// - Function : https://api-docs.aeternity.io/#/transaction/PostSpend +// +// > Get a spend transaction object +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// SpendTx +// +// Docs: https://api-docs.aeternity.io/#/definitions/SpendTx +// Docs: https://github.com/aeternity/aeternity/blob/v6.4.0/apps/aehttp/priv/swagger.yaml#L2187-L2209 +//------------------------------------------------------------------- +type SpendTx = + {recipient_id : string, + amount : number, + fee : number, + ttl? : number, + sender_id : string, + nonce? : number, + payload : string}; + + + +async function +PostSpend(endpoint_url : string, + body_obj : SpendTx) + : Promise +{ + let url = `${endpoint_url}/debug/transactions/spend`; + let ret = await net.post_json(url, body_obj); + return ret; +} + + + +//------------------------------------------------------------------- +// PostTransaction +// +// Docs: https://api-docs.aeternity.io/#/contract/PostTransaction +// +// > Post a new transaction +//------------------------------------------------------------------- + +async function +PostTransaction(endpoint_url : string, + body_obj : Tx) + : Promise +{ + // console.log('body_obj', body_obj); + let url = `${endpoint_url}/transactions`; + let ret = await net.post_json_response(url, body_obj); + return ret; +} + + + +//------------------------------------------------------------------- +// GetTransactionInfoByHash +// +// Docs: https://api-docs.aeternity.io/#/contract/GetTransactionInfoByHash +//------------------------------------------------------------------- + +async function +GetTransactionInfoByHash(endpoint_url : string, + hash : string) + : Promise +{ + // console.log('body_obj', body_obj); + let url = `${endpoint_url}/transactions/${hash}/info`; + let ret = await net.get_json_response(url); + return ret; +} + + +//------------------------------------------------------------------- +// EXPORTS +//------------------------------------------------------------------- + +// type exports +export type { + ErrorReason, + Tx, + SpendTx, + ContractCreateTx +}; + +export { + GetAccountNextNonce, + PostContractCreate, + create_contract, + PostSpend, + PostTransaction +}; diff --git a/ts/parasite/src/net.ts b/ts/parasite/src/net.ts new file mode 100644 index 0000000..8416dbf --- /dev/null +++ b/ts/parasite/src/net.ts @@ -0,0 +1,85 @@ +//------------------------------------------------------------------- +// Common networking functions +// +// Mozilla fetch docs : https://developer.mozilla.org/en-US/docs/Web/API/fetch +//------------------------------------------------------------------- + + + +/* pf = pretty format +*/ +function pf(x : any) : string +{ + return JSON.stringify(x, undefined, 4); +} + + + +//------------------------------------------------------------------- +// FUNCTIONS +//------------------------------------------------------------------- + +// GET request with return type of JSON +async function +get_json(url: string) + : Promise +{ + let response = await fetch(url); + let ret = await response.json(); + return ret; +} + + + +// GET request with return type of JSON +async function +get_json_response(url: string) + : Promise +{ + let response = await fetch(url); + //let ret = await response.json(); + return response; +} + + + +// POST request with content type of json and return type of JSON +async function +post_json(url : string, + body_obj : any) + : Promise +{ + let body_str = pf(body_obj); + let req_opts = {method : 'POST', + body : body_str, + headers : {"Content-Type": "application/json"}}; + let response = await fetch(url, req_opts); + let ret = await response.json(); + return ret; +} + + +async function +post_json_response(url : string, + body_obj : any) + : Promise +{ + let body_str = pf(body_obj); + let req_opts = {method : 'POST', + body : body_str, + headers : {"Content-Type": "application/json"}}; + let response = await fetch(url, req_opts); + return response; +} + + + +//------------------------------------------------------------------- +// EXPORTS +//------------------------------------------------------------------- +export { + get_json, + get_json_response, + post_json, + post_json_response +} diff --git a/ts/parasite/tsconfig.json b/ts/parasite/tsconfig.json new file mode 100644 index 0000000..c870354 --- /dev/null +++ b/ts/parasite/tsconfig.json @@ -0,0 +1,16 @@ +{"compilerOptions" : {"target" : "es2022", + "strict" : true, + "esModuleInterop" : true, + "skipLibCheck" : true, + "forceConsistentCasingInFileNames" : true, + "noImplicitAny" : true, + "strictNullChecks" : true, + "strictPropertyInitialization" : true, + "sourceMap" : true, + "outDir" : "dist", + "declaration" : true}, + "$schema" : "https://json.schemastore.org/tsconfig", + "display" : "Recommended", + "include" : ["src/**/*"], + "exclude" : ["src/jx_include"], + "composite" : true} diff --git a/ts/sidekick/.gitignore b/ts/sidekick/.gitignore new file mode 100644 index 0000000..6b22493 --- /dev/null +++ b/ts/sidekick/.gitignore @@ -0,0 +1,19 @@ +*.swp +*.swo +*.beam +dist_js +sidekick_dist +docs +examples/examples_dist_js +www_tree +prose +*.hi +*.o +sidekick_mindist +sidekick_fulldist +dist +jx_mindist +*jx_include* +jex_mindist +src/jex_include +erl_crash.dump diff --git a/ts/sidekick/LICENSE b/ts/sidekick/LICENSE new file mode 100644 index 0000000..2e52b8d --- /dev/null +++ b/ts/sidekick/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2022 Peter Harpending + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/ts/sidekick/Makefile b/ts/sidekick/Makefile new file mode 100644 index 0000000..131d276 --- /dev/null +++ b/ts/sidekick/Makefile @@ -0,0 +1,15 @@ +all: prepare build + +deploy: prepare build mindist push + +prepare: + jx pull -f + +build: + jx build -f + +mindist: + jx mindist -f + +push: + jx push -f diff --git a/ts/sidekick/README.md b/ts/sidekick/README.md new file mode 100644 index 0000000..be83c94 --- /dev/null +++ b/ts/sidekick/README.md @@ -0,0 +1,136 @@ +# Sidekick + +Sidekick is a simple JavaScript library for talking to an Aeternity +browser wallet extension such as Jaeck Russell or Superhero from the document +context of a webpage. + +> One of the most important things for designing a computer, which I +> think most designers don't do, is you study the problem you want to +> solve. And then use what you learn from studying the problem you +> want to solve to put in the mechanisms needed to solve it in the +> computer you're building. No more, no less. + +— Gerald Jay Sussman + + + +# Build Prereqs + +Assuming Ubuntu 18.04. Adapt these instructions for your own system. + +You need + +- `npm` to build TypeScript +- `tsc` to compile +- [`jx`][jx] to orchestrate the build properly +- Python 3.6 or later to run `jx` + +[jx]: https://gitlab.com/pharpend/jx/-/tree/master/#jx-secure-typescript-package-manager + +Steps + + sudo snap refresh + sudo snap install node --channel 18/stable + npm install -g typescript + wget https://gitlab.com/pharpend/jx/-/raw/master/jx -O ~/.local/bin/jx + chmod u+x ~/.local/bin/jx + + + +## Protip: avoid using `sudo npm install -g` + +If you want to avoid using sudo + +``` +mkdir ~/.npm-packages +npm config set prefix "${HOME}/.npm-packages" +``` + +Edit `~/.bashrc` or `~/.zshrc` with + +``` +NPM_PACKAGES="${HOME}/.npm-packages" +export PATH=$NPM_PACKAGES/bin:$PATH +``` + + + +# Build + + make + + +# Examples + +There is a repository of examples at https://gitlab.com/pharpend/sidekick_examples.git + +# What sidekick is NOT + +Sidekick is **not** ideal if you operate in the Node/NPM ecosystem. If you are +working in the Node/NPM ecosystem, you probably want the [Aeternity JavaScript +SDK](https://github.com/aeternity/aepp-sdk-js/). Every single +Aeternity-related thing you could ever possibly want to do is possible to +accomplish with the SDK. + +All that Sidekick knows how to do is talk to a browser wallet extension. In an +application, there is a great deal of necessary functionality (e.g. forming +transactions for the wallet to sign) which sidekick assumes your server-side +code has already handled. + +In all software there is a tradeoff between simplicity and number of features. +Sidekick is very simple: 2300 lines of TypeScript, including comments, with no +dependencies. Therefore Sidekick intentionally only has a very limited set of +features. + + + +# Where is the NPM package or the webpack bundle? + +There isn't one. + +## Why? + +Sidekick is a library that deals with cryptocurrency. The security +model is based on transparency and trust. A user must be able to +inspect code that is running on his hardware handling his money. + +We don't support NPM because of the security issues that NPM +introduces. Briefly, the code can change at any time under the +developer's nose without the developer knowing. The [leftpad +debacle][lpad] and the [RIAEvangelist debacle][ria] are good examples +of the types of vulnerabilities that package managers like NPM +enable. + +[lpad]: https://archive.ph/Qsh7j +[ria]: https://archive.ph/OF5I9 + +We don't use something like webpack because that introduces an opaque +rewrite using an untrusted tool. Webpack could plausibly alter the +runtime behavior of the program, and we would have no way to detect +that. Even if we trusted webpack, how do we obtain webpack? NPM. +So. + +It is debatable whether or not we should trust the TypeScript +compiler (TSC). On the whole, TSC probably makes Sidekick more +secure, simply by virtue of increasing overall code quality and +eliminating the largest categories of potential bugs. Moreover, the +output that TSC produces is human-readable, and has a very +straightforward mapping to the original source code. A human can +easily read the TSC-generated JavaScript tree, even without the aid +of the source map, and have a high degree of faith that the code is +trustworthy and that TSC is behaving as promised. + + + + +Source: https://github.com/sindresorhus/guides/blob/main/npm-global-without-sudo.md + + +# How to build and view documentation + +``` +make build_docs +make serve_docs +``` + + diff --git a/ts/sidekick/icons/sidekick_icon_128.png b/ts/sidekick/icons/sidekick_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..fe60ff26f8efc4592548771f93ec87b76b73f812 GIT binary patch literal 2883 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 literal 0 HcmV?d00001 diff --git a/ts/sidekick/icons/sidekick_icon_128.xcf b/ts/sidekick/icons/sidekick_icon_128.xcf new file mode 100644 index 0000000000000000000000000000000000000000..a1a9aeba26dd48b6461a9e0d6ee50987b887ac36 GIT binary patch literal 5815 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 l7T8e3rLHF