From ee30eaaf7fcf5a17630bdb42e2c205f6a2e61dfe Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 25 May 2023 15:05:48 -0600 Subject: [PATCH] [wip] factoring vdk out into several packages --- libs/{vdk => vdk_base58}/.gitignore | 0 libs/{vdk => vdk_base58}/jex.eterms | 0 libs/{vdk => vdk_base58}/notes | 0 libs/{vdk => vdk_base58}/scratch/anth.ts | 0 libs/{vdk => vdk_base58}/scratch/bin.ts | 0 libs/{vdk => vdk_base58}/scratch/scratch.txt | 0 libs/{vdk => vdk_base58}/scratch/sp.ts | 0 libs/{vdk => vdk_base58}/src/b58.ts | 0 libs/{vdk => vdk_base58}/src/b64.ts | 0 libs/{vdk => vdk_base58}/src/bin.ts | 0 libs/{vdk => vdk_base58}/src/faert.ts | 0 libs/{vdk => vdk_base58}/src/rlp.ts | 0 libs/{vdk => vdk_base58}/src/safe.ts | 0 libs/{sidekick => vdk_base58}/tsconfig.json | 0 libs/vdk_base64/.gitignore | 10 + libs/vdk_base64/jex.eterms | 5 + libs/vdk_base64/notes | 24 + libs/vdk_base64/scratch/anth.ts | 403 +++++++++++ libs/vdk_base64/scratch/bin.ts | 112 +++ libs/vdk_base64/scratch/scratch.txt | 159 +++++ libs/vdk_base64/scratch/sp.ts | 23 + .../local-vdk-0.1.0 => vdk_base64}/src/b58.ts | 0 .../local-vdk-0.1.0 => vdk_base64}/src/b64.ts | 0 .../local-vdk-0.1.0 => vdk_base64}/src/bin.ts | 0 .../src/faert.ts | 0 .../local-vdk-0.1.0 => vdk_base64}/src/rlp.ts | 0 .../src/safe.ts | 0 .../tsconfig.json | 0 libs/vdk_bin/.gitignore | 10 + libs/vdk_bin/jex.eterms | 5 + libs/vdk_bin/notes | 24 + libs/vdk_bin/scratch/anth.ts | 403 +++++++++++ libs/vdk_bin/scratch/bin.ts | 112 +++ libs/vdk_bin/scratch/scratch.txt | 159 +++++ libs/vdk_bin/scratch/sp.ts | 23 + .../local-vdk-0.1.0 => vdk_bin}/src/b58.ts | 0 .../local-vdk-0.1.0 => vdk_bin}/src/b64.ts | 0 .../local-vdk-0.1.0 => vdk_bin}/src/bin.ts | 0 .../local-vdk-0.1.0 => vdk_bin}/src/faert.ts | 0 .../local-vdk-0.1.0 => vdk_bin}/src/rlp.ts | 0 .../local-vdk-0.1.0 => vdk_bin}/src/safe.ts | 0 libs/{vdk => vdk_bin}/tsconfig.json | 0 libs/vdk_faert/.gitignore | 10 + libs/vdk_faert/jex.eterms | 5 + libs/vdk_faert/notes | 24 + libs/vdk_faert/scratch/anth.ts | 403 +++++++++++ libs/vdk_faert/scratch/bin.ts | 112 +++ libs/vdk_faert/scratch/scratch.txt | 159 +++++ libs/vdk_faert/scratch/sp.ts | 23 + .../local-vdk-0.1.0 => vdk_faert}/src/b58.ts | 0 .../local-vdk-0.1.0 => vdk_faert}/src/b64.ts | 0 .../local-vdk-0.1.0 => vdk_faert}/src/bin.ts | 0 .../src/faert.ts | 0 .../local-vdk-0.1.0 => vdk_faert}/src/rlp.ts | 0 .../local-vdk-0.1.0 => vdk_faert}/src/safe.ts | 0 libs/{vdk_cases => vdk_faert}/tsconfig.json | 0 libs/vdk_rlp/.gitignore | 10 + libs/vdk_rlp/jex.eterms | 5 + libs/vdk_rlp/notes | 24 + libs/vdk_rlp/scratch/anth.ts | 403 +++++++++++ libs/vdk_rlp/scratch/bin.ts | 112 +++ libs/vdk_rlp/scratch/scratch.txt | 159 +++++ libs/vdk_rlp/scratch/sp.ts | 23 + libs/vdk_rlp/src/b58.ts | 418 +++++++++++ libs/vdk_rlp/src/b64.ts | 655 ++++++++++++++++++ libs/vdk_rlp/src/bin.ts | 377 ++++++++++ libs/vdk_rlp/src/faert.ts | 399 +++++++++++ libs/vdk_rlp/src/rlp.ts | 521 ++++++++++++++ libs/vdk_rlp/src/safe.ts | 112 +++ libs/vdk_rlp/tsconfig.json | 16 + libs/vdk_safe/.gitignore | 10 + libs/vdk_safe/jex.eterms | 5 + libs/vdk_safe/notes | 24 + libs/vdk_safe/scratch/anth.ts | 403 +++++++++++ libs/vdk_safe/scratch/bin.ts | 112 +++ libs/vdk_safe/scratch/scratch.txt | 159 +++++ libs/vdk_safe/scratch/sp.ts | 23 + libs/vdk_safe/src/vdk_safe.ts | 112 +++ libs/vdk_safe/tsconfig.json | 16 + .../dist/cases.d.ts | 0 .../dist/cases.js | 0 .../dist/cases.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/b58.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b58.js | 0 .../local-vdk-0.1.0/dist/b58.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/b64.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b64.js | 0 .../local-vdk-0.1.0/dist/b64.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/bin.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/bin.js | 0 .../local-vdk-0.1.0/dist/bin.js.map | 0 .../local-vdk-0.1.0/dist/faert.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/faert.js | 0 .../local-vdk-0.1.0/dist/faert.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.js | 0 .../local-vdk-0.1.0/dist/rlp.js.map | 0 .../local-vdk-0.1.0/dist/safe.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/safe.js | 0 .../local-vdk-0.1.0/dist/safe.js.map | 0 .../jex_include/local-vdk-0.1.0/src/b58.ts | 418 +++++++++++ .../jex_include/local-vdk-0.1.0/src/b64.ts | 655 ++++++++++++++++++ .../jex_include/local-vdk-0.1.0/src/bin.ts | 377 ++++++++++ .../jex_include/local-vdk-0.1.0/src/faert.ts | 399 +++++++++++ .../jex_include/local-vdk-0.1.0/src/rlp.ts | 521 ++++++++++++++ .../jex_include/local-vdk-0.1.0/src/safe.ts | 112 +++ .../dist/rlpcases.d.ts | 0 .../dist/rlpcases.js | 0 .../dist/rlpcases.js.map | 0 .../{vdk_cases => vdk_tests_cases}/jex.eterms | 0 .../jex_mindist/dist/cases.d.ts | 0 .../jex_mindist/dist/cases.js | 0 .../jex_mindist/dist/cases.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/b58.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b58.js | 0 .../local-vdk-0.1.0/dist/b58.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/b64.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b64.js | 0 .../local-vdk-0.1.0/dist/b64.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/bin.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/bin.js | 0 .../local-vdk-0.1.0/dist/bin.js.map | 0 .../local-vdk-0.1.0/dist/faert.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/faert.js | 0 .../local-vdk-0.1.0/dist/faert.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.js | 0 .../local-vdk-0.1.0/dist/rlp.js.map | 0 .../local-vdk-0.1.0/dist/safe.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/safe.js | 0 .../local-vdk-0.1.0/dist/safe.js.map | 0 .../jex_include/local-vdk-0.1.0/src/b58.ts | 418 +++++++++++ .../jex_include/local-vdk-0.1.0/src/b64.ts | 655 ++++++++++++++++++ .../jex_include/local-vdk-0.1.0/src/bin.ts | 377 ++++++++++ .../jex_include/local-vdk-0.1.0/src/faert.ts | 399 +++++++++++ .../jex_include/local-vdk-0.1.0/src/rlp.ts | 521 ++++++++++++++ .../jex_include/local-vdk-0.1.0/src/safe.ts | 112 +++ .../jex_mindist/dist/rlpcases.d.ts | 0 .../jex_mindist/dist/rlpcases.js | 0 .../jex_mindist/dist/rlpcases.js.map | 0 .../jex_mindist/src/cases.ts | 0 .../jex_mindist/src/rlpcases.ts | 0 .../src/cases.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b58.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b58.js | 0 .../local-vdk-0.1.0/dist/b58.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/b64.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/b64.js | 0 .../local-vdk-0.1.0/dist/b64.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/bin.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/bin.js | 0 .../local-vdk-0.1.0/dist/bin.js.map | 0 .../local-vdk-0.1.0/dist/faert.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/faert.js | 0 .../local-vdk-0.1.0/dist/faert.js.map | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/rlp.js | 0 .../local-vdk-0.1.0/dist/rlp.js.map | 0 .../local-vdk-0.1.0/dist/safe.d.ts | 0 .../jex_include/local-vdk-0.1.0/dist/safe.js | 0 .../local-vdk-0.1.0/dist/safe.js.map | 0 .../jex_include/local-vdk-0.1.0/src/b58.ts | 418 +++++++++++ .../jex_include/local-vdk-0.1.0/src/b64.ts | 655 ++++++++++++++++++ .../jex_include/local-vdk-0.1.0/src/bin.ts | 377 ++++++++++ .../jex_include/local-vdk-0.1.0/src/faert.ts | 399 +++++++++++ .../jex_include/local-vdk-0.1.0/src/rlp.ts | 521 ++++++++++++++ .../jex_include/local-vdk-0.1.0/src/safe.ts | 112 +++ .../src/rlpcases.ts | 0 .../testgen/.gitignore | 0 .../testgen/b58.erl | 0 .../testgen/b58.py | 0 .../testgen/b58_2.erl | 0 .../testgen/b58_cases | 0 .../testgen/b58_cases.eterms | 0 .../testgen/bits.erl | 0 .../testgen/rlp.erl | 0 .../testgen/rlp_testgen.erl | 0 .../testgen/rlpcases.py | 0 .../testgen/rlptests.py | 0 .../testgen/tg_b64.erl | 0 libs/vdk_tests_cases/tsconfig.json | 16 + {libs/sidekick => sidekick}/.gitignore | 0 {libs/sidekick => sidekick}/LICENSE | 0 {libs/sidekick => sidekick}/README.md | 0 .../icons/sidekick_icon_128.png | Bin .../icons/sidekick_icon_128.xcf | Bin {libs/sidekick => sidekick}/jex.eterms | 0 .../scratch/STYLE_GUIDE.md | 0 {libs/sidekick => sidekick}/scratch/TODO.md | 0 .../scratch/async_tests.html | 0 .../scratch/async_tests.js | 0 .../examples_old/contract-examples/LICENSE | 0 .../contract-examples/base_contract.aes | 0 .../contract-examples/crypto_hamster.aes | 0 .../examples_old/contract-examples/floyd.aes | 0 .../contract-examples/sales_contract.aes | 0 .../examples_html/do_a_transfer.html | 0 .../examples_old/examples_html/hello.html | 0 .../examples_old/examples_html/ide.html | 0 .../examples_old/examples_html/maerket.html | 0 .../examples_src_ts/do_a_transfer.ts | 0 .../examples_old/examples_src_ts/ide.ts | 0 .../examples_src_ts/parasite/ae_compiler.ts | 0 .../examples_src_ts/parasite/ae_node.ts | 0 .../examples_src_ts/parasite/net.ts | 0 .../scratch/examples_old/favicon.ico | Bin .../scratch/examples_old/index.html | 0 .../scratch/examples_old/style.css | 0 .../scratch/examples_old/tsconfig.json | 0 .../scratch/examples_old/why.html | 0 .../sidekick => sidekick}/scratch/logging.ts | 0 .../scratch/old-makefile | 0 .../scratch/old-readme.md | 0 .../sidekick => sidekick}/scratch/scratch.txt | 0 {libs/sidekick => sidekick}/src/sidekick.ts | 0 sidekick/tsconfig.json | 16 + .../.gitignore | 0 .../LICENSE | 0 .../README.md | 0 .../connection.html | 0 .../favicon.ico | Bin .../hello.html | 0 .../index.html | 0 .../jex.eterms | 0 .../scratch/logging.html | 0 .../scratch/logging.ts | 0 .../scratch/logging_http.html | 0 .../scratch/logging_http.ts | 0 .../src/connection.ts | 0 .../src/hello.ts | 0 sidekick_examples/tsconfig.json | 16 + 231 files changed, 13800 insertions(+) rename libs/{vdk => vdk_base58}/.gitignore (100%) rename libs/{vdk => vdk_base58}/jex.eterms (100%) rename libs/{vdk => vdk_base58}/notes (100%) rename libs/{vdk => vdk_base58}/scratch/anth.ts (100%) rename libs/{vdk => vdk_base58}/scratch/bin.ts (100%) rename libs/{vdk => vdk_base58}/scratch/scratch.txt (100%) rename libs/{vdk => vdk_base58}/scratch/sp.ts (100%) rename libs/{vdk => vdk_base58}/src/b58.ts (100%) rename libs/{vdk => vdk_base58}/src/b64.ts (100%) rename libs/{vdk => vdk_base58}/src/bin.ts (100%) rename libs/{vdk => vdk_base58}/src/faert.ts (100%) rename libs/{vdk => vdk_base58}/src/rlp.ts (100%) rename libs/{vdk => vdk_base58}/src/safe.ts (100%) rename libs/{sidekick => vdk_base58}/tsconfig.json (100%) create mode 100644 libs/vdk_base64/.gitignore create mode 100644 libs/vdk_base64/jex.eterms create mode 100644 libs/vdk_base64/notes create mode 100644 libs/vdk_base64/scratch/anth.ts create mode 100644 libs/vdk_base64/scratch/bin.ts create mode 100644 libs/vdk_base64/scratch/scratch.txt create mode 100644 libs/vdk_base64/scratch/sp.ts rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/b58.ts (100%) rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/b64.ts (100%) rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/bin.ts (100%) rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/faert.ts (100%) rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/rlp.ts (100%) rename libs/{vdk_cases/dist/jex_include/local-vdk-0.1.0 => vdk_base64}/src/safe.ts (100%) rename libs/{sidekick_examples => vdk_base64}/tsconfig.json (100%) create mode 100644 libs/vdk_bin/.gitignore create mode 100644 libs/vdk_bin/jex.eterms create mode 100644 libs/vdk_bin/notes create mode 100644 libs/vdk_bin/scratch/anth.ts create mode 100644 libs/vdk_bin/scratch/bin.ts create mode 100644 libs/vdk_bin/scratch/scratch.txt create mode 100644 libs/vdk_bin/scratch/sp.ts rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/b58.ts (100%) rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/b64.ts (100%) rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/bin.ts (100%) rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/faert.ts (100%) rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/rlp.ts (100%) rename libs/{vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0 => vdk_bin}/src/safe.ts (100%) rename libs/{vdk => vdk_bin}/tsconfig.json (100%) create mode 100644 libs/vdk_faert/.gitignore create mode 100644 libs/vdk_faert/jex.eterms create mode 100644 libs/vdk_faert/notes create mode 100644 libs/vdk_faert/scratch/anth.ts create mode 100644 libs/vdk_faert/scratch/bin.ts create mode 100644 libs/vdk_faert/scratch/scratch.txt create mode 100644 libs/vdk_faert/scratch/sp.ts rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/b58.ts (100%) rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/b64.ts (100%) rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/bin.ts (100%) rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/faert.ts (100%) rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/rlp.ts (100%) rename libs/{vdk_cases/src/jex_include/local-vdk-0.1.0 => vdk_faert}/src/safe.ts (100%) rename libs/{vdk_cases => vdk_faert}/tsconfig.json (100%) create mode 100644 libs/vdk_rlp/.gitignore create mode 100644 libs/vdk_rlp/jex.eterms create mode 100644 libs/vdk_rlp/notes create mode 100644 libs/vdk_rlp/scratch/anth.ts create mode 100644 libs/vdk_rlp/scratch/bin.ts create mode 100644 libs/vdk_rlp/scratch/scratch.txt create mode 100644 libs/vdk_rlp/scratch/sp.ts create mode 100644 libs/vdk_rlp/src/b58.ts create mode 100644 libs/vdk_rlp/src/b64.ts create mode 100644 libs/vdk_rlp/src/bin.ts create mode 100644 libs/vdk_rlp/src/faert.ts create mode 100644 libs/vdk_rlp/src/rlp.ts create mode 100644 libs/vdk_rlp/src/safe.ts create mode 100644 libs/vdk_rlp/tsconfig.json create mode 100644 libs/vdk_safe/.gitignore create mode 100644 libs/vdk_safe/jex.eterms create mode 100644 libs/vdk_safe/notes create mode 100644 libs/vdk_safe/scratch/anth.ts create mode 100644 libs/vdk_safe/scratch/bin.ts create mode 100644 libs/vdk_safe/scratch/scratch.txt create mode 100644 libs/vdk_safe/scratch/sp.ts create mode 100644 libs/vdk_safe/src/vdk_safe.ts create mode 100644 libs/vdk_safe/tsconfig.json rename libs/{vdk_cases => vdk_tests_cases}/dist/cases.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/cases.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/cases.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b58.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b64.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/bin.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/faert.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/rlp.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/safe.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map (100%) create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts create mode 100644 libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts rename libs/{vdk_cases => vdk_tests_cases}/dist/rlpcases.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/rlpcases.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/dist/rlpcases.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex.eterms (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/cases.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/cases.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/cases.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map (100%) create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts create mode 100644 libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/rlpcases.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/rlpcases.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/dist/rlpcases.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/src/cases.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/jex_mindist/src/rlpcases.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/cases.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b58.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b58.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b58.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b64.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b64.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/b64.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/bin.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/bin.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/bin.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/faert.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/faert.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/faert.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/rlp.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/rlp.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/rlp.js.map (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/safe.d.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/safe.js (100%) rename libs/{vdk_cases => vdk_tests_cases}/src/jex_include/local-vdk-0.1.0/dist/safe.js.map (100%) create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts create mode 100644 libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts rename libs/{vdk_cases => vdk_tests_cases}/src/rlpcases.ts (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/.gitignore (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/b58.erl (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/b58.py (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/b58_2.erl (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/b58_cases (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/b58_cases.eterms (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/bits.erl (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/rlp.erl (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/rlp_testgen.erl (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/rlpcases.py (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/rlptests.py (100%) rename libs/{vdk_cases => vdk_tests_cases}/testgen/tg_b64.erl (100%) create mode 100644 libs/vdk_tests_cases/tsconfig.json rename {libs/sidekick => sidekick}/.gitignore (100%) rename {libs/sidekick => sidekick}/LICENSE (100%) rename {libs/sidekick => sidekick}/README.md (100%) rename {libs/sidekick => sidekick}/icons/sidekick_icon_128.png (100%) rename {libs/sidekick => sidekick}/icons/sidekick_icon_128.xcf (100%) rename {libs/sidekick => sidekick}/jex.eterms (100%) rename {libs/sidekick => sidekick}/scratch/STYLE_GUIDE.md (100%) rename {libs/sidekick => sidekick}/scratch/TODO.md (100%) rename {libs/sidekick => sidekick}/scratch/async_tests.html (100%) rename {libs/sidekick => sidekick}/scratch/async_tests.js (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/contract-examples/LICENSE (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/contract-examples/base_contract.aes (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/contract-examples/crypto_hamster.aes (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/contract-examples/floyd.aes (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/contract-examples/sales_contract.aes (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_html/do_a_transfer.html (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_html/hello.html (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_html/ide.html (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_html/maerket.html (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_src_ts/do_a_transfer.ts (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_src_ts/ide.ts (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_src_ts/parasite/ae_node.ts (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/examples_src_ts/parasite/net.ts (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/favicon.ico (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/index.html (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/style.css (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/tsconfig.json (100%) rename {libs/sidekick => sidekick}/scratch/examples_old/why.html (100%) rename {libs/sidekick => sidekick}/scratch/logging.ts (100%) rename {libs/sidekick => sidekick}/scratch/old-makefile (100%) rename {libs/sidekick => sidekick}/scratch/old-readme.md (100%) rename {libs/sidekick => sidekick}/scratch/scratch.txt (100%) rename {libs/sidekick => sidekick}/src/sidekick.ts (100%) create mode 100644 sidekick/tsconfig.json rename {libs/sidekick_examples => sidekick_examples}/.gitignore (100%) rename {libs/sidekick_examples => sidekick_examples}/LICENSE (100%) rename {libs/sidekick_examples => sidekick_examples}/README.md (100%) rename {libs/sidekick_examples => sidekick_examples}/connection.html (100%) rename {libs/sidekick_examples => sidekick_examples}/favicon.ico (100%) rename {libs/sidekick_examples => sidekick_examples}/hello.html (100%) rename {libs/sidekick_examples => sidekick_examples}/index.html (100%) rename {libs/sidekick_examples => sidekick_examples}/jex.eterms (100%) rename {libs/sidekick_examples => sidekick_examples}/scratch/logging.html (100%) rename {libs/sidekick_examples => sidekick_examples}/scratch/logging.ts (100%) rename {libs/sidekick_examples => sidekick_examples}/scratch/logging_http.html (100%) rename {libs/sidekick_examples => sidekick_examples}/scratch/logging_http.ts (100%) rename {libs/sidekick_examples => sidekick_examples}/src/connection.ts (100%) rename {libs/sidekick_examples => sidekick_examples}/src/hello.ts (100%) create mode 100644 sidekick_examples/tsconfig.json diff --git a/libs/vdk/.gitignore b/libs/vdk_base58/.gitignore similarity index 100% rename from libs/vdk/.gitignore rename to libs/vdk_base58/.gitignore diff --git a/libs/vdk/jex.eterms b/libs/vdk_base58/jex.eterms similarity index 100% rename from libs/vdk/jex.eterms rename to libs/vdk_base58/jex.eterms diff --git a/libs/vdk/notes b/libs/vdk_base58/notes similarity index 100% rename from libs/vdk/notes rename to libs/vdk_base58/notes diff --git a/libs/vdk/scratch/anth.ts b/libs/vdk_base58/scratch/anth.ts similarity index 100% rename from libs/vdk/scratch/anth.ts rename to libs/vdk_base58/scratch/anth.ts diff --git a/libs/vdk/scratch/bin.ts b/libs/vdk_base58/scratch/bin.ts similarity index 100% rename from libs/vdk/scratch/bin.ts rename to libs/vdk_base58/scratch/bin.ts diff --git a/libs/vdk/scratch/scratch.txt b/libs/vdk_base58/scratch/scratch.txt similarity index 100% rename from libs/vdk/scratch/scratch.txt rename to libs/vdk_base58/scratch/scratch.txt diff --git a/libs/vdk/scratch/sp.ts b/libs/vdk_base58/scratch/sp.ts similarity index 100% rename from libs/vdk/scratch/sp.ts rename to libs/vdk_base58/scratch/sp.ts diff --git a/libs/vdk/src/b58.ts b/libs/vdk_base58/src/b58.ts similarity index 100% rename from libs/vdk/src/b58.ts rename to libs/vdk_base58/src/b58.ts diff --git a/libs/vdk/src/b64.ts b/libs/vdk_base58/src/b64.ts similarity index 100% rename from libs/vdk/src/b64.ts rename to libs/vdk_base58/src/b64.ts diff --git a/libs/vdk/src/bin.ts b/libs/vdk_base58/src/bin.ts similarity index 100% rename from libs/vdk/src/bin.ts rename to libs/vdk_base58/src/bin.ts diff --git a/libs/vdk/src/faert.ts b/libs/vdk_base58/src/faert.ts similarity index 100% rename from libs/vdk/src/faert.ts rename to libs/vdk_base58/src/faert.ts diff --git a/libs/vdk/src/rlp.ts b/libs/vdk_base58/src/rlp.ts similarity index 100% rename from libs/vdk/src/rlp.ts rename to libs/vdk_base58/src/rlp.ts diff --git a/libs/vdk/src/safe.ts b/libs/vdk_base58/src/safe.ts similarity index 100% rename from libs/vdk/src/safe.ts rename to libs/vdk_base58/src/safe.ts diff --git a/libs/sidekick/tsconfig.json b/libs/vdk_base58/tsconfig.json similarity index 100% rename from libs/sidekick/tsconfig.json rename to libs/vdk_base58/tsconfig.json diff --git a/libs/vdk_base64/.gitignore b/libs/vdk_base64/.gitignore new file mode 100644 index 0000000..3967e48 --- /dev/null +++ b/libs/vdk_base64/.gitignore @@ -0,0 +1,10 @@ +dist +jex_mindist +jex_include +*.beam +*.swp +*.swo +test/testgen/b58_cases_2.eterms +test/testgen/b58_cases_3.eterms +__pycache__ +docs/ diff --git a/libs/vdk_base64/jex.eterms b/libs/vdk_base64/jex.eterms new file mode 100644 index 0000000..8f88098 --- /dev/null +++ b/libs/vdk_base64/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, vdk}. +{version, "0.1.0"}. +{deps, []}. diff --git a/libs/vdk_base64/notes b/libs/vdk_base64/notes new file mode 100644 index 0000000..97c0e0c --- /dev/null +++ b/libs/vdk_base64/notes @@ -0,0 +1,24 @@ +done: +- base64/base58 encoding/decoding +- rlp + +today: +- pull apart data + +tomorrow: +- basic easy serialization/deserialization +- serialize/deserialize examples +- (maybe) pull in awcp/sidekick +- sidekick: sign/propagate and sign/noprop + +later: + +- general node querying with parasite (think about autogeneration) +- simple aetto/ae/etc unit converter +- ghetto sophia ide +- general "send money to someone" page + +GUI: + +- simple testnet explorer +- contract ide diff --git a/libs/vdk_base64/scratch/anth.ts b/libs/vdk_base64/scratch/anth.ts new file mode 100644 index 0000000..6117067 --- /dev/null +++ b/libs/vdk_base64/scratch/anth.ts @@ -0,0 +1,403 @@ +/** + * Node API constructor/deconstructor + * + * This is similar to serialization/deserialization, but not quite the same + * thing. It converts back and forth between different forms of + * "api-serialized" data. + * + * References: + * 1. https://github.com/aeternity/protocol/blob/master/serializations.md + * 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md + * + * ## General type rules + * + * ``` + * ERLANG TYPE | JS TYPE + * ------------------------------- + * integer | bigint + * list | Array + * binary | Uint8Array + * ``` + * + * # Example + * + * We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`. + * + * We can tell by the `tx_` prefix that this represents transaction data of + * some sort. But the rest of the data is totally opaque. The task of this + * module is to "humanize" that `tx_...` string and show what data is contained + * in the rest of it. + * + * The remainder of the string is a base64-encoded bytestring + * + * ```erlang + * 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>> + * ``` + * + * That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I + * wrote an RLP decoder. RLP has two types of data: binaries, and + * arbitrary-depth (possibly empty) lists of binaries. + * + * ```erlang + * -type decoded_data() :: binary() | [decoded_data()]. + * + * -spec decode(RLP) -> {Data, Rest} + * when RLP :: binary(), + * Data :: decoded_data(), + * Rest :: binary(). + * ``` + * + * ```erlang + * 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning + * + * ```erlang + * 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 4> SizeX = byte_size(X). + * 94 + * 6> <> = X. + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 10> <> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)). + * <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81, + * 189,184,82,207,164,68,128,43,11,174,236,59,77,...>> + * 11> Hash. + * <<3,182,66,215>> + * 12> Check. + * <<3,182,66,215>> + * ``` + * + * What we're really interested in is `Data` + * + * ```erlang + * 14> {Data, _} = rlp:decode(X). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * `Data` is a list. The first field `<<"\f">>` is meant to be an integer which + * tells us what type of data this is. + * + * ```erlang + * 16> $\f. + * 12 + * ``` + * + * If we look at our table + * (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags), + * we see that a value of `12` is a spend transaction. + * + * The second field `<<1>>` tells us the "version" of the field orderings, + * which we can ignore for now. + * + * The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction) + * + * ```erlang + * [ :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE" + * , :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * , :: int() % <<"\n">> "=" 10 + * , :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000 + * , :: int() % <<0>> "=" 0 + * , :: int() % <<"\t">> "=" 9 + * , :: binary() % <<"hainana">> "=" "hainana" + * ] + * ``` + * + * Our task here is to be able to pull apart the "tx_..." string into its fields. + * + * Converting the binaries to integers is pretty trivial. The only mildly + * annoying thing is the `id` type. + * + * `id`s have two fields: a single-byte prefix which says which type of ID it + * is. In this case, both `id`s have a prefix of `1`, which means they are both + * normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other + * options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or + * names (prefix `2`/`nm_`) + * + * To "api-encode" the name, we first pick the appropriate prefix based on the + * first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then + * double-SHA'd to get the 4-byte check suffix + * + * ```erlang + * 30> SenderBytes = lists:nth(3, Data). + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 31> <<1, SenderAddrBytes/binary>> = SenderBytes. + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 32> DoubleSha = fun(Bytes) -> <> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end. + * #Fun + * 33> "ak_" ++ b58:enc(<>). + * "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(<>). + * "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * ``` + * + * ```js + * > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==") + * {tag : 'SpendTx', + * version : 1n, + * fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE", + * recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv", + * amount : 10n, + * fee : 16660000000000n, + * ttl : 0n, + * nonce : 9n, + * payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}} + * ``` + * + * @module + */ + +export { + // types + tx_str, + deconstructed_tx, + // functions + deconstruct_tx +}; + +import * as b64 from './b64.js' +import * as bin from './bin.js' +import * as rlp from './rlp.js' + + +/** + * Alias type for a `tx_...` string + */ +type tx_str = string; + +/** + * Alias type for a `sg_...` string + */ +type sg_str = string; + +/** + * types of decoded tx we currently support + * @internal + */ +type tx_type_str + = 'SignedTx' + | 'SpendTx' + | 'ContractCreateTx' + | 'ContractCallTx'; + + +/** + * Results of deconstruct_tx + */ +type deconstructed_tx + = {type : 'SignedTx', + version : bigint, + fields : fields_SignedTx} + | {type : 'SpendTx', + version : bigint, + fields : fields_SpendTx} + | {type : 'ContractCreateTx', + version : bigint, + fields : fields_ContractCreateTx} + | {type : 'ContractCallTx' + version : bigint, + fields : fields_ContractCallTx}; + +/** + * Convenient type alias + * + * @internal + */ +type rlpdata = rlp.decoded_data; + + +/** + * Fields types + */ +type fields + = fields_SignedTx + | fields_SpendTx + | fields_ContractCreateTx + | fields_ContractCallTx; + +type fields_SignedTx + = {signatures : Array, + transaction : tx_str}; + +type fields_SpendTx + = {sender : string, + recipient : string, + amount : bigint, + fee : bigint, + ttl : bigint, + nonce : bigint, + payload : Uint8Array}; + +/** + * Deconstruct a Tx + */ +function +deconstruct_tx + (tx_str: tx_str) + : deconstructed_tx +{ + let b64_str : string = tx_str.slice(3); // tx_[...] -> [...] + let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes + let tx_data : Array = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing + let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint + let tts : tx_type_str = tx_type_str(tx_type); + let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]); + let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2)); + return {type : tts, + version : tx_version, + fields : tx_fields}; +} + + +/** + * Data that's "api-encoded" goes through the following stages: + * + * 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings) + * 2. rlp decode data -> bytestring + * 3. bytestring -> <> + * 4. HashedBytestring -> base64/base58 string encoding + * 5. Add string prefix + * + * This function undoes step 3 and step 2, returns back the rlp decode data + * + * FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures + * FIXME: No good way to handle failure cases + * + * @internal + */ +function +shasha_rlp_decode_list + (hashed_bs : Uint8Array) + : Array +{ + let len = hashed_bs.length; + let bytes = hashed_bs.slice(0, len - 4); + let {decoded_data} = rlp.decode(bytes); + return (decoded_data as Array); +} + + + +/** + * Convert an object tag that's a type of transaction to the type string + * + * See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags + * + * @internal + */ +function +tx_type_str + (tx_type_int : bigint) + : tx_type_str +{ + switch (tx_type_int) + { + case 11n: return 'SignedTx'; + case 12n: return 'SpendTx'; + case 42n: return 'ContractCreateTx'; + case 43n: return 'ContractCallTx'; + default: throw new Error('invalid transaction type: ' + tx_type_int); + } +} + + + +/** + * Given an array of data decoded from RLP, convert it to the fields, as + * appropriate as given by the tx type string and the version + */ +function +deconstruct_fields + (tx_type_str : tx_type_str, + tx_version : bigint, + tx_rawfields : Array) + : fields + { + switch (tx_type_str) + { + // case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields); + case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields); + // case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields); + // case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields); + default : throw new Error('invalid tx type str: ' + tx_type_str); + } + } + + +// TODO: do all this in Erlang + function + deconstruct_fields_SpendTx + (fields: Array) + : fields_SpendTx +{ + let sender_bytes = fields[0]; + let recip_bytes = fields[1]; + let amount_bytes = fields[2]; + let fee_bytes = fields[3]; + let ttl_bytes = fields[4]; + let nonce_bytes = fields[5]; + let payload_bytes = fields[6]; + return {sender : encode_id(sender_bytes), + recipient : encode_id(sender_bytes), + amount : bin.bytes_to_bigint(amount_bytes), + fee : bin.bytes_to_bigint(fee_bytes), + ttl : bin.bytes_to_bigint(ttl_bytes), + nonce : bin.bytes_to_bigint(nonce_bytes), + payload : bin.bytes_to_bigint(payload_bytes)}; +} + + +/** + * Convert a binary account/name/etc binary id into the appropriate type of string + * + * @internal + */ +function +encode_id + (id: Uint8Array) + : string +{ + throw new Error('nyi'); +} + +/* +FIXME: +1. work out all this in Erlang to clear conceptual goo +2. think about how i want type safety etc to work +3. think about a language to assert that the data has the correct shape to it +4. get some examples working in Erlang +5. convert erlang code back to ts +*/ diff --git a/libs/vdk_base64/scratch/bin.ts b/libs/vdk_base64/scratch/bin.ts new file mode 100644 index 0000000..47ad66a --- /dev/null +++ b/libs/vdk_base64/scratch/bin.ts @@ -0,0 +1,112 @@ +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_zeros(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // alright so + // we can start by copying the first bytes into result bytes + for (let bytes1_idx0 = 0; + bytes1_idx0 < bytes1.length; + bytes1_idx0++) + { + result_bytes[bytes1_idx0] = bytes1[bytes1_idx0]; + } + + // next + // we need to calculate the left-shift offset + // this will be 8 - (bytes1.bit_length % 8) + let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8); + // so + // bytes1: ABCD_EF00 + // bytes2: GH12_3000 + // result: ABCD_EFGH 1230_0000 + // ah ok, so we need to for each byte in the second array + // take the first however many bits, xor it with the existing byte + // then take the last however many bits and place them into the next byte + // this is super confusing but + // ABCD_EF00 + // GH12_3456 + // operation: + // ABCD_EF00 + // xor 0000_00GH + // = ABCD_EFGH 1234_5600 + // + // then on the next iteration + // 1234_5600 + // abcd_efgh + // -> + // 1234_56ab cdef_gh00 + // + // ah so there's a pattern + // however many trailing 0s there are in the first array + // say there's 2 + // we take the first 2 bits of the upcoming byte + // xor that against the current byte + // take the last 6 bits of the upcoming byte + // set the next byte to that + // + // have to think about edge behavior + // this is ripe for off-by-1 errors + // but i think the general idea is right + // + // so we start the iteration + // on the last byte of the first array + let last_byte_of_first_array_idx0 : number = bytes1.length - 1; + // and we end + // on the second-to-last-byte of the result array + let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2; + // the reason we do that is because we're doing this is because we are + // going along, xoring against the current byte and then setting the next + // byte + // + // ok so + for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; + this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; + this_result_byte_idx0++) + { + let this_result_byte : number = result_bytes[this_result_byte_idx0]; + + // ok here we need to fish out the relevant byte of the second array + // gaaah + // so this will be 0 at the start of the loop + let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0; + let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0]; + + // ok so let's fish out the leading digits + // the number of leading digits is the number of trailing 0s in the first array + let num_leading_digits : number = num_trailing_zeros_in_first_array; + let num_trailing_digits : number = 8 - num_leading_digits; + + // suppose there are 2 leading digits and 6 trailing digits + // ABCD_EFGH + // leading digits are + // ABCD_EFGH >> 6 = 0000_00AB + // trailing digits are + // (ABCD_EFGH << 2) % 255 = CDEF_GH00 + let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits; + let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255; + + // xor the current byte against the leading digits + let new_this_result_byte : number = this_result_byte ^ leading_digits; + result_bytes[this_result_byte_idx0] = new_this_result_byte; + + // set the next byte to the trailing digits + result_bytes[this_result_byte_idx0 + 1] = trailing_digits; + } + + // i think we're done + return {bit_length : result_bit_length, + bytes : result_bytes}; +} + diff --git a/libs/vdk_base64/scratch/scratch.txt b/libs/vdk_base64/scratch/scratch.txt new file mode 100644 index 0000000..9213abc --- /dev/null +++ b/libs/vdk_base64/scratch/scratch.txt @@ -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, + 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); // <> + let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <> + let decoded_datas : Array = rlp.decode(rlp_stuff).decoded_data as Array; // 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); // <> = TagBytes + let vsn : Uint8Array = decoded_datas[1] as Uint8Array; + // tx fields + let tx_fields : Array = 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): 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): SignedTx { + throw new Error('nyi'); +} + + +/** + * Decode a SpendTx + * + * @internal + */ +function +decode_fields_SpendTx(fields: Array): SpendTx { + // [ :: id(), + // :: id(), + // :: int(), + // :: int(), + // :: int(), + // :: int(), + // :: 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): ContractCreateTx { + throw new Error('nyi'); +} + +function +decode_fields_ContractCallTx(fields: Array): 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)}; +} diff --git a/libs/vdk_base64/scratch/sp.ts b/libs/vdk_base64/scratch/sp.ts new file mode 100644 index 0000000..8448945 --- /dev/null +++ b/libs/vdk_base64/scratch/sp.ts @@ -0,0 +1,23 @@ +/** + * Vanillae Seed Phrase Library + * + * Refs: + * 1. BIP 39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki + * + * @module + */ + + +/** + * Get a given number of seed bits. + * + * `how_many` must be a multiple of 33. + * + * Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic + */ +function +seed_and_check_bits + (how_many : number) + : Uint8Array +{ +} diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_base64/src/b58.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts rename to libs/vdk_base64/src/b58.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_base64/src/b64.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts rename to libs/vdk_base64/src/b64.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_base64/src/bin.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts rename to libs/vdk_base64/src/bin.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_base64/src/faert.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts rename to libs/vdk_base64/src/faert.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_base64/src/rlp.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts rename to libs/vdk_base64/src/rlp.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_base64/src/safe.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts rename to libs/vdk_base64/src/safe.ts diff --git a/libs/sidekick_examples/tsconfig.json b/libs/vdk_base64/tsconfig.json similarity index 100% rename from libs/sidekick_examples/tsconfig.json rename to libs/vdk_base64/tsconfig.json diff --git a/libs/vdk_bin/.gitignore b/libs/vdk_bin/.gitignore new file mode 100644 index 0000000..3967e48 --- /dev/null +++ b/libs/vdk_bin/.gitignore @@ -0,0 +1,10 @@ +dist +jex_mindist +jex_include +*.beam +*.swp +*.swo +test/testgen/b58_cases_2.eterms +test/testgen/b58_cases_3.eterms +__pycache__ +docs/ diff --git a/libs/vdk_bin/jex.eterms b/libs/vdk_bin/jex.eterms new file mode 100644 index 0000000..8f88098 --- /dev/null +++ b/libs/vdk_bin/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, vdk}. +{version, "0.1.0"}. +{deps, []}. diff --git a/libs/vdk_bin/notes b/libs/vdk_bin/notes new file mode 100644 index 0000000..97c0e0c --- /dev/null +++ b/libs/vdk_bin/notes @@ -0,0 +1,24 @@ +done: +- base64/base58 encoding/decoding +- rlp + +today: +- pull apart data + +tomorrow: +- basic easy serialization/deserialization +- serialize/deserialize examples +- (maybe) pull in awcp/sidekick +- sidekick: sign/propagate and sign/noprop + +later: + +- general node querying with parasite (think about autogeneration) +- simple aetto/ae/etc unit converter +- ghetto sophia ide +- general "send money to someone" page + +GUI: + +- simple testnet explorer +- contract ide diff --git a/libs/vdk_bin/scratch/anth.ts b/libs/vdk_bin/scratch/anth.ts new file mode 100644 index 0000000..6117067 --- /dev/null +++ b/libs/vdk_bin/scratch/anth.ts @@ -0,0 +1,403 @@ +/** + * Node API constructor/deconstructor + * + * This is similar to serialization/deserialization, but not quite the same + * thing. It converts back and forth between different forms of + * "api-serialized" data. + * + * References: + * 1. https://github.com/aeternity/protocol/blob/master/serializations.md + * 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md + * + * ## General type rules + * + * ``` + * ERLANG TYPE | JS TYPE + * ------------------------------- + * integer | bigint + * list | Array + * binary | Uint8Array + * ``` + * + * # Example + * + * We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`. + * + * We can tell by the `tx_` prefix that this represents transaction data of + * some sort. But the rest of the data is totally opaque. The task of this + * module is to "humanize" that `tx_...` string and show what data is contained + * in the rest of it. + * + * The remainder of the string is a base64-encoded bytestring + * + * ```erlang + * 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>> + * ``` + * + * That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I + * wrote an RLP decoder. RLP has two types of data: binaries, and + * arbitrary-depth (possibly empty) lists of binaries. + * + * ```erlang + * -type decoded_data() :: binary() | [decoded_data()]. + * + * -spec decode(RLP) -> {Data, Rest} + * when RLP :: binary(), + * Data :: decoded_data(), + * Rest :: binary(). + * ``` + * + * ```erlang + * 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning + * + * ```erlang + * 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 4> SizeX = byte_size(X). + * 94 + * 6> <> = X. + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 10> <> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)). + * <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81, + * 189,184,82,207,164,68,128,43,11,174,236,59,77,...>> + * 11> Hash. + * <<3,182,66,215>> + * 12> Check. + * <<3,182,66,215>> + * ``` + * + * What we're really interested in is `Data` + * + * ```erlang + * 14> {Data, _} = rlp:decode(X). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * `Data` is a list. The first field `<<"\f">>` is meant to be an integer which + * tells us what type of data this is. + * + * ```erlang + * 16> $\f. + * 12 + * ``` + * + * If we look at our table + * (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags), + * we see that a value of `12` is a spend transaction. + * + * The second field `<<1>>` tells us the "version" of the field orderings, + * which we can ignore for now. + * + * The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction) + * + * ```erlang + * [ :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE" + * , :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * , :: int() % <<"\n">> "=" 10 + * , :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000 + * , :: int() % <<0>> "=" 0 + * , :: int() % <<"\t">> "=" 9 + * , :: binary() % <<"hainana">> "=" "hainana" + * ] + * ``` + * + * Our task here is to be able to pull apart the "tx_..." string into its fields. + * + * Converting the binaries to integers is pretty trivial. The only mildly + * annoying thing is the `id` type. + * + * `id`s have two fields: a single-byte prefix which says which type of ID it + * is. In this case, both `id`s have a prefix of `1`, which means they are both + * normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other + * options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or + * names (prefix `2`/`nm_`) + * + * To "api-encode" the name, we first pick the appropriate prefix based on the + * first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then + * double-SHA'd to get the 4-byte check suffix + * + * ```erlang + * 30> SenderBytes = lists:nth(3, Data). + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 31> <<1, SenderAddrBytes/binary>> = SenderBytes. + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 32> DoubleSha = fun(Bytes) -> <> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end. + * #Fun + * 33> "ak_" ++ b58:enc(<>). + * "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(<>). + * "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * ``` + * + * ```js + * > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==") + * {tag : 'SpendTx', + * version : 1n, + * fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE", + * recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv", + * amount : 10n, + * fee : 16660000000000n, + * ttl : 0n, + * nonce : 9n, + * payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}} + * ``` + * + * @module + */ + +export { + // types + tx_str, + deconstructed_tx, + // functions + deconstruct_tx +}; + +import * as b64 from './b64.js' +import * as bin from './bin.js' +import * as rlp from './rlp.js' + + +/** + * Alias type for a `tx_...` string + */ +type tx_str = string; + +/** + * Alias type for a `sg_...` string + */ +type sg_str = string; + +/** + * types of decoded tx we currently support + * @internal + */ +type tx_type_str + = 'SignedTx' + | 'SpendTx' + | 'ContractCreateTx' + | 'ContractCallTx'; + + +/** + * Results of deconstruct_tx + */ +type deconstructed_tx + = {type : 'SignedTx', + version : bigint, + fields : fields_SignedTx} + | {type : 'SpendTx', + version : bigint, + fields : fields_SpendTx} + | {type : 'ContractCreateTx', + version : bigint, + fields : fields_ContractCreateTx} + | {type : 'ContractCallTx' + version : bigint, + fields : fields_ContractCallTx}; + +/** + * Convenient type alias + * + * @internal + */ +type rlpdata = rlp.decoded_data; + + +/** + * Fields types + */ +type fields + = fields_SignedTx + | fields_SpendTx + | fields_ContractCreateTx + | fields_ContractCallTx; + +type fields_SignedTx + = {signatures : Array, + transaction : tx_str}; + +type fields_SpendTx + = {sender : string, + recipient : string, + amount : bigint, + fee : bigint, + ttl : bigint, + nonce : bigint, + payload : Uint8Array}; + +/** + * Deconstruct a Tx + */ +function +deconstruct_tx + (tx_str: tx_str) + : deconstructed_tx +{ + let b64_str : string = tx_str.slice(3); // tx_[...] -> [...] + let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes + let tx_data : Array = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing + let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint + let tts : tx_type_str = tx_type_str(tx_type); + let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]); + let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2)); + return {type : tts, + version : tx_version, + fields : tx_fields}; +} + + +/** + * Data that's "api-encoded" goes through the following stages: + * + * 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings) + * 2. rlp decode data -> bytestring + * 3. bytestring -> <> + * 4. HashedBytestring -> base64/base58 string encoding + * 5. Add string prefix + * + * This function undoes step 3 and step 2, returns back the rlp decode data + * + * FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures + * FIXME: No good way to handle failure cases + * + * @internal + */ +function +shasha_rlp_decode_list + (hashed_bs : Uint8Array) + : Array +{ + let len = hashed_bs.length; + let bytes = hashed_bs.slice(0, len - 4); + let {decoded_data} = rlp.decode(bytes); + return (decoded_data as Array); +} + + + +/** + * Convert an object tag that's a type of transaction to the type string + * + * See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags + * + * @internal + */ +function +tx_type_str + (tx_type_int : bigint) + : tx_type_str +{ + switch (tx_type_int) + { + case 11n: return 'SignedTx'; + case 12n: return 'SpendTx'; + case 42n: return 'ContractCreateTx'; + case 43n: return 'ContractCallTx'; + default: throw new Error('invalid transaction type: ' + tx_type_int); + } +} + + + +/** + * Given an array of data decoded from RLP, convert it to the fields, as + * appropriate as given by the tx type string and the version + */ +function +deconstruct_fields + (tx_type_str : tx_type_str, + tx_version : bigint, + tx_rawfields : Array) + : fields + { + switch (tx_type_str) + { + // case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields); + case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields); + // case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields); + // case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields); + default : throw new Error('invalid tx type str: ' + tx_type_str); + } + } + + +// TODO: do all this in Erlang + function + deconstruct_fields_SpendTx + (fields: Array) + : fields_SpendTx +{ + let sender_bytes = fields[0]; + let recip_bytes = fields[1]; + let amount_bytes = fields[2]; + let fee_bytes = fields[3]; + let ttl_bytes = fields[4]; + let nonce_bytes = fields[5]; + let payload_bytes = fields[6]; + return {sender : encode_id(sender_bytes), + recipient : encode_id(sender_bytes), + amount : bin.bytes_to_bigint(amount_bytes), + fee : bin.bytes_to_bigint(fee_bytes), + ttl : bin.bytes_to_bigint(ttl_bytes), + nonce : bin.bytes_to_bigint(nonce_bytes), + payload : bin.bytes_to_bigint(payload_bytes)}; +} + + +/** + * Convert a binary account/name/etc binary id into the appropriate type of string + * + * @internal + */ +function +encode_id + (id: Uint8Array) + : string +{ + throw new Error('nyi'); +} + +/* +FIXME: +1. work out all this in Erlang to clear conceptual goo +2. think about how i want type safety etc to work +3. think about a language to assert that the data has the correct shape to it +4. get some examples working in Erlang +5. convert erlang code back to ts +*/ diff --git a/libs/vdk_bin/scratch/bin.ts b/libs/vdk_bin/scratch/bin.ts new file mode 100644 index 0000000..47ad66a --- /dev/null +++ b/libs/vdk_bin/scratch/bin.ts @@ -0,0 +1,112 @@ +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_zeros(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // alright so + // we can start by copying the first bytes into result bytes + for (let bytes1_idx0 = 0; + bytes1_idx0 < bytes1.length; + bytes1_idx0++) + { + result_bytes[bytes1_idx0] = bytes1[bytes1_idx0]; + } + + // next + // we need to calculate the left-shift offset + // this will be 8 - (bytes1.bit_length % 8) + let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8); + // so + // bytes1: ABCD_EF00 + // bytes2: GH12_3000 + // result: ABCD_EFGH 1230_0000 + // ah ok, so we need to for each byte in the second array + // take the first however many bits, xor it with the existing byte + // then take the last however many bits and place them into the next byte + // this is super confusing but + // ABCD_EF00 + // GH12_3456 + // operation: + // ABCD_EF00 + // xor 0000_00GH + // = ABCD_EFGH 1234_5600 + // + // then on the next iteration + // 1234_5600 + // abcd_efgh + // -> + // 1234_56ab cdef_gh00 + // + // ah so there's a pattern + // however many trailing 0s there are in the first array + // say there's 2 + // we take the first 2 bits of the upcoming byte + // xor that against the current byte + // take the last 6 bits of the upcoming byte + // set the next byte to that + // + // have to think about edge behavior + // this is ripe for off-by-1 errors + // but i think the general idea is right + // + // so we start the iteration + // on the last byte of the first array + let last_byte_of_first_array_idx0 : number = bytes1.length - 1; + // and we end + // on the second-to-last-byte of the result array + let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2; + // the reason we do that is because we're doing this is because we are + // going along, xoring against the current byte and then setting the next + // byte + // + // ok so + for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; + this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; + this_result_byte_idx0++) + { + let this_result_byte : number = result_bytes[this_result_byte_idx0]; + + // ok here we need to fish out the relevant byte of the second array + // gaaah + // so this will be 0 at the start of the loop + let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0; + let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0]; + + // ok so let's fish out the leading digits + // the number of leading digits is the number of trailing 0s in the first array + let num_leading_digits : number = num_trailing_zeros_in_first_array; + let num_trailing_digits : number = 8 - num_leading_digits; + + // suppose there are 2 leading digits and 6 trailing digits + // ABCD_EFGH + // leading digits are + // ABCD_EFGH >> 6 = 0000_00AB + // trailing digits are + // (ABCD_EFGH << 2) % 255 = CDEF_GH00 + let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits; + let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255; + + // xor the current byte against the leading digits + let new_this_result_byte : number = this_result_byte ^ leading_digits; + result_bytes[this_result_byte_idx0] = new_this_result_byte; + + // set the next byte to the trailing digits + result_bytes[this_result_byte_idx0 + 1] = trailing_digits; + } + + // i think we're done + return {bit_length : result_bit_length, + bytes : result_bytes}; +} + diff --git a/libs/vdk_bin/scratch/scratch.txt b/libs/vdk_bin/scratch/scratch.txt new file mode 100644 index 0000000..9213abc --- /dev/null +++ b/libs/vdk_bin/scratch/scratch.txt @@ -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, + 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); // <> + let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <> + let decoded_datas : Array = rlp.decode(rlp_stuff).decoded_data as Array; // 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); // <> = TagBytes + let vsn : Uint8Array = decoded_datas[1] as Uint8Array; + // tx fields + let tx_fields : Array = 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): 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): SignedTx { + throw new Error('nyi'); +} + + +/** + * Decode a SpendTx + * + * @internal + */ +function +decode_fields_SpendTx(fields: Array): SpendTx { + // [ :: id(), + // :: id(), + // :: int(), + // :: int(), + // :: int(), + // :: int(), + // :: 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): ContractCreateTx { + throw new Error('nyi'); +} + +function +decode_fields_ContractCallTx(fields: Array): 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)}; +} diff --git a/libs/vdk_bin/scratch/sp.ts b/libs/vdk_bin/scratch/sp.ts new file mode 100644 index 0000000..8448945 --- /dev/null +++ b/libs/vdk_bin/scratch/sp.ts @@ -0,0 +1,23 @@ +/** + * Vanillae Seed Phrase Library + * + * Refs: + * 1. BIP 39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki + * + * @module + */ + + +/** + * Get a given number of seed bits. + * + * `how_many` must be a multiple of 33. + * + * Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic + */ +function +seed_and_check_bits + (how_many : number) + : Uint8Array +{ +} diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_bin/src/b58.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts rename to libs/vdk_bin/src/b58.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_bin/src/b64.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts rename to libs/vdk_bin/src/b64.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_bin/src/bin.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts rename to libs/vdk_bin/src/bin.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_bin/src/faert.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts rename to libs/vdk_bin/src/faert.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_bin/src/rlp.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts rename to libs/vdk_bin/src/rlp.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_bin/src/safe.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts rename to libs/vdk_bin/src/safe.ts diff --git a/libs/vdk/tsconfig.json b/libs/vdk_bin/tsconfig.json similarity index 100% rename from libs/vdk/tsconfig.json rename to libs/vdk_bin/tsconfig.json diff --git a/libs/vdk_faert/.gitignore b/libs/vdk_faert/.gitignore new file mode 100644 index 0000000..3967e48 --- /dev/null +++ b/libs/vdk_faert/.gitignore @@ -0,0 +1,10 @@ +dist +jex_mindist +jex_include +*.beam +*.swp +*.swo +test/testgen/b58_cases_2.eterms +test/testgen/b58_cases_3.eterms +__pycache__ +docs/ diff --git a/libs/vdk_faert/jex.eterms b/libs/vdk_faert/jex.eterms new file mode 100644 index 0000000..8f88098 --- /dev/null +++ b/libs/vdk_faert/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, vdk}. +{version, "0.1.0"}. +{deps, []}. diff --git a/libs/vdk_faert/notes b/libs/vdk_faert/notes new file mode 100644 index 0000000..97c0e0c --- /dev/null +++ b/libs/vdk_faert/notes @@ -0,0 +1,24 @@ +done: +- base64/base58 encoding/decoding +- rlp + +today: +- pull apart data + +tomorrow: +- basic easy serialization/deserialization +- serialize/deserialize examples +- (maybe) pull in awcp/sidekick +- sidekick: sign/propagate and sign/noprop + +later: + +- general node querying with parasite (think about autogeneration) +- simple aetto/ae/etc unit converter +- ghetto sophia ide +- general "send money to someone" page + +GUI: + +- simple testnet explorer +- contract ide diff --git a/libs/vdk_faert/scratch/anth.ts b/libs/vdk_faert/scratch/anth.ts new file mode 100644 index 0000000..6117067 --- /dev/null +++ b/libs/vdk_faert/scratch/anth.ts @@ -0,0 +1,403 @@ +/** + * Node API constructor/deconstructor + * + * This is similar to serialization/deserialization, but not quite the same + * thing. It converts back and forth between different forms of + * "api-serialized" data. + * + * References: + * 1. https://github.com/aeternity/protocol/blob/master/serializations.md + * 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md + * + * ## General type rules + * + * ``` + * ERLANG TYPE | JS TYPE + * ------------------------------- + * integer | bigint + * list | Array + * binary | Uint8Array + * ``` + * + * # Example + * + * We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`. + * + * We can tell by the `tx_` prefix that this represents transaction data of + * some sort. But the rest of the data is totally opaque. The task of this + * module is to "humanize" that `tx_...` string and show what data is contained + * in the rest of it. + * + * The remainder of the string is a base64-encoded bytestring + * + * ```erlang + * 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>> + * ``` + * + * That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I + * wrote an RLP decoder. RLP has two types of data: binaries, and + * arbitrary-depth (possibly empty) lists of binaries. + * + * ```erlang + * -type decoded_data() :: binary() | [decoded_data()]. + * + * -spec decode(RLP) -> {Data, Rest} + * when RLP :: binary(), + * Data :: decoded_data(), + * Rest :: binary(). + * ``` + * + * ```erlang + * 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning + * + * ```erlang + * 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 4> SizeX = byte_size(X). + * 94 + * 6> <> = X. + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 10> <> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)). + * <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81, + * 189,184,82,207,164,68,128,43,11,174,236,59,77,...>> + * 11> Hash. + * <<3,182,66,215>> + * 12> Check. + * <<3,182,66,215>> + * ``` + * + * What we're really interested in is `Data` + * + * ```erlang + * 14> {Data, _} = rlp:decode(X). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * `Data` is a list. The first field `<<"\f">>` is meant to be an integer which + * tells us what type of data this is. + * + * ```erlang + * 16> $\f. + * 12 + * ``` + * + * If we look at our table + * (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags), + * we see that a value of `12` is a spend transaction. + * + * The second field `<<1>>` tells us the "version" of the field orderings, + * which we can ignore for now. + * + * The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction) + * + * ```erlang + * [ :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE" + * , :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * , :: int() % <<"\n">> "=" 10 + * , :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000 + * , :: int() % <<0>> "=" 0 + * , :: int() % <<"\t">> "=" 9 + * , :: binary() % <<"hainana">> "=" "hainana" + * ] + * ``` + * + * Our task here is to be able to pull apart the "tx_..." string into its fields. + * + * Converting the binaries to integers is pretty trivial. The only mildly + * annoying thing is the `id` type. + * + * `id`s have two fields: a single-byte prefix which says which type of ID it + * is. In this case, both `id`s have a prefix of `1`, which means they are both + * normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other + * options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or + * names (prefix `2`/`nm_`) + * + * To "api-encode" the name, we first pick the appropriate prefix based on the + * first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then + * double-SHA'd to get the 4-byte check suffix + * + * ```erlang + * 30> SenderBytes = lists:nth(3, Data). + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 31> <<1, SenderAddrBytes/binary>> = SenderBytes. + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 32> DoubleSha = fun(Bytes) -> <> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end. + * #Fun + * 33> "ak_" ++ b58:enc(<>). + * "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(<>). + * "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * ``` + * + * ```js + * > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==") + * {tag : 'SpendTx', + * version : 1n, + * fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE", + * recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv", + * amount : 10n, + * fee : 16660000000000n, + * ttl : 0n, + * nonce : 9n, + * payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}} + * ``` + * + * @module + */ + +export { + // types + tx_str, + deconstructed_tx, + // functions + deconstruct_tx +}; + +import * as b64 from './b64.js' +import * as bin from './bin.js' +import * as rlp from './rlp.js' + + +/** + * Alias type for a `tx_...` string + */ +type tx_str = string; + +/** + * Alias type for a `sg_...` string + */ +type sg_str = string; + +/** + * types of decoded tx we currently support + * @internal + */ +type tx_type_str + = 'SignedTx' + | 'SpendTx' + | 'ContractCreateTx' + | 'ContractCallTx'; + + +/** + * Results of deconstruct_tx + */ +type deconstructed_tx + = {type : 'SignedTx', + version : bigint, + fields : fields_SignedTx} + | {type : 'SpendTx', + version : bigint, + fields : fields_SpendTx} + | {type : 'ContractCreateTx', + version : bigint, + fields : fields_ContractCreateTx} + | {type : 'ContractCallTx' + version : bigint, + fields : fields_ContractCallTx}; + +/** + * Convenient type alias + * + * @internal + */ +type rlpdata = rlp.decoded_data; + + +/** + * Fields types + */ +type fields + = fields_SignedTx + | fields_SpendTx + | fields_ContractCreateTx + | fields_ContractCallTx; + +type fields_SignedTx + = {signatures : Array, + transaction : tx_str}; + +type fields_SpendTx + = {sender : string, + recipient : string, + amount : bigint, + fee : bigint, + ttl : bigint, + nonce : bigint, + payload : Uint8Array}; + +/** + * Deconstruct a Tx + */ +function +deconstruct_tx + (tx_str: tx_str) + : deconstructed_tx +{ + let b64_str : string = tx_str.slice(3); // tx_[...] -> [...] + let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes + let tx_data : Array = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing + let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint + let tts : tx_type_str = tx_type_str(tx_type); + let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]); + let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2)); + return {type : tts, + version : tx_version, + fields : tx_fields}; +} + + +/** + * Data that's "api-encoded" goes through the following stages: + * + * 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings) + * 2. rlp decode data -> bytestring + * 3. bytestring -> <> + * 4. HashedBytestring -> base64/base58 string encoding + * 5. Add string prefix + * + * This function undoes step 3 and step 2, returns back the rlp decode data + * + * FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures + * FIXME: No good way to handle failure cases + * + * @internal + */ +function +shasha_rlp_decode_list + (hashed_bs : Uint8Array) + : Array +{ + let len = hashed_bs.length; + let bytes = hashed_bs.slice(0, len - 4); + let {decoded_data} = rlp.decode(bytes); + return (decoded_data as Array); +} + + + +/** + * Convert an object tag that's a type of transaction to the type string + * + * See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags + * + * @internal + */ +function +tx_type_str + (tx_type_int : bigint) + : tx_type_str +{ + switch (tx_type_int) + { + case 11n: return 'SignedTx'; + case 12n: return 'SpendTx'; + case 42n: return 'ContractCreateTx'; + case 43n: return 'ContractCallTx'; + default: throw new Error('invalid transaction type: ' + tx_type_int); + } +} + + + +/** + * Given an array of data decoded from RLP, convert it to the fields, as + * appropriate as given by the tx type string and the version + */ +function +deconstruct_fields + (tx_type_str : tx_type_str, + tx_version : bigint, + tx_rawfields : Array) + : fields + { + switch (tx_type_str) + { + // case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields); + case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields); + // case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields); + // case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields); + default : throw new Error('invalid tx type str: ' + tx_type_str); + } + } + + +// TODO: do all this in Erlang + function + deconstruct_fields_SpendTx + (fields: Array) + : fields_SpendTx +{ + let sender_bytes = fields[0]; + let recip_bytes = fields[1]; + let amount_bytes = fields[2]; + let fee_bytes = fields[3]; + let ttl_bytes = fields[4]; + let nonce_bytes = fields[5]; + let payload_bytes = fields[6]; + return {sender : encode_id(sender_bytes), + recipient : encode_id(sender_bytes), + amount : bin.bytes_to_bigint(amount_bytes), + fee : bin.bytes_to_bigint(fee_bytes), + ttl : bin.bytes_to_bigint(ttl_bytes), + nonce : bin.bytes_to_bigint(nonce_bytes), + payload : bin.bytes_to_bigint(payload_bytes)}; +} + + +/** + * Convert a binary account/name/etc binary id into the appropriate type of string + * + * @internal + */ +function +encode_id + (id: Uint8Array) + : string +{ + throw new Error('nyi'); +} + +/* +FIXME: +1. work out all this in Erlang to clear conceptual goo +2. think about how i want type safety etc to work +3. think about a language to assert that the data has the correct shape to it +4. get some examples working in Erlang +5. convert erlang code back to ts +*/ diff --git a/libs/vdk_faert/scratch/bin.ts b/libs/vdk_faert/scratch/bin.ts new file mode 100644 index 0000000..47ad66a --- /dev/null +++ b/libs/vdk_faert/scratch/bin.ts @@ -0,0 +1,112 @@ +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_zeros(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // alright so + // we can start by copying the first bytes into result bytes + for (let bytes1_idx0 = 0; + bytes1_idx0 < bytes1.length; + bytes1_idx0++) + { + result_bytes[bytes1_idx0] = bytes1[bytes1_idx0]; + } + + // next + // we need to calculate the left-shift offset + // this will be 8 - (bytes1.bit_length % 8) + let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8); + // so + // bytes1: ABCD_EF00 + // bytes2: GH12_3000 + // result: ABCD_EFGH 1230_0000 + // ah ok, so we need to for each byte in the second array + // take the first however many bits, xor it with the existing byte + // then take the last however many bits and place them into the next byte + // this is super confusing but + // ABCD_EF00 + // GH12_3456 + // operation: + // ABCD_EF00 + // xor 0000_00GH + // = ABCD_EFGH 1234_5600 + // + // then on the next iteration + // 1234_5600 + // abcd_efgh + // -> + // 1234_56ab cdef_gh00 + // + // ah so there's a pattern + // however many trailing 0s there are in the first array + // say there's 2 + // we take the first 2 bits of the upcoming byte + // xor that against the current byte + // take the last 6 bits of the upcoming byte + // set the next byte to that + // + // have to think about edge behavior + // this is ripe for off-by-1 errors + // but i think the general idea is right + // + // so we start the iteration + // on the last byte of the first array + let last_byte_of_first_array_idx0 : number = bytes1.length - 1; + // and we end + // on the second-to-last-byte of the result array + let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2; + // the reason we do that is because we're doing this is because we are + // going along, xoring against the current byte and then setting the next + // byte + // + // ok so + for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; + this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; + this_result_byte_idx0++) + { + let this_result_byte : number = result_bytes[this_result_byte_idx0]; + + // ok here we need to fish out the relevant byte of the second array + // gaaah + // so this will be 0 at the start of the loop + let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0; + let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0]; + + // ok so let's fish out the leading digits + // the number of leading digits is the number of trailing 0s in the first array + let num_leading_digits : number = num_trailing_zeros_in_first_array; + let num_trailing_digits : number = 8 - num_leading_digits; + + // suppose there are 2 leading digits and 6 trailing digits + // ABCD_EFGH + // leading digits are + // ABCD_EFGH >> 6 = 0000_00AB + // trailing digits are + // (ABCD_EFGH << 2) % 255 = CDEF_GH00 + let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits; + let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255; + + // xor the current byte against the leading digits + let new_this_result_byte : number = this_result_byte ^ leading_digits; + result_bytes[this_result_byte_idx0] = new_this_result_byte; + + // set the next byte to the trailing digits + result_bytes[this_result_byte_idx0 + 1] = trailing_digits; + } + + // i think we're done + return {bit_length : result_bit_length, + bytes : result_bytes}; +} + diff --git a/libs/vdk_faert/scratch/scratch.txt b/libs/vdk_faert/scratch/scratch.txt new file mode 100644 index 0000000..9213abc --- /dev/null +++ b/libs/vdk_faert/scratch/scratch.txt @@ -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, + 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); // <> + let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <> + let decoded_datas : Array = rlp.decode(rlp_stuff).decoded_data as Array; // 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); // <> = TagBytes + let vsn : Uint8Array = decoded_datas[1] as Uint8Array; + // tx fields + let tx_fields : Array = 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): 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): SignedTx { + throw new Error('nyi'); +} + + +/** + * Decode a SpendTx + * + * @internal + */ +function +decode_fields_SpendTx(fields: Array): SpendTx { + // [ :: id(), + // :: id(), + // :: int(), + // :: int(), + // :: int(), + // :: int(), + // :: 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): ContractCreateTx { + throw new Error('nyi'); +} + +function +decode_fields_ContractCallTx(fields: Array): 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)}; +} diff --git a/libs/vdk_faert/scratch/sp.ts b/libs/vdk_faert/scratch/sp.ts new file mode 100644 index 0000000..8448945 --- /dev/null +++ b/libs/vdk_faert/scratch/sp.ts @@ -0,0 +1,23 @@ +/** + * Vanillae Seed Phrase Library + * + * Refs: + * 1. BIP 39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki + * + * @module + */ + + +/** + * Get a given number of seed bits. + * + * `how_many` must be a multiple of 33. + * + * Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic + */ +function +seed_and_check_bits + (how_many : number) + : Uint8Array +{ +} diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_faert/src/b58.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts rename to libs/vdk_faert/src/b58.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_faert/src/b64.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts rename to libs/vdk_faert/src/b64.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_faert/src/bin.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts rename to libs/vdk_faert/src/bin.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_faert/src/faert.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts rename to libs/vdk_faert/src/faert.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_faert/src/rlp.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts rename to libs/vdk_faert/src/rlp.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_faert/src/safe.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts rename to libs/vdk_faert/src/safe.ts diff --git a/libs/vdk_cases/tsconfig.json b/libs/vdk_faert/tsconfig.json similarity index 100% rename from libs/vdk_cases/tsconfig.json rename to libs/vdk_faert/tsconfig.json diff --git a/libs/vdk_rlp/.gitignore b/libs/vdk_rlp/.gitignore new file mode 100644 index 0000000..3967e48 --- /dev/null +++ b/libs/vdk_rlp/.gitignore @@ -0,0 +1,10 @@ +dist +jex_mindist +jex_include +*.beam +*.swp +*.swo +test/testgen/b58_cases_2.eterms +test/testgen/b58_cases_3.eterms +__pycache__ +docs/ diff --git a/libs/vdk_rlp/jex.eterms b/libs/vdk_rlp/jex.eterms new file mode 100644 index 0000000..8f88098 --- /dev/null +++ b/libs/vdk_rlp/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, vdk}. +{version, "0.1.0"}. +{deps, []}. diff --git a/libs/vdk_rlp/notes b/libs/vdk_rlp/notes new file mode 100644 index 0000000..97c0e0c --- /dev/null +++ b/libs/vdk_rlp/notes @@ -0,0 +1,24 @@ +done: +- base64/base58 encoding/decoding +- rlp + +today: +- pull apart data + +tomorrow: +- basic easy serialization/deserialization +- serialize/deserialize examples +- (maybe) pull in awcp/sidekick +- sidekick: sign/propagate and sign/noprop + +later: + +- general node querying with parasite (think about autogeneration) +- simple aetto/ae/etc unit converter +- ghetto sophia ide +- general "send money to someone" page + +GUI: + +- simple testnet explorer +- contract ide diff --git a/libs/vdk_rlp/scratch/anth.ts b/libs/vdk_rlp/scratch/anth.ts new file mode 100644 index 0000000..6117067 --- /dev/null +++ b/libs/vdk_rlp/scratch/anth.ts @@ -0,0 +1,403 @@ +/** + * Node API constructor/deconstructor + * + * This is similar to serialization/deserialization, but not quite the same + * thing. It converts back and forth between different forms of + * "api-serialized" data. + * + * References: + * 1. https://github.com/aeternity/protocol/blob/master/serializations.md + * 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md + * + * ## General type rules + * + * ``` + * ERLANG TYPE | JS TYPE + * ------------------------------- + * integer | bigint + * list | Array + * binary | Uint8Array + * ``` + * + * # Example + * + * We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`. + * + * We can tell by the `tx_` prefix that this represents transaction data of + * some sort. But the rest of the data is totally opaque. The task of this + * module is to "humanize" that `tx_...` string and show what data is contained + * in the rest of it. + * + * The remainder of the string is a base64-encoded bytestring + * + * ```erlang + * 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>> + * ``` + * + * That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I + * wrote an RLP decoder. RLP has two types of data: binaries, and + * arbitrary-depth (possibly empty) lists of binaries. + * + * ```erlang + * -type decoded_data() :: binary() | [decoded_data()]. + * + * -spec decode(RLP) -> {Data, Rest} + * when RLP :: binary(), + * Data :: decoded_data(), + * Rest :: binary(). + * ``` + * + * ```erlang + * 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning + * + * ```erlang + * 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 4> SizeX = byte_size(X). + * 94 + * 6> <> = X. + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 10> <> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)). + * <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81, + * 189,184,82,207,164,68,128,43,11,174,236,59,77,...>> + * 11> Hash. + * <<3,182,66,215>> + * 12> Check. + * <<3,182,66,215>> + * ``` + * + * What we're really interested in is `Data` + * + * ```erlang + * 14> {Data, _} = rlp:decode(X). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * `Data` is a list. The first field `<<"\f">>` is meant to be an integer which + * tells us what type of data this is. + * + * ```erlang + * 16> $\f. + * 12 + * ``` + * + * If we look at our table + * (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags), + * we see that a value of `12` is a spend transaction. + * + * The second field `<<1>>` tells us the "version" of the field orderings, + * which we can ignore for now. + * + * The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction) + * + * ```erlang + * [ :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE" + * , :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * , :: int() % <<"\n">> "=" 10 + * , :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000 + * , :: int() % <<0>> "=" 0 + * , :: int() % <<"\t">> "=" 9 + * , :: binary() % <<"hainana">> "=" "hainana" + * ] + * ``` + * + * Our task here is to be able to pull apart the "tx_..." string into its fields. + * + * Converting the binaries to integers is pretty trivial. The only mildly + * annoying thing is the `id` type. + * + * `id`s have two fields: a single-byte prefix which says which type of ID it + * is. In this case, both `id`s have a prefix of `1`, which means they are both + * normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other + * options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or + * names (prefix `2`/`nm_`) + * + * To "api-encode" the name, we first pick the appropriate prefix based on the + * first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then + * double-SHA'd to get the 4-byte check suffix + * + * ```erlang + * 30> SenderBytes = lists:nth(3, Data). + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 31> <<1, SenderAddrBytes/binary>> = SenderBytes. + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 32> DoubleSha = fun(Bytes) -> <> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end. + * #Fun + * 33> "ak_" ++ b58:enc(<>). + * "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(<>). + * "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * ``` + * + * ```js + * > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==") + * {tag : 'SpendTx', + * version : 1n, + * fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE", + * recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv", + * amount : 10n, + * fee : 16660000000000n, + * ttl : 0n, + * nonce : 9n, + * payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}} + * ``` + * + * @module + */ + +export { + // types + tx_str, + deconstructed_tx, + // functions + deconstruct_tx +}; + +import * as b64 from './b64.js' +import * as bin from './bin.js' +import * as rlp from './rlp.js' + + +/** + * Alias type for a `tx_...` string + */ +type tx_str = string; + +/** + * Alias type for a `sg_...` string + */ +type sg_str = string; + +/** + * types of decoded tx we currently support + * @internal + */ +type tx_type_str + = 'SignedTx' + | 'SpendTx' + | 'ContractCreateTx' + | 'ContractCallTx'; + + +/** + * Results of deconstruct_tx + */ +type deconstructed_tx + = {type : 'SignedTx', + version : bigint, + fields : fields_SignedTx} + | {type : 'SpendTx', + version : bigint, + fields : fields_SpendTx} + | {type : 'ContractCreateTx', + version : bigint, + fields : fields_ContractCreateTx} + | {type : 'ContractCallTx' + version : bigint, + fields : fields_ContractCallTx}; + +/** + * Convenient type alias + * + * @internal + */ +type rlpdata = rlp.decoded_data; + + +/** + * Fields types + */ +type fields + = fields_SignedTx + | fields_SpendTx + | fields_ContractCreateTx + | fields_ContractCallTx; + +type fields_SignedTx + = {signatures : Array, + transaction : tx_str}; + +type fields_SpendTx + = {sender : string, + recipient : string, + amount : bigint, + fee : bigint, + ttl : bigint, + nonce : bigint, + payload : Uint8Array}; + +/** + * Deconstruct a Tx + */ +function +deconstruct_tx + (tx_str: tx_str) + : deconstructed_tx +{ + let b64_str : string = tx_str.slice(3); // tx_[...] -> [...] + let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes + let tx_data : Array = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing + let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint + let tts : tx_type_str = tx_type_str(tx_type); + let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]); + let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2)); + return {type : tts, + version : tx_version, + fields : tx_fields}; +} + + +/** + * Data that's "api-encoded" goes through the following stages: + * + * 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings) + * 2. rlp decode data -> bytestring + * 3. bytestring -> <> + * 4. HashedBytestring -> base64/base58 string encoding + * 5. Add string prefix + * + * This function undoes step 3 and step 2, returns back the rlp decode data + * + * FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures + * FIXME: No good way to handle failure cases + * + * @internal + */ +function +shasha_rlp_decode_list + (hashed_bs : Uint8Array) + : Array +{ + let len = hashed_bs.length; + let bytes = hashed_bs.slice(0, len - 4); + let {decoded_data} = rlp.decode(bytes); + return (decoded_data as Array); +} + + + +/** + * Convert an object tag that's a type of transaction to the type string + * + * See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags + * + * @internal + */ +function +tx_type_str + (tx_type_int : bigint) + : tx_type_str +{ + switch (tx_type_int) + { + case 11n: return 'SignedTx'; + case 12n: return 'SpendTx'; + case 42n: return 'ContractCreateTx'; + case 43n: return 'ContractCallTx'; + default: throw new Error('invalid transaction type: ' + tx_type_int); + } +} + + + +/** + * Given an array of data decoded from RLP, convert it to the fields, as + * appropriate as given by the tx type string and the version + */ +function +deconstruct_fields + (tx_type_str : tx_type_str, + tx_version : bigint, + tx_rawfields : Array) + : fields + { + switch (tx_type_str) + { + // case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields); + case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields); + // case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields); + // case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields); + default : throw new Error('invalid tx type str: ' + tx_type_str); + } + } + + +// TODO: do all this in Erlang + function + deconstruct_fields_SpendTx + (fields: Array) + : fields_SpendTx +{ + let sender_bytes = fields[0]; + let recip_bytes = fields[1]; + let amount_bytes = fields[2]; + let fee_bytes = fields[3]; + let ttl_bytes = fields[4]; + let nonce_bytes = fields[5]; + let payload_bytes = fields[6]; + return {sender : encode_id(sender_bytes), + recipient : encode_id(sender_bytes), + amount : bin.bytes_to_bigint(amount_bytes), + fee : bin.bytes_to_bigint(fee_bytes), + ttl : bin.bytes_to_bigint(ttl_bytes), + nonce : bin.bytes_to_bigint(nonce_bytes), + payload : bin.bytes_to_bigint(payload_bytes)}; +} + + +/** + * Convert a binary account/name/etc binary id into the appropriate type of string + * + * @internal + */ +function +encode_id + (id: Uint8Array) + : string +{ + throw new Error('nyi'); +} + +/* +FIXME: +1. work out all this in Erlang to clear conceptual goo +2. think about how i want type safety etc to work +3. think about a language to assert that the data has the correct shape to it +4. get some examples working in Erlang +5. convert erlang code back to ts +*/ diff --git a/libs/vdk_rlp/scratch/bin.ts b/libs/vdk_rlp/scratch/bin.ts new file mode 100644 index 0000000..47ad66a --- /dev/null +++ b/libs/vdk_rlp/scratch/bin.ts @@ -0,0 +1,112 @@ +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_zeros(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // alright so + // we can start by copying the first bytes into result bytes + for (let bytes1_idx0 = 0; + bytes1_idx0 < bytes1.length; + bytes1_idx0++) + { + result_bytes[bytes1_idx0] = bytes1[bytes1_idx0]; + } + + // next + // we need to calculate the left-shift offset + // this will be 8 - (bytes1.bit_length % 8) + let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8); + // so + // bytes1: ABCD_EF00 + // bytes2: GH12_3000 + // result: ABCD_EFGH 1230_0000 + // ah ok, so we need to for each byte in the second array + // take the first however many bits, xor it with the existing byte + // then take the last however many bits and place them into the next byte + // this is super confusing but + // ABCD_EF00 + // GH12_3456 + // operation: + // ABCD_EF00 + // xor 0000_00GH + // = ABCD_EFGH 1234_5600 + // + // then on the next iteration + // 1234_5600 + // abcd_efgh + // -> + // 1234_56ab cdef_gh00 + // + // ah so there's a pattern + // however many trailing 0s there are in the first array + // say there's 2 + // we take the first 2 bits of the upcoming byte + // xor that against the current byte + // take the last 6 bits of the upcoming byte + // set the next byte to that + // + // have to think about edge behavior + // this is ripe for off-by-1 errors + // but i think the general idea is right + // + // so we start the iteration + // on the last byte of the first array + let last_byte_of_first_array_idx0 : number = bytes1.length - 1; + // and we end + // on the second-to-last-byte of the result array + let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2; + // the reason we do that is because we're doing this is because we are + // going along, xoring against the current byte and then setting the next + // byte + // + // ok so + for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; + this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; + this_result_byte_idx0++) + { + let this_result_byte : number = result_bytes[this_result_byte_idx0]; + + // ok here we need to fish out the relevant byte of the second array + // gaaah + // so this will be 0 at the start of the loop + let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0; + let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0]; + + // ok so let's fish out the leading digits + // the number of leading digits is the number of trailing 0s in the first array + let num_leading_digits : number = num_trailing_zeros_in_first_array; + let num_trailing_digits : number = 8 - num_leading_digits; + + // suppose there are 2 leading digits and 6 trailing digits + // ABCD_EFGH + // leading digits are + // ABCD_EFGH >> 6 = 0000_00AB + // trailing digits are + // (ABCD_EFGH << 2) % 255 = CDEF_GH00 + let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits; + let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255; + + // xor the current byte against the leading digits + let new_this_result_byte : number = this_result_byte ^ leading_digits; + result_bytes[this_result_byte_idx0] = new_this_result_byte; + + // set the next byte to the trailing digits + result_bytes[this_result_byte_idx0 + 1] = trailing_digits; + } + + // i think we're done + return {bit_length : result_bit_length, + bytes : result_bytes}; +} + diff --git a/libs/vdk_rlp/scratch/scratch.txt b/libs/vdk_rlp/scratch/scratch.txt new file mode 100644 index 0000000..9213abc --- /dev/null +++ b/libs/vdk_rlp/scratch/scratch.txt @@ -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, + 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); // <> + let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <> + let decoded_datas : Array = rlp.decode(rlp_stuff).decoded_data as Array; // 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); // <> = TagBytes + let vsn : Uint8Array = decoded_datas[1] as Uint8Array; + // tx fields + let tx_fields : Array = 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): 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): SignedTx { + throw new Error('nyi'); +} + + +/** + * Decode a SpendTx + * + * @internal + */ +function +decode_fields_SpendTx(fields: Array): SpendTx { + // [ :: id(), + // :: id(), + // :: int(), + // :: int(), + // :: int(), + // :: int(), + // :: 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): ContractCreateTx { + throw new Error('nyi'); +} + +function +decode_fields_ContractCallTx(fields: Array): 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)}; +} diff --git a/libs/vdk_rlp/scratch/sp.ts b/libs/vdk_rlp/scratch/sp.ts new file mode 100644 index 0000000..8448945 --- /dev/null +++ b/libs/vdk_rlp/scratch/sp.ts @@ -0,0 +1,23 @@ +/** + * Vanillae Seed Phrase Library + * + * Refs: + * 1. BIP 39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki + * + * @module + */ + + +/** + * Get a given number of seed bits. + * + * `how_many` must be a multiple of 33. + * + * Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic + */ +function +seed_and_check_bits + (how_many : number) + : Uint8Array +{ +} diff --git a/libs/vdk_rlp/src/b58.ts b/libs/vdk_rlp/src/b58.ts new file mode 100644 index 0000000..1fd30b8 --- /dev/null +++ b/libs/vdk_rlp/src/b58.ts @@ -0,0 +1,418 @@ +/** + * Base58 encoding/decoding + */ + +export { + encode, + decode +} + + + +//============================================================================= +// ENCODING +//============================================================================= + +/** + * Encode a Uint8Array into base58 + */ +function +encode + (binary : Uint8Array) + : string +{ + let num_leading_zeros : number = nlz(binary); + let rest : Uint8Array = binary.slice(num_leading_zeros); + let ones : string = encode_zeros(num_leading_zeros); + let rest_b58 : string = encode_rest(rest); + let result : string = ones + rest_b58; + return result; +} + + + +/** + * count the number of leading zeros in a uint8array + * + * @internal + */ +function +nlz + (bytes: Uint8Array) + : number +{ + let n = 0; + for (let this_byte of bytes) + { + if (0 === this_byte) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '1's for however many leading zeros there are + * + * @internal + */ +function +encode_zeros + (how_many : number) + : string +{ + let ones : string = ''; + for (let i = 1; + i <= how_many; + i++) + { + ones += '1'; + } + + return ones; +} + + + +/** + * Encode a Uint8Array that has no leading zeros + * + * @internal + */ +function +encode_rest + (bytes : Uint8Array) + : string +{ + let bytes_bignum : bigint = bytes_to_bigint(bytes); + let result : string = bignum_to_base58(bytes_bignum); + return result; +} + + + +/** + * Convert a bytestring to a bignum + * + * @internal + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_byte of bytes) + { + acc_bigint <<= 8n; + acc_bigint += BigInt(this_byte); + } + return acc_bigint; +} + + + +/** + * Convert a BigInt to Base58 + * + * @internal + */ +function +bignum_to_base58 + (q: bigint) + : string +{ + let s = ''; + while (q !== 0n) + { + let this_n : bigint = q % 58n; + q /= 58n; + + let this_b58_char : string = bigint_to_char(this_n); + s = this_b58_char + s; + } + return s; +} + + + +//============================================================================= +// DECODING +//============================================================================= + +/** + * Decode a Base58 string into a Uint8Array + */ +function +decode + (base58: string) + : Uint8Array +{ + let num_leading_ones : number = nlo(base58); + let rest : string = base58.slice(num_leading_ones); + let zeros : Array = decode_ones(num_leading_ones); + let rest_arr : Array = decode_rest(rest); + let pre_result : Array = zeros.concat(rest_arr); + return new Uint8Array(pre_result); +} + + + +/** + * count the number of leading 1 characters in a uint8array + * + * @internal + */ +function +nlo + (base58: string) + : number +{ + let n = 0; + for (let this_char of base58) + { + if ('1' === this_char) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '0's for however many leading ones there are + * + * @internal + */ +function +decode_ones + (how_many : number) + : Array +{ + let zeros : Array = []; + for (let i = 1; + i <= how_many; + i++) + { + zeros.push(0); + } + + return zeros; +} + + + +/** + * Decode a string that has no leading 1s + * + * @internal + */ +function +decode_rest + (base58: string) + : Array +{ + let result_bignum : bigint = base58_to_bigint(base58); + let result : Array = bigint_to_base256(result_bignum); + return result; +} + + + +/** + * Convert a base58 string to a bignum + * + * @internal + */ +function +base58_to_bigint + (base58: string) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_char of base58) + { + acc_bigint *= 58n; + acc_bigint += char_to_bigint(this_char); + } + return acc_bigint; +} + + +/** + * convert a bignum into a byte array + * + * @end + */ +function +bigint_to_base256 + (q: bigint) + : Array +{ + let arr_reverse = []; + while(q !== 0n) + { + let r: number = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return arr_reverse; +} + + +//============================================================================= +// TRANSLATION TABLES +//============================================================================= + + +/** + * Base58 integer -> character conversion table + * + * @internal + */ +function +bigint_to_char + (n: bigint) + : string +{ + switch(n) { + case 0n: return '1'; + case 1n: return '2'; + case 2n: return '3'; + case 3n: return '4'; + case 4n: return '5'; + case 5n: return '6'; + case 6n: return '7'; + case 7n: return '8'; + case 8n: return '9'; + case 9n: return 'A'; + case 10n: return 'B'; + case 11n: return 'C'; + case 12n: return 'D'; + case 13n: return 'E'; + case 14n: return 'F'; + case 15n: return 'G'; + case 16n: return 'H'; + case 17n: return 'J'; + case 18n: return 'K'; + case 19n: return 'L'; + case 20n: return 'M'; + case 21n: return 'N'; + case 22n: return 'P'; + case 23n: return 'Q'; + case 24n: return 'R'; + case 25n: return 'S'; + case 26n: return 'T'; + case 27n: return 'U'; + case 28n: return 'V'; + case 29n: return 'W'; + case 30n: return 'X'; + case 31n: return 'Y'; + case 32n: return 'Z'; + case 33n: return 'a'; + case 34n: return 'b'; + case 35n: return 'c'; + case 36n: return 'd'; + case 37n: return 'e'; + case 38n: return 'f'; + case 39n: return 'g'; + case 40n: return 'h'; + case 41n: return 'i'; + case 42n: return 'j'; + case 43n: return 'k'; + case 44n: return 'm'; + case 45n: return 'n'; + case 46n: return 'o'; + case 47n: return 'p'; + case 48n: return 'q'; + case 49n: return 'r'; + case 50n: return 's'; + case 51n: return 't'; + case 52n: return 'u'; + case 53n: return 'v'; + case 54n: return 'w'; + case 55n: return 'x'; + case 56n: return 'y'; + case 57n: return 'z'; + default: + throw new Error('invalid base58 bigint: ' + n); + } +} + + + +/** + * Base58 character -> integer conversion table + * + * @internal + */ +function +char_to_bigint + (s: string) + : bigint +{ + switch(s) { + case '1': return 0n; + case '2': return 1n; + case '3': return 2n; + case '4': return 3n; + case '5': return 4n; + case '6': return 5n; + case '7': return 6n; + case '8': return 7n; + case '9': return 8n; + case 'A': return 9n; + case 'B': return 10n; + case 'C': return 11n; + case 'D': return 12n; + case 'E': return 13n; + case 'F': return 14n; + case 'G': return 15n; + case 'H': return 16n; + case 'J': return 17n; + case 'K': return 18n; + case 'L': return 19n; + case 'M': return 20n; + case 'N': return 21n; + case 'P': return 22n; + case 'Q': return 23n; + case 'R': return 24n; + case 'S': return 25n; + case 'T': return 26n; + case 'U': return 27n; + case 'V': return 28n; + case 'W': return 29n; + case 'X': return 30n; + case 'Y': return 31n; + case 'Z': return 32n; + case 'a': return 33n; + case 'b': return 34n; + case 'c': return 35n; + case 'd': return 36n; + case 'e': return 37n; + case 'f': return 38n; + case 'g': return 39n; + case 'h': return 40n; + case 'i': return 41n; + case 'j': return 42n; + case 'k': return 43n; + case 'm': return 44n; + case 'n': return 45n; + case 'o': return 46n; + case 'p': return 47n; + case 'q': return 48n; + case 'r': return 49n; + case 's': return 50n; + case 't': return 51n; + case 'u': return 52n; + case 'v': return 53n; + case 'w': return 54n; + case 'x': return 55n; + case 'y': return 56n; + case 'z': return 57n; + default: + throw new Error('invalid base58 char: ' + s); + } +} diff --git a/libs/vdk_rlp/src/b64.ts b/libs/vdk_rlp/src/b64.ts new file mode 100644 index 0000000..d82cd73 --- /dev/null +++ b/libs/vdk_rlp/src/b64.ts @@ -0,0 +1,655 @@ +/** + * Base64 Utility Functions in TypeScript + */ + +export { + encode, + decode +} + + + +/** + * Encode an array of bytes as a Uint8Array in base64 notation. + */ +function +encode + (bytes: Uint8Array) + : string +{ + // slice the array + // length of head is a multiple of 3 + // treat the tail as a special case + let {head, tail, tail_len} = slice3k(bytes); + let head_str : string = encode_head(head); + let tail_str : string = encode_tail(tail, tail_len); + return head_str + tail_str; +} + + + +type slice3k + = {head : Uint8Array, + tail : Uint8Array, + tail_len : number}; + +/** + * Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and + * the remaining 0,1, or 2 bytes, put them in tail + * + * @internal + */ +function +slice3k + (bytes: Uint8Array) + : slice3k +{ + let len : number = bytes.length; + // too lazy to look up how to do integer division in js so this will do + let tail_len : number = len % 3; + let head_len : number = len - tail_len; + // for slice: + // first argument is the 0-index of the start + // second - first is the length of the slice + let head : Uint8Array = bytes.slice(0, head_len); + // empty second argument means go to the end + let tail : Uint8Array = bytes.slice(head_len); + return {head : head, + tail : tail, + tail_len : tail_len}; +} + + + +/** + * Encode a Uint8Array whose length is known to be a multiple of 3 + * + * @internal + */ +function +encode_head + (head_bytes: Uint8Array) + : string +{ + // can assume length of bytes is a multiple of 3 + // start index at 0 + // increment by 3 + let head_bytes_len : number = head_bytes.length; + let max_idx0 : number = head_bytes_len - 1; + + let head_str_acc : string = ''; + for(let this_3slice_start_idx0 = 0; + this_3slice_start_idx0 <= max_idx0; + this_3slice_start_idx0 += 3) + { + let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3); + let this_3slice_str : string = encode3(this_3slice_bytes); + head_str_acc += this_3slice_str; + } + + return head_str_acc; +} + + + +/** + * Encode a 3 bytes into base64 notation + * + * @internal + */ +function +encode3 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + let b2 : number = bytes[2]; + + // ABCDEFGH 12345678 abcdefgh + // b0 b1 b2 + // ABCDEF GH1234 5678ab cdefgh + // n0 n1 n2 n3 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // b2 = abcdefgh + // b2 >> 6 = ______ab + // n2 = __5678ab + let n2 : number = ((b1 % 16) << 2) + (b2 >> 6); + // b2 = abcdefgh + // 64 = _1______ + // n3 = __cdefgh + let n3 : number = b2 % 64; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + let s3 : string = int2char(n3); + + // retrvn + return s0 + s1 + s2 + s3; +} + + + +/** + * Encode the final 0, 1, or 2 bytes + * + * @internal + */ +function +encode_tail + (tail_bytes : Uint8Array, + tail_len : number) + : string +{ + switch(tail_len) { + case 0: return ''; + case 1: return encode1(tail_bytes); + case 2: return encode2(tail_bytes); + default: + throw new Error('encode_tail with tail_len = ' + tail_len); + } +} + + + +/** + * Encode a single byte + * + * @internal + */ +function +encode1 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + // n0 = __ABCDEF + // b0 = ABCDEFGH + // b0 >> 2 = __ABCDEF + let n0 : number = b0 >> 2; + // n1 = __GH____ + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + let n1 : number = (b0 % 4) << 4; + + return int2char(n0) + int2char(n1) + '=='; +} + + + +/** + * Encode two bytes + * + * @internal + */ +function +encode2 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + + // ABCDEFGH 12345678 + // b0 b1 + // ABCDEF GH1234 5678__ + // n0 n1 n2 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // n2 = __5678__ + let n2 : number = (b1 % 16) << 2; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + + // retrvn + return s0 + s1 + s2 + '='; +} + + + +/** + * Decode a base64-encoded string + */ +function +decode + (base64_str : string) + : Uint8Array +{ + // length of the string is guaranteed to be a multiple of 4 + // if the string is empty, return the empty array + let len = base64_str.length; + // this branching contains the implicit assertion that the length is a + // multiple of 4. If this is not true, the bottom branch is triggered. + // general case goes first because speeeeeed + if ( (4 < len) + && (0 === (len % 4))) + { + // split the head and tail + let tail_start_idx0 : number = len - 4; + let head_s : string = base64_str.slice(0, tail_start_idx0); + let tail_s : string = base64_str.slice(tail_start_idx0); + // Using arrays because Uint8Arrays don't have a concat operation + let head_arr : Array = decode_head(head_s); + let tail_arr : Array = decode_tail(tail_s); + // silly to put these in variables but this is exactly the type of + // situation where JS type insanity shows up + // + // see: i forgot + // > [1,2,3] + [4,5,6] + // '1,2,34,5,6' + // + // Originally, I used + like some sort of moron who codes in a sane + // language + // + // seriously what is this language + // + // this is some clown behavior + let total_arr : Array = head_arr.concat(tail_arr); + return new Uint8Array(total_arr); + } + // special case if the length is exactly 4 + else if (4 === len) + { + // it's just a tail + return new Uint8Array(decode_tail(base64_str)); + } + // empty string + else if (0 === len) + { + return new Uint8Array([]); + } + else + { + throw new Error('base64 decode: invalid string length: ' + len); + } +} + + + +/** + * Decode a string known to not have any padding + * + * @internal + */ +function +decode_head + (s: string) + : Array +{ + // go 4 characters at a time + let max_i0 : number = s.length - 1; + let decoded_acc : Array = []; + for(let i0 = 0; + i0 <= max_i0; + i0 += 4) + { + let this_slice_s : string = s.slice(i0, i0 + 4); + let this_slice_arr : Array = decode3(this_slice_s); + // update accumulator + decoded_acc = decoded_acc.concat(this_slice_arr); + } + return decoded_acc; +} + + + +/** + * Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte + * + * @internal + */ +function +decode_tail + (s: string) + : Array +{ + // all that matters right now is the last 2 chars + // s0, s1, s2, s3 + // 0 based indexing is so annoying + let s2 = s[2]; + let s3 = s[3]; + + // braaaaaaaaaaaaaaaaaench + // two equals signs means 1 byte + if (('=' === s3) && ('=' === s2)) { + return decode1(s); + } + // one equals sign means 2 bytes + else if (('=' === s3)) { + return decode2(s); + } + // 0 equals signs means 3 bytes + else { + return decode3(s); + } +} + + + +/** + * Decode a 4-character long base64 string corresponding to 3 bytes + * + * @internal + */ +function +decode3 + (s: string) + : Array +{ + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + let s3 : string = s[3]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + let n3 : number = char2int(s3); + + // abcdef gh1234 5678ab cdefgh + // n0 n1 n2 n3 + // abcdefgh 12345678 abcdefgh + // b0 b1 b2 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678ab + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + // n2 = __5678ab + // 4 = _____1__ + // n2 % 4 = ______ab + // (n2 % 4) << 6 = ab______ + // n3 = __cdefgh + let b2 : number = ((n2 % 4) << 6) + n3; + + return [b0, b1, b2]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode2 + (s: string) + : Array +{ + // xyz= + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + + // abcdef gh1234 5678__ + // n0 n1 n2 + // abcdefgh 12345678 + // b0 b1 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678__ + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + + return [b0, b1]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode1 + (s: string) + : Array +{ + // xy== + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + + // abcdef gh____ + // n0 n1 + // abcdefgh + // b0 + + // n0 = __abcdef + // n1 = __gh____ + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + + return [b0]; +} + + + + +// FIXME: these tables would *probably* be faster if they were made into objects + +/** + * Conversion table for base64 encode + * + * @internal + */ +function +int2char + (n: number) + : string +{ + switch(n) { + case 0: return 'A'; + case 1: return 'B'; + case 2: return 'C'; + case 3: return 'D'; + case 4: return 'E'; + case 5: return 'F'; + case 6: return 'G'; + case 7: return 'H'; + case 8: return 'I'; + case 9: return 'J'; + case 10: return 'K'; + case 11: return 'L'; + case 12: return 'M'; + case 13: return 'N'; + case 14: return 'O'; + case 15: return 'P'; + case 16: return 'Q'; + case 17: return 'R'; + case 18: return 'S'; + case 19: return 'T'; + case 20: return 'U'; + case 21: return 'V'; + case 22: return 'W'; + case 23: return 'X'; + case 24: return 'Y'; + case 25: return 'Z'; + case 26: return 'a'; + case 27: return 'b'; + case 28: return 'c'; + case 29: return 'd'; + case 30: return 'e'; + case 31: return 'f'; + case 32: return 'g'; + case 33: return 'h'; + case 34: return 'i'; + case 35: return 'j'; + case 36: return 'k'; + case 37: return 'l'; + case 38: return 'm'; + case 39: return 'n'; + case 40: return 'o'; + case 41: return 'p'; + case 42: return 'q'; + case 43: return 'r'; + case 44: return 's'; + case 45: return 't'; + case 46: return 'u'; + case 47: return 'v'; + case 48: return 'w'; + case 49: return 'x'; + case 50: return 'y'; + case 51: return 'z'; + case 52: return '0'; + case 53: return '1'; + case 54: return '2'; + case 55: return '3'; + case 56: return '4'; + case 57: return '5'; + case 58: return '6'; + case 59: return '7'; + case 60: return '8'; + case 61: return '9'; + case 62: return '+'; + case 63: return '/'; + default: throw new Error("invalid base64 encode byte: " + n); + } +} + + + +/** + * Conversion table for base64 decode + * + * @internal + */ +function +char2int + (s: string) + : number +{ + switch(s) { + case 'A': return 0; + case 'B': return 1; + case 'C': return 2; + case 'D': return 3; + case 'E': return 4; + case 'F': return 5; + case 'G': return 6; + case 'H': return 7; + case 'I': return 8; + case 'J': return 9; + case 'K': return 10; + case 'L': return 11; + case 'M': return 12; + case 'N': return 13; + case 'O': return 14; + case 'P': return 15; + case 'Q': return 16; + case 'R': return 17; + case 'S': return 18; + case 'T': return 19; + case 'U': return 20; + case 'V': return 21; + case 'W': return 22; + case 'X': return 23; + case 'Y': return 24; + case 'Z': return 25; + case 'a': return 26; + case 'b': return 27; + case 'c': return 28; + case 'd': return 29; + case 'e': return 30; + case 'f': return 31; + case 'g': return 32; + case 'h': return 33; + case 'i': return 34; + case 'j': return 35; + case 'k': return 36; + case 'l': return 37; + case 'm': return 38; + case 'n': return 39; + case 'o': return 40; + case 'p': return 41; + case 'q': return 42; + case 'r': return 43; + case 's': return 44; + case 't': return 45; + case 'u': return 46; + case 'v': return 47; + case 'w': return 48; + case 'x': return 49; + case 'y': return 50; + case 'z': return 51; + case '0': return 52; + case '1': return 53; + case '2': return 54; + case '3': return 55; + case '4': return 56; + case '5': return 57; + case '6': return 58; + case '7': return 59; + case '8': return 60; + case '9': return 61; + case '+': return 62; + case '/': return 63; + default: throw new Error("invalid base64 character: " + s); + } +} diff --git a/libs/vdk_rlp/src/bin.ts b/libs/vdk_rlp/src/bin.ts new file mode 100644 index 0000000..958aa2a --- /dev/null +++ b/libs/vdk_rlp/src/bin.ts @@ -0,0 +1,377 @@ +/** + * Miscellaneous binary utility functions + * + * @module + */ + +export { + bytes_to_bigint, + bigint_to_bytes, + concat, + strong_rand_bytes +}; + + + +/** + * Concatenate two arrays + */ +function +concat + (arr1 : Uint8Array, + arr2 : Uint8Array) + : Uint8Array +{ + let len1 : number = arr1.length; + let len2 : number = arr2.length; + let arr1_idx0_offset : number = 0; + let arr2_idx0_offset : number = len1; + let result_len : number = len1 + len2; + let result : Uint8Array = new Uint8Array(result_len); + // copy first array into result + for (let arr1_idx0 = 0; + arr1_idx0 < len1; + arr1_idx0++) + { + // no offset here + let result_idx0 : number = arr1_idx0 + arr1_idx0_offset; + result[result_idx0] = arr1[arr1_idx0]; + } + // copy second array into result + for (let arr2_idx0 = 0; + arr2_idx0 < len2; + arr2_idx0++) + { + // offset by the length of the first array + let result_idx0 : number = arr2_idx0 + arr2_idx0_offset; + result[result_idx0] = arr2[arr2_idx0]; + } + return result; +} + + + +/** + * Cryptographically random bytes + */ +function +strong_rand_bytes + (how_many : number) + : Uint8Array +{ + let arr = new Uint8Array(how_many); + (new Crypto()).getRandomValues(arr); + return arr; +} + + + +/** + * Convert a byte array to a bigint + * + * Equivalent to `binary:decode_unsigned/1` from Erlang + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let n : bigint = 0n; + for (let b of bytes) { + // move first, then add + // otherwise it ends on a move + // imperative languages are for losers + n <<= 8n; + n += BigInt(b); + } + return n; +} + + + +/** + * Convert a bigint to a byte array + * + * Equivalent to `binary:encode_unsigned/1` from Erlang + * + * Requires input to be positive + */ +function +bigint_to_bytes + (q: bigint) + : Uint8Array +{ + if (q < 0n) { + throw new Error('q < 0n: ' + q); + } + + let arr_reverse = []; + while (q > 0n) { + let r = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return new Uint8Array(arr_reverse); +} + + + +/** + * Oh no, bitstrings in a language that only has bytestrings + * + * By convention these are `Uint8Array`s with byte length `ceil(bit_length / + * 8)`, and all trailing bits are zero. + */ +type bits = + {bit_length : number, + bytes : Uint8Array}; + + + +/** + * Get an uninitialized bitstring + * + * @internal + */ +function +bits_null + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 0. + */ +function +bits_zeros + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + for (let i0 = 0; + i0 < byte_length; + i0++) + { + result[i0] = 0; + } + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 1. + */ +function +bits_ones + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + + // fill everything except the last byte with 255s + for (let i0 = 0; + i0 < (byte_length - 1); + i0++) + { + result[i0] = 255; + } + + // alright so the last byte + // ok so the number of leading 0s is + // 8 - (bit_length % 8) + let num_trailing_zero_bits : number = 8 - (bit_length % 8); + // the trailing byte is 255 << that + // e.g. 3 trailing 0s + // 1111_1111 -> 1111_1000 + let last_byte : number = 255 << num_trailing_zero_bits; + let last_byte_idx0 : number = byte_length - 1; + result[last_byte_idx0] = last_byte; + + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get the bit at a given 0-index + */ +function +bits_i0th + (bit_idx0 : number, + bits : bits) + : number +{ + // first task is figuring out what byte we're at + // for instance if we want bit 27 + // 3*8 = 24 =< 27 < 4*8 + // so it's Math.floor(bit_idx0 / 8) + let byte_idx0 : number = Math.floor(bit_idx0 / 8); + // let's fetch the byte and work with that + let the_byte : number = bits.bytes[byte_idx0]; + + // ok so let's go with 27 again + // 27 = 3 mod 8 + // so we bitshift right by (8 - 3) + // and then take the remainder dividing by 2 + // --B-_---- -> ----_---B -> 0000_000B + let bsr : number = 8 - (bit_idx0 % 8); + return (the_byte >> bsr) % 2; +} + + + +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_null(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // go along each byte in result, and compute the byte boundary + for (let i = 0; + i < result_bytes.length; + i++) + { + // ABCD_EFGH _ + // 0123_4567 8 + // this is the bit index of the leftmost bit in this byte + let start_bit_bi0 : number = i * 8; + let next_start_bit_bi0 : number = start_bit_bi0 + 8; + // does the bit at the beginning of this byte correspond to the first array? + // strict comparison: + // suppose i = 0, + // suppose bit_length1 is 0 + // then this says no, go to second array + // suppose bl1 = 1, + // this says start at first array + let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length; + // weak comparison: + // suppose bit_length1 = 8 + // ABCD_EFGH _ + // 0123_4567 8 + // ^ + // start_bit_bi0 ^ next_start_bit_bi0 + let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length; + // is this a bytes1 byte + let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array; + let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array; + + // need to work out the ping_pong bs up here because js is dumb and I + // can't put lets between elseifs + // alright, now we're in the case of only copying from the second array + // we have two cases: + // ping-pong: + // ABCD_EFGH 1234_5678 + // - ---- --- + // copying these bits + // ping: + // ABCD_EFGH + // - ---- 000 + // + // how to distinguish between these two?? + // + // we're in the ping case when the start_bit_bi0 corresponds to the + // final byte of the second array + // + // ok + // + // we need to compute the bit address in the second array that + // corresponds to the bit address at the beginning of this byte in the + // result array + let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length; + // I think the variable is + // bits_left_to_copy = bits2.bit_length - bit_addr2_bi0 + // no + 1 because the current bit is uncopied, + // so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we + // have the last bit to copy + // cases: + // bits_left_to_copy <= 0 -> + // this would mean we have more bits to copy, but are out of + // source bits. should be impossible if bits_null is correct + // bits_left_to_copy <= 8 -> + // this would mean we are going to fill this last byte in the + // result array with the correct bits from array2, but how we + // do this will depend on how those are arranged in bytes2 + // (whether we grab one or two bytes) + // + // this is the tricky case + // + // I think what matters here is the byte address in the second array + // if we're on the last byte + // 8 < bits_left_to_copy -> + // this means we can safely grab two bytes from bytes2, and do + // our bitshifting to make it correct + // + // FIXME + let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0; + // no the variable that matters is which byte we're on in the result + let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8); + let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1; + let bitshift_amt : number = bits1.bit_length % 8; + + let ping : boolean = next_bytes2_addr_i0 === bytes2.length; + + + // simple case: this is a byte from the first array + // just copy it + if (is_bytes1_byte) + result_bytes[i] = bytes1[i]; + // this is a boundary byte + // further cases: + // second bit length is 0 -> + // ABCD_EF-- + // ^ starting here + // just copy first byte and move along + else if (is_boundary_byte && (bits2.bit_length === 0)) + result_bytes[i] = bytes1[i]; + // boundary byte, and there is at least one byte in the second array + else if (is_boundary_byte) + { + // copy over the first byte + result_bytes[i] = bytes1[i]; + // take the first byte from the second array + let first_byte_of_second_array : number = bytes2[0]; + // bytes1: + // ABCD_EF00 + // 6 + // bytes2: + // 1234_5678 + // 0000_0012 + // and bitshift it right by that amount + let bitshift_amt : number = bits1.bit_length % 8; + result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt; + } + // last byte of second array + else if (ping) + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt); + // ping-pong: not last byte of second array + else + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt); + } + + return {bit_length : result_bit_length, + bytes : result_bytes}; + +} diff --git a/libs/vdk_rlp/src/faert.ts b/libs/vdk_rlp/src/faert.ts new file mode 100644 index 0000000..16c4c43 --- /dev/null +++ b/libs/vdk_rlp/src/faert.ts @@ -0,0 +1,399 @@ +/** + * FÆRT: Fast Æternity Recovery Text + * + * Reference: https://gitlab.com/zxq9/passgas/-/blob/83607fedb08be5dfd03210e331f9c76125bb3467/fullofbeans + * + * @module + */ + +export { + encode, + decode, + byte_of_word, + check_word, + check_byte, + words +} + +import * as safe from './safe.js'; + + + +/** + * Encode a bytestring into FÆRT + */ +function +encode + (bytes : Uint8Array) + : string +{ + // initial accumulator + let words_acc : Array = []; + + // go along each byte and look up the word + // add it to the accumulator + for (let this_byte of bytes) + { + let this_word = words[this_byte]; + words_acc.push(this_word); + } + + // prepend check word to phrase + // yes craig it would be faster to do the check byte inline + // the usage case here is 64 byte strings + // double pass is not a big deal + return check_word(bytes) + ' ' + words_acc.join(' '); +} + + + +/** + * Decode a FAERT string into bytes + */ +function +decode + (faert : string) + : safe.Safe +{ + let all_words : Array = faert.split(' '); + let input_check_word : string = all_words[0]; + let input_words : Array = all_words.slice(1) + + // accumulator + let computed_bytes : Array = []; + + // loop over words and figure out accumulator + for (let this_input_word of input_words) + { + let maybe_this_byte : safe.Safe = byte_of_word(this_input_word); + // if the word is an allowable word, add it to the accumulator + if (maybe_this_byte.ok) + { + let this_byte : number = maybe_this_byte.result; + computed_bytes.push(this_byte); + } + // error case, propagate the error up the call chain + else + return maybe_this_byte; + } + + // at this point, we can assume we correctly decoded all words + // compute the check byte + let computed_bytes_u8s : Uint8Array = new Uint8Array(computed_bytes); + let computed_check_word : string = check_word(computed_bytes_u8s); + + // check if it is correct + // if so, return the computed bytes + if (computed_check_word === input_check_word) + return safe.ok(computed_bytes_u8s); + // otherwise, return an error + else + return safe.error('checksum failure! computed check word: ' + computed_check_word + '; input check word: ' + input_check_word); +} + + + +/** + * given a word, find its index + */ +function +byte_of_word + (word: string) + : safe.Safe +{ + for (let i=0; i<=255; i++) + { + if (word === words[i]) + return safe.ok(i); + } + return safe.error('invalid word: ' + word); +} + + + +/** + * compute the check word of an array + */ +function +check_word + (bytes: Uint8Array) + : string +{ + return words[check_byte(bytes)]; +} + + +/** + * given an array, compute the xor of all the bytes in the array + */ +function +check_byte + (bytes: Uint8Array) + : number +{ + let check_byte = 0; + for (let this_byte of bytes) + check_byte ^= this_byte; + return check_byte; +} + + + +let words = [ + "able", + "abuse", + "acquire", + "adjust", + "agent", + "air", + "alert", + "alpha", + "anger", + "answer", + "any", + "argue", + "around", + "assume", + "asthma", + "aunt", + "awkward", + "balcony", + "barrel", + "because", + "behave", + "bicycle", + "bike", + "blind", + "blouse", + "bonus", + "bottom", + "breeze", + "brother", + "bubble", + "burger", + "butter", + "can", + "cannon", + "cargo", + "catalog", + "caught", + "century", + "chaos", + "chicken", + "churn", + "cinnamon", + "clever", + "clock", + "clown", + "collect", + "conduct", + "convince", + "correct", + "cradle", + "crane", + "crime", + "cross", + "crystal", + "curve", + "daughter", + "debris", + "decrease", + "deny", + "deputy", + "despair", + "diesel", + "dinner", + "distance", + "document", + "donate", + "drama", + "drink", + "dutch", + "earth", + "educate", + "elbow", + "employ", + "endless", + "enlist", + "enter", + "equal", + "eternal", + "example", + "exhibit", + "exotic", + "faculty", + "famous", + "fault", + "feature", + "fiber", + "filter", + "firm", + "flame", + "flush", + "follow", + "forward", + "frame", + "fringe", + "future", + "game", + "gate", + "gift", + "glare", + "glow", + "goat", + "grab", + "grief", + "guilt", + "hand", + "hawk", + "health", + "hen", + "hire", + "honey", + "hover", + "hurry", + "hybrid", + "impose", + "inch", + "inherit", + "injury", + "install", + "iron", + "jazz", + "joke", + "just", + "kit", + "kitchen", + "language", + "laundry", + "leader", + "lend", + "leopard", + "license", + "live", + "lobster", + "lounge", + "magnet", + "mail", + "mansion", + "mass", + "math", + "media", + "message", + "metal", + "misery", + "mix", + "more", + "mountain", + "museum", + "mutual", + "narrow", + "nerve", + "next", + "note", + "obey", + "obtain", + "offer", + "once", + "orange", + "ostrich", + "over", + "owner", + "palace", + "patch", + "pave", + "pen", + "phrase", + "piece", + "pizza", + "plunge", + "polar", + "pool", + "power", + "pretty", + "private", + "protect", + "pulp", + "purity", + "quit", + "quote", + "raise", + "razor", + "recall", + "region", + "relief", + "rent", + "rescue", + "review", + "ride", + "risk", + "roof", + "rough", + "saddle", + "salmon", + "sand", + "scene", + "scrub", + "season", + "seek", + "series", + "shed", + "shoe", + "sick", + "silent", + "situate", + "skill", + "slide", + "slush", + "snack", + "solar", + "soul", + "special", + "sphere", + "spot", + "spy", + "stairs", + "stereo", + "strategy", + "stuff", + "success", + "sunny", + "surge", + "swear", + "symptom", + "tail", + "ten", + "tent", + "theme", + "thumb", + "tiny", + "toe", + "tooth", + "topic", + "trade", + "trash", + "trophy", + "truly", + "tumble", + "typical", + "uncle", + "unfair", + "until", + "upper", + "useless", + "van", + "venue", + "video", + "visa", + "vocal", + "walk", + "waste", + "wedding", + "weekend", + "wide", + "winner", + "wise", + "wood", + "wrist", + "zero" +] diff --git a/libs/vdk_rlp/src/rlp.ts b/libs/vdk_rlp/src/rlp.ts new file mode 100644 index 0000000..8c48b75 --- /dev/null +++ b/libs/vdk_rlp/src/rlp.ts @@ -0,0 +1,521 @@ +/** + * Ethereum Recursive-Length Prefix Encoding implementation + * + * Two reference implementations: + * + * 1. Ethereum Python implementation + * 2. My Erlang implementation (agrees with Ethereum Python in randomized + * tests) + * + * This work can be found in ../test/testgen/ + * + * FIXME: need to have Safe assertions for "i am decoding a list", "i am + * decoding a binary", "I am decoding something with no remainder", etc + * + * @module + */ + +export { + decoded_data, + decode_result, + decode, + encode +} + + +//============================================================================= +//============================================================================= +// DECODING +//============================================================================= +//============================================================================= + + +/** + * Data that RLP can encode. Also the result of decoding. + */ +type decoded_data + = Uint8Array + | Array; + + + +/** + * Decoding can have a remainder + */ +type decode_result + = {decoded_data : decoded_data, + remainder : Uint8Array}; + + + +/** + * Decode an RLP-encoded bytestring into a `decode_result` (decoded data + + * whatever wasn't consumed) + */ +function +decode + (bytes: Uint8Array) + : decode_result +{ + // check the first byte + let first_byte: number = bytes[0]; + let rest : Uint8Array = bytes.slice(1); + // if the first byte is between 0 and 127, that is the data + if + (first_byte <= 127) { + return dr(new Uint8Array([first_byte]), rest); + } + // if the first byte is between 128 and 183 = 128 + 55, it is a bytestring + // and the length is Byte - 128 + else if + (first_byte <= 183) { + let payload_byte_length : number = first_byte - 128; + let payload : Uint8Array = rest.slice(0, payload_byte_length); + let rest2 : Uint8Array = rest.slice(payload_byte_length); + return dr(payload, rest2); + } + // if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a + // bytestring. the byte length of bytestring is FirstByte - 183. Then pull + // out the actual data + else if + (first_byte <= 191) { + let byte_length_of_byte_length : number = first_byte - 183; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(bytes, rest2); + } + // If the first byte is between 192 and 247 = 192 + 55, it is a list. The + // byte length of the list-payload is FirstByte - 192. Then the list + // payload, which needs to be decoded on its own. + else if + (first_byte <= 247) { + let byte_length_of_list : number = first_byte - 192; + let list_payload : Uint8Array = rest.slice(0, byte_length_of_list); + let list : Array = decode_list(list_payload); + let rest2 : Uint8Array = rest.slice(byte_length_of_list); + return dr(list, rest2); + } + // If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a + // list. The byte length of the byte length of the list-payload is + // FirstByte - 247. Then the byte length of the list. Then the list + // payload, which needs to be decoded on its own. + else { + let byte_length_of_byte_length : number = first_byte - 247; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let list : Array = decode_list(list_bytes); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(list, rest2); + } +} + + + +/** + * Decode a list payload (non-prefixed) into an `Array`. + * + * Repeatedly decodes an element off the list until remainder is empty. + * + * @internal + */ +function +decode_list + (bytes: Uint8Array) + : Array +{ + let arr : Array = []; + while (bytes.length > 0) { + // grab an item off the bytes + let {decoded_data, remainder} = decode(bytes); + // push it + arr.push(decoded_data); + // update bytes + bytes = remainder; + } + return arr; +} + + + +/** + * Convert bytestring to number + * + * @internal + */ +function +bytes_to_number + (bytes: Uint8Array) + : number +{ + let n : number = 0; + for (let b of bytes) + { + n <<= 8; + n += b; + } + return n; +} + + + +/** + * Make a `decode_result` containing the two arguments + * + * @internal + */ +function +dr + (x : decoded_data, + y : Uint8Array) + : decode_result +{ + return {decoded_data : x, + remainder : y}; +} + + + +//============================================================================= +//============================================================================= +// ENCODING +//============================================================================= +//============================================================================= + +/** + * Encode some decoded data + */ +function +encode + (data: decoded_data) + : Uint8Array +{ + // is it an array or data + if + (is_binary(data)) { + return encode_binary(data as Uint8Array); + } + else if + (is_list(data)) { + return encode_list(data as Array); + } + else { + throw new Error('encode told to encode something that is not an array or a binary: ' + data); + } +} + + + +/** + * Encode a binary into RLP + * + * @internal + */ +function +encode_binary + (bytes: Uint8Array) + : Uint8Array +{ + let len: number = bytes.length; + // single byte case when the byte is between 0..127 + // result is the bytestring containing the byte itself + if + ((len === 1) && + (bytes[0] <= 127)){ + return bytes; + } + // if the bytestring is 0..55 bytes long, the first byte in the result is + // 128 + Length, the rest of the result bytestring is the input string + else if + (len <= 55) { + // construct the result + // <<128 + Len, Bytes/binary>> + let result : Uint8Array = new Uint8Array(len + 1); + // first byte is 128 + length + result[0] = 128 + len; + // copy input bytes into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + let result_idx0 : number = input_idx0 + 1; + result[result_idx0] = bytes[input_idx0]; + } + return result; + } + // if the bytestring is more than 55 bytes long, the first byte is 183 + + // ByteLengthOfByteLength, followed by the byte length, followed by the + // bytes + else { + let len_bytes : Uint8Array = encode_unsigned(len); + let len_bytes_length : number = len_bytes.length; + // total array is + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len); + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + result[0] = 183 + len_bytes_length; + // copy len_bytes into result + for (let len_bytes_idx0 = 0; + len_bytes_idx0 < len_bytes_length; + len_bytes_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = len_bytes_idx0 + 1; + result[result_idx0] = len_bytes[len_bytes_idx0]; + } + // copy original byte array into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = input_idx0 + (1 + len_bytes_length); + result[result_idx0] = bytes[input_idx0]; + } + // finally, return the result + return result; + } +} + + + +/** + * Encode a list + * + * @internal + */ +function +encode_list + (list: Array) + : Uint8Array +{ + // first encode every element in the list, then branch + let payloads : Array = list.map(encode); + let payload : Uint8Array = uint8arr_concat(payloads); + let payload_size : number = payload.length; + // if the payload size is in 0..55, then the first byte is 192 + payload + // size, followed by the payload + if + (payload_size <= 55) { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + let result : Uint8Array = new Uint8Array(1 + payload_size); + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + result[0] = 192 + payload_size; + // copy the rest of the payload into result + for (let payload_idx0 = 0; + payload_idx0 < payload_size; + payload_idx0++) + { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + let result_idx0: number = payload_idx0 + 1; + result[result_idx0] = payload[payload_idx0]; + } + return result; + } + // if the payload size is greater than 55, the first byte is 247 + + // size_of_payload_size, followed by the payload size, followed by the + // payload + else { + // compute the payload size size + let payload_size_bytes : Uint8Array = encode_unsigned(payload_size); + let payload_size_size : number = payload_size_bytes.length; + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size); + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // first byte is 247 + payload_size_size + result[0] = 247 + payload_size_size; + // copy the payload_size_bytes into result + for (let psb_idx0 = 0; + psb_idx0 < payload_size_size; + psb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 byte for this + let result_idx0 : number = psb_idx0 + 1; + result[result_idx0] = payload_size_bytes[psb_idx0]; + } + // copy the payload into result + for (let pb_idx0 = 0; + pb_idx0 < payload_size; + pb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 + payload_size_size bytes for this + let result_idx0 : number = pb_idx0 + (1 + payload_size_size); + result[result_idx0] = payload[pb_idx0]; + } + // finally, return result + return result; + } +} + + + +/** + * Encode a number as a bytestring + * + * Not for general use, this assumes the input number is greater than 55 + * + * @internal + */ +function +encode_unsigned + (n: number) + : Uint8Array +{ + // can assume that n initially is greater than 55 + // have to encode it in reverse order + let arr : Array = []; + // repeated division by 256 with remainder + // + // example: suppose bign was 1234 and we were dividing by 10 + // + // iteration 0: + // bign = 1234 + // arr = [] + // --- + // bign / 10 = 123 + // bign % 10 = 4 + // --- + // bign = 123 + // arr = [4] + // + // iteration 1: + // bign = 123 + // arr = [4] + // --- + // bign / 10 = 12 + // bign % 10 = 3 + // --- + // bign = 12 + // arr = [4, 3] + // + // iteration 2: + // bign = 12 + // arr = [4, 3] + // --- + // bign / 10 = 1 + // bign % 10 = 2 + // --- + // bign = 1 + // arr = [4, 3, 2] + // + // iteration 3: + // bign = 1 + // arr = [4, 3, 2] + // --- + // bign / 10 = 0 + // bign % 10 = 1 + // --- + // bign = 0 + // arr = [4, 3, 2, 1] + // + // iteration 4: + // bign = 0 + // arr = [4, 3, 2, 1] + // --- + // DONE + while (n > 0) { + arr.push(n % 256); + n >>= 8; + } + // reverse and make into Uint8Array + arr.reverse(); + return new Uint8Array(arr); +} + + + +/** + * concatenate an array of `Uint8Array`s into a single Uint8Array + * + * @internal + */ +function +uint8arr_concat + (arrs: Array) + : Uint8Array +{ + // total length + let total_len = arrs.reduce(// fold + function (acc_len: number, this_uint8array: Uint8Array): number { + return acc_len + this_uint8array.length; + }, + // initial accumulator + 0); + // start up result + let result : Uint8Array = new Uint8Array(total_len); + let result_idx0 : number = 0; + // concatenate + for (let this_uint8arr of arrs) { + // bytewise copy of this uint8array + for (let this_uint8arr_idx0 = 0; + this_uint8arr_idx0 < this_uint8arr.length; + this_uint8arr_idx0++) + { + // copy byte and increment result idx0 + result[result_idx0] = this_uint8arr[this_uint8arr_idx0]; + result_idx0++; + } + } + return result; +} + +/** + * returns true if input is `instanceof Uint8Array` + * + * @internal + */ +function +is_binary + (x: any) + : boolean +{ + return (x instanceof Uint8Array); +} + + + +/** + * returns true if input is `instanceof Array` + * + * @internal + */ +function +is_list + (x: any) + : boolean +{ + return (x instanceof Array); +} diff --git a/libs/vdk_rlp/src/safe.ts b/libs/vdk_rlp/src/safe.ts new file mode 100644 index 0000000..b3e4c0c --- /dev/null +++ b/libs/vdk_rlp/src/safe.ts @@ -0,0 +1,112 @@ +/** + * "Safe" error handling + * + * The idea here is that there are situations where it is known + * + * ```typescript + * type Safe + * = Ok + * | Error; + * + * type Ok + * = {ok : true, + * result : ok_t}; + * + * type Error + * = {ok : false, + * error : err_t}; + * ``` + * + * 1. a given function call is likely to fail + * 2. the likely errors can be enumerated + * + * These are called "positive errors". An example would be a page script + * asking a browser wallet extension to sign a transaction. The following + * errors, among others, are likely: + * + * - the user does not have a wallet installed + * - the user has a wallet but does not have the correct signing key + * - the user rejects the transaction + * - the sign request timed out + * + * These errors should not generate exceptions, as these behaviors are to some + * degree "expected". + */ + +export { + Safe, + Ok, + Error, + ok, + error, + unsafe +} + + +/** + * Type that catches positive errors + */ +type Safe + = Ok + | Error; + + +/** + * Ok type + */ +type Ok + = {ok : true, + result : ok_t}; + + +/** + * Error type + */ +type Error + = {ok : false, + error : err_t}; + + +/** + * Constructs an `Ok` value from a pure value + */ +function +ok + + (x : ok_t) + : Ok +{ + return {ok: true, result: x}; +} + + + +/** + * Constructs an `Error` value from a pure value + */ +function +error + + (x: err_t) + : Error +{ + return {ok: false, error: x}; +} + + + +/** + * Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the + * `err_t` + */ +function +unsafe + + (x: Safe) + : ok_t +{ + if (x.ok) + return x.result; + else + throw x.error; +} diff --git a/libs/vdk_rlp/tsconfig.json b/libs/vdk_rlp/tsconfig.json new file mode 100644 index 0000000..f240cdc --- /dev/null +++ b/libs/vdk_rlp/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/jex_include"], + "composite" : true} diff --git a/libs/vdk_safe/.gitignore b/libs/vdk_safe/.gitignore new file mode 100644 index 0000000..3967e48 --- /dev/null +++ b/libs/vdk_safe/.gitignore @@ -0,0 +1,10 @@ +dist +jex_mindist +jex_include +*.beam +*.swp +*.swo +test/testgen/b58_cases_2.eterms +test/testgen/b58_cases_3.eterms +__pycache__ +docs/ diff --git a/libs/vdk_safe/jex.eterms b/libs/vdk_safe/jex.eterms new file mode 100644 index 0000000..6c420f1 --- /dev/null +++ b/libs/vdk_safe/jex.eterms @@ -0,0 +1,5 @@ +{type, library}. +{realm, local}. +{name, vdk_safe}. +{version, "0.1.0"}. +{deps, []}. diff --git a/libs/vdk_safe/notes b/libs/vdk_safe/notes new file mode 100644 index 0000000..97c0e0c --- /dev/null +++ b/libs/vdk_safe/notes @@ -0,0 +1,24 @@ +done: +- base64/base58 encoding/decoding +- rlp + +today: +- pull apart data + +tomorrow: +- basic easy serialization/deserialization +- serialize/deserialize examples +- (maybe) pull in awcp/sidekick +- sidekick: sign/propagate and sign/noprop + +later: + +- general node querying with parasite (think about autogeneration) +- simple aetto/ae/etc unit converter +- ghetto sophia ide +- general "send money to someone" page + +GUI: + +- simple testnet explorer +- contract ide diff --git a/libs/vdk_safe/scratch/anth.ts b/libs/vdk_safe/scratch/anth.ts new file mode 100644 index 0000000..6117067 --- /dev/null +++ b/libs/vdk_safe/scratch/anth.ts @@ -0,0 +1,403 @@ +/** + * Node API constructor/deconstructor + * + * This is similar to serialization/deserialization, but not quite the same + * thing. It converts back and forth between different forms of + * "api-serialized" data. + * + * References: + * 1. https://github.com/aeternity/protocol/blob/master/serializations.md + * 2. https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md + * + * ## General type rules + * + * ``` + * ERLANG TYPE | JS TYPE + * ------------------------------- + * integer | bigint + * list | Array + * binary | Uint8Array + * ``` + * + * # Example + * + * We start with the string `tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==`. + * + * We can tell by the `tx_` prefix that this represents transaction data of + * some sort. But the rest of the data is totally opaque. The task of this + * module is to "humanize" that `tx_...` string and show what data is contained + * in the rest of it. + * + * The remainder of the string is a base64-encoded bytestring + * + * ```erlang + * 3> io:format("~tw~n", [base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)]). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35,118,68,68,244,48,85,218,93,85,120,164,154,55,90,250,24,220,161,1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234,89,203,191,216,108,88,186,7,3,234,139,205,10,184,167,108,10,134,15,38,245,97,200,0,0,9,135,104,97,105,110,97,110,97,3,182,66,215>> + * ``` + * + * That bytestring contains data encoded using Ethereum's RLP codec. Luckily, I + * wrote an RLP decoder. RLP has two types of data: binaries, and + * arbitrary-depth (possibly empty) lists of binaries. + * + * ```erlang + * -type decoded_data() :: binary() | [decoded_data()]. + * + * -spec decode(RLP) -> {Data, Rest} + * when RLP :: binary(), + * Data :: decoded_data(), + * Rest :: binary(). + * ``` + * + * ```erlang + * 2> rlp:decode(base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>)). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * As expected, we get back the return tuple `{Data, Rest}`. `Rest` is the double-sha256 of the beginning + * + * ```erlang + * 3> X = base64:decode(<<"+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==">>). + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 4> SizeX = byte_size(X). + * 94 + * 6> <> = X. + * <<248,88,12,1,161,1,201,99,126,70,0,231,171,59,194,230, + * 127,198,64,232,35,118,68,68,244,48,85,218,93,...>> + * 10> <> = crypto:hash(sha256, crypto:hash(sha256, RLPEncodedData)). + * <<3,182,66,215,195,99,112,99,25,7,84,31,151,188,149,81, + * 189,184,82,207,164,68,128,43,11,174,236,59,77,...>> + * 11> Hash. + * <<3,182,66,215>> + * 12> Check. + * <<3,182,66,215>> + * ``` + * + * What we're really interested in is `Data` + * + * ```erlang + * 14> {Data, _} = rlp:decode(X). + * {[<<"\f">>, + * <<1>>, + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,...>>, + * <<1,123,102,230,195,5,7,8,54,228,233,121,32,82,61,195,234, + * 89,203,191,216,108,88,186,...>>, + * <<"\n">>, + * <<15,38,245,97,200,0>>, + * <<0>>, + * <<"\t">>,<<"hainana">>], + * <<3,182,66,215>>} + * ``` + * + * `Data` is a list. The first field `<<"\f">>` is meant to be an integer which + * tells us what type of data this is. + * + * ```erlang + * 16> $\f. + * 12 + * ``` + * + * If we look at our table + * (https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags), + * we see that a value of `12` is a spend transaction. + * + * The second field `<<1>>` tells us the "version" of the field orderings, + * which we can ignore for now. + * + * The remaining fields are the fields of a spend transaction (https://github.com/aeternity/protocol/blob/master/serializations.md#spend-transaction) + * + * ```erlang + * [ :: id() % <<1,201,99,126,...> "=" "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE" + * , :: id() % <<1,123,102,230,...> "=" "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * , :: int() % <<"\n">> "=" 10 + * , :: int() % <<15,38,245,97,200,0>> "=" 16_660_000_000_000 + * , :: int() % <<0>> "=" 0 + * , :: int() % <<"\t">> "=" 9 + * , :: binary() % <<"hainana">> "=" "hainana" + * ] + * ``` + * + * Our task here is to be able to pull apart the "tx_..." string into its fields. + * + * Converting the binaries to integers is pretty trivial. The only mildly + * annoying thing is the `id` type. + * + * `id`s have two fields: a single-byte prefix which says which type of ID it + * is. In this case, both `id`s have a prefix of `1`, which means they are both + * normal accounts (hence the `ak_` prefix on the "api-encoded" id). The other + * options are oracles (prefix `4`/`ok_`), contracts (prefix `5`/`ct_`), or + * names (prefix `2`/`nm_`) + * + * To "api-encode" the name, we first pick the appropriate prefix based on the + * first byte (in this case `1 -> "ak_"). The remaining 32 bytes are then + * double-SHA'd to get the 4-byte check suffix + * + * ```erlang + * 30> SenderBytes = lists:nth(3, Data). + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 31> <<1, SenderAddrBytes/binary>> = SenderBytes. + * <<1,201,99,126,70,0,231,171,59,194,230,127,198,64,232,35, + * 118,68,68,244,48,85,218,93,85,120,164,154,55,...>> + * 32> DoubleSha = fun(Bytes) -> <> = crypto:hash(sha256, crypto:hash(sha256, Bytes)), Foo end. + * #Fun + * 33> "ak_" ++ b58:enc(<>). + * "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(<>). + * "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv" + * ``` + * + * ```js + * > anth.deconstruct("tx_+FgMAaEByWN+RgDnqzvC5n/GQOgjdkRE9DBV2l1VeKSaN1r6GNyhAXtm5sMFBwg25Ol5IFI9w+pZy7/YbFi6BwPqi80KuKdsCoYPJvVhyAAACYdoYWluYW5hA7ZC1w==") + * {tag : 'SpendTx', + * version : 1n, + * fields : {sender : "ak_2XhCkjzTwcq1coXSSzHJoMZkUzTwnjH88zmPGkkowUsFNTo9UE", + * recipient : "ak_wM8yFU8eSETXU7VSN48HMDmevGoCMiuveQZgkPuRn1nTiRqyv", + * amount : 10n, + * fee : 16660000000000n, + * ttl : 0n, + * nonce : 9n, + * payload : Uint8Array([104, 97, 105, 110, 97, 110, 97])}} + * ``` + * + * @module + */ + +export { + // types + tx_str, + deconstructed_tx, + // functions + deconstruct_tx +}; + +import * as b64 from './b64.js' +import * as bin from './bin.js' +import * as rlp from './rlp.js' + + +/** + * Alias type for a `tx_...` string + */ +type tx_str = string; + +/** + * Alias type for a `sg_...` string + */ +type sg_str = string; + +/** + * types of decoded tx we currently support + * @internal + */ +type tx_type_str + = 'SignedTx' + | 'SpendTx' + | 'ContractCreateTx' + | 'ContractCallTx'; + + +/** + * Results of deconstruct_tx + */ +type deconstructed_tx + = {type : 'SignedTx', + version : bigint, + fields : fields_SignedTx} + | {type : 'SpendTx', + version : bigint, + fields : fields_SpendTx} + | {type : 'ContractCreateTx', + version : bigint, + fields : fields_ContractCreateTx} + | {type : 'ContractCallTx' + version : bigint, + fields : fields_ContractCallTx}; + +/** + * Convenient type alias + * + * @internal + */ +type rlpdata = rlp.decoded_data; + + +/** + * Fields types + */ +type fields + = fields_SignedTx + | fields_SpendTx + | fields_ContractCreateTx + | fields_ContractCallTx; + +type fields_SignedTx + = {signatures : Array, + transaction : tx_str}; + +type fields_SpendTx + = {sender : string, + recipient : string, + amount : bigint, + fee : bigint, + ttl : bigint, + nonce : bigint, + payload : Uint8Array}; + +/** + * Deconstruct a Tx + */ +function +deconstruct_tx + (tx_str: tx_str) + : deconstructed_tx +{ + let b64_str : string = tx_str.slice(3); // tx_[...] -> [...] + let tx_rlp_encoded : Uint8Array = b64.decode(b64_str); // [...] -> bytes + let tx_data : Array = shasha_rlp_decode_list(tx_rlp_encoded); // decode data and check the double-sha thing + let tx_type : bigint = bin.bytes_to_bigint(tx_data[0]); // get a bigint + let tts : tx_type_str = tx_type_str(tx_type); + let tx_version : bigint = bin.bytes_to_bigint(tx_data[1]); + let tx_fields : fields = deconstruct_fields(tts, tx_version, tx_data.slice(2)); + return {type : tts, + version : tx_version, + fields : tx_fields}; +} + + +/** + * Data that's "api-encoded" goes through the following stages: + * + * 1. data structure -> rlp decode data (arbitrary-depth [possibly 0] list of bytestrings) + * 2. rlp decode data -> bytestring + * 3. bytestring -> <> + * 4. HashedBytestring -> base64/base58 string encoding + * 5. Add string prefix + * + * This function undoes step 3 and step 2, returns back the rlp decode data + * + * FIXME: Does not check double-sha (yet); need to figure out way to handle hash failures + * FIXME: No good way to handle failure cases + * + * @internal + */ +function +shasha_rlp_decode_list + (hashed_bs : Uint8Array) + : Array +{ + let len = hashed_bs.length; + let bytes = hashed_bs.slice(0, len - 4); + let {decoded_data} = rlp.decode(bytes); + return (decoded_data as Array); +} + + + +/** + * Convert an object tag that's a type of transaction to the type string + * + * See: https://github.com/aeternity/protocol/blob/master/serializations.md#table-of-object-tags + * + * @internal + */ +function +tx_type_str + (tx_type_int : bigint) + : tx_type_str +{ + switch (tx_type_int) + { + case 11n: return 'SignedTx'; + case 12n: return 'SpendTx'; + case 42n: return 'ContractCreateTx'; + case 43n: return 'ContractCallTx'; + default: throw new Error('invalid transaction type: ' + tx_type_int); + } +} + + + +/** + * Given an array of data decoded from RLP, convert it to the fields, as + * appropriate as given by the tx type string and the version + */ +function +deconstruct_fields + (tx_type_str : tx_type_str, + tx_version : bigint, + tx_rawfields : Array) + : fields + { + switch (tx_type_str) + { + // case 'SignedTx' : return deconstruct_fields_SignedTx(tx_rawfields); + case 'SpendTx' : return deconstruct_fields_SpendTx(tx_rawfields); + // case 'ContractCreateTx' : return deconstruct_fields_ContractCreateTx(tx_rawfields); + // case 'ContractCallTx' : return deconstruct_fields_ContractCallTx(tx_rawfields); + default : throw new Error('invalid tx type str: ' + tx_type_str); + } + } + + +// TODO: do all this in Erlang + function + deconstruct_fields_SpendTx + (fields: Array) + : fields_SpendTx +{ + let sender_bytes = fields[0]; + let recip_bytes = fields[1]; + let amount_bytes = fields[2]; + let fee_bytes = fields[3]; + let ttl_bytes = fields[4]; + let nonce_bytes = fields[5]; + let payload_bytes = fields[6]; + return {sender : encode_id(sender_bytes), + recipient : encode_id(sender_bytes), + amount : bin.bytes_to_bigint(amount_bytes), + fee : bin.bytes_to_bigint(fee_bytes), + ttl : bin.bytes_to_bigint(ttl_bytes), + nonce : bin.bytes_to_bigint(nonce_bytes), + payload : bin.bytes_to_bigint(payload_bytes)}; +} + + +/** + * Convert a binary account/name/etc binary id into the appropriate type of string + * + * @internal + */ +function +encode_id + (id: Uint8Array) + : string +{ + throw new Error('nyi'); +} + +/* +FIXME: +1. work out all this in Erlang to clear conceptual goo +2. think about how i want type safety etc to work +3. think about a language to assert that the data has the correct shape to it +4. get some examples working in Erlang +5. convert erlang code back to ts +*/ diff --git a/libs/vdk_safe/scratch/bin.ts b/libs/vdk_safe/scratch/bin.ts new file mode 100644 index 0000000..47ad66a --- /dev/null +++ b/libs/vdk_safe/scratch/bin.ts @@ -0,0 +1,112 @@ +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_zeros(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // alright so + // we can start by copying the first bytes into result bytes + for (let bytes1_idx0 = 0; + bytes1_idx0 < bytes1.length; + bytes1_idx0++) + { + result_bytes[bytes1_idx0] = bytes1[bytes1_idx0]; + } + + // next + // we need to calculate the left-shift offset + // this will be 8 - (bytes1.bit_length % 8) + let num_trailing_zeros_in_first_array : number = 8 - (bits1.bit_length % 8); + // so + // bytes1: ABCD_EF00 + // bytes2: GH12_3000 + // result: ABCD_EFGH 1230_0000 + // ah ok, so we need to for each byte in the second array + // take the first however many bits, xor it with the existing byte + // then take the last however many bits and place them into the next byte + // this is super confusing but + // ABCD_EF00 + // GH12_3456 + // operation: + // ABCD_EF00 + // xor 0000_00GH + // = ABCD_EFGH 1234_5600 + // + // then on the next iteration + // 1234_5600 + // abcd_efgh + // -> + // 1234_56ab cdef_gh00 + // + // ah so there's a pattern + // however many trailing 0s there are in the first array + // say there's 2 + // we take the first 2 bits of the upcoming byte + // xor that against the current byte + // take the last 6 bits of the upcoming byte + // set the next byte to that + // + // have to think about edge behavior + // this is ripe for off-by-1 errors + // but i think the general idea is right + // + // so we start the iteration + // on the last byte of the first array + let last_byte_of_first_array_idx0 : number = bytes1.length - 1; + // and we end + // on the second-to-last-byte of the result array + let second_to_last_byte_of_result_array_idx0 : number = result_bytes.length - 2; + // the reason we do that is because we're doing this is because we are + // going along, xoring against the current byte and then setting the next + // byte + // + // ok so + for (let this_result_byte_idx0 = last_byte_of_first_array_idx0; + this_result_byte_idx0 <= second_to_last_byte_of_result_array_idx0; + this_result_byte_idx0++) + { + let this_result_byte : number = result_bytes[this_result_byte_idx0]; + + // ok here we need to fish out the relevant byte of the second array + // gaaah + // so this will be 0 at the start of the loop + let relevant_byte_of_second_array_idx0 : number = this_result_byte_idx0 - last_byte_of_first_array_idx0; + let relevant_byte_of_second_array : number = bytes2[relevant_byte_of_second_array_idx0]; + + // ok so let's fish out the leading digits + // the number of leading digits is the number of trailing 0s in the first array + let num_leading_digits : number = num_trailing_zeros_in_first_array; + let num_trailing_digits : number = 8 - num_leading_digits; + + // suppose there are 2 leading digits and 6 trailing digits + // ABCD_EFGH + // leading digits are + // ABCD_EFGH >> 6 = 0000_00AB + // trailing digits are + // (ABCD_EFGH << 2) % 255 = CDEF_GH00 + let leading_digits : number = relevant_byte_of_second_array >> num_trailing_digits; + let trailing_digits : number = (relevant_byte_of_second_array << num_leading_digits) % 255; + + // xor the current byte against the leading digits + let new_this_result_byte : number = this_result_byte ^ leading_digits; + result_bytes[this_result_byte_idx0] = new_this_result_byte; + + // set the next byte to the trailing digits + result_bytes[this_result_byte_idx0 + 1] = trailing_digits; + } + + // i think we're done + return {bit_length : result_bit_length, + bytes : result_bytes}; +} + diff --git a/libs/vdk_safe/scratch/scratch.txt b/libs/vdk_safe/scratch/scratch.txt new file mode 100644 index 0000000..9213abc --- /dev/null +++ b/libs/vdk_safe/scratch/scratch.txt @@ -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, + 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); // <> + let rlp_stuff : Uint8Array = stuff.slice(0, stuff.length - 4); // <> + let decoded_datas : Array = rlp.decode(rlp_stuff).decoded_data as Array; // 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); // <> = TagBytes + let vsn : Uint8Array = decoded_datas[1] as Uint8Array; + // tx fields + let tx_fields : Array = 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): 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): SignedTx { + throw new Error('nyi'); +} + + +/** + * Decode a SpendTx + * + * @internal + */ +function +decode_fields_SpendTx(fields: Array): SpendTx { + // [ :: id(), + // :: id(), + // :: int(), + // :: int(), + // :: int(), + // :: int(), + // :: 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): ContractCreateTx { + throw new Error('nyi'); +} + +function +decode_fields_ContractCallTx(fields: Array): 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)}; +} diff --git a/libs/vdk_safe/scratch/sp.ts b/libs/vdk_safe/scratch/sp.ts new file mode 100644 index 0000000..8448945 --- /dev/null +++ b/libs/vdk_safe/scratch/sp.ts @@ -0,0 +1,23 @@ +/** + * Vanillae Seed Phrase Library + * + * Refs: + * 1. BIP 39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki + * + * @module + */ + + +/** + * Get a given number of seed bits. + * + * `how_many` must be a multiple of 33. + * + * Ref: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic + */ +function +seed_and_check_bits + (how_many : number) + : Uint8Array +{ +} diff --git a/libs/vdk_safe/src/vdk_safe.ts b/libs/vdk_safe/src/vdk_safe.ts new file mode 100644 index 0000000..1918e93 --- /dev/null +++ b/libs/vdk_safe/src/vdk_safe.ts @@ -0,0 +1,112 @@ +/** + * "Safe" error handling + * + * The idea here is that there are situations where it is known + * + * 1. a given function call is likely to fail + * 2. the likely errors can be enumerated + * + * ```typescript + * type Safe + * = Ok + * | Error; + * + * type Ok + * = {ok : true, + * result : ok_t}; + * + * type Error + * = {ok : false, + * error : err_t}; + * ``` + * + * These are called "positive errors". An example would be a page script + * asking a browser wallet extension to sign a transaction. The following + * errors, among others, are likely: + * + * - the user does not have a wallet installed + * - the user has a wallet but does not have the correct signing key + * - the user rejects the transaction + * - the sign request timed out + * + * These errors should not generate exceptions, as these behaviors are to some + * degree "expected". + */ + +export { + Safe, + Ok, + Error, + ok, + error, + unsafe +} + + +/** + * Type that catches positive errors + */ +type Safe + = Ok + | Error; + + +/** + * Ok type + */ +type Ok + = {ok : true, + result : ok_t}; + + +/** + * Error type + */ +type Error + = {ok : false, + error : err_t}; + + +/** + * Constructs an `Ok` value from a pure value + */ +function +ok + + (x : ok_t) + : Ok +{ + return {ok: true, result: x}; +} + + + +/** + * Constructs an `Error` value from a pure value + */ +function +error + + (x: err_t) + : Error +{ + return {ok: false, error: x}; +} + + + +/** + * Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the + * `err_t` + */ +function +unsafe + + (x: Safe) + : ok_t +{ + if (x.ok) + return x.result; + else + throw x.error; +} diff --git a/libs/vdk_safe/tsconfig.json b/libs/vdk_safe/tsconfig.json new file mode 100644 index 0000000..f240cdc --- /dev/null +++ b/libs/vdk_safe/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/jex_include"], + "composite" : true} diff --git a/libs/vdk_cases/dist/cases.d.ts b/libs/vdk_tests_cases/dist/cases.d.ts similarity index 100% rename from libs/vdk_cases/dist/cases.d.ts rename to libs/vdk_tests_cases/dist/cases.d.ts diff --git a/libs/vdk_cases/dist/cases.js b/libs/vdk_tests_cases/dist/cases.js similarity index 100% rename from libs/vdk_cases/dist/cases.js rename to libs/vdk_tests_cases/dist/cases.js diff --git a/libs/vdk_cases/dist/cases.js.map b/libs/vdk_tests_cases/dist/cases.js.map similarity index 100% rename from libs/vdk_cases/dist/cases.js.map rename to libs/vdk_tests_cases/dist/cases.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js diff --git a/libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map similarity index 100% rename from libs/vdk_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map rename to libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts new file mode 100644 index 0000000..1fd30b8 --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b58.ts @@ -0,0 +1,418 @@ +/** + * Base58 encoding/decoding + */ + +export { + encode, + decode +} + + + +//============================================================================= +// ENCODING +//============================================================================= + +/** + * Encode a Uint8Array into base58 + */ +function +encode + (binary : Uint8Array) + : string +{ + let num_leading_zeros : number = nlz(binary); + let rest : Uint8Array = binary.slice(num_leading_zeros); + let ones : string = encode_zeros(num_leading_zeros); + let rest_b58 : string = encode_rest(rest); + let result : string = ones + rest_b58; + return result; +} + + + +/** + * count the number of leading zeros in a uint8array + * + * @internal + */ +function +nlz + (bytes: Uint8Array) + : number +{ + let n = 0; + for (let this_byte of bytes) + { + if (0 === this_byte) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '1's for however many leading zeros there are + * + * @internal + */ +function +encode_zeros + (how_many : number) + : string +{ + let ones : string = ''; + for (let i = 1; + i <= how_many; + i++) + { + ones += '1'; + } + + return ones; +} + + + +/** + * Encode a Uint8Array that has no leading zeros + * + * @internal + */ +function +encode_rest + (bytes : Uint8Array) + : string +{ + let bytes_bignum : bigint = bytes_to_bigint(bytes); + let result : string = bignum_to_base58(bytes_bignum); + return result; +} + + + +/** + * Convert a bytestring to a bignum + * + * @internal + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_byte of bytes) + { + acc_bigint <<= 8n; + acc_bigint += BigInt(this_byte); + } + return acc_bigint; +} + + + +/** + * Convert a BigInt to Base58 + * + * @internal + */ +function +bignum_to_base58 + (q: bigint) + : string +{ + let s = ''; + while (q !== 0n) + { + let this_n : bigint = q % 58n; + q /= 58n; + + let this_b58_char : string = bigint_to_char(this_n); + s = this_b58_char + s; + } + return s; +} + + + +//============================================================================= +// DECODING +//============================================================================= + +/** + * Decode a Base58 string into a Uint8Array + */ +function +decode + (base58: string) + : Uint8Array +{ + let num_leading_ones : number = nlo(base58); + let rest : string = base58.slice(num_leading_ones); + let zeros : Array = decode_ones(num_leading_ones); + let rest_arr : Array = decode_rest(rest); + let pre_result : Array = zeros.concat(rest_arr); + return new Uint8Array(pre_result); +} + + + +/** + * count the number of leading 1 characters in a uint8array + * + * @internal + */ +function +nlo + (base58: string) + : number +{ + let n = 0; + for (let this_char of base58) + { + if ('1' === this_char) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '0's for however many leading ones there are + * + * @internal + */ +function +decode_ones + (how_many : number) + : Array +{ + let zeros : Array = []; + for (let i = 1; + i <= how_many; + i++) + { + zeros.push(0); + } + + return zeros; +} + + + +/** + * Decode a string that has no leading 1s + * + * @internal + */ +function +decode_rest + (base58: string) + : Array +{ + let result_bignum : bigint = base58_to_bigint(base58); + let result : Array = bigint_to_base256(result_bignum); + return result; +} + + + +/** + * Convert a base58 string to a bignum + * + * @internal + */ +function +base58_to_bigint + (base58: string) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_char of base58) + { + acc_bigint *= 58n; + acc_bigint += char_to_bigint(this_char); + } + return acc_bigint; +} + + +/** + * convert a bignum into a byte array + * + * @end + */ +function +bigint_to_base256 + (q: bigint) + : Array +{ + let arr_reverse = []; + while(q !== 0n) + { + let r: number = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return arr_reverse; +} + + +//============================================================================= +// TRANSLATION TABLES +//============================================================================= + + +/** + * Base58 integer -> character conversion table + * + * @internal + */ +function +bigint_to_char + (n: bigint) + : string +{ + switch(n) { + case 0n: return '1'; + case 1n: return '2'; + case 2n: return '3'; + case 3n: return '4'; + case 4n: return '5'; + case 5n: return '6'; + case 6n: return '7'; + case 7n: return '8'; + case 8n: return '9'; + case 9n: return 'A'; + case 10n: return 'B'; + case 11n: return 'C'; + case 12n: return 'D'; + case 13n: return 'E'; + case 14n: return 'F'; + case 15n: return 'G'; + case 16n: return 'H'; + case 17n: return 'J'; + case 18n: return 'K'; + case 19n: return 'L'; + case 20n: return 'M'; + case 21n: return 'N'; + case 22n: return 'P'; + case 23n: return 'Q'; + case 24n: return 'R'; + case 25n: return 'S'; + case 26n: return 'T'; + case 27n: return 'U'; + case 28n: return 'V'; + case 29n: return 'W'; + case 30n: return 'X'; + case 31n: return 'Y'; + case 32n: return 'Z'; + case 33n: return 'a'; + case 34n: return 'b'; + case 35n: return 'c'; + case 36n: return 'd'; + case 37n: return 'e'; + case 38n: return 'f'; + case 39n: return 'g'; + case 40n: return 'h'; + case 41n: return 'i'; + case 42n: return 'j'; + case 43n: return 'k'; + case 44n: return 'm'; + case 45n: return 'n'; + case 46n: return 'o'; + case 47n: return 'p'; + case 48n: return 'q'; + case 49n: return 'r'; + case 50n: return 's'; + case 51n: return 't'; + case 52n: return 'u'; + case 53n: return 'v'; + case 54n: return 'w'; + case 55n: return 'x'; + case 56n: return 'y'; + case 57n: return 'z'; + default: + throw new Error('invalid base58 bigint: ' + n); + } +} + + + +/** + * Base58 character -> integer conversion table + * + * @internal + */ +function +char_to_bigint + (s: string) + : bigint +{ + switch(s) { + case '1': return 0n; + case '2': return 1n; + case '3': return 2n; + case '4': return 3n; + case '5': return 4n; + case '6': return 5n; + case '7': return 6n; + case '8': return 7n; + case '9': return 8n; + case 'A': return 9n; + case 'B': return 10n; + case 'C': return 11n; + case 'D': return 12n; + case 'E': return 13n; + case 'F': return 14n; + case 'G': return 15n; + case 'H': return 16n; + case 'J': return 17n; + case 'K': return 18n; + case 'L': return 19n; + case 'M': return 20n; + case 'N': return 21n; + case 'P': return 22n; + case 'Q': return 23n; + case 'R': return 24n; + case 'S': return 25n; + case 'T': return 26n; + case 'U': return 27n; + case 'V': return 28n; + case 'W': return 29n; + case 'X': return 30n; + case 'Y': return 31n; + case 'Z': return 32n; + case 'a': return 33n; + case 'b': return 34n; + case 'c': return 35n; + case 'd': return 36n; + case 'e': return 37n; + case 'f': return 38n; + case 'g': return 39n; + case 'h': return 40n; + case 'i': return 41n; + case 'j': return 42n; + case 'k': return 43n; + case 'm': return 44n; + case 'n': return 45n; + case 'o': return 46n; + case 'p': return 47n; + case 'q': return 48n; + case 'r': return 49n; + case 's': return 50n; + case 't': return 51n; + case 'u': return 52n; + case 'v': return 53n; + case 'w': return 54n; + case 'x': return 55n; + case 'y': return 56n; + case 'z': return 57n; + default: + throw new Error('invalid base58 char: ' + s); + } +} diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts new file mode 100644 index 0000000..d82cd73 --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/b64.ts @@ -0,0 +1,655 @@ +/** + * Base64 Utility Functions in TypeScript + */ + +export { + encode, + decode +} + + + +/** + * Encode an array of bytes as a Uint8Array in base64 notation. + */ +function +encode + (bytes: Uint8Array) + : string +{ + // slice the array + // length of head is a multiple of 3 + // treat the tail as a special case + let {head, tail, tail_len} = slice3k(bytes); + let head_str : string = encode_head(head); + let tail_str : string = encode_tail(tail, tail_len); + return head_str + tail_str; +} + + + +type slice3k + = {head : Uint8Array, + tail : Uint8Array, + tail_len : number}; + +/** + * Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and + * the remaining 0,1, or 2 bytes, put them in tail + * + * @internal + */ +function +slice3k + (bytes: Uint8Array) + : slice3k +{ + let len : number = bytes.length; + // too lazy to look up how to do integer division in js so this will do + let tail_len : number = len % 3; + let head_len : number = len - tail_len; + // for slice: + // first argument is the 0-index of the start + // second - first is the length of the slice + let head : Uint8Array = bytes.slice(0, head_len); + // empty second argument means go to the end + let tail : Uint8Array = bytes.slice(head_len); + return {head : head, + tail : tail, + tail_len : tail_len}; +} + + + +/** + * Encode a Uint8Array whose length is known to be a multiple of 3 + * + * @internal + */ +function +encode_head + (head_bytes: Uint8Array) + : string +{ + // can assume length of bytes is a multiple of 3 + // start index at 0 + // increment by 3 + let head_bytes_len : number = head_bytes.length; + let max_idx0 : number = head_bytes_len - 1; + + let head_str_acc : string = ''; + for(let this_3slice_start_idx0 = 0; + this_3slice_start_idx0 <= max_idx0; + this_3slice_start_idx0 += 3) + { + let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3); + let this_3slice_str : string = encode3(this_3slice_bytes); + head_str_acc += this_3slice_str; + } + + return head_str_acc; +} + + + +/** + * Encode a 3 bytes into base64 notation + * + * @internal + */ +function +encode3 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + let b2 : number = bytes[2]; + + // ABCDEFGH 12345678 abcdefgh + // b0 b1 b2 + // ABCDEF GH1234 5678ab cdefgh + // n0 n1 n2 n3 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // b2 = abcdefgh + // b2 >> 6 = ______ab + // n2 = __5678ab + let n2 : number = ((b1 % 16) << 2) + (b2 >> 6); + // b2 = abcdefgh + // 64 = _1______ + // n3 = __cdefgh + let n3 : number = b2 % 64; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + let s3 : string = int2char(n3); + + // retrvn + return s0 + s1 + s2 + s3; +} + + + +/** + * Encode the final 0, 1, or 2 bytes + * + * @internal + */ +function +encode_tail + (tail_bytes : Uint8Array, + tail_len : number) + : string +{ + switch(tail_len) { + case 0: return ''; + case 1: return encode1(tail_bytes); + case 2: return encode2(tail_bytes); + default: + throw new Error('encode_tail with tail_len = ' + tail_len); + } +} + + + +/** + * Encode a single byte + * + * @internal + */ +function +encode1 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + // n0 = __ABCDEF + // b0 = ABCDEFGH + // b0 >> 2 = __ABCDEF + let n0 : number = b0 >> 2; + // n1 = __GH____ + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + let n1 : number = (b0 % 4) << 4; + + return int2char(n0) + int2char(n1) + '=='; +} + + + +/** + * Encode two bytes + * + * @internal + */ +function +encode2 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + + // ABCDEFGH 12345678 + // b0 b1 + // ABCDEF GH1234 5678__ + // n0 n1 n2 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // n2 = __5678__ + let n2 : number = (b1 % 16) << 2; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + + // retrvn + return s0 + s1 + s2 + '='; +} + + + +/** + * Decode a base64-encoded string + */ +function +decode + (base64_str : string) + : Uint8Array +{ + // length of the string is guaranteed to be a multiple of 4 + // if the string is empty, return the empty array + let len = base64_str.length; + // this branching contains the implicit assertion that the length is a + // multiple of 4. If this is not true, the bottom branch is triggered. + // general case goes first because speeeeeed + if ( (4 < len) + && (0 === (len % 4))) + { + // split the head and tail + let tail_start_idx0 : number = len - 4; + let head_s : string = base64_str.slice(0, tail_start_idx0); + let tail_s : string = base64_str.slice(tail_start_idx0); + // Using arrays because Uint8Arrays don't have a concat operation + let head_arr : Array = decode_head(head_s); + let tail_arr : Array = decode_tail(tail_s); + // silly to put these in variables but this is exactly the type of + // situation where JS type insanity shows up + // + // see: i forgot + // > [1,2,3] + [4,5,6] + // '1,2,34,5,6' + // + // Originally, I used + like some sort of moron who codes in a sane + // language + // + // seriously what is this language + // + // this is some clown behavior + let total_arr : Array = head_arr.concat(tail_arr); + return new Uint8Array(total_arr); + } + // special case if the length is exactly 4 + else if (4 === len) + { + // it's just a tail + return new Uint8Array(decode_tail(base64_str)); + } + // empty string + else if (0 === len) + { + return new Uint8Array([]); + } + else + { + throw new Error('base64 decode: invalid string length: ' + len); + } +} + + + +/** + * Decode a string known to not have any padding + * + * @internal + */ +function +decode_head + (s: string) + : Array +{ + // go 4 characters at a time + let max_i0 : number = s.length - 1; + let decoded_acc : Array = []; + for(let i0 = 0; + i0 <= max_i0; + i0 += 4) + { + let this_slice_s : string = s.slice(i0, i0 + 4); + let this_slice_arr : Array = decode3(this_slice_s); + // update accumulator + decoded_acc = decoded_acc.concat(this_slice_arr); + } + return decoded_acc; +} + + + +/** + * Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte + * + * @internal + */ +function +decode_tail + (s: string) + : Array +{ + // all that matters right now is the last 2 chars + // s0, s1, s2, s3 + // 0 based indexing is so annoying + let s2 = s[2]; + let s3 = s[3]; + + // braaaaaaaaaaaaaaaaaench + // two equals signs means 1 byte + if (('=' === s3) && ('=' === s2)) { + return decode1(s); + } + // one equals sign means 2 bytes + else if (('=' === s3)) { + return decode2(s); + } + // 0 equals signs means 3 bytes + else { + return decode3(s); + } +} + + + +/** + * Decode a 4-character long base64 string corresponding to 3 bytes + * + * @internal + */ +function +decode3 + (s: string) + : Array +{ + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + let s3 : string = s[3]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + let n3 : number = char2int(s3); + + // abcdef gh1234 5678ab cdefgh + // n0 n1 n2 n3 + // abcdefgh 12345678 abcdefgh + // b0 b1 b2 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678ab + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + // n2 = __5678ab + // 4 = _____1__ + // n2 % 4 = ______ab + // (n2 % 4) << 6 = ab______ + // n3 = __cdefgh + let b2 : number = ((n2 % 4) << 6) + n3; + + return [b0, b1, b2]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode2 + (s: string) + : Array +{ + // xyz= + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + + // abcdef gh1234 5678__ + // n0 n1 n2 + // abcdefgh 12345678 + // b0 b1 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678__ + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + + return [b0, b1]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode1 + (s: string) + : Array +{ + // xy== + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + + // abcdef gh____ + // n0 n1 + // abcdefgh + // b0 + + // n0 = __abcdef + // n1 = __gh____ + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + + return [b0]; +} + + + + +// FIXME: these tables would *probably* be faster if they were made into objects + +/** + * Conversion table for base64 encode + * + * @internal + */ +function +int2char + (n: number) + : string +{ + switch(n) { + case 0: return 'A'; + case 1: return 'B'; + case 2: return 'C'; + case 3: return 'D'; + case 4: return 'E'; + case 5: return 'F'; + case 6: return 'G'; + case 7: return 'H'; + case 8: return 'I'; + case 9: return 'J'; + case 10: return 'K'; + case 11: return 'L'; + case 12: return 'M'; + case 13: return 'N'; + case 14: return 'O'; + case 15: return 'P'; + case 16: return 'Q'; + case 17: return 'R'; + case 18: return 'S'; + case 19: return 'T'; + case 20: return 'U'; + case 21: return 'V'; + case 22: return 'W'; + case 23: return 'X'; + case 24: return 'Y'; + case 25: return 'Z'; + case 26: return 'a'; + case 27: return 'b'; + case 28: return 'c'; + case 29: return 'd'; + case 30: return 'e'; + case 31: return 'f'; + case 32: return 'g'; + case 33: return 'h'; + case 34: return 'i'; + case 35: return 'j'; + case 36: return 'k'; + case 37: return 'l'; + case 38: return 'm'; + case 39: return 'n'; + case 40: return 'o'; + case 41: return 'p'; + case 42: return 'q'; + case 43: return 'r'; + case 44: return 's'; + case 45: return 't'; + case 46: return 'u'; + case 47: return 'v'; + case 48: return 'w'; + case 49: return 'x'; + case 50: return 'y'; + case 51: return 'z'; + case 52: return '0'; + case 53: return '1'; + case 54: return '2'; + case 55: return '3'; + case 56: return '4'; + case 57: return '5'; + case 58: return '6'; + case 59: return '7'; + case 60: return '8'; + case 61: return '9'; + case 62: return '+'; + case 63: return '/'; + default: throw new Error("invalid base64 encode byte: " + n); + } +} + + + +/** + * Conversion table for base64 decode + * + * @internal + */ +function +char2int + (s: string) + : number +{ + switch(s) { + case 'A': return 0; + case 'B': return 1; + case 'C': return 2; + case 'D': return 3; + case 'E': return 4; + case 'F': return 5; + case 'G': return 6; + case 'H': return 7; + case 'I': return 8; + case 'J': return 9; + case 'K': return 10; + case 'L': return 11; + case 'M': return 12; + case 'N': return 13; + case 'O': return 14; + case 'P': return 15; + case 'Q': return 16; + case 'R': return 17; + case 'S': return 18; + case 'T': return 19; + case 'U': return 20; + case 'V': return 21; + case 'W': return 22; + case 'X': return 23; + case 'Y': return 24; + case 'Z': return 25; + case 'a': return 26; + case 'b': return 27; + case 'c': return 28; + case 'd': return 29; + case 'e': return 30; + case 'f': return 31; + case 'g': return 32; + case 'h': return 33; + case 'i': return 34; + case 'j': return 35; + case 'k': return 36; + case 'l': return 37; + case 'm': return 38; + case 'n': return 39; + case 'o': return 40; + case 'p': return 41; + case 'q': return 42; + case 'r': return 43; + case 's': return 44; + case 't': return 45; + case 'u': return 46; + case 'v': return 47; + case 'w': return 48; + case 'x': return 49; + case 'y': return 50; + case 'z': return 51; + case '0': return 52; + case '1': return 53; + case '2': return 54; + case '3': return 55; + case '4': return 56; + case '5': return 57; + case '6': return 58; + case '7': return 59; + case '8': return 60; + case '9': return 61; + case '+': return 62; + case '/': return 63; + default: throw new Error("invalid base64 character: " + s); + } +} diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts new file mode 100644 index 0000000..958aa2a --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/bin.ts @@ -0,0 +1,377 @@ +/** + * Miscellaneous binary utility functions + * + * @module + */ + +export { + bytes_to_bigint, + bigint_to_bytes, + concat, + strong_rand_bytes +}; + + + +/** + * Concatenate two arrays + */ +function +concat + (arr1 : Uint8Array, + arr2 : Uint8Array) + : Uint8Array +{ + let len1 : number = arr1.length; + let len2 : number = arr2.length; + let arr1_idx0_offset : number = 0; + let arr2_idx0_offset : number = len1; + let result_len : number = len1 + len2; + let result : Uint8Array = new Uint8Array(result_len); + // copy first array into result + for (let arr1_idx0 = 0; + arr1_idx0 < len1; + arr1_idx0++) + { + // no offset here + let result_idx0 : number = arr1_idx0 + arr1_idx0_offset; + result[result_idx0] = arr1[arr1_idx0]; + } + // copy second array into result + for (let arr2_idx0 = 0; + arr2_idx0 < len2; + arr2_idx0++) + { + // offset by the length of the first array + let result_idx0 : number = arr2_idx0 + arr2_idx0_offset; + result[result_idx0] = arr2[arr2_idx0]; + } + return result; +} + + + +/** + * Cryptographically random bytes + */ +function +strong_rand_bytes + (how_many : number) + : Uint8Array +{ + let arr = new Uint8Array(how_many); + (new Crypto()).getRandomValues(arr); + return arr; +} + + + +/** + * Convert a byte array to a bigint + * + * Equivalent to `binary:decode_unsigned/1` from Erlang + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let n : bigint = 0n; + for (let b of bytes) { + // move first, then add + // otherwise it ends on a move + // imperative languages are for losers + n <<= 8n; + n += BigInt(b); + } + return n; +} + + + +/** + * Convert a bigint to a byte array + * + * Equivalent to `binary:encode_unsigned/1` from Erlang + * + * Requires input to be positive + */ +function +bigint_to_bytes + (q: bigint) + : Uint8Array +{ + if (q < 0n) { + throw new Error('q < 0n: ' + q); + } + + let arr_reverse = []; + while (q > 0n) { + let r = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return new Uint8Array(arr_reverse); +} + + + +/** + * Oh no, bitstrings in a language that only has bytestrings + * + * By convention these are `Uint8Array`s with byte length `ceil(bit_length / + * 8)`, and all trailing bits are zero. + */ +type bits = + {bit_length : number, + bytes : Uint8Array}; + + + +/** + * Get an uninitialized bitstring + * + * @internal + */ +function +bits_null + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 0. + */ +function +bits_zeros + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + for (let i0 = 0; + i0 < byte_length; + i0++) + { + result[i0] = 0; + } + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 1. + */ +function +bits_ones + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + + // fill everything except the last byte with 255s + for (let i0 = 0; + i0 < (byte_length - 1); + i0++) + { + result[i0] = 255; + } + + // alright so the last byte + // ok so the number of leading 0s is + // 8 - (bit_length % 8) + let num_trailing_zero_bits : number = 8 - (bit_length % 8); + // the trailing byte is 255 << that + // e.g. 3 trailing 0s + // 1111_1111 -> 1111_1000 + let last_byte : number = 255 << num_trailing_zero_bits; + let last_byte_idx0 : number = byte_length - 1; + result[last_byte_idx0] = last_byte; + + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get the bit at a given 0-index + */ +function +bits_i0th + (bit_idx0 : number, + bits : bits) + : number +{ + // first task is figuring out what byte we're at + // for instance if we want bit 27 + // 3*8 = 24 =< 27 < 4*8 + // so it's Math.floor(bit_idx0 / 8) + let byte_idx0 : number = Math.floor(bit_idx0 / 8); + // let's fetch the byte and work with that + let the_byte : number = bits.bytes[byte_idx0]; + + // ok so let's go with 27 again + // 27 = 3 mod 8 + // so we bitshift right by (8 - 3) + // and then take the remainder dividing by 2 + // --B-_---- -> ----_---B -> 0000_000B + let bsr : number = 8 - (bit_idx0 % 8); + return (the_byte >> bsr) % 2; +} + + + +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_null(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // go along each byte in result, and compute the byte boundary + for (let i = 0; + i < result_bytes.length; + i++) + { + // ABCD_EFGH _ + // 0123_4567 8 + // this is the bit index of the leftmost bit in this byte + let start_bit_bi0 : number = i * 8; + let next_start_bit_bi0 : number = start_bit_bi0 + 8; + // does the bit at the beginning of this byte correspond to the first array? + // strict comparison: + // suppose i = 0, + // suppose bit_length1 is 0 + // then this says no, go to second array + // suppose bl1 = 1, + // this says start at first array + let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length; + // weak comparison: + // suppose bit_length1 = 8 + // ABCD_EFGH _ + // 0123_4567 8 + // ^ + // start_bit_bi0 ^ next_start_bit_bi0 + let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length; + // is this a bytes1 byte + let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array; + let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array; + + // need to work out the ping_pong bs up here because js is dumb and I + // can't put lets between elseifs + // alright, now we're in the case of only copying from the second array + // we have two cases: + // ping-pong: + // ABCD_EFGH 1234_5678 + // - ---- --- + // copying these bits + // ping: + // ABCD_EFGH + // - ---- 000 + // + // how to distinguish between these two?? + // + // we're in the ping case when the start_bit_bi0 corresponds to the + // final byte of the second array + // + // ok + // + // we need to compute the bit address in the second array that + // corresponds to the bit address at the beginning of this byte in the + // result array + let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length; + // I think the variable is + // bits_left_to_copy = bits2.bit_length - bit_addr2_bi0 + // no + 1 because the current bit is uncopied, + // so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we + // have the last bit to copy + // cases: + // bits_left_to_copy <= 0 -> + // this would mean we have more bits to copy, but are out of + // source bits. should be impossible if bits_null is correct + // bits_left_to_copy <= 8 -> + // this would mean we are going to fill this last byte in the + // result array with the correct bits from array2, but how we + // do this will depend on how those are arranged in bytes2 + // (whether we grab one or two bytes) + // + // this is the tricky case + // + // I think what matters here is the byte address in the second array + // if we're on the last byte + // 8 < bits_left_to_copy -> + // this means we can safely grab two bytes from bytes2, and do + // our bitshifting to make it correct + // + // FIXME + let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0; + // no the variable that matters is which byte we're on in the result + let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8); + let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1; + let bitshift_amt : number = bits1.bit_length % 8; + + let ping : boolean = next_bytes2_addr_i0 === bytes2.length; + + + // simple case: this is a byte from the first array + // just copy it + if (is_bytes1_byte) + result_bytes[i] = bytes1[i]; + // this is a boundary byte + // further cases: + // second bit length is 0 -> + // ABCD_EF-- + // ^ starting here + // just copy first byte and move along + else if (is_boundary_byte && (bits2.bit_length === 0)) + result_bytes[i] = bytes1[i]; + // boundary byte, and there is at least one byte in the second array + else if (is_boundary_byte) + { + // copy over the first byte + result_bytes[i] = bytes1[i]; + // take the first byte from the second array + let first_byte_of_second_array : number = bytes2[0]; + // bytes1: + // ABCD_EF00 + // 6 + // bytes2: + // 1234_5678 + // 0000_0012 + // and bitshift it right by that amount + let bitshift_amt : number = bits1.bit_length % 8; + result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt; + } + // last byte of second array + else if (ping) + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt); + // ping-pong: not last byte of second array + else + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt); + } + + return {bit_length : result_bit_length, + bytes : result_bytes}; + +} diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts new file mode 100644 index 0000000..16c4c43 --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/faert.ts @@ -0,0 +1,399 @@ +/** + * FÆRT: Fast Æternity Recovery Text + * + * Reference: https://gitlab.com/zxq9/passgas/-/blob/83607fedb08be5dfd03210e331f9c76125bb3467/fullofbeans + * + * @module + */ + +export { + encode, + decode, + byte_of_word, + check_word, + check_byte, + words +} + +import * as safe from './safe.js'; + + + +/** + * Encode a bytestring into FÆRT + */ +function +encode + (bytes : Uint8Array) + : string +{ + // initial accumulator + let words_acc : Array = []; + + // go along each byte and look up the word + // add it to the accumulator + for (let this_byte of bytes) + { + let this_word = words[this_byte]; + words_acc.push(this_word); + } + + // prepend check word to phrase + // yes craig it would be faster to do the check byte inline + // the usage case here is 64 byte strings + // double pass is not a big deal + return check_word(bytes) + ' ' + words_acc.join(' '); +} + + + +/** + * Decode a FAERT string into bytes + */ +function +decode + (faert : string) + : safe.Safe +{ + let all_words : Array = faert.split(' '); + let input_check_word : string = all_words[0]; + let input_words : Array = all_words.slice(1) + + // accumulator + let computed_bytes : Array = []; + + // loop over words and figure out accumulator + for (let this_input_word of input_words) + { + let maybe_this_byte : safe.Safe = byte_of_word(this_input_word); + // if the word is an allowable word, add it to the accumulator + if (maybe_this_byte.ok) + { + let this_byte : number = maybe_this_byte.result; + computed_bytes.push(this_byte); + } + // error case, propagate the error up the call chain + else + return maybe_this_byte; + } + + // at this point, we can assume we correctly decoded all words + // compute the check byte + let computed_bytes_u8s : Uint8Array = new Uint8Array(computed_bytes); + let computed_check_word : string = check_word(computed_bytes_u8s); + + // check if it is correct + // if so, return the computed bytes + if (computed_check_word === input_check_word) + return safe.ok(computed_bytes_u8s); + // otherwise, return an error + else + return safe.error('checksum failure! computed check word: ' + computed_check_word + '; input check word: ' + input_check_word); +} + + + +/** + * given a word, find its index + */ +function +byte_of_word + (word: string) + : safe.Safe +{ + for (let i=0; i<=255; i++) + { + if (word === words[i]) + return safe.ok(i); + } + return safe.error('invalid word: ' + word); +} + + + +/** + * compute the check word of an array + */ +function +check_word + (bytes: Uint8Array) + : string +{ + return words[check_byte(bytes)]; +} + + +/** + * given an array, compute the xor of all the bytes in the array + */ +function +check_byte + (bytes: Uint8Array) + : number +{ + let check_byte = 0; + for (let this_byte of bytes) + check_byte ^= this_byte; + return check_byte; +} + + + +let words = [ + "able", + "abuse", + "acquire", + "adjust", + "agent", + "air", + "alert", + "alpha", + "anger", + "answer", + "any", + "argue", + "around", + "assume", + "asthma", + "aunt", + "awkward", + "balcony", + "barrel", + "because", + "behave", + "bicycle", + "bike", + "blind", + "blouse", + "bonus", + "bottom", + "breeze", + "brother", + "bubble", + "burger", + "butter", + "can", + "cannon", + "cargo", + "catalog", + "caught", + "century", + "chaos", + "chicken", + "churn", + "cinnamon", + "clever", + "clock", + "clown", + "collect", + "conduct", + "convince", + "correct", + "cradle", + "crane", + "crime", + "cross", + "crystal", + "curve", + "daughter", + "debris", + "decrease", + "deny", + "deputy", + "despair", + "diesel", + "dinner", + "distance", + "document", + "donate", + "drama", + "drink", + "dutch", + "earth", + "educate", + "elbow", + "employ", + "endless", + "enlist", + "enter", + "equal", + "eternal", + "example", + "exhibit", + "exotic", + "faculty", + "famous", + "fault", + "feature", + "fiber", + "filter", + "firm", + "flame", + "flush", + "follow", + "forward", + "frame", + "fringe", + "future", + "game", + "gate", + "gift", + "glare", + "glow", + "goat", + "grab", + "grief", + "guilt", + "hand", + "hawk", + "health", + "hen", + "hire", + "honey", + "hover", + "hurry", + "hybrid", + "impose", + "inch", + "inherit", + "injury", + "install", + "iron", + "jazz", + "joke", + "just", + "kit", + "kitchen", + "language", + "laundry", + "leader", + "lend", + "leopard", + "license", + "live", + "lobster", + "lounge", + "magnet", + "mail", + "mansion", + "mass", + "math", + "media", + "message", + "metal", + "misery", + "mix", + "more", + "mountain", + "museum", + "mutual", + "narrow", + "nerve", + "next", + "note", + "obey", + "obtain", + "offer", + "once", + "orange", + "ostrich", + "over", + "owner", + "palace", + "patch", + "pave", + "pen", + "phrase", + "piece", + "pizza", + "plunge", + "polar", + "pool", + "power", + "pretty", + "private", + "protect", + "pulp", + "purity", + "quit", + "quote", + "raise", + "razor", + "recall", + "region", + "relief", + "rent", + "rescue", + "review", + "ride", + "risk", + "roof", + "rough", + "saddle", + "salmon", + "sand", + "scene", + "scrub", + "season", + "seek", + "series", + "shed", + "shoe", + "sick", + "silent", + "situate", + "skill", + "slide", + "slush", + "snack", + "solar", + "soul", + "special", + "sphere", + "spot", + "spy", + "stairs", + "stereo", + "strategy", + "stuff", + "success", + "sunny", + "surge", + "swear", + "symptom", + "tail", + "ten", + "tent", + "theme", + "thumb", + "tiny", + "toe", + "tooth", + "topic", + "trade", + "trash", + "trophy", + "truly", + "tumble", + "typical", + "uncle", + "unfair", + "until", + "upper", + "useless", + "van", + "venue", + "video", + "visa", + "vocal", + "walk", + "waste", + "wedding", + "weekend", + "wide", + "winner", + "wise", + "wood", + "wrist", + "zero" +] diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts new file mode 100644 index 0000000..8c48b75 --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/rlp.ts @@ -0,0 +1,521 @@ +/** + * Ethereum Recursive-Length Prefix Encoding implementation + * + * Two reference implementations: + * + * 1. Ethereum Python implementation + * 2. My Erlang implementation (agrees with Ethereum Python in randomized + * tests) + * + * This work can be found in ../test/testgen/ + * + * FIXME: need to have Safe assertions for "i am decoding a list", "i am + * decoding a binary", "I am decoding something with no remainder", etc + * + * @module + */ + +export { + decoded_data, + decode_result, + decode, + encode +} + + +//============================================================================= +//============================================================================= +// DECODING +//============================================================================= +//============================================================================= + + +/** + * Data that RLP can encode. Also the result of decoding. + */ +type decoded_data + = Uint8Array + | Array; + + + +/** + * Decoding can have a remainder + */ +type decode_result + = {decoded_data : decoded_data, + remainder : Uint8Array}; + + + +/** + * Decode an RLP-encoded bytestring into a `decode_result` (decoded data + + * whatever wasn't consumed) + */ +function +decode + (bytes: Uint8Array) + : decode_result +{ + // check the first byte + let first_byte: number = bytes[0]; + let rest : Uint8Array = bytes.slice(1); + // if the first byte is between 0 and 127, that is the data + if + (first_byte <= 127) { + return dr(new Uint8Array([first_byte]), rest); + } + // if the first byte is between 128 and 183 = 128 + 55, it is a bytestring + // and the length is Byte - 128 + else if + (first_byte <= 183) { + let payload_byte_length : number = first_byte - 128; + let payload : Uint8Array = rest.slice(0, payload_byte_length); + let rest2 : Uint8Array = rest.slice(payload_byte_length); + return dr(payload, rest2); + } + // if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a + // bytestring. the byte length of bytestring is FirstByte - 183. Then pull + // out the actual data + else if + (first_byte <= 191) { + let byte_length_of_byte_length : number = first_byte - 183; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(bytes, rest2); + } + // If the first byte is between 192 and 247 = 192 + 55, it is a list. The + // byte length of the list-payload is FirstByte - 192. Then the list + // payload, which needs to be decoded on its own. + else if + (first_byte <= 247) { + let byte_length_of_list : number = first_byte - 192; + let list_payload : Uint8Array = rest.slice(0, byte_length_of_list); + let list : Array = decode_list(list_payload); + let rest2 : Uint8Array = rest.slice(byte_length_of_list); + return dr(list, rest2); + } + // If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a + // list. The byte length of the byte length of the list-payload is + // FirstByte - 247. Then the byte length of the list. Then the list + // payload, which needs to be decoded on its own. + else { + let byte_length_of_byte_length : number = first_byte - 247; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let list : Array = decode_list(list_bytes); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(list, rest2); + } +} + + + +/** + * Decode a list payload (non-prefixed) into an `Array`. + * + * Repeatedly decodes an element off the list until remainder is empty. + * + * @internal + */ +function +decode_list + (bytes: Uint8Array) + : Array +{ + let arr : Array = []; + while (bytes.length > 0) { + // grab an item off the bytes + let {decoded_data, remainder} = decode(bytes); + // push it + arr.push(decoded_data); + // update bytes + bytes = remainder; + } + return arr; +} + + + +/** + * Convert bytestring to number + * + * @internal + */ +function +bytes_to_number + (bytes: Uint8Array) + : number +{ + let n : number = 0; + for (let b of bytes) + { + n <<= 8; + n += b; + } + return n; +} + + + +/** + * Make a `decode_result` containing the two arguments + * + * @internal + */ +function +dr + (x : decoded_data, + y : Uint8Array) + : decode_result +{ + return {decoded_data : x, + remainder : y}; +} + + + +//============================================================================= +//============================================================================= +// ENCODING +//============================================================================= +//============================================================================= + +/** + * Encode some decoded data + */ +function +encode + (data: decoded_data) + : Uint8Array +{ + // is it an array or data + if + (is_binary(data)) { + return encode_binary(data as Uint8Array); + } + else if + (is_list(data)) { + return encode_list(data as Array); + } + else { + throw new Error('encode told to encode something that is not an array or a binary: ' + data); + } +} + + + +/** + * Encode a binary into RLP + * + * @internal + */ +function +encode_binary + (bytes: Uint8Array) + : Uint8Array +{ + let len: number = bytes.length; + // single byte case when the byte is between 0..127 + // result is the bytestring containing the byte itself + if + ((len === 1) && + (bytes[0] <= 127)){ + return bytes; + } + // if the bytestring is 0..55 bytes long, the first byte in the result is + // 128 + Length, the rest of the result bytestring is the input string + else if + (len <= 55) { + // construct the result + // <<128 + Len, Bytes/binary>> + let result : Uint8Array = new Uint8Array(len + 1); + // first byte is 128 + length + result[0] = 128 + len; + // copy input bytes into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + let result_idx0 : number = input_idx0 + 1; + result[result_idx0] = bytes[input_idx0]; + } + return result; + } + // if the bytestring is more than 55 bytes long, the first byte is 183 + + // ByteLengthOfByteLength, followed by the byte length, followed by the + // bytes + else { + let len_bytes : Uint8Array = encode_unsigned(len); + let len_bytes_length : number = len_bytes.length; + // total array is + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len); + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + result[0] = 183 + len_bytes_length; + // copy len_bytes into result + for (let len_bytes_idx0 = 0; + len_bytes_idx0 < len_bytes_length; + len_bytes_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = len_bytes_idx0 + 1; + result[result_idx0] = len_bytes[len_bytes_idx0]; + } + // copy original byte array into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = input_idx0 + (1 + len_bytes_length); + result[result_idx0] = bytes[input_idx0]; + } + // finally, return the result + return result; + } +} + + + +/** + * Encode a list + * + * @internal + */ +function +encode_list + (list: Array) + : Uint8Array +{ + // first encode every element in the list, then branch + let payloads : Array = list.map(encode); + let payload : Uint8Array = uint8arr_concat(payloads); + let payload_size : number = payload.length; + // if the payload size is in 0..55, then the first byte is 192 + payload + // size, followed by the payload + if + (payload_size <= 55) { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + let result : Uint8Array = new Uint8Array(1 + payload_size); + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + result[0] = 192 + payload_size; + // copy the rest of the payload into result + for (let payload_idx0 = 0; + payload_idx0 < payload_size; + payload_idx0++) + { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + let result_idx0: number = payload_idx0 + 1; + result[result_idx0] = payload[payload_idx0]; + } + return result; + } + // if the payload size is greater than 55, the first byte is 247 + + // size_of_payload_size, followed by the payload size, followed by the + // payload + else { + // compute the payload size size + let payload_size_bytes : Uint8Array = encode_unsigned(payload_size); + let payload_size_size : number = payload_size_bytes.length; + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size); + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // first byte is 247 + payload_size_size + result[0] = 247 + payload_size_size; + // copy the payload_size_bytes into result + for (let psb_idx0 = 0; + psb_idx0 < payload_size_size; + psb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 byte for this + let result_idx0 : number = psb_idx0 + 1; + result[result_idx0] = payload_size_bytes[psb_idx0]; + } + // copy the payload into result + for (let pb_idx0 = 0; + pb_idx0 < payload_size; + pb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 + payload_size_size bytes for this + let result_idx0 : number = pb_idx0 + (1 + payload_size_size); + result[result_idx0] = payload[pb_idx0]; + } + // finally, return result + return result; + } +} + + + +/** + * Encode a number as a bytestring + * + * Not for general use, this assumes the input number is greater than 55 + * + * @internal + */ +function +encode_unsigned + (n: number) + : Uint8Array +{ + // can assume that n initially is greater than 55 + // have to encode it in reverse order + let arr : Array = []; + // repeated division by 256 with remainder + // + // example: suppose bign was 1234 and we were dividing by 10 + // + // iteration 0: + // bign = 1234 + // arr = [] + // --- + // bign / 10 = 123 + // bign % 10 = 4 + // --- + // bign = 123 + // arr = [4] + // + // iteration 1: + // bign = 123 + // arr = [4] + // --- + // bign / 10 = 12 + // bign % 10 = 3 + // --- + // bign = 12 + // arr = [4, 3] + // + // iteration 2: + // bign = 12 + // arr = [4, 3] + // --- + // bign / 10 = 1 + // bign % 10 = 2 + // --- + // bign = 1 + // arr = [4, 3, 2] + // + // iteration 3: + // bign = 1 + // arr = [4, 3, 2] + // --- + // bign / 10 = 0 + // bign % 10 = 1 + // --- + // bign = 0 + // arr = [4, 3, 2, 1] + // + // iteration 4: + // bign = 0 + // arr = [4, 3, 2, 1] + // --- + // DONE + while (n > 0) { + arr.push(n % 256); + n >>= 8; + } + // reverse and make into Uint8Array + arr.reverse(); + return new Uint8Array(arr); +} + + + +/** + * concatenate an array of `Uint8Array`s into a single Uint8Array + * + * @internal + */ +function +uint8arr_concat + (arrs: Array) + : Uint8Array +{ + // total length + let total_len = arrs.reduce(// fold + function (acc_len: number, this_uint8array: Uint8Array): number { + return acc_len + this_uint8array.length; + }, + // initial accumulator + 0); + // start up result + let result : Uint8Array = new Uint8Array(total_len); + let result_idx0 : number = 0; + // concatenate + for (let this_uint8arr of arrs) { + // bytewise copy of this uint8array + for (let this_uint8arr_idx0 = 0; + this_uint8arr_idx0 < this_uint8arr.length; + this_uint8arr_idx0++) + { + // copy byte and increment result idx0 + result[result_idx0] = this_uint8arr[this_uint8arr_idx0]; + result_idx0++; + } + } + return result; +} + +/** + * returns true if input is `instanceof Uint8Array` + * + * @internal + */ +function +is_binary + (x: any) + : boolean +{ + return (x instanceof Uint8Array); +} + + + +/** + * returns true if input is `instanceof Array` + * + * @internal + */ +function +is_list + (x: any) + : boolean +{ + return (x instanceof Array); +} diff --git a/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts new file mode 100644 index 0000000..b3e4c0c --- /dev/null +++ b/libs/vdk_tests_cases/dist/jex_include/local-vdk-0.1.0/src/safe.ts @@ -0,0 +1,112 @@ +/** + * "Safe" error handling + * + * The idea here is that there are situations where it is known + * + * ```typescript + * type Safe + * = Ok + * | Error; + * + * type Ok + * = {ok : true, + * result : ok_t}; + * + * type Error + * = {ok : false, + * error : err_t}; + * ``` + * + * 1. a given function call is likely to fail + * 2. the likely errors can be enumerated + * + * These are called "positive errors". An example would be a page script + * asking a browser wallet extension to sign a transaction. The following + * errors, among others, are likely: + * + * - the user does not have a wallet installed + * - the user has a wallet but does not have the correct signing key + * - the user rejects the transaction + * - the sign request timed out + * + * These errors should not generate exceptions, as these behaviors are to some + * degree "expected". + */ + +export { + Safe, + Ok, + Error, + ok, + error, + unsafe +} + + +/** + * Type that catches positive errors + */ +type Safe + = Ok + | Error; + + +/** + * Ok type + */ +type Ok + = {ok : true, + result : ok_t}; + + +/** + * Error type + */ +type Error + = {ok : false, + error : err_t}; + + +/** + * Constructs an `Ok` value from a pure value + */ +function +ok + + (x : ok_t) + : Ok +{ + return {ok: true, result: x}; +} + + + +/** + * Constructs an `Error` value from a pure value + */ +function +error + + (x: err_t) + : Error +{ + return {ok: false, error: x}; +} + + + +/** + * Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the + * `err_t` + */ +function +unsafe + + (x: Safe) + : ok_t +{ + if (x.ok) + return x.result; + else + throw x.error; +} diff --git a/libs/vdk_cases/dist/rlpcases.d.ts b/libs/vdk_tests_cases/dist/rlpcases.d.ts similarity index 100% rename from libs/vdk_cases/dist/rlpcases.d.ts rename to libs/vdk_tests_cases/dist/rlpcases.d.ts diff --git a/libs/vdk_cases/dist/rlpcases.js b/libs/vdk_tests_cases/dist/rlpcases.js similarity index 100% rename from libs/vdk_cases/dist/rlpcases.js rename to libs/vdk_tests_cases/dist/rlpcases.js diff --git a/libs/vdk_cases/dist/rlpcases.js.map b/libs/vdk_tests_cases/dist/rlpcases.js.map similarity index 100% rename from libs/vdk_cases/dist/rlpcases.js.map rename to libs/vdk_tests_cases/dist/rlpcases.js.map diff --git a/libs/vdk_cases/jex.eterms b/libs/vdk_tests_cases/jex.eterms similarity index 100% rename from libs/vdk_cases/jex.eterms rename to libs/vdk_tests_cases/jex.eterms diff --git a/libs/vdk_cases/jex_mindist/dist/cases.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/cases.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/cases.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/cases.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/cases.js b/libs/vdk_tests_cases/jex_mindist/dist/cases.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/cases.js rename to libs/vdk_tests_cases/jex_mindist/dist/cases.js diff --git a/libs/vdk_cases/jex_mindist/dist/cases.js.map b/libs/vdk_tests_cases/jex_mindist/dist/cases.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/cases.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/cases.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b58.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/b64.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/bin.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/faert.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/rlp.js.map diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js diff --git a/libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/dist/safe.js.map diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts new file mode 100644 index 0000000..1fd30b8 --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b58.ts @@ -0,0 +1,418 @@ +/** + * Base58 encoding/decoding + */ + +export { + encode, + decode +} + + + +//============================================================================= +// ENCODING +//============================================================================= + +/** + * Encode a Uint8Array into base58 + */ +function +encode + (binary : Uint8Array) + : string +{ + let num_leading_zeros : number = nlz(binary); + let rest : Uint8Array = binary.slice(num_leading_zeros); + let ones : string = encode_zeros(num_leading_zeros); + let rest_b58 : string = encode_rest(rest); + let result : string = ones + rest_b58; + return result; +} + + + +/** + * count the number of leading zeros in a uint8array + * + * @internal + */ +function +nlz + (bytes: Uint8Array) + : number +{ + let n = 0; + for (let this_byte of bytes) + { + if (0 === this_byte) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '1's for however many leading zeros there are + * + * @internal + */ +function +encode_zeros + (how_many : number) + : string +{ + let ones : string = ''; + for (let i = 1; + i <= how_many; + i++) + { + ones += '1'; + } + + return ones; +} + + + +/** + * Encode a Uint8Array that has no leading zeros + * + * @internal + */ +function +encode_rest + (bytes : Uint8Array) + : string +{ + let bytes_bignum : bigint = bytes_to_bigint(bytes); + let result : string = bignum_to_base58(bytes_bignum); + return result; +} + + + +/** + * Convert a bytestring to a bignum + * + * @internal + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_byte of bytes) + { + acc_bigint <<= 8n; + acc_bigint += BigInt(this_byte); + } + return acc_bigint; +} + + + +/** + * Convert a BigInt to Base58 + * + * @internal + */ +function +bignum_to_base58 + (q: bigint) + : string +{ + let s = ''; + while (q !== 0n) + { + let this_n : bigint = q % 58n; + q /= 58n; + + let this_b58_char : string = bigint_to_char(this_n); + s = this_b58_char + s; + } + return s; +} + + + +//============================================================================= +// DECODING +//============================================================================= + +/** + * Decode a Base58 string into a Uint8Array + */ +function +decode + (base58: string) + : Uint8Array +{ + let num_leading_ones : number = nlo(base58); + let rest : string = base58.slice(num_leading_ones); + let zeros : Array = decode_ones(num_leading_ones); + let rest_arr : Array = decode_rest(rest); + let pre_result : Array = zeros.concat(rest_arr); + return new Uint8Array(pre_result); +} + + + +/** + * count the number of leading 1 characters in a uint8array + * + * @internal + */ +function +nlo + (base58: string) + : number +{ + let n = 0; + for (let this_char of base58) + { + if ('1' === this_char) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '0's for however many leading ones there are + * + * @internal + */ +function +decode_ones + (how_many : number) + : Array +{ + let zeros : Array = []; + for (let i = 1; + i <= how_many; + i++) + { + zeros.push(0); + } + + return zeros; +} + + + +/** + * Decode a string that has no leading 1s + * + * @internal + */ +function +decode_rest + (base58: string) + : Array +{ + let result_bignum : bigint = base58_to_bigint(base58); + let result : Array = bigint_to_base256(result_bignum); + return result; +} + + + +/** + * Convert a base58 string to a bignum + * + * @internal + */ +function +base58_to_bigint + (base58: string) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_char of base58) + { + acc_bigint *= 58n; + acc_bigint += char_to_bigint(this_char); + } + return acc_bigint; +} + + +/** + * convert a bignum into a byte array + * + * @end + */ +function +bigint_to_base256 + (q: bigint) + : Array +{ + let arr_reverse = []; + while(q !== 0n) + { + let r: number = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return arr_reverse; +} + + +//============================================================================= +// TRANSLATION TABLES +//============================================================================= + + +/** + * Base58 integer -> character conversion table + * + * @internal + */ +function +bigint_to_char + (n: bigint) + : string +{ + switch(n) { + case 0n: return '1'; + case 1n: return '2'; + case 2n: return '3'; + case 3n: return '4'; + case 4n: return '5'; + case 5n: return '6'; + case 6n: return '7'; + case 7n: return '8'; + case 8n: return '9'; + case 9n: return 'A'; + case 10n: return 'B'; + case 11n: return 'C'; + case 12n: return 'D'; + case 13n: return 'E'; + case 14n: return 'F'; + case 15n: return 'G'; + case 16n: return 'H'; + case 17n: return 'J'; + case 18n: return 'K'; + case 19n: return 'L'; + case 20n: return 'M'; + case 21n: return 'N'; + case 22n: return 'P'; + case 23n: return 'Q'; + case 24n: return 'R'; + case 25n: return 'S'; + case 26n: return 'T'; + case 27n: return 'U'; + case 28n: return 'V'; + case 29n: return 'W'; + case 30n: return 'X'; + case 31n: return 'Y'; + case 32n: return 'Z'; + case 33n: return 'a'; + case 34n: return 'b'; + case 35n: return 'c'; + case 36n: return 'd'; + case 37n: return 'e'; + case 38n: return 'f'; + case 39n: return 'g'; + case 40n: return 'h'; + case 41n: return 'i'; + case 42n: return 'j'; + case 43n: return 'k'; + case 44n: return 'm'; + case 45n: return 'n'; + case 46n: return 'o'; + case 47n: return 'p'; + case 48n: return 'q'; + case 49n: return 'r'; + case 50n: return 's'; + case 51n: return 't'; + case 52n: return 'u'; + case 53n: return 'v'; + case 54n: return 'w'; + case 55n: return 'x'; + case 56n: return 'y'; + case 57n: return 'z'; + default: + throw new Error('invalid base58 bigint: ' + n); + } +} + + + +/** + * Base58 character -> integer conversion table + * + * @internal + */ +function +char_to_bigint + (s: string) + : bigint +{ + switch(s) { + case '1': return 0n; + case '2': return 1n; + case '3': return 2n; + case '4': return 3n; + case '5': return 4n; + case '6': return 5n; + case '7': return 6n; + case '8': return 7n; + case '9': return 8n; + case 'A': return 9n; + case 'B': return 10n; + case 'C': return 11n; + case 'D': return 12n; + case 'E': return 13n; + case 'F': return 14n; + case 'G': return 15n; + case 'H': return 16n; + case 'J': return 17n; + case 'K': return 18n; + case 'L': return 19n; + case 'M': return 20n; + case 'N': return 21n; + case 'P': return 22n; + case 'Q': return 23n; + case 'R': return 24n; + case 'S': return 25n; + case 'T': return 26n; + case 'U': return 27n; + case 'V': return 28n; + case 'W': return 29n; + case 'X': return 30n; + case 'Y': return 31n; + case 'Z': return 32n; + case 'a': return 33n; + case 'b': return 34n; + case 'c': return 35n; + case 'd': return 36n; + case 'e': return 37n; + case 'f': return 38n; + case 'g': return 39n; + case 'h': return 40n; + case 'i': return 41n; + case 'j': return 42n; + case 'k': return 43n; + case 'm': return 44n; + case 'n': return 45n; + case 'o': return 46n; + case 'p': return 47n; + case 'q': return 48n; + case 'r': return 49n; + case 's': return 50n; + case 't': return 51n; + case 'u': return 52n; + case 'v': return 53n; + case 'w': return 54n; + case 'x': return 55n; + case 'y': return 56n; + case 'z': return 57n; + default: + throw new Error('invalid base58 char: ' + s); + } +} diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts new file mode 100644 index 0000000..d82cd73 --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/b64.ts @@ -0,0 +1,655 @@ +/** + * Base64 Utility Functions in TypeScript + */ + +export { + encode, + decode +} + + + +/** + * Encode an array of bytes as a Uint8Array in base64 notation. + */ +function +encode + (bytes: Uint8Array) + : string +{ + // slice the array + // length of head is a multiple of 3 + // treat the tail as a special case + let {head, tail, tail_len} = slice3k(bytes); + let head_str : string = encode_head(head); + let tail_str : string = encode_tail(tail, tail_len); + return head_str + tail_str; +} + + + +type slice3k + = {head : Uint8Array, + tail : Uint8Array, + tail_len : number}; + +/** + * Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and + * the remaining 0,1, or 2 bytes, put them in tail + * + * @internal + */ +function +slice3k + (bytes: Uint8Array) + : slice3k +{ + let len : number = bytes.length; + // too lazy to look up how to do integer division in js so this will do + let tail_len : number = len % 3; + let head_len : number = len - tail_len; + // for slice: + // first argument is the 0-index of the start + // second - first is the length of the slice + let head : Uint8Array = bytes.slice(0, head_len); + // empty second argument means go to the end + let tail : Uint8Array = bytes.slice(head_len); + return {head : head, + tail : tail, + tail_len : tail_len}; +} + + + +/** + * Encode a Uint8Array whose length is known to be a multiple of 3 + * + * @internal + */ +function +encode_head + (head_bytes: Uint8Array) + : string +{ + // can assume length of bytes is a multiple of 3 + // start index at 0 + // increment by 3 + let head_bytes_len : number = head_bytes.length; + let max_idx0 : number = head_bytes_len - 1; + + let head_str_acc : string = ''; + for(let this_3slice_start_idx0 = 0; + this_3slice_start_idx0 <= max_idx0; + this_3slice_start_idx0 += 3) + { + let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3); + let this_3slice_str : string = encode3(this_3slice_bytes); + head_str_acc += this_3slice_str; + } + + return head_str_acc; +} + + + +/** + * Encode a 3 bytes into base64 notation + * + * @internal + */ +function +encode3 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + let b2 : number = bytes[2]; + + // ABCDEFGH 12345678 abcdefgh + // b0 b1 b2 + // ABCDEF GH1234 5678ab cdefgh + // n0 n1 n2 n3 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // b2 = abcdefgh + // b2 >> 6 = ______ab + // n2 = __5678ab + let n2 : number = ((b1 % 16) << 2) + (b2 >> 6); + // b2 = abcdefgh + // 64 = _1______ + // n3 = __cdefgh + let n3 : number = b2 % 64; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + let s3 : string = int2char(n3); + + // retrvn + return s0 + s1 + s2 + s3; +} + + + +/** + * Encode the final 0, 1, or 2 bytes + * + * @internal + */ +function +encode_tail + (tail_bytes : Uint8Array, + tail_len : number) + : string +{ + switch(tail_len) { + case 0: return ''; + case 1: return encode1(tail_bytes); + case 2: return encode2(tail_bytes); + default: + throw new Error('encode_tail with tail_len = ' + tail_len); + } +} + + + +/** + * Encode a single byte + * + * @internal + */ +function +encode1 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + // n0 = __ABCDEF + // b0 = ABCDEFGH + // b0 >> 2 = __ABCDEF + let n0 : number = b0 >> 2; + // n1 = __GH____ + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + let n1 : number = (b0 % 4) << 4; + + return int2char(n0) + int2char(n1) + '=='; +} + + + +/** + * Encode two bytes + * + * @internal + */ +function +encode2 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + + // ABCDEFGH 12345678 + // b0 b1 + // ABCDEF GH1234 5678__ + // n0 n1 n2 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // n2 = __5678__ + let n2 : number = (b1 % 16) << 2; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + + // retrvn + return s0 + s1 + s2 + '='; +} + + + +/** + * Decode a base64-encoded string + */ +function +decode + (base64_str : string) + : Uint8Array +{ + // length of the string is guaranteed to be a multiple of 4 + // if the string is empty, return the empty array + let len = base64_str.length; + // this branching contains the implicit assertion that the length is a + // multiple of 4. If this is not true, the bottom branch is triggered. + // general case goes first because speeeeeed + if ( (4 < len) + && (0 === (len % 4))) + { + // split the head and tail + let tail_start_idx0 : number = len - 4; + let head_s : string = base64_str.slice(0, tail_start_idx0); + let tail_s : string = base64_str.slice(tail_start_idx0); + // Using arrays because Uint8Arrays don't have a concat operation + let head_arr : Array = decode_head(head_s); + let tail_arr : Array = decode_tail(tail_s); + // silly to put these in variables but this is exactly the type of + // situation where JS type insanity shows up + // + // see: i forgot + // > [1,2,3] + [4,5,6] + // '1,2,34,5,6' + // + // Originally, I used + like some sort of moron who codes in a sane + // language + // + // seriously what is this language + // + // this is some clown behavior + let total_arr : Array = head_arr.concat(tail_arr); + return new Uint8Array(total_arr); + } + // special case if the length is exactly 4 + else if (4 === len) + { + // it's just a tail + return new Uint8Array(decode_tail(base64_str)); + } + // empty string + else if (0 === len) + { + return new Uint8Array([]); + } + else + { + throw new Error('base64 decode: invalid string length: ' + len); + } +} + + + +/** + * Decode a string known to not have any padding + * + * @internal + */ +function +decode_head + (s: string) + : Array +{ + // go 4 characters at a time + let max_i0 : number = s.length - 1; + let decoded_acc : Array = []; + for(let i0 = 0; + i0 <= max_i0; + i0 += 4) + { + let this_slice_s : string = s.slice(i0, i0 + 4); + let this_slice_arr : Array = decode3(this_slice_s); + // update accumulator + decoded_acc = decoded_acc.concat(this_slice_arr); + } + return decoded_acc; +} + + + +/** + * Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte + * + * @internal + */ +function +decode_tail + (s: string) + : Array +{ + // all that matters right now is the last 2 chars + // s0, s1, s2, s3 + // 0 based indexing is so annoying + let s2 = s[2]; + let s3 = s[3]; + + // braaaaaaaaaaaaaaaaaench + // two equals signs means 1 byte + if (('=' === s3) && ('=' === s2)) { + return decode1(s); + } + // one equals sign means 2 bytes + else if (('=' === s3)) { + return decode2(s); + } + // 0 equals signs means 3 bytes + else { + return decode3(s); + } +} + + + +/** + * Decode a 4-character long base64 string corresponding to 3 bytes + * + * @internal + */ +function +decode3 + (s: string) + : Array +{ + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + let s3 : string = s[3]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + let n3 : number = char2int(s3); + + // abcdef gh1234 5678ab cdefgh + // n0 n1 n2 n3 + // abcdefgh 12345678 abcdefgh + // b0 b1 b2 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678ab + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + // n2 = __5678ab + // 4 = _____1__ + // n2 % 4 = ______ab + // (n2 % 4) << 6 = ab______ + // n3 = __cdefgh + let b2 : number = ((n2 % 4) << 6) + n3; + + return [b0, b1, b2]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode2 + (s: string) + : Array +{ + // xyz= + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + + // abcdef gh1234 5678__ + // n0 n1 n2 + // abcdefgh 12345678 + // b0 b1 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678__ + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + + return [b0, b1]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode1 + (s: string) + : Array +{ + // xy== + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + + // abcdef gh____ + // n0 n1 + // abcdefgh + // b0 + + // n0 = __abcdef + // n1 = __gh____ + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + + return [b0]; +} + + + + +// FIXME: these tables would *probably* be faster if they were made into objects + +/** + * Conversion table for base64 encode + * + * @internal + */ +function +int2char + (n: number) + : string +{ + switch(n) { + case 0: return 'A'; + case 1: return 'B'; + case 2: return 'C'; + case 3: return 'D'; + case 4: return 'E'; + case 5: return 'F'; + case 6: return 'G'; + case 7: return 'H'; + case 8: return 'I'; + case 9: return 'J'; + case 10: return 'K'; + case 11: return 'L'; + case 12: return 'M'; + case 13: return 'N'; + case 14: return 'O'; + case 15: return 'P'; + case 16: return 'Q'; + case 17: return 'R'; + case 18: return 'S'; + case 19: return 'T'; + case 20: return 'U'; + case 21: return 'V'; + case 22: return 'W'; + case 23: return 'X'; + case 24: return 'Y'; + case 25: return 'Z'; + case 26: return 'a'; + case 27: return 'b'; + case 28: return 'c'; + case 29: return 'd'; + case 30: return 'e'; + case 31: return 'f'; + case 32: return 'g'; + case 33: return 'h'; + case 34: return 'i'; + case 35: return 'j'; + case 36: return 'k'; + case 37: return 'l'; + case 38: return 'm'; + case 39: return 'n'; + case 40: return 'o'; + case 41: return 'p'; + case 42: return 'q'; + case 43: return 'r'; + case 44: return 's'; + case 45: return 't'; + case 46: return 'u'; + case 47: return 'v'; + case 48: return 'w'; + case 49: return 'x'; + case 50: return 'y'; + case 51: return 'z'; + case 52: return '0'; + case 53: return '1'; + case 54: return '2'; + case 55: return '3'; + case 56: return '4'; + case 57: return '5'; + case 58: return '6'; + case 59: return '7'; + case 60: return '8'; + case 61: return '9'; + case 62: return '+'; + case 63: return '/'; + default: throw new Error("invalid base64 encode byte: " + n); + } +} + + + +/** + * Conversion table for base64 decode + * + * @internal + */ +function +char2int + (s: string) + : number +{ + switch(s) { + case 'A': return 0; + case 'B': return 1; + case 'C': return 2; + case 'D': return 3; + case 'E': return 4; + case 'F': return 5; + case 'G': return 6; + case 'H': return 7; + case 'I': return 8; + case 'J': return 9; + case 'K': return 10; + case 'L': return 11; + case 'M': return 12; + case 'N': return 13; + case 'O': return 14; + case 'P': return 15; + case 'Q': return 16; + case 'R': return 17; + case 'S': return 18; + case 'T': return 19; + case 'U': return 20; + case 'V': return 21; + case 'W': return 22; + case 'X': return 23; + case 'Y': return 24; + case 'Z': return 25; + case 'a': return 26; + case 'b': return 27; + case 'c': return 28; + case 'd': return 29; + case 'e': return 30; + case 'f': return 31; + case 'g': return 32; + case 'h': return 33; + case 'i': return 34; + case 'j': return 35; + case 'k': return 36; + case 'l': return 37; + case 'm': return 38; + case 'n': return 39; + case 'o': return 40; + case 'p': return 41; + case 'q': return 42; + case 'r': return 43; + case 's': return 44; + case 't': return 45; + case 'u': return 46; + case 'v': return 47; + case 'w': return 48; + case 'x': return 49; + case 'y': return 50; + case 'z': return 51; + case '0': return 52; + case '1': return 53; + case '2': return 54; + case '3': return 55; + case '4': return 56; + case '5': return 57; + case '6': return 58; + case '7': return 59; + case '8': return 60; + case '9': return 61; + case '+': return 62; + case '/': return 63; + default: throw new Error("invalid base64 character: " + s); + } +} diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts new file mode 100644 index 0000000..958aa2a --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/bin.ts @@ -0,0 +1,377 @@ +/** + * Miscellaneous binary utility functions + * + * @module + */ + +export { + bytes_to_bigint, + bigint_to_bytes, + concat, + strong_rand_bytes +}; + + + +/** + * Concatenate two arrays + */ +function +concat + (arr1 : Uint8Array, + arr2 : Uint8Array) + : Uint8Array +{ + let len1 : number = arr1.length; + let len2 : number = arr2.length; + let arr1_idx0_offset : number = 0; + let arr2_idx0_offset : number = len1; + let result_len : number = len1 + len2; + let result : Uint8Array = new Uint8Array(result_len); + // copy first array into result + for (let arr1_idx0 = 0; + arr1_idx0 < len1; + arr1_idx0++) + { + // no offset here + let result_idx0 : number = arr1_idx0 + arr1_idx0_offset; + result[result_idx0] = arr1[arr1_idx0]; + } + // copy second array into result + for (let arr2_idx0 = 0; + arr2_idx0 < len2; + arr2_idx0++) + { + // offset by the length of the first array + let result_idx0 : number = arr2_idx0 + arr2_idx0_offset; + result[result_idx0] = arr2[arr2_idx0]; + } + return result; +} + + + +/** + * Cryptographically random bytes + */ +function +strong_rand_bytes + (how_many : number) + : Uint8Array +{ + let arr = new Uint8Array(how_many); + (new Crypto()).getRandomValues(arr); + return arr; +} + + + +/** + * Convert a byte array to a bigint + * + * Equivalent to `binary:decode_unsigned/1` from Erlang + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let n : bigint = 0n; + for (let b of bytes) { + // move first, then add + // otherwise it ends on a move + // imperative languages are for losers + n <<= 8n; + n += BigInt(b); + } + return n; +} + + + +/** + * Convert a bigint to a byte array + * + * Equivalent to `binary:encode_unsigned/1` from Erlang + * + * Requires input to be positive + */ +function +bigint_to_bytes + (q: bigint) + : Uint8Array +{ + if (q < 0n) { + throw new Error('q < 0n: ' + q); + } + + let arr_reverse = []; + while (q > 0n) { + let r = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return new Uint8Array(arr_reverse); +} + + + +/** + * Oh no, bitstrings in a language that only has bytestrings + * + * By convention these are `Uint8Array`s with byte length `ceil(bit_length / + * 8)`, and all trailing bits are zero. + */ +type bits = + {bit_length : number, + bytes : Uint8Array}; + + + +/** + * Get an uninitialized bitstring + * + * @internal + */ +function +bits_null + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 0. + */ +function +bits_zeros + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + for (let i0 = 0; + i0 < byte_length; + i0++) + { + result[i0] = 0; + } + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 1. + */ +function +bits_ones + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + + // fill everything except the last byte with 255s + for (let i0 = 0; + i0 < (byte_length - 1); + i0++) + { + result[i0] = 255; + } + + // alright so the last byte + // ok so the number of leading 0s is + // 8 - (bit_length % 8) + let num_trailing_zero_bits : number = 8 - (bit_length % 8); + // the trailing byte is 255 << that + // e.g. 3 trailing 0s + // 1111_1111 -> 1111_1000 + let last_byte : number = 255 << num_trailing_zero_bits; + let last_byte_idx0 : number = byte_length - 1; + result[last_byte_idx0] = last_byte; + + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get the bit at a given 0-index + */ +function +bits_i0th + (bit_idx0 : number, + bits : bits) + : number +{ + // first task is figuring out what byte we're at + // for instance if we want bit 27 + // 3*8 = 24 =< 27 < 4*8 + // so it's Math.floor(bit_idx0 / 8) + let byte_idx0 : number = Math.floor(bit_idx0 / 8); + // let's fetch the byte and work with that + let the_byte : number = bits.bytes[byte_idx0]; + + // ok so let's go with 27 again + // 27 = 3 mod 8 + // so we bitshift right by (8 - 3) + // and then take the remainder dividing by 2 + // --B-_---- -> ----_---B -> 0000_000B + let bsr : number = 8 - (bit_idx0 % 8); + return (the_byte >> bsr) % 2; +} + + + +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_null(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // go along each byte in result, and compute the byte boundary + for (let i = 0; + i < result_bytes.length; + i++) + { + // ABCD_EFGH _ + // 0123_4567 8 + // this is the bit index of the leftmost bit in this byte + let start_bit_bi0 : number = i * 8; + let next_start_bit_bi0 : number = start_bit_bi0 + 8; + // does the bit at the beginning of this byte correspond to the first array? + // strict comparison: + // suppose i = 0, + // suppose bit_length1 is 0 + // then this says no, go to second array + // suppose bl1 = 1, + // this says start at first array + let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length; + // weak comparison: + // suppose bit_length1 = 8 + // ABCD_EFGH _ + // 0123_4567 8 + // ^ + // start_bit_bi0 ^ next_start_bit_bi0 + let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length; + // is this a bytes1 byte + let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array; + let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array; + + // need to work out the ping_pong bs up here because js is dumb and I + // can't put lets between elseifs + // alright, now we're in the case of only copying from the second array + // we have two cases: + // ping-pong: + // ABCD_EFGH 1234_5678 + // - ---- --- + // copying these bits + // ping: + // ABCD_EFGH + // - ---- 000 + // + // how to distinguish between these two?? + // + // we're in the ping case when the start_bit_bi0 corresponds to the + // final byte of the second array + // + // ok + // + // we need to compute the bit address in the second array that + // corresponds to the bit address at the beginning of this byte in the + // result array + let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length; + // I think the variable is + // bits_left_to_copy = bits2.bit_length - bit_addr2_bi0 + // no + 1 because the current bit is uncopied, + // so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we + // have the last bit to copy + // cases: + // bits_left_to_copy <= 0 -> + // this would mean we have more bits to copy, but are out of + // source bits. should be impossible if bits_null is correct + // bits_left_to_copy <= 8 -> + // this would mean we are going to fill this last byte in the + // result array with the correct bits from array2, but how we + // do this will depend on how those are arranged in bytes2 + // (whether we grab one or two bytes) + // + // this is the tricky case + // + // I think what matters here is the byte address in the second array + // if we're on the last byte + // 8 < bits_left_to_copy -> + // this means we can safely grab two bytes from bytes2, and do + // our bitshifting to make it correct + // + // FIXME + let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0; + // no the variable that matters is which byte we're on in the result + let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8); + let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1; + let bitshift_amt : number = bits1.bit_length % 8; + + let ping : boolean = next_bytes2_addr_i0 === bytes2.length; + + + // simple case: this is a byte from the first array + // just copy it + if (is_bytes1_byte) + result_bytes[i] = bytes1[i]; + // this is a boundary byte + // further cases: + // second bit length is 0 -> + // ABCD_EF-- + // ^ starting here + // just copy first byte and move along + else if (is_boundary_byte && (bits2.bit_length === 0)) + result_bytes[i] = bytes1[i]; + // boundary byte, and there is at least one byte in the second array + else if (is_boundary_byte) + { + // copy over the first byte + result_bytes[i] = bytes1[i]; + // take the first byte from the second array + let first_byte_of_second_array : number = bytes2[0]; + // bytes1: + // ABCD_EF00 + // 6 + // bytes2: + // 1234_5678 + // 0000_0012 + // and bitshift it right by that amount + let bitshift_amt : number = bits1.bit_length % 8; + result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt; + } + // last byte of second array + else if (ping) + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt); + // ping-pong: not last byte of second array + else + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt); + } + + return {bit_length : result_bit_length, + bytes : result_bytes}; + +} diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts new file mode 100644 index 0000000..16c4c43 --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/faert.ts @@ -0,0 +1,399 @@ +/** + * FÆRT: Fast Æternity Recovery Text + * + * Reference: https://gitlab.com/zxq9/passgas/-/blob/83607fedb08be5dfd03210e331f9c76125bb3467/fullofbeans + * + * @module + */ + +export { + encode, + decode, + byte_of_word, + check_word, + check_byte, + words +} + +import * as safe from './safe.js'; + + + +/** + * Encode a bytestring into FÆRT + */ +function +encode + (bytes : Uint8Array) + : string +{ + // initial accumulator + let words_acc : Array = []; + + // go along each byte and look up the word + // add it to the accumulator + for (let this_byte of bytes) + { + let this_word = words[this_byte]; + words_acc.push(this_word); + } + + // prepend check word to phrase + // yes craig it would be faster to do the check byte inline + // the usage case here is 64 byte strings + // double pass is not a big deal + return check_word(bytes) + ' ' + words_acc.join(' '); +} + + + +/** + * Decode a FAERT string into bytes + */ +function +decode + (faert : string) + : safe.Safe +{ + let all_words : Array = faert.split(' '); + let input_check_word : string = all_words[0]; + let input_words : Array = all_words.slice(1) + + // accumulator + let computed_bytes : Array = []; + + // loop over words and figure out accumulator + for (let this_input_word of input_words) + { + let maybe_this_byte : safe.Safe = byte_of_word(this_input_word); + // if the word is an allowable word, add it to the accumulator + if (maybe_this_byte.ok) + { + let this_byte : number = maybe_this_byte.result; + computed_bytes.push(this_byte); + } + // error case, propagate the error up the call chain + else + return maybe_this_byte; + } + + // at this point, we can assume we correctly decoded all words + // compute the check byte + let computed_bytes_u8s : Uint8Array = new Uint8Array(computed_bytes); + let computed_check_word : string = check_word(computed_bytes_u8s); + + // check if it is correct + // if so, return the computed bytes + if (computed_check_word === input_check_word) + return safe.ok(computed_bytes_u8s); + // otherwise, return an error + else + return safe.error('checksum failure! computed check word: ' + computed_check_word + '; input check word: ' + input_check_word); +} + + + +/** + * given a word, find its index + */ +function +byte_of_word + (word: string) + : safe.Safe +{ + for (let i=0; i<=255; i++) + { + if (word === words[i]) + return safe.ok(i); + } + return safe.error('invalid word: ' + word); +} + + + +/** + * compute the check word of an array + */ +function +check_word + (bytes: Uint8Array) + : string +{ + return words[check_byte(bytes)]; +} + + +/** + * given an array, compute the xor of all the bytes in the array + */ +function +check_byte + (bytes: Uint8Array) + : number +{ + let check_byte = 0; + for (let this_byte of bytes) + check_byte ^= this_byte; + return check_byte; +} + + + +let words = [ + "able", + "abuse", + "acquire", + "adjust", + "agent", + "air", + "alert", + "alpha", + "anger", + "answer", + "any", + "argue", + "around", + "assume", + "asthma", + "aunt", + "awkward", + "balcony", + "barrel", + "because", + "behave", + "bicycle", + "bike", + "blind", + "blouse", + "bonus", + "bottom", + "breeze", + "brother", + "bubble", + "burger", + "butter", + "can", + "cannon", + "cargo", + "catalog", + "caught", + "century", + "chaos", + "chicken", + "churn", + "cinnamon", + "clever", + "clock", + "clown", + "collect", + "conduct", + "convince", + "correct", + "cradle", + "crane", + "crime", + "cross", + "crystal", + "curve", + "daughter", + "debris", + "decrease", + "deny", + "deputy", + "despair", + "diesel", + "dinner", + "distance", + "document", + "donate", + "drama", + "drink", + "dutch", + "earth", + "educate", + "elbow", + "employ", + "endless", + "enlist", + "enter", + "equal", + "eternal", + "example", + "exhibit", + "exotic", + "faculty", + "famous", + "fault", + "feature", + "fiber", + "filter", + "firm", + "flame", + "flush", + "follow", + "forward", + "frame", + "fringe", + "future", + "game", + "gate", + "gift", + "glare", + "glow", + "goat", + "grab", + "grief", + "guilt", + "hand", + "hawk", + "health", + "hen", + "hire", + "honey", + "hover", + "hurry", + "hybrid", + "impose", + "inch", + "inherit", + "injury", + "install", + "iron", + "jazz", + "joke", + "just", + "kit", + "kitchen", + "language", + "laundry", + "leader", + "lend", + "leopard", + "license", + "live", + "lobster", + "lounge", + "magnet", + "mail", + "mansion", + "mass", + "math", + "media", + "message", + "metal", + "misery", + "mix", + "more", + "mountain", + "museum", + "mutual", + "narrow", + "nerve", + "next", + "note", + "obey", + "obtain", + "offer", + "once", + "orange", + "ostrich", + "over", + "owner", + "palace", + "patch", + "pave", + "pen", + "phrase", + "piece", + "pizza", + "plunge", + "polar", + "pool", + "power", + "pretty", + "private", + "protect", + "pulp", + "purity", + "quit", + "quote", + "raise", + "razor", + "recall", + "region", + "relief", + "rent", + "rescue", + "review", + "ride", + "risk", + "roof", + "rough", + "saddle", + "salmon", + "sand", + "scene", + "scrub", + "season", + "seek", + "series", + "shed", + "shoe", + "sick", + "silent", + "situate", + "skill", + "slide", + "slush", + "snack", + "solar", + "soul", + "special", + "sphere", + "spot", + "spy", + "stairs", + "stereo", + "strategy", + "stuff", + "success", + "sunny", + "surge", + "swear", + "symptom", + "tail", + "ten", + "tent", + "theme", + "thumb", + "tiny", + "toe", + "tooth", + "topic", + "trade", + "trash", + "trophy", + "truly", + "tumble", + "typical", + "uncle", + "unfair", + "until", + "upper", + "useless", + "van", + "venue", + "video", + "visa", + "vocal", + "walk", + "waste", + "wedding", + "weekend", + "wide", + "winner", + "wise", + "wood", + "wrist", + "zero" +] diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts new file mode 100644 index 0000000..8c48b75 --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/rlp.ts @@ -0,0 +1,521 @@ +/** + * Ethereum Recursive-Length Prefix Encoding implementation + * + * Two reference implementations: + * + * 1. Ethereum Python implementation + * 2. My Erlang implementation (agrees with Ethereum Python in randomized + * tests) + * + * This work can be found in ../test/testgen/ + * + * FIXME: need to have Safe assertions for "i am decoding a list", "i am + * decoding a binary", "I am decoding something with no remainder", etc + * + * @module + */ + +export { + decoded_data, + decode_result, + decode, + encode +} + + +//============================================================================= +//============================================================================= +// DECODING +//============================================================================= +//============================================================================= + + +/** + * Data that RLP can encode. Also the result of decoding. + */ +type decoded_data + = Uint8Array + | Array; + + + +/** + * Decoding can have a remainder + */ +type decode_result + = {decoded_data : decoded_data, + remainder : Uint8Array}; + + + +/** + * Decode an RLP-encoded bytestring into a `decode_result` (decoded data + + * whatever wasn't consumed) + */ +function +decode + (bytes: Uint8Array) + : decode_result +{ + // check the first byte + let first_byte: number = bytes[0]; + let rest : Uint8Array = bytes.slice(1); + // if the first byte is between 0 and 127, that is the data + if + (first_byte <= 127) { + return dr(new Uint8Array([first_byte]), rest); + } + // if the first byte is between 128 and 183 = 128 + 55, it is a bytestring + // and the length is Byte - 128 + else if + (first_byte <= 183) { + let payload_byte_length : number = first_byte - 128; + let payload : Uint8Array = rest.slice(0, payload_byte_length); + let rest2 : Uint8Array = rest.slice(payload_byte_length); + return dr(payload, rest2); + } + // if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a + // bytestring. the byte length of bytestring is FirstByte - 183. Then pull + // out the actual data + else if + (first_byte <= 191) { + let byte_length_of_byte_length : number = first_byte - 183; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(bytes, rest2); + } + // If the first byte is between 192 and 247 = 192 + 55, it is a list. The + // byte length of the list-payload is FirstByte - 192. Then the list + // payload, which needs to be decoded on its own. + else if + (first_byte <= 247) { + let byte_length_of_list : number = first_byte - 192; + let list_payload : Uint8Array = rest.slice(0, byte_length_of_list); + let list : Array = decode_list(list_payload); + let rest2 : Uint8Array = rest.slice(byte_length_of_list); + return dr(list, rest2); + } + // If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a + // list. The byte length of the byte length of the list-payload is + // FirstByte - 247. Then the byte length of the list. Then the list + // payload, which needs to be decoded on its own. + else { + let byte_length_of_byte_length : number = first_byte - 247; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let list : Array = decode_list(list_bytes); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(list, rest2); + } +} + + + +/** + * Decode a list payload (non-prefixed) into an `Array`. + * + * Repeatedly decodes an element off the list until remainder is empty. + * + * @internal + */ +function +decode_list + (bytes: Uint8Array) + : Array +{ + let arr : Array = []; + while (bytes.length > 0) { + // grab an item off the bytes + let {decoded_data, remainder} = decode(bytes); + // push it + arr.push(decoded_data); + // update bytes + bytes = remainder; + } + return arr; +} + + + +/** + * Convert bytestring to number + * + * @internal + */ +function +bytes_to_number + (bytes: Uint8Array) + : number +{ + let n : number = 0; + for (let b of bytes) + { + n <<= 8; + n += b; + } + return n; +} + + + +/** + * Make a `decode_result` containing the two arguments + * + * @internal + */ +function +dr + (x : decoded_data, + y : Uint8Array) + : decode_result +{ + return {decoded_data : x, + remainder : y}; +} + + + +//============================================================================= +//============================================================================= +// ENCODING +//============================================================================= +//============================================================================= + +/** + * Encode some decoded data + */ +function +encode + (data: decoded_data) + : Uint8Array +{ + // is it an array or data + if + (is_binary(data)) { + return encode_binary(data as Uint8Array); + } + else if + (is_list(data)) { + return encode_list(data as Array); + } + else { + throw new Error('encode told to encode something that is not an array or a binary: ' + data); + } +} + + + +/** + * Encode a binary into RLP + * + * @internal + */ +function +encode_binary + (bytes: Uint8Array) + : Uint8Array +{ + let len: number = bytes.length; + // single byte case when the byte is between 0..127 + // result is the bytestring containing the byte itself + if + ((len === 1) && + (bytes[0] <= 127)){ + return bytes; + } + // if the bytestring is 0..55 bytes long, the first byte in the result is + // 128 + Length, the rest of the result bytestring is the input string + else if + (len <= 55) { + // construct the result + // <<128 + Len, Bytes/binary>> + let result : Uint8Array = new Uint8Array(len + 1); + // first byte is 128 + length + result[0] = 128 + len; + // copy input bytes into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + let result_idx0 : number = input_idx0 + 1; + result[result_idx0] = bytes[input_idx0]; + } + return result; + } + // if the bytestring is more than 55 bytes long, the first byte is 183 + + // ByteLengthOfByteLength, followed by the byte length, followed by the + // bytes + else { + let len_bytes : Uint8Array = encode_unsigned(len); + let len_bytes_length : number = len_bytes.length; + // total array is + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len); + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + result[0] = 183 + len_bytes_length; + // copy len_bytes into result + for (let len_bytes_idx0 = 0; + len_bytes_idx0 < len_bytes_length; + len_bytes_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = len_bytes_idx0 + 1; + result[result_idx0] = len_bytes[len_bytes_idx0]; + } + // copy original byte array into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = input_idx0 + (1 + len_bytes_length); + result[result_idx0] = bytes[input_idx0]; + } + // finally, return the result + return result; + } +} + + + +/** + * Encode a list + * + * @internal + */ +function +encode_list + (list: Array) + : Uint8Array +{ + // first encode every element in the list, then branch + let payloads : Array = list.map(encode); + let payload : Uint8Array = uint8arr_concat(payloads); + let payload_size : number = payload.length; + // if the payload size is in 0..55, then the first byte is 192 + payload + // size, followed by the payload + if + (payload_size <= 55) { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + let result : Uint8Array = new Uint8Array(1 + payload_size); + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + result[0] = 192 + payload_size; + // copy the rest of the payload into result + for (let payload_idx0 = 0; + payload_idx0 < payload_size; + payload_idx0++) + { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + let result_idx0: number = payload_idx0 + 1; + result[result_idx0] = payload[payload_idx0]; + } + return result; + } + // if the payload size is greater than 55, the first byte is 247 + + // size_of_payload_size, followed by the payload size, followed by the + // payload + else { + // compute the payload size size + let payload_size_bytes : Uint8Array = encode_unsigned(payload_size); + let payload_size_size : number = payload_size_bytes.length; + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size); + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // first byte is 247 + payload_size_size + result[0] = 247 + payload_size_size; + // copy the payload_size_bytes into result + for (let psb_idx0 = 0; + psb_idx0 < payload_size_size; + psb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 byte for this + let result_idx0 : number = psb_idx0 + 1; + result[result_idx0] = payload_size_bytes[psb_idx0]; + } + // copy the payload into result + for (let pb_idx0 = 0; + pb_idx0 < payload_size; + pb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 + payload_size_size bytes for this + let result_idx0 : number = pb_idx0 + (1 + payload_size_size); + result[result_idx0] = payload[pb_idx0]; + } + // finally, return result + return result; + } +} + + + +/** + * Encode a number as a bytestring + * + * Not for general use, this assumes the input number is greater than 55 + * + * @internal + */ +function +encode_unsigned + (n: number) + : Uint8Array +{ + // can assume that n initially is greater than 55 + // have to encode it in reverse order + let arr : Array = []; + // repeated division by 256 with remainder + // + // example: suppose bign was 1234 and we were dividing by 10 + // + // iteration 0: + // bign = 1234 + // arr = [] + // --- + // bign / 10 = 123 + // bign % 10 = 4 + // --- + // bign = 123 + // arr = [4] + // + // iteration 1: + // bign = 123 + // arr = [4] + // --- + // bign / 10 = 12 + // bign % 10 = 3 + // --- + // bign = 12 + // arr = [4, 3] + // + // iteration 2: + // bign = 12 + // arr = [4, 3] + // --- + // bign / 10 = 1 + // bign % 10 = 2 + // --- + // bign = 1 + // arr = [4, 3, 2] + // + // iteration 3: + // bign = 1 + // arr = [4, 3, 2] + // --- + // bign / 10 = 0 + // bign % 10 = 1 + // --- + // bign = 0 + // arr = [4, 3, 2, 1] + // + // iteration 4: + // bign = 0 + // arr = [4, 3, 2, 1] + // --- + // DONE + while (n > 0) { + arr.push(n % 256); + n >>= 8; + } + // reverse and make into Uint8Array + arr.reverse(); + return new Uint8Array(arr); +} + + + +/** + * concatenate an array of `Uint8Array`s into a single Uint8Array + * + * @internal + */ +function +uint8arr_concat + (arrs: Array) + : Uint8Array +{ + // total length + let total_len = arrs.reduce(// fold + function (acc_len: number, this_uint8array: Uint8Array): number { + return acc_len + this_uint8array.length; + }, + // initial accumulator + 0); + // start up result + let result : Uint8Array = new Uint8Array(total_len); + let result_idx0 : number = 0; + // concatenate + for (let this_uint8arr of arrs) { + // bytewise copy of this uint8array + for (let this_uint8arr_idx0 = 0; + this_uint8arr_idx0 < this_uint8arr.length; + this_uint8arr_idx0++) + { + // copy byte and increment result idx0 + result[result_idx0] = this_uint8arr[this_uint8arr_idx0]; + result_idx0++; + } + } + return result; +} + +/** + * returns true if input is `instanceof Uint8Array` + * + * @internal + */ +function +is_binary + (x: any) + : boolean +{ + return (x instanceof Uint8Array); +} + + + +/** + * returns true if input is `instanceof Array` + * + * @internal + */ +function +is_list + (x: any) + : boolean +{ + return (x instanceof Array); +} diff --git a/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts new file mode 100644 index 0000000..b3e4c0c --- /dev/null +++ b/libs/vdk_tests_cases/jex_mindist/dist/jex_include/local-vdk-0.1.0/src/safe.ts @@ -0,0 +1,112 @@ +/** + * "Safe" error handling + * + * The idea here is that there are situations where it is known + * + * ```typescript + * type Safe + * = Ok + * | Error; + * + * type Ok + * = {ok : true, + * result : ok_t}; + * + * type Error + * = {ok : false, + * error : err_t}; + * ``` + * + * 1. a given function call is likely to fail + * 2. the likely errors can be enumerated + * + * These are called "positive errors". An example would be a page script + * asking a browser wallet extension to sign a transaction. The following + * errors, among others, are likely: + * + * - the user does not have a wallet installed + * - the user has a wallet but does not have the correct signing key + * - the user rejects the transaction + * - the sign request timed out + * + * These errors should not generate exceptions, as these behaviors are to some + * degree "expected". + */ + +export { + Safe, + Ok, + Error, + ok, + error, + unsafe +} + + +/** + * Type that catches positive errors + */ +type Safe + = Ok + | Error; + + +/** + * Ok type + */ +type Ok + = {ok : true, + result : ok_t}; + + +/** + * Error type + */ +type Error + = {ok : false, + error : err_t}; + + +/** + * Constructs an `Ok` value from a pure value + */ +function +ok + + (x : ok_t) + : Ok +{ + return {ok: true, result: x}; +} + + + +/** + * Constructs an `Error` value from a pure value + */ +function +error + + (x: err_t) + : Error +{ + return {ok: false, error: x}; +} + + + +/** + * Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the + * `err_t` + */ +function +unsafe + + (x: Safe) + : ok_t +{ + if (x.ok) + return x.result; + else + throw x.error; +} diff --git a/libs/vdk_cases/jex_mindist/dist/rlpcases.d.ts b/libs/vdk_tests_cases/jex_mindist/dist/rlpcases.d.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/rlpcases.d.ts rename to libs/vdk_tests_cases/jex_mindist/dist/rlpcases.d.ts diff --git a/libs/vdk_cases/jex_mindist/dist/rlpcases.js b/libs/vdk_tests_cases/jex_mindist/dist/rlpcases.js similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/rlpcases.js rename to libs/vdk_tests_cases/jex_mindist/dist/rlpcases.js diff --git a/libs/vdk_cases/jex_mindist/dist/rlpcases.js.map b/libs/vdk_tests_cases/jex_mindist/dist/rlpcases.js.map similarity index 100% rename from libs/vdk_cases/jex_mindist/dist/rlpcases.js.map rename to libs/vdk_tests_cases/jex_mindist/dist/rlpcases.js.map diff --git a/libs/vdk_cases/jex_mindist/src/cases.ts b/libs/vdk_tests_cases/jex_mindist/src/cases.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/src/cases.ts rename to libs/vdk_tests_cases/jex_mindist/src/cases.ts diff --git a/libs/vdk_cases/jex_mindist/src/rlpcases.ts b/libs/vdk_tests_cases/jex_mindist/src/rlpcases.ts similarity index 100% rename from libs/vdk_cases/jex_mindist/src/rlpcases.ts rename to libs/vdk_tests_cases/jex_mindist/src/rlpcases.ts diff --git a/libs/vdk_cases/src/cases.ts b/libs/vdk_tests_cases/src/cases.ts similarity index 100% rename from libs/vdk_cases/src/cases.ts rename to libs/vdk_tests_cases/src/cases.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b58.js.map diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/b64.js.map diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/bin.js.map diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/faert.js.map diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/rlp.js.map diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.d.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.d.ts similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.d.ts rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.d.ts diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js diff --git a/libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js.map b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js.map similarity index 100% rename from libs/vdk_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js.map rename to libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/dist/safe.js.map diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts new file mode 100644 index 0000000..1fd30b8 --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b58.ts @@ -0,0 +1,418 @@ +/** + * Base58 encoding/decoding + */ + +export { + encode, + decode +} + + + +//============================================================================= +// ENCODING +//============================================================================= + +/** + * Encode a Uint8Array into base58 + */ +function +encode + (binary : Uint8Array) + : string +{ + let num_leading_zeros : number = nlz(binary); + let rest : Uint8Array = binary.slice(num_leading_zeros); + let ones : string = encode_zeros(num_leading_zeros); + let rest_b58 : string = encode_rest(rest); + let result : string = ones + rest_b58; + return result; +} + + + +/** + * count the number of leading zeros in a uint8array + * + * @internal + */ +function +nlz + (bytes: Uint8Array) + : number +{ + let n = 0; + for (let this_byte of bytes) + { + if (0 === this_byte) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '1's for however many leading zeros there are + * + * @internal + */ +function +encode_zeros + (how_many : number) + : string +{ + let ones : string = ''; + for (let i = 1; + i <= how_many; + i++) + { + ones += '1'; + } + + return ones; +} + + + +/** + * Encode a Uint8Array that has no leading zeros + * + * @internal + */ +function +encode_rest + (bytes : Uint8Array) + : string +{ + let bytes_bignum : bigint = bytes_to_bigint(bytes); + let result : string = bignum_to_base58(bytes_bignum); + return result; +} + + + +/** + * Convert a bytestring to a bignum + * + * @internal + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_byte of bytes) + { + acc_bigint <<= 8n; + acc_bigint += BigInt(this_byte); + } + return acc_bigint; +} + + + +/** + * Convert a BigInt to Base58 + * + * @internal + */ +function +bignum_to_base58 + (q: bigint) + : string +{ + let s = ''; + while (q !== 0n) + { + let this_n : bigint = q % 58n; + q /= 58n; + + let this_b58_char : string = bigint_to_char(this_n); + s = this_b58_char + s; + } + return s; +} + + + +//============================================================================= +// DECODING +//============================================================================= + +/** + * Decode a Base58 string into a Uint8Array + */ +function +decode + (base58: string) + : Uint8Array +{ + let num_leading_ones : number = nlo(base58); + let rest : string = base58.slice(num_leading_ones); + let zeros : Array = decode_ones(num_leading_ones); + let rest_arr : Array = decode_rest(rest); + let pre_result : Array = zeros.concat(rest_arr); + return new Uint8Array(pre_result); +} + + + +/** + * count the number of leading 1 characters in a uint8array + * + * @internal + */ +function +nlo + (base58: string) + : number +{ + let n = 0; + for (let this_char of base58) + { + if ('1' === this_char) { n++; } + else { break; } + } + return n; +} + + + +/** + * Generate a bunch of '0's for however many leading ones there are + * + * @internal + */ +function +decode_ones + (how_many : number) + : Array +{ + let zeros : Array = []; + for (let i = 1; + i <= how_many; + i++) + { + zeros.push(0); + } + + return zeros; +} + + + +/** + * Decode a string that has no leading 1s + * + * @internal + */ +function +decode_rest + (base58: string) + : Array +{ + let result_bignum : bigint = base58_to_bigint(base58); + let result : Array = bigint_to_base256(result_bignum); + return result; +} + + + +/** + * Convert a base58 string to a bignum + * + * @internal + */ +function +base58_to_bigint + (base58: string) + : bigint +{ + let acc_bigint : bigint = 0n; + for(let this_char of base58) + { + acc_bigint *= 58n; + acc_bigint += char_to_bigint(this_char); + } + return acc_bigint; +} + + +/** + * convert a bignum into a byte array + * + * @end + */ +function +bigint_to_base256 + (q: bigint) + : Array +{ + let arr_reverse = []; + while(q !== 0n) + { + let r: number = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return arr_reverse; +} + + +//============================================================================= +// TRANSLATION TABLES +//============================================================================= + + +/** + * Base58 integer -> character conversion table + * + * @internal + */ +function +bigint_to_char + (n: bigint) + : string +{ + switch(n) { + case 0n: return '1'; + case 1n: return '2'; + case 2n: return '3'; + case 3n: return '4'; + case 4n: return '5'; + case 5n: return '6'; + case 6n: return '7'; + case 7n: return '8'; + case 8n: return '9'; + case 9n: return 'A'; + case 10n: return 'B'; + case 11n: return 'C'; + case 12n: return 'D'; + case 13n: return 'E'; + case 14n: return 'F'; + case 15n: return 'G'; + case 16n: return 'H'; + case 17n: return 'J'; + case 18n: return 'K'; + case 19n: return 'L'; + case 20n: return 'M'; + case 21n: return 'N'; + case 22n: return 'P'; + case 23n: return 'Q'; + case 24n: return 'R'; + case 25n: return 'S'; + case 26n: return 'T'; + case 27n: return 'U'; + case 28n: return 'V'; + case 29n: return 'W'; + case 30n: return 'X'; + case 31n: return 'Y'; + case 32n: return 'Z'; + case 33n: return 'a'; + case 34n: return 'b'; + case 35n: return 'c'; + case 36n: return 'd'; + case 37n: return 'e'; + case 38n: return 'f'; + case 39n: return 'g'; + case 40n: return 'h'; + case 41n: return 'i'; + case 42n: return 'j'; + case 43n: return 'k'; + case 44n: return 'm'; + case 45n: return 'n'; + case 46n: return 'o'; + case 47n: return 'p'; + case 48n: return 'q'; + case 49n: return 'r'; + case 50n: return 's'; + case 51n: return 't'; + case 52n: return 'u'; + case 53n: return 'v'; + case 54n: return 'w'; + case 55n: return 'x'; + case 56n: return 'y'; + case 57n: return 'z'; + default: + throw new Error('invalid base58 bigint: ' + n); + } +} + + + +/** + * Base58 character -> integer conversion table + * + * @internal + */ +function +char_to_bigint + (s: string) + : bigint +{ + switch(s) { + case '1': return 0n; + case '2': return 1n; + case '3': return 2n; + case '4': return 3n; + case '5': return 4n; + case '6': return 5n; + case '7': return 6n; + case '8': return 7n; + case '9': return 8n; + case 'A': return 9n; + case 'B': return 10n; + case 'C': return 11n; + case 'D': return 12n; + case 'E': return 13n; + case 'F': return 14n; + case 'G': return 15n; + case 'H': return 16n; + case 'J': return 17n; + case 'K': return 18n; + case 'L': return 19n; + case 'M': return 20n; + case 'N': return 21n; + case 'P': return 22n; + case 'Q': return 23n; + case 'R': return 24n; + case 'S': return 25n; + case 'T': return 26n; + case 'U': return 27n; + case 'V': return 28n; + case 'W': return 29n; + case 'X': return 30n; + case 'Y': return 31n; + case 'Z': return 32n; + case 'a': return 33n; + case 'b': return 34n; + case 'c': return 35n; + case 'd': return 36n; + case 'e': return 37n; + case 'f': return 38n; + case 'g': return 39n; + case 'h': return 40n; + case 'i': return 41n; + case 'j': return 42n; + case 'k': return 43n; + case 'm': return 44n; + case 'n': return 45n; + case 'o': return 46n; + case 'p': return 47n; + case 'q': return 48n; + case 'r': return 49n; + case 's': return 50n; + case 't': return 51n; + case 'u': return 52n; + case 'v': return 53n; + case 'w': return 54n; + case 'x': return 55n; + case 'y': return 56n; + case 'z': return 57n; + default: + throw new Error('invalid base58 char: ' + s); + } +} diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts new file mode 100644 index 0000000..d82cd73 --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/b64.ts @@ -0,0 +1,655 @@ +/** + * Base64 Utility Functions in TypeScript + */ + +export { + encode, + decode +} + + + +/** + * Encode an array of bytes as a Uint8Array in base64 notation. + */ +function +encode + (bytes: Uint8Array) + : string +{ + // slice the array + // length of head is a multiple of 3 + // treat the tail as a special case + let {head, tail, tail_len} = slice3k(bytes); + let head_str : string = encode_head(head); + let tail_str : string = encode_tail(tail, tail_len); + return head_str + tail_str; +} + + + +type slice3k + = {head : Uint8Array, + tail : Uint8Array, + tail_len : number}; + +/** + * Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and + * the remaining 0,1, or 2 bytes, put them in tail + * + * @internal + */ +function +slice3k + (bytes: Uint8Array) + : slice3k +{ + let len : number = bytes.length; + // too lazy to look up how to do integer division in js so this will do + let tail_len : number = len % 3; + let head_len : number = len - tail_len; + // for slice: + // first argument is the 0-index of the start + // second - first is the length of the slice + let head : Uint8Array = bytes.slice(0, head_len); + // empty second argument means go to the end + let tail : Uint8Array = bytes.slice(head_len); + return {head : head, + tail : tail, + tail_len : tail_len}; +} + + + +/** + * Encode a Uint8Array whose length is known to be a multiple of 3 + * + * @internal + */ +function +encode_head + (head_bytes: Uint8Array) + : string +{ + // can assume length of bytes is a multiple of 3 + // start index at 0 + // increment by 3 + let head_bytes_len : number = head_bytes.length; + let max_idx0 : number = head_bytes_len - 1; + + let head_str_acc : string = ''; + for(let this_3slice_start_idx0 = 0; + this_3slice_start_idx0 <= max_idx0; + this_3slice_start_idx0 += 3) + { + let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3); + let this_3slice_str : string = encode3(this_3slice_bytes); + head_str_acc += this_3slice_str; + } + + return head_str_acc; +} + + + +/** + * Encode a 3 bytes into base64 notation + * + * @internal + */ +function +encode3 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + let b2 : number = bytes[2]; + + // ABCDEFGH 12345678 abcdefgh + // b0 b1 b2 + // ABCDEF GH1234 5678ab cdefgh + // n0 n1 n2 n3 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // b2 = abcdefgh + // b2 >> 6 = ______ab + // n2 = __5678ab + let n2 : number = ((b1 % 16) << 2) + (b2 >> 6); + // b2 = abcdefgh + // 64 = _1______ + // n3 = __cdefgh + let n3 : number = b2 % 64; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + let s3 : string = int2char(n3); + + // retrvn + return s0 + s1 + s2 + s3; +} + + + +/** + * Encode the final 0, 1, or 2 bytes + * + * @internal + */ +function +encode_tail + (tail_bytes : Uint8Array, + tail_len : number) + : string +{ + switch(tail_len) { + case 0: return ''; + case 1: return encode1(tail_bytes); + case 2: return encode2(tail_bytes); + default: + throw new Error('encode_tail with tail_len = ' + tail_len); + } +} + + + +/** + * Encode a single byte + * + * @internal + */ +function +encode1 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + // n0 = __ABCDEF + // b0 = ABCDEFGH + // b0 >> 2 = __ABCDEF + let n0 : number = b0 >> 2; + // n1 = __GH____ + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + let n1 : number = (b0 % 4) << 4; + + return int2char(n0) + int2char(n1) + '=='; +} + + + +/** + * Encode two bytes + * + * @internal + */ +function +encode2 + (bytes: Uint8Array) + : string +{ + let b0 : number = bytes[0]; + let b1 : number = bytes[1]; + + // ABCDEFGH 12345678 + // b0 b1 + // ABCDEF GH1234 5678__ + // n0 n1 n2 + let n0 : number = b0 >> 2; + // b0 = ABCDEFGH + // 4 = _____1__ + // b0 % 4 = ______GH + // (b0 % 4) << 4 = __GH____ + // b1 = 12345678 + // b1 >> 4 = ____1234 + // n1 = __GH1234 + let n1 : number = ((b0 % 4) << 4) + (b1 >> 4); + // b1 = 12345678 + // 16 = ___1____ + // b1 % 16 = ____5678 + // (b1 % 16) << 2 = __5678__ + // n2 = __5678__ + let n2 : number = (b1 % 16) << 2; + + // convert to chars + let s0 : string = int2char(n0); + let s1 : string = int2char(n1); + let s2 : string = int2char(n2); + + // retrvn + return s0 + s1 + s2 + '='; +} + + + +/** + * Decode a base64-encoded string + */ +function +decode + (base64_str : string) + : Uint8Array +{ + // length of the string is guaranteed to be a multiple of 4 + // if the string is empty, return the empty array + let len = base64_str.length; + // this branching contains the implicit assertion that the length is a + // multiple of 4. If this is not true, the bottom branch is triggered. + // general case goes first because speeeeeed + if ( (4 < len) + && (0 === (len % 4))) + { + // split the head and tail + let tail_start_idx0 : number = len - 4; + let head_s : string = base64_str.slice(0, tail_start_idx0); + let tail_s : string = base64_str.slice(tail_start_idx0); + // Using arrays because Uint8Arrays don't have a concat operation + let head_arr : Array = decode_head(head_s); + let tail_arr : Array = decode_tail(tail_s); + // silly to put these in variables but this is exactly the type of + // situation where JS type insanity shows up + // + // see: i forgot + // > [1,2,3] + [4,5,6] + // '1,2,34,5,6' + // + // Originally, I used + like some sort of moron who codes in a sane + // language + // + // seriously what is this language + // + // this is some clown behavior + let total_arr : Array = head_arr.concat(tail_arr); + return new Uint8Array(total_arr); + } + // special case if the length is exactly 4 + else if (4 === len) + { + // it's just a tail + return new Uint8Array(decode_tail(base64_str)); + } + // empty string + else if (0 === len) + { + return new Uint8Array([]); + } + else + { + throw new Error('base64 decode: invalid string length: ' + len); + } +} + + + +/** + * Decode a string known to not have any padding + * + * @internal + */ +function +decode_head + (s: string) + : Array +{ + // go 4 characters at a time + let max_i0 : number = s.length - 1; + let decoded_acc : Array = []; + for(let i0 = 0; + i0 <= max_i0; + i0 += 4) + { + let this_slice_s : string = s.slice(i0, i0 + 4); + let this_slice_arr : Array = decode3(this_slice_s); + // update accumulator + decoded_acc = decoded_acc.concat(this_slice_arr); + } + return decoded_acc; +} + + + +/** + * Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte + * + * @internal + */ +function +decode_tail + (s: string) + : Array +{ + // all that matters right now is the last 2 chars + // s0, s1, s2, s3 + // 0 based indexing is so annoying + let s2 = s[2]; + let s3 = s[3]; + + // braaaaaaaaaaaaaaaaaench + // two equals signs means 1 byte + if (('=' === s3) && ('=' === s2)) { + return decode1(s); + } + // one equals sign means 2 bytes + else if (('=' === s3)) { + return decode2(s); + } + // 0 equals signs means 3 bytes + else { + return decode3(s); + } +} + + + +/** + * Decode a 4-character long base64 string corresponding to 3 bytes + * + * @internal + */ +function +decode3 + (s: string) + : Array +{ + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + let s3 : string = s[3]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + let n3 : number = char2int(s3); + + // abcdef gh1234 5678ab cdefgh + // n0 n1 n2 n3 + // abcdefgh 12345678 abcdefgh + // b0 b1 b2 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678ab + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + // n2 = __5678ab + // 4 = _____1__ + // n2 % 4 = ______ab + // (n2 % 4) << 6 = ab______ + // n3 = __cdefgh + let b2 : number = ((n2 % 4) << 6) + n3; + + return [b0, b1, b2]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode2 + (s: string) + : Array +{ + // xyz= + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + let s2 : string = s[2]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + let n2 : number = char2int(s2); + + // abcdef gh1234 5678__ + // n0 n1 n2 + // abcdefgh 12345678 + // b0 b1 + + // n0 = __abcdef + // n1 = __gh1234 + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + // n1 = __gh1234 + // 16 = ___1____ + // n1 % 16 = ____1234 + // (n1 % 16) << 4 = 1234____ + // n2 = __5678__ + // n2 >> 2 = ____5678 + // b1 = 12345678 + let b1 : number = ((n1 % 16) << 4) + (n2 >> 2); + + return [b0, b1]; +} + + + +/** + * Decode a 4-character long base64 string corresponding to 2 bytes + * + * @internal + */ +function +decode1 + (s: string) + : Array +{ + // xy== + // pull out strings + let s0 : string = s[0]; + let s1 : string = s[1]; + + // convert to numbers + let n0 : number = char2int(s0); + let n1 : number = char2int(s1); + + // abcdef gh____ + // n0 n1 + // abcdefgh + // b0 + + // n0 = __abcdef + // n1 = __gh____ + // n0 << 2 = abcdef__ + // n1 >> 4 = ______gh + // b0 = abcdefgh + let b0 : number = (n0 << 2) + (n1 >> 4); + + return [b0]; +} + + + + +// FIXME: these tables would *probably* be faster if they were made into objects + +/** + * Conversion table for base64 encode + * + * @internal + */ +function +int2char + (n: number) + : string +{ + switch(n) { + case 0: return 'A'; + case 1: return 'B'; + case 2: return 'C'; + case 3: return 'D'; + case 4: return 'E'; + case 5: return 'F'; + case 6: return 'G'; + case 7: return 'H'; + case 8: return 'I'; + case 9: return 'J'; + case 10: return 'K'; + case 11: return 'L'; + case 12: return 'M'; + case 13: return 'N'; + case 14: return 'O'; + case 15: return 'P'; + case 16: return 'Q'; + case 17: return 'R'; + case 18: return 'S'; + case 19: return 'T'; + case 20: return 'U'; + case 21: return 'V'; + case 22: return 'W'; + case 23: return 'X'; + case 24: return 'Y'; + case 25: return 'Z'; + case 26: return 'a'; + case 27: return 'b'; + case 28: return 'c'; + case 29: return 'd'; + case 30: return 'e'; + case 31: return 'f'; + case 32: return 'g'; + case 33: return 'h'; + case 34: return 'i'; + case 35: return 'j'; + case 36: return 'k'; + case 37: return 'l'; + case 38: return 'm'; + case 39: return 'n'; + case 40: return 'o'; + case 41: return 'p'; + case 42: return 'q'; + case 43: return 'r'; + case 44: return 's'; + case 45: return 't'; + case 46: return 'u'; + case 47: return 'v'; + case 48: return 'w'; + case 49: return 'x'; + case 50: return 'y'; + case 51: return 'z'; + case 52: return '0'; + case 53: return '1'; + case 54: return '2'; + case 55: return '3'; + case 56: return '4'; + case 57: return '5'; + case 58: return '6'; + case 59: return '7'; + case 60: return '8'; + case 61: return '9'; + case 62: return '+'; + case 63: return '/'; + default: throw new Error("invalid base64 encode byte: " + n); + } +} + + + +/** + * Conversion table for base64 decode + * + * @internal + */ +function +char2int + (s: string) + : number +{ + switch(s) { + case 'A': return 0; + case 'B': return 1; + case 'C': return 2; + case 'D': return 3; + case 'E': return 4; + case 'F': return 5; + case 'G': return 6; + case 'H': return 7; + case 'I': return 8; + case 'J': return 9; + case 'K': return 10; + case 'L': return 11; + case 'M': return 12; + case 'N': return 13; + case 'O': return 14; + case 'P': return 15; + case 'Q': return 16; + case 'R': return 17; + case 'S': return 18; + case 'T': return 19; + case 'U': return 20; + case 'V': return 21; + case 'W': return 22; + case 'X': return 23; + case 'Y': return 24; + case 'Z': return 25; + case 'a': return 26; + case 'b': return 27; + case 'c': return 28; + case 'd': return 29; + case 'e': return 30; + case 'f': return 31; + case 'g': return 32; + case 'h': return 33; + case 'i': return 34; + case 'j': return 35; + case 'k': return 36; + case 'l': return 37; + case 'm': return 38; + case 'n': return 39; + case 'o': return 40; + case 'p': return 41; + case 'q': return 42; + case 'r': return 43; + case 's': return 44; + case 't': return 45; + case 'u': return 46; + case 'v': return 47; + case 'w': return 48; + case 'x': return 49; + case 'y': return 50; + case 'z': return 51; + case '0': return 52; + case '1': return 53; + case '2': return 54; + case '3': return 55; + case '4': return 56; + case '5': return 57; + case '6': return 58; + case '7': return 59; + case '8': return 60; + case '9': return 61; + case '+': return 62; + case '/': return 63; + default: throw new Error("invalid base64 character: " + s); + } +} diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts new file mode 100644 index 0000000..958aa2a --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/bin.ts @@ -0,0 +1,377 @@ +/** + * Miscellaneous binary utility functions + * + * @module + */ + +export { + bytes_to_bigint, + bigint_to_bytes, + concat, + strong_rand_bytes +}; + + + +/** + * Concatenate two arrays + */ +function +concat + (arr1 : Uint8Array, + arr2 : Uint8Array) + : Uint8Array +{ + let len1 : number = arr1.length; + let len2 : number = arr2.length; + let arr1_idx0_offset : number = 0; + let arr2_idx0_offset : number = len1; + let result_len : number = len1 + len2; + let result : Uint8Array = new Uint8Array(result_len); + // copy first array into result + for (let arr1_idx0 = 0; + arr1_idx0 < len1; + arr1_idx0++) + { + // no offset here + let result_idx0 : number = arr1_idx0 + arr1_idx0_offset; + result[result_idx0] = arr1[arr1_idx0]; + } + // copy second array into result + for (let arr2_idx0 = 0; + arr2_idx0 < len2; + arr2_idx0++) + { + // offset by the length of the first array + let result_idx0 : number = arr2_idx0 + arr2_idx0_offset; + result[result_idx0] = arr2[arr2_idx0]; + } + return result; +} + + + +/** + * Cryptographically random bytes + */ +function +strong_rand_bytes + (how_many : number) + : Uint8Array +{ + let arr = new Uint8Array(how_many); + (new Crypto()).getRandomValues(arr); + return arr; +} + + + +/** + * Convert a byte array to a bigint + * + * Equivalent to `binary:decode_unsigned/1` from Erlang + */ +function +bytes_to_bigint + (bytes: Uint8Array) + : bigint +{ + let n : bigint = 0n; + for (let b of bytes) { + // move first, then add + // otherwise it ends on a move + // imperative languages are for losers + n <<= 8n; + n += BigInt(b); + } + return n; +} + + + +/** + * Convert a bigint to a byte array + * + * Equivalent to `binary:encode_unsigned/1` from Erlang + * + * Requires input to be positive + */ +function +bigint_to_bytes + (q: bigint) + : Uint8Array +{ + if (q < 0n) { + throw new Error('q < 0n: ' + q); + } + + let arr_reverse = []; + while (q > 0n) { + let r = Number(q % 256n); + q /= 256n; + arr_reverse.push(r); + } + arr_reverse.reverse(); + return new Uint8Array(arr_reverse); +} + + + +/** + * Oh no, bitstrings in a language that only has bytestrings + * + * By convention these are `Uint8Array`s with byte length `ceil(bit_length / + * 8)`, and all trailing bits are zero. + */ +type bits = + {bit_length : number, + bytes : Uint8Array}; + + + +/** + * Get an uninitialized bitstring + * + * @internal + */ +function +bits_null + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 0. + */ +function +bits_zeros + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + for (let i0 = 0; + i0 < byte_length; + i0++) + { + result[i0] = 0; + } + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get a bitstring of a given length where every value is 1. + */ +function +bits_ones + (bit_length : number) + : bits +{ + let byte_length : number = Math.ceil(bit_length / 8); + let result : Uint8Array = new Uint8Array(byte_length); + + // fill everything except the last byte with 255s + for (let i0 = 0; + i0 < (byte_length - 1); + i0++) + { + result[i0] = 255; + } + + // alright so the last byte + // ok so the number of leading 0s is + // 8 - (bit_length % 8) + let num_trailing_zero_bits : number = 8 - (bit_length % 8); + // the trailing byte is 255 << that + // e.g. 3 trailing 0s + // 1111_1111 -> 1111_1000 + let last_byte : number = 255 << num_trailing_zero_bits; + let last_byte_idx0 : number = byte_length - 1; + result[last_byte_idx0] = last_byte; + + return {bit_length : bit_length, + bytes : result}; +} + + + +/** + * Get the bit at a given 0-index + */ +function +bits_i0th + (bit_idx0 : number, + bits : bits) + : number +{ + // first task is figuring out what byte we're at + // for instance if we want bit 27 + // 3*8 = 24 =< 27 < 4*8 + // so it's Math.floor(bit_idx0 / 8) + let byte_idx0 : number = Math.floor(bit_idx0 / 8); + // let's fetch the byte and work with that + let the_byte : number = bits.bytes[byte_idx0]; + + // ok so let's go with 27 again + // 27 = 3 mod 8 + // so we bitshift right by (8 - 3) + // and then take the remainder dividing by 2 + // --B-_---- -> ----_---B -> 0000_000B + let bsr : number = 8 - (bit_idx0 % 8); + return (the_byte >> bsr) % 2; +} + + + +/** + * Concatenate two bitstrings + */ +function +bits_concat + (bits1 : bits, + bits2 : bits) + : bits +{ + let result_bit_length : number = bits1.bit_length + bits2.bit_length; + let bytes1 : Uint8Array = bits1.bytes; + let bytes2 : Uint8Array = bits2.bytes; + // using zeros here because of our xor trick in a minute + let result_bits : bits = bits_null(result_bit_length); + let result_bytes : Uint8Array = result_bits.bytes; + + // go along each byte in result, and compute the byte boundary + for (let i = 0; + i < result_bytes.length; + i++) + { + // ABCD_EFGH _ + // 0123_4567 8 + // this is the bit index of the leftmost bit in this byte + let start_bit_bi0 : number = i * 8; + let next_start_bit_bi0 : number = start_bit_bi0 + 8; + // does the bit at the beginning of this byte correspond to the first array? + // strict comparison: + // suppose i = 0, + // suppose bit_length1 is 0 + // then this says no, go to second array + // suppose bl1 = 1, + // this says start at first array + let start_bit_is_of_first_array : boolean = start_bit_bi0 < bits1.bit_length; + // weak comparison: + // suppose bit_length1 = 8 + // ABCD_EFGH _ + // 0123_4567 8 + // ^ + // start_bit_bi0 ^ next_start_bit_bi0 + let stop_bit_is_of_first_array : boolean = next_start_bit_bi0 <= bits1.bit_length; + // is this a bytes1 byte + let is_bytes1_byte : boolean = start_bit_is_of_first_array && stop_bit_is_of_first_array; + let is_boundary_byte : boolean = start_bit_is_of_first_array && !stop_bit_is_of_first_array; + + // need to work out the ping_pong bs up here because js is dumb and I + // can't put lets between elseifs + // alright, now we're in the case of only copying from the second array + // we have two cases: + // ping-pong: + // ABCD_EFGH 1234_5678 + // - ---- --- + // copying these bits + // ping: + // ABCD_EFGH + // - ---- 000 + // + // how to distinguish between these two?? + // + // we're in the ping case when the start_bit_bi0 corresponds to the + // final byte of the second array + // + // ok + // + // we need to compute the bit address in the second array that + // corresponds to the bit address at the beginning of this byte in the + // result array + let bits2_addr_bi0 : number = start_bit_bi0 - bits1.bit_length; + // I think the variable is + // bits_left_to_copy = bits2.bit_length - bit_addr2_bi0 + // no + 1 because the current bit is uncopied, + // so if bit_addr2_bi0 = 7 and bits2.bit_length is 8, it means we + // have the last bit to copy + // cases: + // bits_left_to_copy <= 0 -> + // this would mean we have more bits to copy, but are out of + // source bits. should be impossible if bits_null is correct + // bits_left_to_copy <= 8 -> + // this would mean we are going to fill this last byte in the + // result array with the correct bits from array2, but how we + // do this will depend on how those are arranged in bytes2 + // (whether we grab one or two bytes) + // + // this is the tricky case + // + // I think what matters here is the byte address in the second array + // if we're on the last byte + // 8 < bits_left_to_copy -> + // this means we can safely grab two bytes from bytes2, and do + // our bitshifting to make it correct + // + // FIXME + let bits_left_to_copy : number = bits2.bit_length - bits2_addr_bi0; + // no the variable that matters is which byte we're on in the result + let this_bytes2_addr_i0 : number = Math.floor(bits2_addr_bi0 / 8); + let next_bytes2_addr_i0 : number = this_bytes2_addr_i0 + 1; + let bitshift_amt : number = bits1.bit_length % 8; + + let ping : boolean = next_bytes2_addr_i0 === bytes2.length; + + + // simple case: this is a byte from the first array + // just copy it + if (is_bytes1_byte) + result_bytes[i] = bytes1[i]; + // this is a boundary byte + // further cases: + // second bit length is 0 -> + // ABCD_EF-- + // ^ starting here + // just copy first byte and move along + else if (is_boundary_byte && (bits2.bit_length === 0)) + result_bytes[i] = bytes1[i]; + // boundary byte, and there is at least one byte in the second array + else if (is_boundary_byte) + { + // copy over the first byte + result_bytes[i] = bytes1[i]; + // take the first byte from the second array + let first_byte_of_second_array : number = bytes2[0]; + // bytes1: + // ABCD_EF00 + // 6 + // bytes2: + // 1234_5678 + // 0000_0012 + // and bitshift it right by that amount + let bitshift_amt : number = bits1.bit_length % 8; + result_bytes[i] ^= first_byte_of_second_array >> bitshift_amt; + } + // last byte of second array + else if (ping) + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt); + // ping-pong: not last byte of second array + else + result_bytes[i] = (bytes2[this_bytes2_addr_i0] << bitshift_amt) ^ (bytes2[next_bytes2_addr_i0] << bitshift_amt); + } + + return {bit_length : result_bit_length, + bytes : result_bytes}; + +} diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts new file mode 100644 index 0000000..16c4c43 --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/faert.ts @@ -0,0 +1,399 @@ +/** + * FÆRT: Fast Æternity Recovery Text + * + * Reference: https://gitlab.com/zxq9/passgas/-/blob/83607fedb08be5dfd03210e331f9c76125bb3467/fullofbeans + * + * @module + */ + +export { + encode, + decode, + byte_of_word, + check_word, + check_byte, + words +} + +import * as safe from './safe.js'; + + + +/** + * Encode a bytestring into FÆRT + */ +function +encode + (bytes : Uint8Array) + : string +{ + // initial accumulator + let words_acc : Array = []; + + // go along each byte and look up the word + // add it to the accumulator + for (let this_byte of bytes) + { + let this_word = words[this_byte]; + words_acc.push(this_word); + } + + // prepend check word to phrase + // yes craig it would be faster to do the check byte inline + // the usage case here is 64 byte strings + // double pass is not a big deal + return check_word(bytes) + ' ' + words_acc.join(' '); +} + + + +/** + * Decode a FAERT string into bytes + */ +function +decode + (faert : string) + : safe.Safe +{ + let all_words : Array = faert.split(' '); + let input_check_word : string = all_words[0]; + let input_words : Array = all_words.slice(1) + + // accumulator + let computed_bytes : Array = []; + + // loop over words and figure out accumulator + for (let this_input_word of input_words) + { + let maybe_this_byte : safe.Safe = byte_of_word(this_input_word); + // if the word is an allowable word, add it to the accumulator + if (maybe_this_byte.ok) + { + let this_byte : number = maybe_this_byte.result; + computed_bytes.push(this_byte); + } + // error case, propagate the error up the call chain + else + return maybe_this_byte; + } + + // at this point, we can assume we correctly decoded all words + // compute the check byte + let computed_bytes_u8s : Uint8Array = new Uint8Array(computed_bytes); + let computed_check_word : string = check_word(computed_bytes_u8s); + + // check if it is correct + // if so, return the computed bytes + if (computed_check_word === input_check_word) + return safe.ok(computed_bytes_u8s); + // otherwise, return an error + else + return safe.error('checksum failure! computed check word: ' + computed_check_word + '; input check word: ' + input_check_word); +} + + + +/** + * given a word, find its index + */ +function +byte_of_word + (word: string) + : safe.Safe +{ + for (let i=0; i<=255; i++) + { + if (word === words[i]) + return safe.ok(i); + } + return safe.error('invalid word: ' + word); +} + + + +/** + * compute the check word of an array + */ +function +check_word + (bytes: Uint8Array) + : string +{ + return words[check_byte(bytes)]; +} + + +/** + * given an array, compute the xor of all the bytes in the array + */ +function +check_byte + (bytes: Uint8Array) + : number +{ + let check_byte = 0; + for (let this_byte of bytes) + check_byte ^= this_byte; + return check_byte; +} + + + +let words = [ + "able", + "abuse", + "acquire", + "adjust", + "agent", + "air", + "alert", + "alpha", + "anger", + "answer", + "any", + "argue", + "around", + "assume", + "asthma", + "aunt", + "awkward", + "balcony", + "barrel", + "because", + "behave", + "bicycle", + "bike", + "blind", + "blouse", + "bonus", + "bottom", + "breeze", + "brother", + "bubble", + "burger", + "butter", + "can", + "cannon", + "cargo", + "catalog", + "caught", + "century", + "chaos", + "chicken", + "churn", + "cinnamon", + "clever", + "clock", + "clown", + "collect", + "conduct", + "convince", + "correct", + "cradle", + "crane", + "crime", + "cross", + "crystal", + "curve", + "daughter", + "debris", + "decrease", + "deny", + "deputy", + "despair", + "diesel", + "dinner", + "distance", + "document", + "donate", + "drama", + "drink", + "dutch", + "earth", + "educate", + "elbow", + "employ", + "endless", + "enlist", + "enter", + "equal", + "eternal", + "example", + "exhibit", + "exotic", + "faculty", + "famous", + "fault", + "feature", + "fiber", + "filter", + "firm", + "flame", + "flush", + "follow", + "forward", + "frame", + "fringe", + "future", + "game", + "gate", + "gift", + "glare", + "glow", + "goat", + "grab", + "grief", + "guilt", + "hand", + "hawk", + "health", + "hen", + "hire", + "honey", + "hover", + "hurry", + "hybrid", + "impose", + "inch", + "inherit", + "injury", + "install", + "iron", + "jazz", + "joke", + "just", + "kit", + "kitchen", + "language", + "laundry", + "leader", + "lend", + "leopard", + "license", + "live", + "lobster", + "lounge", + "magnet", + "mail", + "mansion", + "mass", + "math", + "media", + "message", + "metal", + "misery", + "mix", + "more", + "mountain", + "museum", + "mutual", + "narrow", + "nerve", + "next", + "note", + "obey", + "obtain", + "offer", + "once", + "orange", + "ostrich", + "over", + "owner", + "palace", + "patch", + "pave", + "pen", + "phrase", + "piece", + "pizza", + "plunge", + "polar", + "pool", + "power", + "pretty", + "private", + "protect", + "pulp", + "purity", + "quit", + "quote", + "raise", + "razor", + "recall", + "region", + "relief", + "rent", + "rescue", + "review", + "ride", + "risk", + "roof", + "rough", + "saddle", + "salmon", + "sand", + "scene", + "scrub", + "season", + "seek", + "series", + "shed", + "shoe", + "sick", + "silent", + "situate", + "skill", + "slide", + "slush", + "snack", + "solar", + "soul", + "special", + "sphere", + "spot", + "spy", + "stairs", + "stereo", + "strategy", + "stuff", + "success", + "sunny", + "surge", + "swear", + "symptom", + "tail", + "ten", + "tent", + "theme", + "thumb", + "tiny", + "toe", + "tooth", + "topic", + "trade", + "trash", + "trophy", + "truly", + "tumble", + "typical", + "uncle", + "unfair", + "until", + "upper", + "useless", + "van", + "venue", + "video", + "visa", + "vocal", + "walk", + "waste", + "wedding", + "weekend", + "wide", + "winner", + "wise", + "wood", + "wrist", + "zero" +] diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts new file mode 100644 index 0000000..8c48b75 --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/rlp.ts @@ -0,0 +1,521 @@ +/** + * Ethereum Recursive-Length Prefix Encoding implementation + * + * Two reference implementations: + * + * 1. Ethereum Python implementation + * 2. My Erlang implementation (agrees with Ethereum Python in randomized + * tests) + * + * This work can be found in ../test/testgen/ + * + * FIXME: need to have Safe assertions for "i am decoding a list", "i am + * decoding a binary", "I am decoding something with no remainder", etc + * + * @module + */ + +export { + decoded_data, + decode_result, + decode, + encode +} + + +//============================================================================= +//============================================================================= +// DECODING +//============================================================================= +//============================================================================= + + +/** + * Data that RLP can encode. Also the result of decoding. + */ +type decoded_data + = Uint8Array + | Array; + + + +/** + * Decoding can have a remainder + */ +type decode_result + = {decoded_data : decoded_data, + remainder : Uint8Array}; + + + +/** + * Decode an RLP-encoded bytestring into a `decode_result` (decoded data + + * whatever wasn't consumed) + */ +function +decode + (bytes: Uint8Array) + : decode_result +{ + // check the first byte + let first_byte: number = bytes[0]; + let rest : Uint8Array = bytes.slice(1); + // if the first byte is between 0 and 127, that is the data + if + (first_byte <= 127) { + return dr(new Uint8Array([first_byte]), rest); + } + // if the first byte is between 128 and 183 = 128 + 55, it is a bytestring + // and the length is Byte - 128 + else if + (first_byte <= 183) { + let payload_byte_length : number = first_byte - 128; + let payload : Uint8Array = rest.slice(0, payload_byte_length); + let rest2 : Uint8Array = rest.slice(payload_byte_length); + return dr(payload, rest2); + } + // if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a + // bytestring. the byte length of bytestring is FirstByte - 183. Then pull + // out the actual data + else if + (first_byte <= 191) { + let byte_length_of_byte_length : number = first_byte - 183; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(bytes, rest2); + } + // If the first byte is between 192 and 247 = 192 + 55, it is a list. The + // byte length of the list-payload is FirstByte - 192. Then the list + // payload, which needs to be decoded on its own. + else if + (first_byte <= 247) { + let byte_length_of_list : number = first_byte - 192; + let list_payload : Uint8Array = rest.slice(0, byte_length_of_list); + let list : Array = decode_list(list_payload); + let rest2 : Uint8Array = rest.slice(byte_length_of_list); + return dr(list, rest2); + } + // If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a + // list. The byte length of the byte length of the list-payload is + // FirstByte - 247. Then the byte length of the list. Then the list + // payload, which needs to be decoded on its own. + else { + let byte_length_of_byte_length : number = first_byte - 247; + let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length); + let byte_length : number = bytes_to_number(bytes_of_byte_length); + let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length, + byte_length + byte_length_of_byte_length); + let list : Array = decode_list(list_bytes); + let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length); + return dr(list, rest2); + } +} + + + +/** + * Decode a list payload (non-prefixed) into an `Array`. + * + * Repeatedly decodes an element off the list until remainder is empty. + * + * @internal + */ +function +decode_list + (bytes: Uint8Array) + : Array +{ + let arr : Array = []; + while (bytes.length > 0) { + // grab an item off the bytes + let {decoded_data, remainder} = decode(bytes); + // push it + arr.push(decoded_data); + // update bytes + bytes = remainder; + } + return arr; +} + + + +/** + * Convert bytestring to number + * + * @internal + */ +function +bytes_to_number + (bytes: Uint8Array) + : number +{ + let n : number = 0; + for (let b of bytes) + { + n <<= 8; + n += b; + } + return n; +} + + + +/** + * Make a `decode_result` containing the two arguments + * + * @internal + */ +function +dr + (x : decoded_data, + y : Uint8Array) + : decode_result +{ + return {decoded_data : x, + remainder : y}; +} + + + +//============================================================================= +//============================================================================= +// ENCODING +//============================================================================= +//============================================================================= + +/** + * Encode some decoded data + */ +function +encode + (data: decoded_data) + : Uint8Array +{ + // is it an array or data + if + (is_binary(data)) { + return encode_binary(data as Uint8Array); + } + else if + (is_list(data)) { + return encode_list(data as Array); + } + else { + throw new Error('encode told to encode something that is not an array or a binary: ' + data); + } +} + + + +/** + * Encode a binary into RLP + * + * @internal + */ +function +encode_binary + (bytes: Uint8Array) + : Uint8Array +{ + let len: number = bytes.length; + // single byte case when the byte is between 0..127 + // result is the bytestring containing the byte itself + if + ((len === 1) && + (bytes[0] <= 127)){ + return bytes; + } + // if the bytestring is 0..55 bytes long, the first byte in the result is + // 128 + Length, the rest of the result bytestring is the input string + else if + (len <= 55) { + // construct the result + // <<128 + Len, Bytes/binary>> + let result : Uint8Array = new Uint8Array(len + 1); + // first byte is 128 + length + result[0] = 128 + len; + // copy input bytes into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + let result_idx0 : number = input_idx0 + 1; + result[result_idx0] = bytes[input_idx0]; + } + return result; + } + // if the bytestring is more than 55 bytes long, the first byte is 183 + + // ByteLengthOfByteLength, followed by the byte length, followed by the + // bytes + else { + let len_bytes : Uint8Array = encode_unsigned(len); + let len_bytes_length : number = len_bytes.length; + // total array is + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len); + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + result[0] = 183 + len_bytes_length; + // copy len_bytes into result + for (let len_bytes_idx0 = 0; + len_bytes_idx0 < len_bytes_length; + len_bytes_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = len_bytes_idx0 + 1; + result[result_idx0] = len_bytes[len_bytes_idx0]; + } + // copy original byte array into result + for (let input_idx0 = 0; + input_idx0 < len; + input_idx0++) + { + // <<183 + len_bytes_length, len_bytes, Bytes>> + // 1 byte len_bytes_length bytes len bytes + // + // ^ YOU ARE HERE + let result_idx0: number = input_idx0 + (1 + len_bytes_length); + result[result_idx0] = bytes[input_idx0]; + } + // finally, return the result + return result; + } +} + + + +/** + * Encode a list + * + * @internal + */ +function +encode_list + (list: Array) + : Uint8Array +{ + // first encode every element in the list, then branch + let payloads : Array = list.map(encode); + let payload : Uint8Array = uint8arr_concat(payloads); + let payload_size : number = payload.length; + // if the payload size is in 0..55, then the first byte is 192 + payload + // size, followed by the payload + if + (payload_size <= 55) { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + let result : Uint8Array = new Uint8Array(1 + payload_size); + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + result[0] = 192 + payload_size; + // copy the rest of the payload into result + for (let payload_idx0 = 0; + payload_idx0 < payload_size; + payload_idx0++) + { + // result: <<(192 + Payload_Size), Payload/binary >>; + // 1 byte payload_size bytes + // + // ^ YOU ARE HERE + let result_idx0: number = payload_idx0 + 1; + result[result_idx0] = payload[payload_idx0]; + } + return result; + } + // if the payload size is greater than 55, the first byte is 247 + + // size_of_payload_size, followed by the payload size, followed by the + // payload + else { + // compute the payload size size + let payload_size_bytes : Uint8Array = encode_unsigned(payload_size); + let payload_size_size : number = payload_size_bytes.length; + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size); + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // first byte is 247 + payload_size_size + result[0] = 247 + payload_size_size; + // copy the payload_size_bytes into result + for (let psb_idx0 = 0; + psb_idx0 < payload_size_size; + psb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 byte for this + let result_idx0 : number = psb_idx0 + 1; + result[result_idx0] = payload_size_bytes[psb_idx0]; + } + // copy the payload into result + for (let pb_idx0 = 0; + pb_idx0 < payload_size; + pb_idx0++) + { + // result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >> + // 1 byte payload_size_size bytes payload_size bytes + // + // ^ YOU ARE HERE + // offset is 1 + payload_size_size bytes for this + let result_idx0 : number = pb_idx0 + (1 + payload_size_size); + result[result_idx0] = payload[pb_idx0]; + } + // finally, return result + return result; + } +} + + + +/** + * Encode a number as a bytestring + * + * Not for general use, this assumes the input number is greater than 55 + * + * @internal + */ +function +encode_unsigned + (n: number) + : Uint8Array +{ + // can assume that n initially is greater than 55 + // have to encode it in reverse order + let arr : Array = []; + // repeated division by 256 with remainder + // + // example: suppose bign was 1234 and we were dividing by 10 + // + // iteration 0: + // bign = 1234 + // arr = [] + // --- + // bign / 10 = 123 + // bign % 10 = 4 + // --- + // bign = 123 + // arr = [4] + // + // iteration 1: + // bign = 123 + // arr = [4] + // --- + // bign / 10 = 12 + // bign % 10 = 3 + // --- + // bign = 12 + // arr = [4, 3] + // + // iteration 2: + // bign = 12 + // arr = [4, 3] + // --- + // bign / 10 = 1 + // bign % 10 = 2 + // --- + // bign = 1 + // arr = [4, 3, 2] + // + // iteration 3: + // bign = 1 + // arr = [4, 3, 2] + // --- + // bign / 10 = 0 + // bign % 10 = 1 + // --- + // bign = 0 + // arr = [4, 3, 2, 1] + // + // iteration 4: + // bign = 0 + // arr = [4, 3, 2, 1] + // --- + // DONE + while (n > 0) { + arr.push(n % 256); + n >>= 8; + } + // reverse and make into Uint8Array + arr.reverse(); + return new Uint8Array(arr); +} + + + +/** + * concatenate an array of `Uint8Array`s into a single Uint8Array + * + * @internal + */ +function +uint8arr_concat + (arrs: Array) + : Uint8Array +{ + // total length + let total_len = arrs.reduce(// fold + function (acc_len: number, this_uint8array: Uint8Array): number { + return acc_len + this_uint8array.length; + }, + // initial accumulator + 0); + // start up result + let result : Uint8Array = new Uint8Array(total_len); + let result_idx0 : number = 0; + // concatenate + for (let this_uint8arr of arrs) { + // bytewise copy of this uint8array + for (let this_uint8arr_idx0 = 0; + this_uint8arr_idx0 < this_uint8arr.length; + this_uint8arr_idx0++) + { + // copy byte and increment result idx0 + result[result_idx0] = this_uint8arr[this_uint8arr_idx0]; + result_idx0++; + } + } + return result; +} + +/** + * returns true if input is `instanceof Uint8Array` + * + * @internal + */ +function +is_binary + (x: any) + : boolean +{ + return (x instanceof Uint8Array); +} + + + +/** + * returns true if input is `instanceof Array` + * + * @internal + */ +function +is_list + (x: any) + : boolean +{ + return (x instanceof Array); +} diff --git a/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts new file mode 100644 index 0000000..b3e4c0c --- /dev/null +++ b/libs/vdk_tests_cases/src/jex_include/local-vdk-0.1.0/src/safe.ts @@ -0,0 +1,112 @@ +/** + * "Safe" error handling + * + * The idea here is that there are situations where it is known + * + * ```typescript + * type Safe + * = Ok + * | Error; + * + * type Ok + * = {ok : true, + * result : ok_t}; + * + * type Error + * = {ok : false, + * error : err_t}; + * ``` + * + * 1. a given function call is likely to fail + * 2. the likely errors can be enumerated + * + * These are called "positive errors". An example would be a page script + * asking a browser wallet extension to sign a transaction. The following + * errors, among others, are likely: + * + * - the user does not have a wallet installed + * - the user has a wallet but does not have the correct signing key + * - the user rejects the transaction + * - the sign request timed out + * + * These errors should not generate exceptions, as these behaviors are to some + * degree "expected". + */ + +export { + Safe, + Ok, + Error, + ok, + error, + unsafe +} + + +/** + * Type that catches positive errors + */ +type Safe + = Ok + | Error; + + +/** + * Ok type + */ +type Ok + = {ok : true, + result : ok_t}; + + +/** + * Error type + */ +type Error + = {ok : false, + error : err_t}; + + +/** + * Constructs an `Ok` value from a pure value + */ +function +ok + + (x : ok_t) + : Ok +{ + return {ok: true, result: x}; +} + + + +/** + * Constructs an `Error` value from a pure value + */ +function +error + + (x: err_t) + : Error +{ + return {ok: false, error: x}; +} + + + +/** + * Takes a `Safe` value, if `ok`, returns the `ok_t`, or if an error throws the + * `err_t` + */ +function +unsafe + + (x: Safe) + : ok_t +{ + if (x.ok) + return x.result; + else + throw x.error; +} diff --git a/libs/vdk_cases/src/rlpcases.ts b/libs/vdk_tests_cases/src/rlpcases.ts similarity index 100% rename from libs/vdk_cases/src/rlpcases.ts rename to libs/vdk_tests_cases/src/rlpcases.ts diff --git a/libs/vdk_cases/testgen/.gitignore b/libs/vdk_tests_cases/testgen/.gitignore similarity index 100% rename from libs/vdk_cases/testgen/.gitignore rename to libs/vdk_tests_cases/testgen/.gitignore diff --git a/libs/vdk_cases/testgen/b58.erl b/libs/vdk_tests_cases/testgen/b58.erl similarity index 100% rename from libs/vdk_cases/testgen/b58.erl rename to libs/vdk_tests_cases/testgen/b58.erl diff --git a/libs/vdk_cases/testgen/b58.py b/libs/vdk_tests_cases/testgen/b58.py similarity index 100% rename from libs/vdk_cases/testgen/b58.py rename to libs/vdk_tests_cases/testgen/b58.py diff --git a/libs/vdk_cases/testgen/b58_2.erl b/libs/vdk_tests_cases/testgen/b58_2.erl similarity index 100% rename from libs/vdk_cases/testgen/b58_2.erl rename to libs/vdk_tests_cases/testgen/b58_2.erl diff --git a/libs/vdk_cases/testgen/b58_cases b/libs/vdk_tests_cases/testgen/b58_cases similarity index 100% rename from libs/vdk_cases/testgen/b58_cases rename to libs/vdk_tests_cases/testgen/b58_cases diff --git a/libs/vdk_cases/testgen/b58_cases.eterms b/libs/vdk_tests_cases/testgen/b58_cases.eterms similarity index 100% rename from libs/vdk_cases/testgen/b58_cases.eterms rename to libs/vdk_tests_cases/testgen/b58_cases.eterms diff --git a/libs/vdk_cases/testgen/bits.erl b/libs/vdk_tests_cases/testgen/bits.erl similarity index 100% rename from libs/vdk_cases/testgen/bits.erl rename to libs/vdk_tests_cases/testgen/bits.erl diff --git a/libs/vdk_cases/testgen/rlp.erl b/libs/vdk_tests_cases/testgen/rlp.erl similarity index 100% rename from libs/vdk_cases/testgen/rlp.erl rename to libs/vdk_tests_cases/testgen/rlp.erl diff --git a/libs/vdk_cases/testgen/rlp_testgen.erl b/libs/vdk_tests_cases/testgen/rlp_testgen.erl similarity index 100% rename from libs/vdk_cases/testgen/rlp_testgen.erl rename to libs/vdk_tests_cases/testgen/rlp_testgen.erl diff --git a/libs/vdk_cases/testgen/rlpcases.py b/libs/vdk_tests_cases/testgen/rlpcases.py similarity index 100% rename from libs/vdk_cases/testgen/rlpcases.py rename to libs/vdk_tests_cases/testgen/rlpcases.py diff --git a/libs/vdk_cases/testgen/rlptests.py b/libs/vdk_tests_cases/testgen/rlptests.py similarity index 100% rename from libs/vdk_cases/testgen/rlptests.py rename to libs/vdk_tests_cases/testgen/rlptests.py diff --git a/libs/vdk_cases/testgen/tg_b64.erl b/libs/vdk_tests_cases/testgen/tg_b64.erl similarity index 100% rename from libs/vdk_cases/testgen/tg_b64.erl rename to libs/vdk_tests_cases/testgen/tg_b64.erl diff --git a/libs/vdk_tests_cases/tsconfig.json b/libs/vdk_tests_cases/tsconfig.json new file mode 100644 index 0000000..f240cdc --- /dev/null +++ b/libs/vdk_tests_cases/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/jex_include"], + "composite" : true} diff --git a/libs/sidekick/.gitignore b/sidekick/.gitignore similarity index 100% rename from libs/sidekick/.gitignore rename to sidekick/.gitignore diff --git a/libs/sidekick/LICENSE b/sidekick/LICENSE similarity index 100% rename from libs/sidekick/LICENSE rename to sidekick/LICENSE diff --git a/libs/sidekick/README.md b/sidekick/README.md similarity index 100% rename from libs/sidekick/README.md rename to sidekick/README.md diff --git a/libs/sidekick/icons/sidekick_icon_128.png b/sidekick/icons/sidekick_icon_128.png similarity index 100% rename from libs/sidekick/icons/sidekick_icon_128.png rename to sidekick/icons/sidekick_icon_128.png diff --git a/libs/sidekick/icons/sidekick_icon_128.xcf b/sidekick/icons/sidekick_icon_128.xcf similarity index 100% rename from libs/sidekick/icons/sidekick_icon_128.xcf rename to sidekick/icons/sidekick_icon_128.xcf diff --git a/libs/sidekick/jex.eterms b/sidekick/jex.eterms similarity index 100% rename from libs/sidekick/jex.eterms rename to sidekick/jex.eterms diff --git a/libs/sidekick/scratch/STYLE_GUIDE.md b/sidekick/scratch/STYLE_GUIDE.md similarity index 100% rename from libs/sidekick/scratch/STYLE_GUIDE.md rename to sidekick/scratch/STYLE_GUIDE.md diff --git a/libs/sidekick/scratch/TODO.md b/sidekick/scratch/TODO.md similarity index 100% rename from libs/sidekick/scratch/TODO.md rename to sidekick/scratch/TODO.md diff --git a/libs/sidekick/scratch/async_tests.html b/sidekick/scratch/async_tests.html similarity index 100% rename from libs/sidekick/scratch/async_tests.html rename to sidekick/scratch/async_tests.html diff --git a/libs/sidekick/scratch/async_tests.js b/sidekick/scratch/async_tests.js similarity index 100% rename from libs/sidekick/scratch/async_tests.js rename to sidekick/scratch/async_tests.js diff --git a/libs/sidekick/scratch/examples_old/contract-examples/LICENSE b/sidekick/scratch/examples_old/contract-examples/LICENSE similarity index 100% rename from libs/sidekick/scratch/examples_old/contract-examples/LICENSE rename to sidekick/scratch/examples_old/contract-examples/LICENSE diff --git a/libs/sidekick/scratch/examples_old/contract-examples/base_contract.aes b/sidekick/scratch/examples_old/contract-examples/base_contract.aes similarity index 100% rename from libs/sidekick/scratch/examples_old/contract-examples/base_contract.aes rename to sidekick/scratch/examples_old/contract-examples/base_contract.aes diff --git a/libs/sidekick/scratch/examples_old/contract-examples/crypto_hamster.aes b/sidekick/scratch/examples_old/contract-examples/crypto_hamster.aes similarity index 100% rename from libs/sidekick/scratch/examples_old/contract-examples/crypto_hamster.aes rename to sidekick/scratch/examples_old/contract-examples/crypto_hamster.aes diff --git a/libs/sidekick/scratch/examples_old/contract-examples/floyd.aes b/sidekick/scratch/examples_old/contract-examples/floyd.aes similarity index 100% rename from libs/sidekick/scratch/examples_old/contract-examples/floyd.aes rename to sidekick/scratch/examples_old/contract-examples/floyd.aes diff --git a/libs/sidekick/scratch/examples_old/contract-examples/sales_contract.aes b/sidekick/scratch/examples_old/contract-examples/sales_contract.aes similarity index 100% rename from libs/sidekick/scratch/examples_old/contract-examples/sales_contract.aes rename to sidekick/scratch/examples_old/contract-examples/sales_contract.aes diff --git a/libs/sidekick/scratch/examples_old/examples_html/do_a_transfer.html b/sidekick/scratch/examples_old/examples_html/do_a_transfer.html similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_html/do_a_transfer.html rename to sidekick/scratch/examples_old/examples_html/do_a_transfer.html diff --git a/libs/sidekick/scratch/examples_old/examples_html/hello.html b/sidekick/scratch/examples_old/examples_html/hello.html similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_html/hello.html rename to sidekick/scratch/examples_old/examples_html/hello.html diff --git a/libs/sidekick/scratch/examples_old/examples_html/ide.html b/sidekick/scratch/examples_old/examples_html/ide.html similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_html/ide.html rename to sidekick/scratch/examples_old/examples_html/ide.html diff --git a/libs/sidekick/scratch/examples_old/examples_html/maerket.html b/sidekick/scratch/examples_old/examples_html/maerket.html similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_html/maerket.html rename to sidekick/scratch/examples_old/examples_html/maerket.html diff --git a/libs/sidekick/scratch/examples_old/examples_src_ts/do_a_transfer.ts b/sidekick/scratch/examples_old/examples_src_ts/do_a_transfer.ts similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_src_ts/do_a_transfer.ts rename to sidekick/scratch/examples_old/examples_src_ts/do_a_transfer.ts diff --git a/libs/sidekick/scratch/examples_old/examples_src_ts/ide.ts b/sidekick/scratch/examples_old/examples_src_ts/ide.ts similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_src_ts/ide.ts rename to sidekick/scratch/examples_old/examples_src_ts/ide.ts diff --git a/libs/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts b/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts rename to sidekick/scratch/examples_old/examples_src_ts/parasite/ae_compiler.ts diff --git a/libs/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_node.ts b/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_node.ts similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_src_ts/parasite/ae_node.ts rename to sidekick/scratch/examples_old/examples_src_ts/parasite/ae_node.ts diff --git a/libs/sidekick/scratch/examples_old/examples_src_ts/parasite/net.ts b/sidekick/scratch/examples_old/examples_src_ts/parasite/net.ts similarity index 100% rename from libs/sidekick/scratch/examples_old/examples_src_ts/parasite/net.ts rename to sidekick/scratch/examples_old/examples_src_ts/parasite/net.ts diff --git a/libs/sidekick/scratch/examples_old/favicon.ico b/sidekick/scratch/examples_old/favicon.ico similarity index 100% rename from libs/sidekick/scratch/examples_old/favicon.ico rename to sidekick/scratch/examples_old/favicon.ico diff --git a/libs/sidekick/scratch/examples_old/index.html b/sidekick/scratch/examples_old/index.html similarity index 100% rename from libs/sidekick/scratch/examples_old/index.html rename to sidekick/scratch/examples_old/index.html diff --git a/libs/sidekick/scratch/examples_old/style.css b/sidekick/scratch/examples_old/style.css similarity index 100% rename from libs/sidekick/scratch/examples_old/style.css rename to sidekick/scratch/examples_old/style.css diff --git a/libs/sidekick/scratch/examples_old/tsconfig.json b/sidekick/scratch/examples_old/tsconfig.json similarity index 100% rename from libs/sidekick/scratch/examples_old/tsconfig.json rename to sidekick/scratch/examples_old/tsconfig.json diff --git a/libs/sidekick/scratch/examples_old/why.html b/sidekick/scratch/examples_old/why.html similarity index 100% rename from libs/sidekick/scratch/examples_old/why.html rename to sidekick/scratch/examples_old/why.html diff --git a/libs/sidekick/scratch/logging.ts b/sidekick/scratch/logging.ts similarity index 100% rename from libs/sidekick/scratch/logging.ts rename to sidekick/scratch/logging.ts diff --git a/libs/sidekick/scratch/old-makefile b/sidekick/scratch/old-makefile similarity index 100% rename from libs/sidekick/scratch/old-makefile rename to sidekick/scratch/old-makefile diff --git a/libs/sidekick/scratch/old-readme.md b/sidekick/scratch/old-readme.md similarity index 100% rename from libs/sidekick/scratch/old-readme.md rename to sidekick/scratch/old-readme.md diff --git a/libs/sidekick/scratch/scratch.txt b/sidekick/scratch/scratch.txt similarity index 100% rename from libs/sidekick/scratch/scratch.txt rename to sidekick/scratch/scratch.txt diff --git a/libs/sidekick/src/sidekick.ts b/sidekick/src/sidekick.ts similarity index 100% rename from libs/sidekick/src/sidekick.ts rename to sidekick/src/sidekick.ts diff --git a/sidekick/tsconfig.json b/sidekick/tsconfig.json new file mode 100644 index 0000000..f240cdc --- /dev/null +++ b/sidekick/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/jex_include"], + "composite" : true} diff --git a/libs/sidekick_examples/.gitignore b/sidekick_examples/.gitignore similarity index 100% rename from libs/sidekick_examples/.gitignore rename to sidekick_examples/.gitignore diff --git a/libs/sidekick_examples/LICENSE b/sidekick_examples/LICENSE similarity index 100% rename from libs/sidekick_examples/LICENSE rename to sidekick_examples/LICENSE diff --git a/libs/sidekick_examples/README.md b/sidekick_examples/README.md similarity index 100% rename from libs/sidekick_examples/README.md rename to sidekick_examples/README.md diff --git a/libs/sidekick_examples/connection.html b/sidekick_examples/connection.html similarity index 100% rename from libs/sidekick_examples/connection.html rename to sidekick_examples/connection.html diff --git a/libs/sidekick_examples/favicon.ico b/sidekick_examples/favicon.ico similarity index 100% rename from libs/sidekick_examples/favicon.ico rename to sidekick_examples/favicon.ico diff --git a/libs/sidekick_examples/hello.html b/sidekick_examples/hello.html similarity index 100% rename from libs/sidekick_examples/hello.html rename to sidekick_examples/hello.html diff --git a/libs/sidekick_examples/index.html b/sidekick_examples/index.html similarity index 100% rename from libs/sidekick_examples/index.html rename to sidekick_examples/index.html diff --git a/libs/sidekick_examples/jex.eterms b/sidekick_examples/jex.eterms similarity index 100% rename from libs/sidekick_examples/jex.eterms rename to sidekick_examples/jex.eterms diff --git a/libs/sidekick_examples/scratch/logging.html b/sidekick_examples/scratch/logging.html similarity index 100% rename from libs/sidekick_examples/scratch/logging.html rename to sidekick_examples/scratch/logging.html diff --git a/libs/sidekick_examples/scratch/logging.ts b/sidekick_examples/scratch/logging.ts similarity index 100% rename from libs/sidekick_examples/scratch/logging.ts rename to sidekick_examples/scratch/logging.ts diff --git a/libs/sidekick_examples/scratch/logging_http.html b/sidekick_examples/scratch/logging_http.html similarity index 100% rename from libs/sidekick_examples/scratch/logging_http.html rename to sidekick_examples/scratch/logging_http.html diff --git a/libs/sidekick_examples/scratch/logging_http.ts b/sidekick_examples/scratch/logging_http.ts similarity index 100% rename from libs/sidekick_examples/scratch/logging_http.ts rename to sidekick_examples/scratch/logging_http.ts diff --git a/libs/sidekick_examples/src/connection.ts b/sidekick_examples/src/connection.ts similarity index 100% rename from libs/sidekick_examples/src/connection.ts rename to sidekick_examples/src/connection.ts diff --git a/libs/sidekick_examples/src/hello.ts b/sidekick_examples/src/hello.ts similarity index 100% rename from libs/sidekick_examples/src/hello.ts rename to sidekick_examples/src/hello.ts diff --git a/sidekick_examples/tsconfig.json b/sidekick_examples/tsconfig.json new file mode 100644 index 0000000..f240cdc --- /dev/null +++ b/sidekick_examples/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/jex_include"], + "composite" : true}