github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/tx.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package avm
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"go.uber.org/zap"
    12  
    13  	"github.com/MetalBlockchain/metalgo/database"
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/snow/choices"
    16  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm"
    17  	"github.com/MetalBlockchain/metalgo/utils/set"
    18  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    19  	"github.com/MetalBlockchain/metalgo/vms/avm/txs/executor"
    20  )
    21  
    22  var (
    23  	_ snowstorm.Tx = (*Tx)(nil)
    24  
    25  	errTxNotProcessing  = errors.New("transaction is not processing")
    26  	errUnexpectedReject = errors.New("attempting to reject transaction")
    27  )
    28  
    29  type Tx struct {
    30  	vm *VM
    31  	tx *txs.Tx
    32  }
    33  
    34  func (tx *Tx) ID() ids.ID {
    35  	return tx.tx.ID()
    36  }
    37  
    38  func (tx *Tx) Accept(context.Context) error {
    39  	if s := tx.Status(); s != choices.Processing {
    40  		return fmt.Errorf("%w: %s", errTxNotProcessing, s)
    41  	}
    42  
    43  	if err := tx.vm.onAccept(tx.tx); err != nil {
    44  		return err
    45  	}
    46  
    47  	executor := &executor.Executor{
    48  		Codec: tx.vm.txBackend.Codec,
    49  		State: tx.vm.state,
    50  		Tx:    tx.tx,
    51  	}
    52  	err := tx.tx.Unsigned.Visit(executor)
    53  	if err != nil {
    54  		return fmt.Errorf("error staging accepted state changes: %w", err)
    55  	}
    56  
    57  	tx.vm.state.AddTx(tx.tx)
    58  
    59  	commitBatch, err := tx.vm.state.CommitBatch()
    60  	if err != nil {
    61  		txID := tx.tx.ID()
    62  		return fmt.Errorf("couldn't create commitBatch while processing tx %s: %w", txID, err)
    63  	}
    64  
    65  	defer tx.vm.state.Abort()
    66  	err = tx.vm.ctx.SharedMemory.Apply(
    67  		executor.AtomicRequests,
    68  		commitBatch,
    69  	)
    70  	if err != nil {
    71  		txID := tx.tx.ID()
    72  		return fmt.Errorf("error committing accepted state changes while processing tx %s: %w", txID, err)
    73  	}
    74  
    75  	return tx.vm.metrics.MarkTxAccepted(tx.tx)
    76  }
    77  
    78  func (*Tx) Reject(context.Context) error {
    79  	return errUnexpectedReject
    80  }
    81  
    82  func (tx *Tx) Status() choices.Status {
    83  	txID := tx.tx.ID()
    84  	_, err := tx.vm.state.GetTx(txID)
    85  	switch err {
    86  	case nil:
    87  		return choices.Accepted
    88  	case database.ErrNotFound:
    89  		return choices.Processing
    90  	default:
    91  		tx.vm.ctx.Log.Error("failed looking up tx status",
    92  			zap.Stringer("txID", txID),
    93  			zap.Error(err),
    94  		)
    95  		return choices.Processing
    96  	}
    97  }
    98  
    99  func (tx *Tx) MissingDependencies() (set.Set[ids.ID], error) {
   100  	txIDs := set.Set[ids.ID]{}
   101  	for _, in := range tx.tx.Unsigned.InputUTXOs() {
   102  		if in.Symbolic() {
   103  			continue
   104  		}
   105  		txID, _ := in.InputSource()
   106  
   107  		_, err := tx.vm.state.GetTx(txID)
   108  		switch err {
   109  		case nil:
   110  			// Tx was already accepted
   111  		case database.ErrNotFound:
   112  			txIDs.Add(txID)
   113  		default:
   114  			return nil, err
   115  		}
   116  	}
   117  	return txIDs, nil
   118  }
   119  
   120  func (tx *Tx) Bytes() []byte {
   121  	return tx.tx.Bytes()
   122  }
   123  
   124  func (tx *Tx) Verify(context.Context) error {
   125  	if s := tx.Status(); s != choices.Processing {
   126  		return fmt.Errorf("%w: %s", errTxNotProcessing, s)
   127  	}
   128  	return tx.tx.Unsigned.Visit(&executor.SemanticVerifier{
   129  		Backend: tx.vm.txBackend,
   130  		State:   tx.vm.state,
   131  		Tx:      tx.tx,
   132  	})
   133  }