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 Tx Indexer Extension #1143

Merged
merged 25 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d613350
Remove rejected from controller interface
aaronbuchwald Jul 15, 2024
aeb04bd
Update mocks and remove unused mock_auth from mocks.mockgen.txt
aaronbuchwald Jul 15, 2024
0b4167c
Refactor tx indexing into indexer extension
aaronbuchwald Jul 15, 2024
a776c7d
Close txDB in morpheusvm on shutdown
aaronbuchwald Jul 15, 2024
5806dc1
Update name of unused result param to _
aaronbuchwald Jul 15, 2024
496f4d4
Merge branch 'main' into tx-indexer
aaronbuchwald Jul 15, 2024
5fe1ac0
fix lint
aaronbuchwald Jul 15, 2024
4df39fa
Update license year
aaronbuchwald Jul 15, 2024
0303667
Address nits
aaronbuchwald Jul 16, 2024
7236bd2
move action handler to its own accepted subscriber
aaronbuchwald Jul 16, 2024
4c365aa
Add interface check for noopTxIndexer
aaronbuchwald Jul 16, 2024
9dd485b
Fix typo
aaronbuchwald Jul 16, 2024
cff7da7
fix unused receivers
aaronbuchwald Jul 16, 2024
6c925f7
fix linting
aaronbuchwald Jul 16, 2024
6b516e2
Merge branch 'main' into tx-indexer
aaronbuchwald Jul 16, 2024
707dad3
invoke combined subscriber to fix integration test
aaronbuchwald Jul 16, 2024
2e6067f
move action handler prior to tx indexer to fix pre-existing race cond…
aaronbuchwald Jul 16, 2024
0770055
Add TODO to integration test flagging flaky test
aaronbuchwald Jul 16, 2024
e0c3c68
Merge branch 'main' into tx-indexer
aaronbuchwald Jul 17, 2024
9850970
Noop -> NoOp
aaronbuchwald Jul 18, 2024
650572e
Address comments
aaronbuchwald Jul 18, 2024
1ccb8a2
Merge branch 'main' into tx-indexer
aaronbuchwald Jul 18, 2024
f9d658f
Export NoOpTxIndexer
aaronbuchwald Jul 18, 2024
6296694
rename combinedAcceptedSubscriber AcceptedSubscribers
aaronbuchwald Jul 18, 2024
8a06837
address comments
aaronbuchwald Jul 18, 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
28 changes: 28 additions & 0 deletions examples/morpheusvm/controller/action_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package controller

import (
"context"

"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/examples/morpheusvm/actions"
"github.com/ava-labs/hypersdk/extension/indexer"
)

var _ indexer.SuccessfulTxSubscriber = (*actionHandler)(nil)

type actionHandler struct {
c *Controller
}

