github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/bootstrap/tx_job.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package bootstrap
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"go.uber.org/zap"
    13  
    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/snow/engine/avalanche/bootstrap/queue"
    18  	"github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex"
    19  	"github.com/MetalBlockchain/metalgo/utils/logging"
    20  	"github.com/MetalBlockchain/metalgo/utils/set"
    21  )
    22  
    23  var errMissingTxDependenciesOnAccept = errors.New("attempting to accept a transaction with missing dependencies")
    24  
    25  type txParser struct {
    26  	log         logging.Logger
    27  	numAccepted prometheus.Counter
    28  	vm          vertex.LinearizableVM
    29  }
    30  
    31  func (p *txParser) Parse(ctx context.Context, txBytes []byte) (queue.Job, error) {
    32  	tx, err := p.vm.ParseTx(ctx, txBytes)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	return &txJob{
    37  		log:         p.log,
    38  		numAccepted: p.numAccepted,
    39  		tx:          tx,
    40  	}, nil
    41  }
    42  
    43  type txJob struct {
    44  	log         logging.Logger
    45  	numAccepted prometheus.Counter
    46  	tx          snowstorm.Tx
    47  }
    48  
    49  func (t *txJob) ID() ids.ID {
    50  	return t.tx.ID()
    51  }
    52  
    53  func (t *txJob) MissingDependencies(context.Context) (set.Set[ids.ID], error) {
    54  	return t.tx.MissingDependencies()
    55  }
    56  
    57  // Returns true if this tx job has at least 1 missing dependency
    58  func (t *txJob) HasMissingDependencies(context.Context) (bool, error) {
    59  	deps, err := t.tx.MissingDependencies()
    60  	return deps.Len() > 0, err
    61  }
    62  
    63  func (t *txJob) Execute(ctx context.Context) error {
    64  	hasMissingDeps, err := t.HasMissingDependencies(ctx)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	if hasMissingDeps {
    69  		return errMissingTxDependenciesOnAccept
    70  	}
    71  
    72  	status := t.tx.Status()
    73  	switch status {
    74  	case choices.Unknown, choices.Rejected:
    75  		return fmt.Errorf("attempting to execute transaction with status %s", status)
    76  	case choices.Processing:
    77  		txID := t.tx.ID()
    78  		if err := t.tx.Verify(ctx); err != nil {
    79  			t.log.Error("transaction failed verification during bootstrapping",
    80  				zap.Stringer("txID", txID),
    81  				zap.Error(err),
    82  			)
    83  			return fmt.Errorf("failed to verify transaction in bootstrapping: %w", err)
    84  		}
    85  
    86  		t.numAccepted.Inc()
    87  		t.log.Trace("accepting transaction in bootstrapping",
    88  			zap.Stringer("txID", txID),
    89  		)
    90  		if err := t.tx.Accept(ctx); err != nil {
    91  			t.log.Error("transaction failed to accept during bootstrapping",
    92  				zap.Stringer("txID", txID),
    93  				zap.Error(err),
    94  			)
    95  			return fmt.Errorf("failed to accept transaction in bootstrapping: %w", err)
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func (t *txJob) Bytes() []byte {
   102  	return t.tx.Bytes()
   103  }