* move stuff in here * reorganizing because zx needs to feel special * add base58/base64 explainer draft
491 lines
17 KiB
Markdown
491 lines
17 KiB
Markdown
# Sidekick
|
|
|
|
Sidekick is a simple JavaScript library for talking to an Aeternity
|
|
browser wallet extension such as Superhero from the document context
|
|
of a webpage.
|
|
|
|
| **Thing** | **URL** |
|
|
| --- | --- |
|
|
| Bug Tracker | https://gitlab.com/DoctorAjayKumar/sidekick/-/issues |
|
|
| Documentation | http://orangepill.healthcare/projects/sidekick/current/docs |
|
|
| Examples | https://gitlab.com/DoctorAjayKumar/sidekick/-/tree/master/examples |
|
|
| Git repository | https://gitlab.com/DoctorAjayKumar/sidekick |
|
|
| Homepage | http://orangepill.healthcare/projects/sidekick |
|
|
| License | ./LICENSE.txt |
|
|
| Maintainer | Dr. Ajay Kumar PHD <DoctorAjayKumar@protonmail.com> |
|
|
| Releases | http://orangepill.healthcare/projects/sidekick/releases |
|
|
|
|
|
|
> One of the most important things for designing a computer, which I
|
|
> think most designers don't do, is you study the problem you want to
|
|
> solve. And then use what you learn from studying the problem you
|
|
> want to solve to put in the mechanisms needed to solve it in the
|
|
> computer you're building. No more, no less.
|
|
|
|
— Gerald Jay Sussman
|
|
|
|
### What sidekick is NOT
|
|
|
|
Sidekick is **not** ideal if you operate in the Node/NPM ecosystem. If you are
|
|
working in the Node/NPM ecosystem, you probably want the [Aeternity JavaScript
|
|
SDK](https://github.com/aeternity/aepp-sdk-js/). Every single
|
|
Aeternity-related thing you could ever possibly want to do is possible to
|
|
accomplish with the SDK.
|
|
|
|
All that Sidekick knows how to do is talk to a browser wallet extension. In an
|
|
application, there is a great deal of necessary functionality (e.g. forming
|
|
transactions for the wallet to sign) which sidekick assumes your server-side
|
|
code has already handled.
|
|
|
|
In all software there is a tradeoff between simplicity and number of features.
|
|
Sidekick is very simple: 2300 lines of TypeScript, including comments, with no
|
|
dependencies. Therefore Sidekick intentionally only has a very limited set of
|
|
features.
|
|
|
|
|
|
# How to use this library
|
|
|
|
To obtain this library, download and unpack a release tarball
|
|
|
|
wget http://zxq9.com/projects/vanillae/sidekick/releases/sidekick_dist_X-Y-Z.tar.gz
|
|
tar xzvf sidekick_dist_X-Y-Z.tar.gz
|
|
|
|
You *should* be able to get all of the functionality you need just
|
|
from the exposed functions in the `sidekick` module. (If this is not
|
|
the case, please report this as a bug.)
|
|
|
|
You probably want to read the documentation of:
|
|
|
|
sidekick.js : top-level function calls
|
|
awcp/awcp.js : explains the return types of top-level sidekick
|
|
functions, explains what sidekick actually does
|
|
for you, and explains how all the various
|
|
modules fit together
|
|
skylight.js : main data structure in sidekick
|
|
helpers.js : what it sounds like
|
|
|
|
There are examples explained in this document. Their source can be
|
|
found in full in the GitLab repository, link above. The examples
|
|
exhibit how to interact with the standard release tarball using
|
|
TypeScript.
|
|
|
|
|
|
## General flow of Sidekick usage
|
|
|
|
1. Import the sidekick.js file:
|
|
|
|
```typescript
|
|
import * as sk from '/path/to/sidekick_dist_X-Y-Z/dist_js/sidekick.js'
|
|
```
|
|
|
|
2. Create a `Skylight`. This is the main data structure of Sidekick.
|
|
|
|
There are two ways to do this
|
|
|
|
1. `start_dwim() : Promise<Skylight>`
|
|
|
|
This is probably the one you want. It starts up the Skylight
|
|
and does the handshake with the wallet (will pop up the
|
|
little window for your user). The effect of this is to
|
|
populate all the variables like the user's public key.
|
|
|
|
Note that this is an async function. It doesn't return until
|
|
the handshake is complete.
|
|
|
|
```typescript
|
|
// Needs to be wrapped in an async function in order to use
|
|
// await keyword
|
|
async function main() : Promise<void> {
|
|
let my_skylight = await sk.start_dwim();
|
|
...
|
|
}
|
|
|
|
main();
|
|
```
|
|
|
|
2. `start : Skylight`
|
|
|
|
This is the vanilla option. All it does is create the
|
|
Skylight object.
|
|
|
|
Beware that this does start up a message queue that passively
|
|
listens for messages from the wallet.
|
|
|
|
|
|
3. Pass the skylight in to various functions along with additional
|
|
calldata generated by your server-side backend code.
|
|
|
|
|
|
## Example 1: Hello World
|
|
|
|
The effect of this example is to print "hello world" to the JS
|
|
console (Ctrl+Shift+C in most browsers). This example is included for
|
|
debugging/mental-quicksand-escape purposes.
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Hello world: Sidekick</title>
|
|
</head>
|
|
<body>
|
|
<h1>Sidekick Example: Hello World</h1>
|
|
|
|
<h4>Check the console</h4>
|
|
<script type="module">
|
|
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
|
sk.hello();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
The important lines are
|
|
|
|
```html
|
|
<script type="module">
|
|
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
|
sk.hello();
|
|
</script>
|
|
```
|
|
|
|
This shows
|
|
|
|
- you need to use `type="module"` in order for imports to work
|
|
- how to do an import
|
|
- how to call functions from an imported module
|
|
|
|
|
|
## Example 2: get the user to sign a transaction
|
|
|
|
Anything you will want the user to do (e.g. sign smart contracts) is
|
|
contained in "get the user to sign a transaction". This example
|
|
illustrates the basic things you need to do in order to accomplish
|
|
that.
|
|
|
|
Sidekick does not provide functionality for forming the transaction
|
|
string you want the user to sign. Your backend should be doing that.
|
|
|
|
The example here uses a shim library called `parasite`, which you can find in
|
|
the GitLab repository in the `examples` directory. Parasite is just a rewrite
|
|
layer in front of the `testnet.aeternity.io` JSON interface. It is not suitable
|
|
for usage in production.
|
|
|
|
This is adapted from
|
|
[`examples/examples_src_ts/do_a_transfer.ts`][dat_ts]. The
|
|
difference is that `do_a_transfer.ts` is written against
|
|
[`examples/examples_html/do_a_transfer.html`][dat_html]. It contains
|
|
a lot of unnecessary detail, like doing things in response to buttons
|
|
being clicked, and filling in DOM textboxes with the results of things.
|
|
|
|
The full example has logic that resembles a register machine, rather
|
|
than a functional program. This example is closer to a functional
|
|
program.
|
|
|
|
[dat_html]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_html/do_a_transfer.html
|
|
[dat_ts]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_src_ts/do_a_transfer.ts
|
|
|
|
|
|
```typescript
|
|
import * as ae_node from './parasite/ae_node.js';
|
|
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
|
|
|
async function
|
|
main()
|
|
: Promise<void>
|
|
{
|
|
//---------------------------------------------------------------
|
|
// STEP 1: MAKE A SKYLIGHT
|
|
//---------------------------------------------------------------
|
|
let skl : sk.Skylight =
|
|
await sk.start_dwim(sk.TIMEOUT_DEF_DETECT,
|
|
sk.TIMEOUT_DEF_CONNECT,
|
|
sk.TIMEOUT_DEF_ADDRESS);
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
// STEP 2: GET THE USER'S ADDRESS
|
|
//---------------------------------------------------------------
|
|
let user_addr : string =
|
|
await sk.address(skl, sk.TIMEOUT_DEF_ADDRESS);
|
|
|
|
// faster/non-async:
|
|
//
|
|
// let user_addr = skl.waellet_address
|
|
//
|
|
// the non-commented code
|
|
//
|
|
// - will crash if the wallet isn't connected
|
|
// - will query the wallet for the address if the wallet *is*
|
|
// connected but the skl.waellet_address field *is not*
|
|
// populated
|
|
// - will crash if that times out after the second argument
|
|
// number of milliseconds
|
|
//
|
|
// if you know with 100% certainty that the address field will be
|
|
// populated (which we do here, because we used connect_dwim),
|
|
// then it's safe to just grab the field from the skylight
|
|
//
|
|
// The problem is that if the address field isn't populated, the
|
|
// commented code defaults to `undefined`, rather than crashing
|
|
// with an error message and callstack trace
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
// STEP 3: FORM THE TRANSACTION
|
|
//
|
|
// This will be different for your application, because you're
|
|
// not using parasite (right?)
|
|
//
|
|
// You need to figure out how to make something like `tx_obj` on
|
|
// your own.
|
|
//
|
|
// `tx_obj` is just `{tx: "some_base58_garbage"}`.
|
|
//---------------------------------------------------------------
|
|
|
|
let target_addr : string =
|
|
'ak_dvNHMgVvdSgDchLsmcUpuFTbMBGfG3E5V9KZnNjLYPyEhcqnL';
|
|
|
|
// amount is in aettos
|
|
let amount : number = 1;
|
|
let endpoint : string = ae_node.URL_TESTNET;
|
|
let spendtx = {'recipient_id' : target_addr,
|
|
'amount' : amount,
|
|
'fee' : ae_node.MIN_FEE,
|
|
'sender_id' : user_addr,
|
|
'payload' : ""};
|
|
let tx_obj = await ae_node.PostSpend(endpoint, spendtx);
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
// STEP 4: HAVE THE USER SIGN THE TRANSACTION
|
|
//
|
|
// There are two options:
|
|
//
|
|
// 1. `tx_sign_no_propagate` simply has the wallet sign the
|
|
// transaction, and return the signed transaction back to you
|
|
//
|
|
// 2. `tx_sign_yes_propagate` has the wallet sign the transaction
|
|
// and also propagates it into the network. It returns a more
|
|
// elaborate data structure, which is documented in somewhere
|
|
// the AWCP module documentation.
|
|
//---------------------------------------------------------------
|
|
|
|
let result =
|
|
await sk.tx_sign_no_propagate(skl,
|
|
tx_obj,
|
|
sk.NETWORK_ID_TESTNET,
|
|
sk.TIMEOUT_DEF_SIGN);
|
|
|
|
console.log(result);
|
|
|
|
}
|
|
|
|
main();
|
|
```
|
|
|
|
|
|
# Known pitfalls
|
|
|
|
- See notes in sidekick module about potential state crossups if your
|
|
document logic is multi-threaded.
|
|
|
|
- Has only been tested against Firefox-on-Linux
|
|
|
|
- Have not worked out good user idioms for handling errors
|
|
|
|
|
|
|
|
|
|
|
|
# How the release is structured and why
|
|
|
|
The idea of the release is that it contains exactly what is needed in
|
|
order to drop Sidekick into your project and start using it. No more,
|
|
no less.
|
|
|
|
A release has the following structure:
|
|
|
|
```
|
|
sidekick_dist_X-Y-Z/
|
|
dist_js/............tsc-generated human-readable JS tree
|
|
foo.js..............the actual code that is run
|
|
foo.d.ts............included so that your TypeScript code can
|
|
typecheck against sidekick
|
|
foo.js.map..........debug symbols that map foo.js to
|
|
locations in the TypeScript source
|
|
src_ts/.............included so that the browser's debugger works
|
|
foo.ts
|
|
README.txt
|
|
LICENSE.txt
|
|
```
|
|
|
|
We use semantic versioning: `X.Y.Z`
|
|
|
|
- A change in `X` means an API-breaking change
|
|
- A change in `Y` means an non-breaking API change
|
|
- A change in `Z` means no change to the API
|
|
|
|
There is no documentation included in the release. There is
|
|
autogenerated API documentation linked above, which is generated from
|
|
this file and the sources that are included in the release.
|
|
|
|
|
|
## What each file does
|
|
|
|
```
|
|
src_ts/.....................TypeScript source for sidekick library
|
|
awcp/.......................Aepp-Waellet Communication Protocol
|
|
awcp.ts.....................Protocol definition
|
|
msgq.ts.....................Block-on-raseev implementation
|
|
msgr.ts.....................Protocol implementation
|
|
helpers.ts..................what it sounds like
|
|
sidekick.ts.................top-level module
|
|
skylight.ts.................primary data structure
|
|
```
|
|
|
|
|
|
## Where is the NPM package or the webpack bundle?
|
|
|
|
There isn't one.
|
|
|
|
|
|
### Why?
|
|
|
|
Sidekick is a library that deals with cryptocurrency. The security
|
|
model is based on transparency and trust. A user must be able to
|
|
inspect code that is running on his hardware handling his money.
|
|
|
|
We don't support NPM because of the security issues that NPM
|
|
introduces. Briefly, the code can change at any time under the
|
|
developer's nose without the developer knowing. The [leftpad
|
|
debacle][lpad] and the [RIAEvangelist debacle][ria] are good examples
|
|
of the types of vulnerabilities that package managers like NPM
|
|
enable.
|
|
|
|
[lpad]: https://archive.ph/Qsh7j
|
|
[ria]: https://archive.ph/OF5I9
|
|
|
|
We don't use something like webpack because that introduces an opaque
|
|
rewrite using an untrusted tool. Webpack could plausibly alter the
|
|
runtime behavior of the program, and we would have no way to detect
|
|
that. Even if we trusted webpack, how do we obtain webpack? NPM.
|
|
So.
|
|
|
|
It is debatable whether or not we should trust the TypeScript
|
|
compiler (TSC). On the whole, TSC probably makes Sidekick more
|
|
secure, simply by virtue of increasing overall code quality and
|
|
eliminating the largest categories of potential bugs. Moreover, the
|
|
output that TSC produces is human-readable, and has a very
|
|
straightforward mapping to the original source code. A human can
|
|
easily read the TSC-generated JavaScript tree, even without the aid
|
|
of the source map, and have a high degree of faith that the code is
|
|
trustworthy and that TSC is behaving as promised.
|
|
|
|
|
|
|
|
# How to obtain the source tree
|
|
|
|
The source tree is included in your release. You can clone the
|
|
git repository with
|
|
|
|
```
|
|
git clone https://gitlab.com/DoctorAjayKumar/sidekick.git
|
|
```
|
|
|
|
|
|
|
|
# Repo file tree (non-exhaustive)
|
|
|
|
|
|
```
|
|
examples/...................Examples
|
|
contract-examples/..........Example Sophia smart contracts
|
|
examples_html/..............Low-budget example interfaces
|
|
do_a_transfer.html......Send money to an arbitrary address
|
|
hello.html..............Hello world
|
|
ide.html................Play around with smart contracts
|
|
examples_src_ts/............TypeScript source for each example
|
|
parasite/...................JavaScript shim that does what
|
|
your backend code ordinarily
|
|
would do
|
|
ae_compiler.ts..............Talk to a remote Sophia
|
|
compiler JSON HTTP interface
|
|
ae_node.ts..................Talk to a node (for forming
|
|
transactions, querying chain,
|
|
etc)
|
|
net.ts......................Network helper functions
|
|
do_a_transfer.ts........Send money to an arbitrary address
|
|
ide.ts..................Play around with smart contracts
|
|
tsconfig.json...............Examples-specific tsc configuration
|
|
src_ts/.....................TypeScript source for sidekick library
|
|
awcp/.......................Aepp-Waellet Communication Protocol
|
|
awcp.ts.....................Protocol definition
|
|
msgq.ts.....................Block-on-raseev implementation
|
|
msgr.ts.....................Protocol implementation
|
|
helpers.ts..................what it sounds like
|
|
sidekick.ts.................top-level module
|
|
skylight.ts.................primary data structure
|
|
LICENSE.txt.................MIT License
|
|
Makefile....................Makefile
|
|
make........................Equivalent to `make build`
|
|
make build..................Run tsc
|
|
make clean..................rm -r dist_js sidekick_dist docs \
|
|
examples/examples_dist_js
|
|
make dist...................Build a release tarball directory
|
|
make jsdoc..................Build the HTML documentation
|
|
make build_examples.........cd examples && tsc
|
|
make serve_examples.........cd examples && python3 -m \
|
|
http.server 8001
|
|
README.txt..................This file
|
|
STYLE_GUIDE.md..............Explains why the code looks so weird
|
|
TODO.md.....................what it sounds like
|
|
tsconfig.json...............Configuration file for tsc
|
|
```
|
|
|
|
|
|
|
|
# How to build a release
|
|
|
|
You will need the TypeScript compiler installed. See prereqs section.
|
|
|
|
make dist
|
|
|
|
|
|
## Prereqs for building source files
|
|
|
|
If you want to edit and rebuild the source files, you need TypeScript
|
|
installed, and you should update npm.
|
|
|
|
npm install -g typescript
|
|
npm install -g npm
|
|
|
|
## Protip: avoid using `sudo npm install -g`
|
|
|
|
If you want to avoid using sudo
|
|
|
|
```
|
|
mkdir ~/.npm-packages
|
|
npm config set prefix "${HOME}/.npm-packages"
|
|
```
|
|
|
|
Edit `~/.bashrc` or `~/.zshrc` with
|
|
|
|
```
|
|
NPM_PACKAGES="${HOME}/.npm-packages"
|
|
export PATH=$NPM_PACKAGES/bin:$PATH
|
|
```
|
|
|
|
Source: https://github.com/sindresorhus/guides/blob/main/npm-global-without-sudo.md
|
|
|
|
|
|
# How to build and view documentation
|
|
|
|
```
|
|
make build_docs
|
|
make serve_docs
|
|
```
|
|
|
|
|