Skip to content

Commit

Permalink
Add post linearization support to the X-chain APIs (ava-labs#2708)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph authored Mar 16, 2023
1 parent 991c32b commit 7cd1663
Show file tree
Hide file tree
Showing 6 changed files with 455 additions and 433 deletions.
48 changes: 48 additions & 0 deletions vms/avm/chain_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/choices"
"github.com/ava-labs/avalanchego/vms/avm/states"
"github.com/ava-labs/avalanchego/vms/avm/txs"
)

var _ states.Chain = (*chainState)(nil)

// chainState wraps the disk state and filters non-accepted transactions from
// being returned in GetTx.
type chainState struct {
states.State
}

func (s *chainState) GetTx(txID ids.ID) (*txs.Tx, error) {
tx, err := s.State.GetTx(txID)
if err != nil {
return nil, err
}

// Before the linearization, transactions were persisted before they were
// marked as Accepted. However, this function aims to only return accepted
// transactions.
status, err := s.State.GetStatus(txID)
if err == database.ErrNotFound {
// If the status wasn't persisted, then the transaction was written
// after the linearization, and is accepted.
return tx, nil
}
if err != nil {
return nil, err
}

// If the status was persisted, then the transaction was written before the
// linearization. If it wasn't marked as accepted, then we treat it as if it
// doesn't exist.
if status != choices.Accepted {
return nil, database.ErrNotFound
}
return tx, nil
}
3 changes: 2 additions & 1 deletion vms/avm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type Client interface {
WalletClient
// GetTxStatus returns the status of [txID]
//
// TODO: deprecate GetTxStatus after the linearization.
// Deprecated: GetTxStatus only returns Accepted or Unknown, GetTx should be
// used instead to determine if the tx was accepted.
GetTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (choices.Status, error)
// ConfirmTx attempts to confirm [txID] by repeatedly checking its status.
// Note: ConfirmTx will block until either the context is done or the client
Expand Down
48 changes: 27 additions & 21 deletions vms/avm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"go.uber.org/zap"

"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/choices"
"github.com/ava-labs/avalanchego/utils"
Expand Down Expand Up @@ -39,7 +40,6 @@ const (
)

var (
errUnknownAssetID = errors.New("unknown asset ID")
errTxNotCreateAsset = errors.New("transaction doesn't create an asset")
errNoMinters = errors.New("no minters provided")
errNoHoldersOrMinters = errors.New("no minters or initialHolders provided")
Expand Down Expand Up @@ -81,6 +81,7 @@ func (s *Service) IssueTx(_ *http.Request, args *api.FormattedTx, reply *api.JSO
return nil
}

// TODO: After the chain is linearized, remove this.
func (s *Service) IssueStopVertex(_ *http.Request, _, _ *struct{}) error {
return s.vm.issueStopVertex()
}
Expand Down Expand Up @@ -163,9 +164,10 @@ func (s *Service) GetAddressTxs(_ *http.Request, args *GetAddressTxsArgs, reply

// GetTxStatus returns the status of the specified transaction
//
// TODO: deprecate GetTxStatus after the linearization.
// Deprecated: GetTxStatus only returns Accepted or Unknown, GetTx should be
// used instead to determine if the tx was accepted.
func (s *Service) GetTxStatus(_ *http.Request, args *api.JSONTxID, reply *GetTxStatusReply) error {
s.vm.ctx.Log.Debug("API called",
s.vm.ctx.Log.Debug("deprecated API called",
zap.String("service", "avm"),
zap.String("method", "getTxStatus"),
zap.Stringer("txID", args.TxID),
Expand All @@ -175,12 +177,18 @@ func (s *Service) GetTxStatus(_ *http.Request, args *api.JSONTxID, reply *GetTxS
return errNilTxID
}

tx := UniqueTx{
vm: s.vm,
txID: args.TxID,
chainState := &chainState{
State: s.vm.state,
}
_, err := chainState.GetTx(args.TxID)
switch err {
case nil:
reply.Status = choices.Accepted
case database.ErrNotFound:
reply.Status = choices.Unknown
default:
return err
}

reply.Status = tx.Status()
return nil
}

Expand All @@ -196,27 +204,25 @@ func (s *Service) GetTx(_ *http.Request, args *api.GetTxArgs, reply *api.GetTxRe
return errNilTxID
}

tx := UniqueTx{
vm: s.vm,
txID: args.TxID,
chainState := &chainState{
State: s.vm.state,
}
if status := tx.Status(); !status.Fetched() {
return errUnknownTx
tx, err := chainState.GetTx(args.TxID)
if err != nil {
return err
}

reply.Encoding = args.Encoding

if args.Encoding == formatting.JSON {
reply.Tx = tx
return tx.Unsigned.Visit(&txInit{
tx: tx.Tx,
tx: tx,
ctx: s.vm.ctx,
typeToFxIndex: s.vm.typeToFxIndex,
fxs: s.vm.fxs,
})
}

var err error
reply.Tx, err = formatting.Encode(args.Encoding, tx.Bytes())
if err != nil {
return fmt.Errorf("couldn't encode tx as string: %w", err)
Expand Down Expand Up @@ -349,12 +355,12 @@ func (s *Service) GetAssetDescription(_ *http.Request, args *GetAssetDescription
return err
}

tx := &UniqueTx{
vm: s.vm,
txID: assetID,
chainState := &chainState{
State: s.vm.state,
}
if status := tx.Status(); !status.Fetched() {
return errUnknownAssetID
tx, err := chainState.GetTx(assetID)
if err != nil {
return err
}
createAssetTx, ok := tx.Unsigned.(*txs.CreateAssetTx)
if !ok {
Expand Down
Loading

0 comments on commit 7cd1663

Please sign in to comment.