Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TokenVM example #18

Merged
merged 88 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
f8c4f68
add tokenvm example
patrick-ogrady Feb 22, 2023
2d76b3d
remove unneeded actions
patrick-ogrady Feb 22, 2023
7338191
simplify storage
patrick-ogrady Feb 22, 2023
2d58bf8
remove unneeded errors
patrick-ogrady Feb 22, 2023
6a62332
update transfer to have assets
patrick-ogrady Feb 22, 2023
7d8f039
cleanup auth
patrick-ogrady Feb 22, 2023
045ff32
update client
patrick-ogrady Feb 22, 2023
03b3a02
update registry
patrick-ogrady Feb 22, 2023
a31b303
update genesis
patrick-ogrady Feb 22, 2023
3a00dd9
remove unneeded utils
patrick-ogrady Feb 22, 2023
a362375
cleanup cmd
patrick-ogrady Feb 22, 2023
ab5b13d
load test works
patrick-ogrady Feb 22, 2023
1d9630b
e2e tests are passing
patrick-ogrady Feb 22, 2023
5787c7c
integration passing
patrick-ogrady Feb 22, 2023
e17bcbe
update root .golangci.yml
patrick-ogrady Feb 22, 2023
a65c9e5
fix lint
patrick-ogrady Feb 22, 2023
0876ca6
add tokenvm static analysis
patrick-ogrady Feb 22, 2023
f055ccf
add more unit tests
patrick-ogrady Feb 22, 2023
f73fe23
run off current hypersdk
patrick-ogrady Feb 22, 2023
4fd3262
go mod tidy
patrick-ogrady Feb 22, 2023
a3b9e25
update README
patrick-ogrady Feb 22, 2023
da2edad
mint registered
patrick-ogrady Feb 22, 2023
3a477f1
basic mint integration test working
patrick-ogrady Feb 22, 2023
b9c23c5
add mint integration tests
patrick-ogrady Feb 22, 2023
8d5fceb
update Future Work
patrick-ogrady Feb 22, 2023
6fbb990
lint
patrick-ogrady Feb 22, 2023
5f3083d
add TODO to README
patrick-ogrady Feb 22, 2023
174ea26
modify Actions.StateKeys
patrick-ogrady Feb 22, 2023
833df24
update mocks
patrick-ogrady Feb 22, 2023
54c1541
add basic create order support
patrick-ogrady Feb 22, 2023
671697f
typo
patrick-ogrady Feb 22, 2023
afa9f18
fix asset sub
patrick-ogrady Feb 22, 2023
f220725
add close order support
patrick-ogrady Feb 22, 2023
ac4c8a4
add fill order
patrick-ogrady Feb 22, 2023
da976c0
add TODO for order book planning
patrick-ogrady Feb 22, 2023
9ed0c39
add order book
patrick-ogrady Feb 22, 2023
f47c18e
add order book updates
patrick-ogrady Feb 22, 2023
b5b8c7d
nits
patrick-ogrady Feb 22, 2023
0f6d062
fix mempool code
patrick-ogrady Feb 22, 2023
089698e
added orders to client
patrick-ogrady Feb 22, 2023
39d77cf
fix lint
patrick-ogrady Feb 22, 2023
3262cf9
pass lint in TokenVM
patrick-ogrady Feb 22, 2023
b30160f
mint to self from multiple accounts
patrick-ogrady Feb 22, 2023
6d3de05
create untracked order
patrick-ogrady Feb 22, 2023
c5abdf2
create tracked order
patrick-ogrady Feb 22, 2023
3041a50
test invalid order creation
patrick-ogrady Feb 22, 2023
f7108fa
add check for swap with invalid balance
patrick-ogrady Feb 23, 2023
abb0334
successful fill
patrick-ogrady Feb 23, 2023
d86515e
close order
patrick-ogrady Feb 23, 2023
2c8410e
create order from first account
patrick-ogrady Feb 23, 2023
babebcf
add a failing test
patrick-ogrady Feb 23, 2023
4d5d7ca
add broken stopper
patrick-ogrady Feb 23, 2023
f8160f0
tests passing
patrick-ogrady Feb 23, 2023
4adb3dd
add more balance checks
patrick-ogrady Feb 23, 2023
2bffad9
make rate discrete
patrick-ogrady Feb 23, 2023
6c88654
update controller for new rate format
patrick-ogrady Feb 23, 2023
816915f
re-adding tests
patrick-ogrady Feb 23, 2023
6287208
add value misaligned test
patrick-ogrady Feb 23, 2023
4acd0fe
add another todo
patrick-ogrady Feb 23, 2023
a71e1ff
fix close order
patrick-ogrady Feb 23, 2023
af333ea
all tests passing again
patrick-ogrady Feb 23, 2023
fca65cd
rename rate to tick
patrick-ogrady Feb 23, 2023
873b9d6
more naming
patrick-ogrady Feb 23, 2023
082c3ef
pass lint
patrick-ogrady Feb 23, 2023
c07fde8
add another item to README
patrick-ogrady Feb 23, 2023
ea79fe6
[TokenVM] Make tokens more powerful (#28)
patrick-ogrady Feb 24, 2023
ace0a1d
add tokenvm
patrick-ogrady Feb 24, 2023
f0155ca
update logo placement
patrick-ogrady Feb 24, 2023
c558ba3
documentation progress
patrick-ogrady Feb 24, 2023
5bc22f2
[tokenvm] Update CLI with new Actions (#32)
patrick-ogrady Feb 24, 2023
1b01f0b
progress on watch
patrick-ogrady Feb 24, 2023
a7e9dcd
add block explorer
patrick-ogrady Feb 24, 2023
b46f12a
more README todos
patrick-ogrady Feb 24, 2023
fbfb166
add expiring orders to future work
patrick-ogrady Feb 24, 2023
7093812
add rate to fill
patrick-ogrady Feb 24, 2023
2427a7a
use const
patrick-ogrady Feb 24, 2023
6368c96
alert if golang misconfigured
patrick-ogrady Feb 24, 2023
2bc3815
cleanup state key usage explanation
patrick-ogrady Feb 24, 2023
f2800d2
cleanup README
patrick-ogrady Feb 24, 2023
ac1b1e3
link consistent hasher
patrick-ogrady Feb 24, 2023
e11bc53
add more to main README
patrick-ogrady Feb 24, 2023
75019ff
add storagevm to future work
patrick-ogrady Feb 24, 2023
8715501
more README work
patrick-ogrady Feb 24, 2023
05206d1
more README progress
patrick-ogrady Feb 24, 2023
ebd8b38
add a lot of README
patrick-ogrady Feb 24, 2023
62ca16c
finish README
patrick-ogrady Feb 25, 2023
81d2165
Merge branch 'main' into tokenvm
patrick-ogrady Feb 25, 2023
8c0c585
fix lint
patrick-ogrady Feb 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/tokenvm-static-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
# See the file LICENSE for licensing terms.

name: TokenVM Static Analysis

on:
push:
branches:
- main
pull_request:

jobs:
Lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
check-latest: true
- name: Run static analysis tests
working-directory: ./examples/tokenvm
shell: bash
run: scripts/tests.lint.sh
32 changes: 32 additions & 0 deletions .github/workflows/tokenvm-sync-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
# See the file LICENSE for licensing terms.

name: TokenVM Sync Tests

on:
push:
branches:
- main
tags:
- "*"
pull_request:

jobs:
tests:
runs-on:
labels: ubuntu-latest-16-cores
steps:
- name: Git checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Run sync tests
working-directory: ./examples/tokenvm
shell: bash
run: scripts/run.sh
env:
MODE: "full-test"
39 changes: 39 additions & 0 deletions .github/workflows/tokenvm-unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
# See the file LICENSE for licensing terms.

name: TokenVM Unit Tests

on:
push:
branches:
- main
tags:
- "*"
pull_request:

jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Run unit tests
working-directory: ./examples/tokenvm
shell: bash
run: scripts/tests.unit.sh
- name: Run integration tests
working-directory: ./examples/tokenvm
shell: bash
run: scripts/tests.integration.sh
- name: Run e2e tests
working-directory: ./examples/tokenvm
shell: bash
run: scripts/run.sh
env:
MODE: "test"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ genesis.json
*.test

dist/
*.pk
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ run:
skip-files:
- ".*\\.pb\\.go$"
- ".*mock.*"
- "merkledb/*"

issues:
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
Expand Down
63 changes: 45 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ happen.
`hypersdk` transactions must specify the keys they will touch in state (read
or write) during execution and authentication so that all relevant data can be
pre-fetched before block execution starts, which ensures all data accessed during
verification of a block is done so in memory). This restriction also enables
transactions to be processed in parallel as distinct, ordered transaction sets
can be trivially formed by looking at the overlap of keys that transactions
will touch.
verification of a block is done so in memory). Notably, the keys specified here
are not keys in a merkle trie (which may be quite volatile) but are instead the
actual keys used to access data by the storage engine (like your address, which
is much less volatile and not as cumbersome of a UX barrier).

