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 }