func (a *actionHandler) Accepted(_ context.Context, tx *chain.Transaction, _ *chain.Result) error {
for _, action := range tx.Actions {
switch action.(type) { //nolint:gocritic
case *actions.Transfer:
a.c.metrics.transfer.Inc()
}
}
return nil
}
56 changes: 19 additions & 37 deletions examples/morpheusvm/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import (
"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/builder"
"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/examples/morpheusvm/actions"
"github.com/ava-labs/hypersdk/examples/morpheusvm/config"
"github.com/ava-labs/hypersdk/examples/morpheusvm/consts"
"github.com/ava-labs/hypersdk/examples/morpheusvm/genesis"
"github.com/ava-labs/hypersdk/examples/morpheusvm/rpc"
"github.com/ava-labs/hypersdk/examples/morpheusvm/storage"
"github.com/ava-labs/hypersdk/examples/morpheusvm/version"
"github.com/ava-labs/hypersdk/extension/indexer"
"github.com/ava-labs/hypersdk/gossiper"
"github.com/ava-labs/hypersdk/pebble"
"github.com/ava-labs/hypersdk/vm"
Expand All @@ -43,7 +43,9 @@ type Controller struct {

metrics *metrics

db database.Database
txDB database.Database
txIndexer indexer.TxIndexer
acceptedSubscriber indexer.AcceptedSubscriber
}

func New() *vm.VM {
Expand Down Expand Up @@ -96,10 +98,20 @@ func (c *Controller) Initialize(
}
snowCtx.Log.Info("loaded genesis", zap.Any("genesis", c.genesis))

c.db, err = hstorage.New(pebble.NewDefaultConfig(), snowCtx.ChainDataDir, "db", gatherer)
c.txDB, err = hstorage.New(pebble.NewDefaultConfig(), snowCtx.ChainDataDir, "db", gatherer)
if err != nil {
return nil, nil, nil, nil, nil, nil, nil, err
}
acceptedSubscribers := []indexer.AcceptedSubscriber{
indexer.NewSuccessfulTxSubscriber(&actionHandler{c: c}),
}
if c.config.StoreTransactions {
c.txIndexer = indexer.NewTxDBIndexer(c.txDB)
acceptedSubscribers = append(acceptedSubscribers, c.txIndexer)
} else {
c.txIndexer = indexer.NewNoOpTxIndexer()
}
c.acceptedSubscriber = indexer.NewAcceptedSubscribers(acceptedSubscribers...)

// Create handlers
//
Expand Down Expand Up @@ -145,40 +157,10 @@ func (c *Controller) StateManager() chain.StateManager {
}

func (c *Controller) Accepted(ctx context.Context, blk *chain.StatelessBlock) error {
batch := c.db.NewBatch()
defer batch.Reset()

results := blk.Results()
for i, tx := range blk.Txs {
result := results[i]
if c.config.StoreTransactions {
err := storage.StoreTransaction(
ctx,
batch,
tx.ID(),
blk.GetTimestamp(),
result.Success,
result.Units,
result.Fee,
)
if err != nil {
return err
}
}
if result.Success {
for _, action := range tx.Actions {
switch action.(type) { //nolint:gocritic
case *actions.Transfer:
c.metrics.transfer.Inc()
}
}
}
}
return batch.Write()
return c.acceptedSubscriber.Accepted(ctx, blk)
}

func (*Controller) Shutdown(context.Context) error {
// Do not close any databases provided during initialization. The VM will
// close any databases your provided.
return nil
func (c *Controller) Shutdown(context.Context) error {
// Close any databases created during initialization
return c.txDB.Close()
}
7 changes: 2 additions & 5 deletions examples/morpheusvm/controller/resolutions.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ func (c *Controller) Tracer() trace.Tracer {
return c.inner.Tracer()
}

func (c *Controller) GetTransaction(
ctx context.Context,
txID ids.ID,
) (bool, int64, bool, fees.Dimensions, uint64, error) {
return storage.GetTransaction(ctx, c.db, txID)
func (c *Controller) GetTransaction(txID ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error) {
return c.txIndexer.GetTransaction(txID)
}

func (c *Controller) GetBalanceFromState(
Expand Down
2 changes: 1 addition & 1 deletion examples/morpheusvm/rpc/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ import (
type Controller interface {
Genesis() *genesis.Genesis
Tracer() trace.Tracer
GetTransaction(context.Context, ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error)
GetTransaction(ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error)
GetBalanceFromState(context.Context, codec.Address) (uint64, error)
}
4 changes: 2 additions & 2 deletions examples/morpheusvm/rpc/jsonrpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ type TxReply struct {
}

func (j *JSONRPCServer) Tx(req *http.Request, args *TxArgs, reply *TxReply) error {
ctx, span := j.c.Tracer().Start(req.Context(), "Server.Tx")
_, span := j.c.Tracer().Start(req.Context(), "Server.Tx")
defer span.End()

found, t, success, units, fee, err := j.c.GetTransaction(ctx, args.TxID)
found, t, success, units, fee, err := j.c.GetTransaction(args.TxID)
if err != nil {
return err
}
Expand Down
67 changes: 0 additions & 67 deletions examples/morpheusvm/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import (
"fmt"

"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"

"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/consts"
"github.com/ava-labs/hypersdk/fees"
"github.com/ava-labs/hypersdk/state"

smath "github.com/ava-labs/avalanchego/utils/math"
Expand All @@ -23,10 +21,6 @@ import (

type ReadState func(context.Context, [][]byte) ([][]byte, []error)

// Metadata
// 0x0/ (tx)
// -> [txID] => timestamp
//
// State
// / (height) => store in root
// -> [heightPrefix] => height
Expand All @@ -37,9 +31,6 @@ type ReadState func(context.Context, [][]byte) ([][]byte, []error)
// 0x3/ (hypersdk-fee)

const (
// Indexes
txPrefix = 0x0

// Active state
balancePrefix = 0x0
heightPrefix = 0x1
Expand All @@ -50,69 +41,11 @@ const (
const BalanceChunks uint16 = 1

var (
failureByte = byte(0x0)
successByte = byte(0x1)
heightKey = []byte{heightPrefix}
timestampKey = []byte{timestampPrefix}
feeKey = []byte{feePrefix}
)

// [txPrefix] + [txID]
func TxKey(id ids.ID) (k []byte) {
k = make([]byte, 1+ids.IDLen)
k[0] = txPrefix
copy(k[1:], id[:])
return
}

func StoreTransaction(
_ context.Context,
db database.KeyValueWriter,
id ids.ID,
t int64,
success bool,
units fees.Dimensions,
fee uint64,
) error {
k := TxKey(id)
v := make([]byte, consts.Uint64Len+1+fees.DimensionsLen+consts.Uint64Len)
binary.BigEndian.PutUint64(v, uint64(t))
if success {
v[consts.Uint64Len] = successByte
} else {
v[consts.Uint64Len] = failureByte
}
copy(v[consts.Uint64Len+1:], units.Bytes())
binary.BigEndian.PutUint64(v[consts.Uint64Len+1+fees.DimensionsLen:], fee)
return db.Put(k, v)
}

func GetTransaction(
_ context.Context,
db database.KeyValueReader,
id ids.ID,
) (bool, int64, bool, fees.Dimensions, uint64, error) {
k := TxKey(id)
v, err := db.Get(k)
if errors.Is(err, database.ErrNotFound) {
return false, 0, false, fees.Dimensions{}, 0, nil
}
if err != nil {
return false, 0, false, fees.Dimensions{}, 0, err
}
t := int64(binary.BigEndian.Uint64(v))
success := true
if v[consts.Uint64Len] == failureByte {
success = false
}
d, err := fees.UnpackDimensions(v[consts.Uint64Len+1 : consts.Uint64Len+1+fees.DimensionsLen])
if err != nil {
return false, 0, false, fees.Dimensions{}, 0, err
}
fee := binary.BigEndian.Uint64(v[consts.Uint64Len+1+fees.DimensionsLen:])
return true, t, success, d, fee, nil
}

// [balancePrefix] + [address]
func BalanceKey(addr codec.Address) (k []byte) {
k = make([]byte, 1+codec.AddressLen+consts.Uint16Len)
Expand Down
56 changes: 56 additions & 0 deletions examples/tokenvm/controller/action_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package controller

import (
"context"

"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/examples/tokenvm/actions"
"github.com/ava-labs/hypersdk/extension/indexer"
)

var _ indexer.SuccessfulTxSubscriber = (*actionHandler)(nil)

type actionHandler struct {
c *Controller
}

func (a *actionHandler) Accepted(_ context.Context, tx *chain.Transaction, result *chain.Result) error {
for i, act := range tx.Actions {
switch action := act.(type) {
case *actions.CreateAsset:
a.c.metrics.createAsset.Inc()
case *actions.MintAsset:
a.c.metrics.mintAsset.Inc()
case *actions.BurnAsset:
a.c.metrics.burnAsset.Inc()
case *actions.Transfer:
a.c.metrics.transfer.Inc()
case *actions.CreateOrder:
a.c.metrics.createOrder.Inc()
a.c.orderBook.Add(chain.CreateActionID(tx.ID(), uint8(i)), tx.Auth.Actor(), action)
case *actions.FillOrder:
a.c.metrics.fillOrder.Inc()
outputs := result.Outputs[i]
for _, output := range outputs {
orderResult, err := actions.UnmarshalOrderResult(output)
if err != nil {
// This should never happen
return err
}
if orderResult.Remaining == 0 {
a.c.orderBook.Remove(action.Order)
continue
}
a.c.orderBook.UpdateRemaining(action.Order, orderResult.Remaining)
}
case *actions.CloseOrder:
a.c.metrics.closeOrder.Inc()
a.c.orderBook.Remove(action.Order)
}
}

return nil
}
Loading
Loading