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 }