This restriction also enables transactions to be processed in parallel as distinct,
ordered transaction sets can be trivially formed by looking at the overlap of keys
that transactions will touch.

_Parallel transaction execution was originally included in `hypersdk` but
removed because the overhead of the naïve mechanism used to group transactions
Expand Down Expand Up @@ -212,11 +216,30 @@ and are stored alongside all other runtime logs. The unification of all of
these functions with avalanchego means existing avalanchego monitoring tools
work out of the box on your `hypervm`.

## Example: `indexvm`
We created the [`indexvm`](https://github.com/ava-labs/indexvm) while
building the `hypersdk` to test out our design decisions and abstractions.
We recommend taking a look at this `hypervm` to gain an in-depth understanding
of how you can build a complex runtime on top of the `hypersdk`.
## Examples
### Beginner: `tokenvm`
We created the [`tokenvm`](./examples/tokenvm) to showcase how to use the
`hypersdk` in an application most readers are already familiar with, token minting
and token trading. The `tokenvm` lets anyone create any asset, mint more of
their asset, modify the metadata of their asset (if they reveal some info), and
burn their asset. Additionally, there is an embedded on-chain exchange that
allows anyone to create orders and fill (partial) orders of anyone else. To
make this example easy to play with, the `tokenvm` also bundles a powerful CLI
tool and serves RPC requests for trades out of an in-memory order book it
maintains by syncing blocks. If you are interested in the intersection of
exchanges and blockchains, it is definitely worth a read (the logic for filling
orders is < 100 lines of code!).

To ensure the `hypersdk` stays reliable as we optimize and evolve the codebase,
we also run E2E tests in the `tokenvm` on each PR to the `hypersdk` core modules.

### Expert: `indexvm`
The [`indexvm`](https://github.com/ava-labs/indexvm) is much more complex than
the `tokenvm` (more elaborate mechanisms and a new use case you may not be
familiar with). It was built during the design of the `hypersdk` to test out the
limits of the abstractions for building complex on-chain mechanisms. We recommend
taking a look at this `hypervm` once you already have familiarity with the `hypersdk` to gain an
even deeper understanding of how you can build a complex runtime on top of the `hypersdk`.

The `indexvm` is dedicated to increasing the usefulness of the world's
content-addressable data (like IPFS) by enabling anyone to "index it" by
Expand All @@ -238,7 +261,7 @@ a small part in this movement by making it easier for anyone to generate
world-class recommendations for anyone on the internet, even if you've never
interacted with them before.

We'll use this example to explain how to use the `hypersdk` below.
We'll use both of these `hypervms` to explain how to use the `hypersdk` below.

## How It Works
To use the `hypersdk`, you must import it into your own `hypervm` and implement the
Expand Down Expand Up @@ -283,8 +306,8 @@ structures utilized by the `hypersdk` and handles both `Accepted` and
`Gossiper`, `Handlers`, and `Database` packages so this is typically a lot of
boilerplate code.

You can view what this looks like in the `indexvm` by clicking this
[link](https://github.com/ava-labs/indexvm/blob/main/controller/controller.go).
You can view what this looks like in the `tokenvm` by clicking this
[link](./examples/tokenvm/controller/controller.go).

### Genesis
```golang
Expand All @@ -300,8 +323,8 @@ start of the network (fee price, enabled txs, etc.). The serialized genesis of
any `hyperchain` is persisted on the P-Chain for anyone to see when the network
is created.

You can view what this looks like in the `indexvm` by clicking this
[link](https://github.com/ava-labs/indexvm/blob/main/genesis/genesis.go).
You can view what this looks like in the `tokenvm` by clicking this
[link](./examples/tokenvm/genesis/genesis.go).

### Action
```golang
Expand All @@ -321,8 +344,8 @@ the blockchain runtime. Specifically, they are "user-defined" element of
any `hypersdk` transaction that is processed by all participants of any
`hyperchain`.

You can view what a simple transfer `Action` looks like [here](https://github.com/ava-labs/indexvm/blob/main/actions/transfer.go)
and what a more complex "index" `Action` looks like [here](https://github.com/ava-labs/indexvm/blob/main/actions/index.go).
You can view what a simple transfer `Action` looks like [here](./examples/tokenvm/actions/transfer.go)
and what a more complex "fill order" `Action` looks like [here](./examples/tokenvm/actions/fill_order.go).

### Auth
```golang
Expand Down Expand Up @@ -407,8 +430,6 @@ out on the Avalanche Discord._
* Use pre-specified state keys to process transactions in parallel (txs with no
overlap can be processed at the same time, create conflict sets on-the-fly
instead of before execution)
* Perform E2E tests and publish nightly performance benchmarks using a simple
hypervm that just performs simple transfers (`dummyvm`)
* Add support for Avalanche Warp Messaging (AWM) so any deployed hypervms
(hyperchains) can communicate with each other ([see ava-labs/xsvm](https://github.com/ava-labs/xsvm))
* Add a WASM runtime module to allow developers to embed smart contract
Expand All @@ -431,3 +452,9 @@ out on the Avalanche Discord._
* Implement support for S3 and PostgreSQL storage backends
* Provide optional auto-serialization/deserialization of `Actions` and `Auth`
if only certain types are used in their definition
* Add a module that could be used to track the location of various pieces
of data across a network ([see consistent
hasher](https://github.com/ava-labs/avalanchego/tree/master/utils/hashing/consistent))
of `hypervm` participants (even better if this is made abstract to any implementer
such that they can just register and request data from it and it is automatically
handled by the network layer)
2 changes: 1 addition & 1 deletion chain/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func BuildBlock(ctx context.Context, vm VM, preferred ids.ID) (snowman.Block, er

// Update block with new transaction
b.Txs = append(b.Txs, next)
b.UnitsConsumed += nextUnits
b.UnitsConsumed += result.Units
surplusFee += (next.Base.UnitPrice - b.UnitPrice) * result.Units
results = append(results, result)
return len(b.Txs) < r.GetMaxBlockTxs(), false, false, nil
Expand Down
7 changes: 5 additions & 2 deletions chain/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ type Action interface {

// Auth may contain an [Actor] that performs a transaction
//
// if attempt to reference missing key, error...it is ok to not use all keys (conditional logic based on state)
StateKeys(Auth) [][]byte
// We provide the [txID] here because different actions like to use this as
// a unique identifier for things created in an action.
//
// If attempt to reference missing key, error...it is ok to not use all keys (conditional logic based on state)
StateKeys(auth Auth, txID ids.ID) [][]byte

// Key distinction with "Auth" is the payment of fees. All non-fee payments
// occur in Execute but Auth handles fees.
Expand Down
8 changes: 4 additions & 4 deletions chain/mock_action.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion chain/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ func (t *Transaction) Expiry() int64 { return t.Base.Timestamp }
func (t *Transaction) UnitPrice() uint64 { return t.Base.UnitPrice }

// It is ok to have duplicate ReadKeys...the processor will skip them
//
// TODO: verify the invariant that [t.id] is set by this point
func (t *Transaction) StateKeys() [][]byte {
return append(t.Action.StateKeys(t.Auth), t.Auth.StateKeys()...)
return append(t.Action.StateKeys(t.Auth, t.ID()), t.Auth.StateKeys()...)
}

// Units is charged whether or not a transaction is successful because state
Expand Down
3 changes: 2 additions & 1 deletion client/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

const (
waitSleep = 3 * time.Second
waitSleep = 500 * time.Millisecond
)

type Modifier interface {
Expand Down Expand Up @@ -65,6 +65,7 @@ func (cli *Client) GenerateTransaction(
}

func Wait(ctx context.Context, check func(ctx context.Context) (bool, error)) error {
time.Sleep(500 * time.Millisecond) // usually a small delay here helps a lot
for ctx.Err() == nil {
exit, err := check(ctx)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion codec/optional_packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (o *OptionalPacker) PackPublicKey(pk crypto.PublicKey) {

func (o *OptionalPacker) UnpackPublicKey(dest *crypto.PublicKey) {
if o.checkBit() {
o.ip.UnpackPublicKey(dest)
o.ip.UnpackPublicKey(true, dest)
}
}

Expand Down
4 changes: 2 additions & 2 deletions codec/packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ func (p *Packer) PackPublicKey(src crypto.PublicKey) {
p.p.PackFixedBytes(src[:])
}

func (p *Packer) UnpackPublicKey(dest *crypto.PublicKey) {
func (p *Packer) UnpackPublicKey(required bool, dest *crypto.PublicKey) {
copy((*dest)[:], p.p.UnpackFixedBytes(crypto.PublicKeyLen))
if *dest == crypto.EmptyPublicKey {
if required && *dest == crypto.EmptyPublicKey {
p.p.Errs.Add(fmt.Errorf("%w: PublicKey field is not populated", ErrFieldNotPopulated))
}
}
Expand Down
1 change: 1 addition & 0 deletions consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
MaxUint = ^uint(0)
MaxInt = int(MaxUint >> 1)
IntLen = 4
Uint16Len = 2
Uint64Len = 8
MaxUint64 = ^uint64(0)
)
Loading