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

[chain] Introduce Multi-Actions and Outputs in Transactions #858

Merged
merged 79 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
0e6b2ba
add generic
wlawt Apr 25, 2024
18580b0
emap passes
wlawt Apr 25, 2024
d9bfb3a
eheap passes
wlawt Apr 25, 2024
25775c6
fix tokenvm orderbook
wlawt Apr 25, 2024
2de3e56
intro ActionID
wlawt Apr 24, 2024
965b313
intro max actions
wlawt Apr 24, 2024
5351388
enforce max actions + loop thru statekeys
wlawt Apr 24, 2024
2e3dc39
fix some tx errors
wlawt Apr 24, 2024
89986fa
fix builder errors
wlawt Apr 24, 2024
06d62fe
fix processor errors
wlawt Apr 24, 2024
5dcf31d
fix more tx errors
wlawt Apr 24, 2024
5c84c69
fix client rpc error
wlawt Apr 24, 2024
197ed91
fix morpheus transfer action
wlawt Apr 24, 2024
d5f589c
fix more morpheus
wlawt Apr 24, 2024
0ca6b1c
morpheusVM compiles
wlawt Apr 25, 2024
def86ad
fix mock gen
wlawt Apr 25, 2024
26088fd
fix morpheus static lint
wlawt Apr 25, 2024
583388f
fix lint and function signatures
wlawt Apr 25, 2024
a8e3a7c
fix rpc GenerateTx
wlawt Apr 25, 2024
487f294
import chain into morpheus actions
wlawt Apr 25, 2024
c643f90
more lints
wlawt Apr 25, 2024
e55bf37
marhsal unmarshal Actions
wlawt Apr 25, 2024
e802373
totalUnits fees.Add
wlawt Apr 25, 2024
719c2c4
fix invalid signature
wlawt Apr 25, 2024
92d9902
fix some tx fees and packing bytes
wlawt Apr 25, 2024
9a0a5df
check maxActions earlier
wlawt Apr 25, 2024
2a546b7
morpheus integration passes
wlawt Apr 25, 2024
a847c73
make action.Execute use actionID
wlawt Apr 25, 2024
574d0ab
intro codec.ActionID
wlawt Apr 26, 2024
4d6be79
fix tokenvm
wlawt Apr 26, 2024
86f22f3
tokevnm integration errors
wlawt Apr 26, 2024
d167753
fix tokenvm integration error
wlawt Apr 26, 2024
7d321c3
tokenvm integration passes
wlawt Apr 26, 2024
651d51a
create long ID
wlawt Apr 26, 2024
24915a1
fix token-cli lint
wlawt Apr 26, 2024
3136c1c
fix token fauct and feed lint
wlawt Apr 26, 2024
c31792f
introduce multiple result.Outputs and morpheus integration passes
wlawt Apr 28, 2024
9fe30c9
tokenvm integration passes using multiple result.Outputs
wlawt Apr 28, 2024
3a36024
increase maxActions for tokenvm to 2
wlawt Apr 29, 2024
c55ba32
add multiple transfers in 1 tx
wlawt Apr 29, 2024
f665821
add create and mint multiple assets
wlawt Apr 30, 2024
23feaa2
add multiple trades
wlawt Apr 30, 2024
94b77a6
add failed fill order
wlawt Apr 30, 2024
da5f235
address lints
wlawt Apr 30, 2024
38a4702
add hrp param to LIDFromString
wlawt May 1, 2024
94b7449
fix x/programs lint
wlawt May 1, 2024
35f51ec
getmaxactionspertx should be uint8
wlawt May 3, 2024
ded3494
update readme
wlawt May 3, 2024
0a1dc9d
self review
wlawt May 3, 2024
d37e43a
fix rust ci
wlawt May 3, 2024
84fdb11
make get action id a helper
wlawt May 5, 2024
cd5fbdb
move GetMaxActionsPerTx
wlawt May 6, 2024
ce2a894
add MaxOutputsPerAction
wlawt May 6, 2024
63c7d3b
actions instead of action
wlawt May 6, 2024
c95fa4a
do sponsorkeys after action iteration
wlawt May 6, 2024
a1a20a5
add action idx and type to ErrActionNotActivated
wlawt May 6, 2024
27e34b9
dont override prev outputs in handleRevert
wlawt May 6, 2024
bc8a814
skip all if one action is failed, do fee comp at the end, refund once
wlawt May 6, 2024
0668242
introduce LIDLen
wlawt May 6, 2024
8010dd7
fix type alias
wlawt May 6, 2024
929c742
use PackLID
wlawt May 6, 2024
7757968
use codec.LIDLen
wlawt May 6, 2024
59da0dc
EstimateMaxUnits over all actions
wlawt May 6, 2024
51dc714
revert is populated in last output of last action
wlawt May 6, 2024
bbd1bd2
fix action Size
wlawt May 6, 2024
385eb7d
fix LID to and from String
wlawt May 6, 2024
c4a270a
make EmptyAddress const more generic
wlawt May 7, 2024
7721b00
if not successful error is last output in last action
wlawt May 7, 2024
a22a37c
add comments in transaction
wlawt May 7, 2024
2f487ef
fix mock gen
wlawt May 7, 2024
43d77e2
fix rust ci
wlawt May 7, 2024
4d7e3dc
[chain] fix result size (#889)
wlawt May 20, 2024
b67c5f0
[chain] Remove execute success (#894)
wlawt May 20, 2024
0ec4919
remove trailing enter
patrick-ogrady May 20, 2024
e9c2ff4
revert `codec.LID` (#920)
patrick-ogrady May 21, 2024
a915282
multi-action nits (#922)
patrick-ogrady May 21, 2024
dec026a
update programs code
patrick-ogrady May 22, 2024
1b64ed6
Merge branch 'main' into multi-action-support
patrick-ogrady May 22, 2024
7acbe89
update action batches section
patrick-ogrady May 22, 2024
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
8 changes: 4 additions & 4 deletions .github/workflows/hypersdk-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
MODE: 'test'

tokenvm-load-tests:
needs: [tokenvm-unit-tests]
needs: [tokenvm-lint, tokenvm-unit-tests]
strategy:
matrix:
level: [v1, v2, v3] # v4 is not supported
Expand All @@ -161,7 +161,7 @@ jobs:
run: GOAMD64=${{ matrix.level }} scripts/tests.load.sh

tokenvm-sync-tests:
needs: [tokenvm-unit-tests]
needs: [tokenvm-lint, tokenvm-unit-tests]
runs-on: ubuntu-20.04-32
timeout-minutes: 25
steps:
Expand Down Expand Up @@ -262,7 +262,7 @@ jobs:
MODE: 'test'

morpheusvm-load-tests:
needs: [morpheusvm-unit-tests]
needs: [morpheusvm-lint, morpheusvm-unit-tests]
strategy:
matrix:
level: [v1, v2, v3] # v4 is not supported
Expand All @@ -283,7 +283,7 @@ jobs:
run: GOAMD64=${{ matrix.level }} scripts/tests.load.sh

morpheusvm-sync-tests:
needs: [morpheusvm-unit-tests]
needs: [morpheusvm-lint, morpheusvm-unit-tests]
runs-on: ubuntu-20.04-32
timeout-minutes: 25
steps:
Expand Down
51 changes: 28 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,26 @@ for a single account and ensure they are ordered) and makes the network layer
more efficient (we can gossip any valid transaction to any node instead of just
the transactions for each account that can be executed at the moment).

### Action Batches and Arbitrary Outputs
Each `hypersdk` transaction specifies an array of `Actions` that
must all execute successfully for any state changes to be committed.
Additionally, each `Action` is permitted to return an array of outputs (each
output is arbitrary bytes defined by the `hypervm`) upon successful execution.

The `tokenvm` uses `Action` batches to offer complex, atomic interactions over simple
primitives (i.e. create order, fill order, and cancel order). For example, a user
can create a transaction that fills 8 orders. If any of the fills fail, all pending
state changes in the transaction are rolled back. The `tokenvm` uses `Action` outputs to
return the remaining units on any partially filled order to power an in-memory orderbook.

The outcome of execution is not stored/indexed by the `hypersdk`. Unlike most other
blockchains/blockchain frameworks, which provide an optional "archival mode" for historical access,
the `hypersdk` only stores what is necessary to validate the next valid block and to help new nodes
sync to the current state. Rather, the `hypersdk` invokes the `hypervm` with all execution
results whenever a block is accepted for it to perform arbitrary operations (as
required by a developer's use case). In this callback, a `hypervm` could store
results in a SQL database or write to a Kafka stream.

### Easy Functionality Upgrades
Every object that can appear on-chain (i.e. `Actions` and/or `Auth`) and every chain
parameter (i.e. `Unit Price`) is scoped by block timestamp. This makes it
Expand All @@ -417,23 +437,6 @@ override the default gossip technique with your own. For example, you may wish
to not have any node-to-node gossip and just require validators to propose
blocks only with the transactions they've received over RPC.

### Transaction Results and Execution Rollback
The `hypersdk` allows for any `Action` to return a result from execution
(which can be any arbitrary bytes), the amount of fee units it consumed, and
whether or not it was successful (if unsuccessful, all state changes are rolled
back). This support is typically required by anyone using the `hypersdk` to
implement a smart contract-based runtime that allows for cost-effective
conditional execution (exiting early if a condition does not hold can be much
cheaper than the full execution of the transaction).

The outcome of execution is not stored/indexed by the `hypersdk`. Unlike most other
blockchains/blockchain frameworks, which provide an optional "archival mode" for historical access,
the `hypersdk` only stores what is necessary to validate the next valid block and to help new nodes
sync to the current state. Rather, the `hypersdk` invokes the `hypervm` with all execution
results whenever a block is accepted for it to perform arbitrary operations (as
required by a developer's use case). In this callback, a `hypervm` could store
results in a SQL database or write to a Kafka stream.

### Support for Generic Storage Backends
When initializing a `hypervm`, the developer explicitly specifies which storage backends
to use for each object type (state vs blocks vs metadata). As noted above, this
Expand Down Expand Up @@ -648,24 +651,23 @@ type Action interface {
// key (formatted as a big-endian uint16). This is used to automatically calculate storage usage.
//
// If any key is removed and then re-created, this will count as a creation instead of a modification.
StateKeys(actor codec.Address, txID ids.ID) state.Keys
StateKeys(actor codec.Address, actionID ids.ID) state.Keys

// Execute actually runs the [Action]. Any state changes that the [Action] performs should
// be done here.
//
// If any keys are touched during [Execute] that are not specified in [StateKeys], the transaction
// will revert and the max fee will be charged.
//
// An error should only be returned if a fatal error was encountered, otherwise [success] should
// be marked as false and fees will still be charged.
//
// If [Execute] returns an error, execution will halt and any state changes will revert.
Execute(
ctx context.Context,
r Rules,
mu state.Mutable,
timestamp int64,
actor codec.Address,
txID ids.ID,
) (success bool, computeUnits uint64, output []byte, err error)
actionID ids.ID,
) (computeUnits uint64, outputs [][]byte, err error)
}
```

Expand Down Expand Up @@ -743,6 +745,9 @@ type Rules interface {
GetMinBlockGap() int64 // in milliseconds
GetMinEmptyBlockGap() int64 // in milliseconds
GetValidityWindow() int64 // in milliseconds

GetMaxActionsPerTx() uint8
GetMaxOutputsPerAction() uint8

GetMinUnitPrice() Dimensions
GetUnitPriceChangeDenominator() Dimensions
Expand Down
2 changes: 1 addition & 1 deletion chain/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/ava-labs/hypersdk/consts"
)

const BaseSize = consts.Uint64Len*2 + consts.IDLen
const BaseSize = consts.Uint64Len*2 + ids.IDLen

type Base struct {
// Timestamp is the expiry of the transaction (inclusive). Once this time passes and the
Expand Down
4 changes: 2 additions & 2 deletions chain/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -799,10 +799,10 @@ func (b *StatelessBlock) FeeManager() *fees.Manager {
}

func (b *StatefulBlock) Marshal() ([]byte, error) {
size := consts.IDLen + consts.Uint64Len + consts.Uint64Len +
size := ids.IDLen + consts.Uint64Len + consts.Uint64Len +
consts.Uint64Len + window.WindowSliceSize +
consts.IntLen + codec.CummSize(b.Txs) +
consts.IDLen + consts.Uint64Len + consts.Uint64Len
ids.IDLen + consts.Uint64Len + consts.Uint64Len

p := codec.NewWriter(size, consts.NetworkSizeLimit)

Expand Down
12 changes: 7 additions & 5 deletions chain/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ type Rules interface {
GetMinEmptyBlockGap() int64 // in milliseconds
GetValidityWindow() int64 // in milliseconds

GetMaxActionsPerTx() uint8
GetMaxOutputsPerAction() uint8

GetMinUnitPrice() fees.Dimensions
GetUnitPriceChangeDenominator() fees.Dimensions
GetWindowTargetUnits() fees.Dimensions
Expand Down Expand Up @@ -238,24 +241,23 @@ type Action interface {
// key (formatted as a big-endian uint16). This is used to automatically calculate storage usage.
//
// If any key is removed and then re-created, this will count as a creation instead of a modification.
StateKeys(actor codec.Address, txID ids.ID) state.Keys
StateKeys(actor codec.Address, actionID ids.ID) state.Keys

// Execute actually runs the [Action]. Any state changes that the [Action] performs should
// be done here.
//
// If any keys are touched during [Execute] that are not specified in [StateKeys], the transaction
// will revert and the max fee will be charged.
//
// An error should only be returned if a fatal error was encountered, otherwise [success] should
// be marked as false and fees will still be charged.
// If [Execute] returns an error, execution will halt and any state changes will revert.
Execute(
ctx context.Context,
r Rules,
mu state.Mutable,
timestamp int64,
actor codec.Address,
txID ids.ID,
) (success bool, computeUnits uint64, output []byte, err error)
actionID ids.ID,
) (computeUnits uint64, outputs [][]byte, err error)
}

type Auth interface {
Expand Down
2 changes: 2 additions & 0 deletions chain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var (
ErrMisalignedTime = errors.New("misaligned time")
ErrInvalidActor = errors.New("invalid actor")
ErrInvalidSponsor = errors.New("invalid sponsor")
ErrTooManyActions = errors.New("too many actions")
ErrTooManyOutputs = errors.New("too many outputs")

// Execution Correctness
ErrInvalidBalance = errors.New("invalid balance")
Expand Down
11 changes: 5 additions & 6 deletions chain/mock_action.go

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

28 changes: 28 additions & 0 deletions chain/mock_rules.go

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

43 changes: 33 additions & 10 deletions chain/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,35 @@ import (

type Result struct {
Success bool
Output []byte
Error []byte

Outputs [][][]byte

Consumed fees.Dimensions
Fee uint64
}

func (r *Result) Size() int {
return consts.BoolLen + codec.BytesLen(r.Output) + fees.DimensionsLen + consts.Uint64Len
outputSize := consts.Uint8Len // actions
for _, action := range r.Outputs {
outputSize += consts.Uint8Len
for _, output := range action {
outputSize += codec.BytesLen(output)
}
}
return consts.BoolLen + codec.BytesLen(r.Error) + outputSize + fees.DimensionsLen + consts.Uint64Len
}

func (r *Result) Marshal(p *codec.Packer) error {
p.PackBool(r.Success)
p.PackBytes(r.Output)
p.PackBytes(r.Error)
p.PackByte(uint8(len(r.Outputs)))
for _, outputs := range r.Outputs {
p.PackByte(uint8(len(outputs)))
for _, output := range outputs {
p.PackBytes(output)
}
}
p.PackFixedBytes(r.Consumed.Bytes())
p.PackUint64(r.Fee)
return nil
Expand All @@ -45,11 +61,20 @@ func UnmarshalResult(p *codec.Packer) (*Result, error) {
result := &Result{
Success: p.UnpackBool(),
}
p.UnpackBytes(consts.MaxInt, false, &result.Output)
if len(result.Output) == 0 {
// Enforce object standardization
result.Output = nil
p.UnpackBytes(consts.MaxInt, false, &result.Error)
outputs := [][][]byte{}
numActions := p.UnpackByte()
for i := uint8(0); i < numActions; i++ {
numOutputs := p.UnpackByte()
actionOutputs := [][]byte{}
for j := uint8(0); j < numOutputs; j++ {
var output []byte
p.UnpackBytes(consts.MaxInt, false, &output)
actionOutputs = append(actionOutputs, output)
}
outputs = append(outputs, actionOutputs)
}
result.Outputs = outputs
consumedRaw := make([]byte, fees.DimensionsLen)
p.UnpackFixedBytes(fees.DimensionsLen, &consumedRaw)
consumed, err := fees.UnpackDimensions(consumedRaw)
Expand All @@ -58,9 +83,7 @@ func UnmarshalResult(p *codec.Packer) (*Result, error) {
}
result.Consumed = consumed
result.Fee = p.UnpackUint64(false)
if !p.Empty() {
return nil, p.Err()
}
// Wait to check if empty until after all results are unpacked.
return result, p.Err()
}

Expand Down
Loading
Loading