github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/block/executor/manager.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "errors" 8 9 "github.com/MetalBlockchain/metalgo/chains/atomic" 10 "github.com/MetalBlockchain/metalgo/ids" 11 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 12 "github.com/MetalBlockchain/metalgo/utils/set" 13 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 14 "github.com/MetalBlockchain/metalgo/vms/avm/block" 15 "github.com/MetalBlockchain/metalgo/vms/avm/metrics" 16 "github.com/MetalBlockchain/metalgo/vms/avm/state" 17 "github.com/MetalBlockchain/metalgo/vms/avm/txs" 18 "github.com/MetalBlockchain/metalgo/vms/avm/txs/executor" 19 "github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool" 20 ) 21 22 var ( 23 _ Manager = (*manager)(nil) 24 25 ErrChainNotSynced = errors.New("chain not synced") 26 ErrConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") 27 ) 28 29 type Manager interface { 30 state.Versions 31 32 // Returns the ID of the most recently accepted block. 33 LastAccepted() ids.ID 34 35 SetPreference(blkID ids.ID) 36 Preferred() ids.ID 37 38 GetBlock(blkID ids.ID) (snowman.Block, error) 39 GetStatelessBlock(blkID ids.ID) (block.Block, error) 40 NewBlock(block.Block) snowman.Block 41 42 // VerifyTx verifies that the transaction can be issued based on the currently 43 // preferred state. This should *not* be used to verify transactions in a block. 44 VerifyTx(tx *txs.Tx) error 45 46 // VerifyUniqueInputs returns nil iff no blocks in the inclusive 47 // ancestry of [blkID] consume an input in [inputs]. 48 VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error 49 } 50 51 func NewManager( 52 mempool mempool.Mempool, 53 metrics metrics.Metrics, 54 state state.State, 55 backend *executor.Backend, 56 clk *mockable.Clock, 57 onAccept func(*txs.Tx) error, 58 ) Manager { 59 lastAccepted := state.GetLastAccepted() 60 return &manager{ 61 backend: backend, 62 state: state, 63 metrics: metrics, 64 mempool: mempool, 65 clk: clk, 66 onAccept: onAccept, 67 blkIDToState: map[ids.ID]*blockState{}, 68 lastAccepted: lastAccepted, 69 preferred: lastAccepted, 70 } 71 } 72 73 type manager struct { 74 backend *executor.Backend 75 state state.State 76 metrics metrics.Metrics 77 mempool mempool.Mempool 78 clk *mockable.Clock 79 // Invariant: onAccept is called when [tx] is being marked as accepted, but 80 // before its state changes are applied. 81 // Invariant: any error returned by onAccept should be considered fatal. 82 onAccept func(*txs.Tx) error 83 84 // blkIDToState is a map from a block's ID to the state of the block. 85 // Blocks are put into this map when they are verified. 86 // Blocks are removed from this map when they are decided. 87 blkIDToState map[ids.ID]*blockState 88 89 // lastAccepted is the ID of the last block that had Accept() called on it. 90 lastAccepted ids.ID 91 preferred ids.ID 92 } 93 94 type blockState struct { 95 statelessBlock block.Block 96 onAcceptState state.Diff 97 importedInputs set.Set[ids.ID] 98 atomicRequests map[ids.ID]*atomic.Requests 99 } 100 101 func (m *manager) GetState(blkID ids.ID) (state.Chain, bool) { 102 // If the block is in the map, it is processing. 103 if state, ok := m.blkIDToState[blkID]; ok { 104 return state.onAcceptState, true 105 } 106 return m.state, blkID == m.lastAccepted 107 } 108 109 func (m *manager) LastAccepted() ids.ID { 110 return m.lastAccepted 111 } 112 113 func (m *manager) SetPreference(blockID ids.ID) { 114 m.preferred = blockID 115 } 116 117 func (m *manager) Preferred() ids.ID { 118 return m.preferred 119 } 120 121 func (m *manager) GetBlock(blkID ids.ID) (snowman.Block, error) { 122 blk, err := m.GetStatelessBlock(blkID) 123 if err != nil { 124 return nil, err 125 } 126 return m.NewBlock(blk), nil 127 } 128 129 func (m *manager) GetStatelessBlock(blkID ids.ID) (block.Block, error) { 130 // See if the block is in memory. 131 if blkState, ok := m.blkIDToState[blkID]; ok { 132 return blkState.statelessBlock, nil 133 } 134 // The block isn't in memory. Check the database. 135 return m.state.GetBlock(blkID) 136 } 137 138 func (m *manager) NewBlock(blk block.Block) snowman.Block { 139 return &Block{ 140 Block: blk, 141 manager: m, 142 } 143 } 144 145 func (m *manager) VerifyTx(tx *txs.Tx) error { 146 if !m.backend.Bootstrapped { 147 return ErrChainNotSynced 148 } 149 150 err := tx.Unsigned.Visit(&executor.SyntacticVerifier{ 151 Backend: m.backend, 152 Tx: tx, 153 }) 154 if err != nil { 155 return err 156 } 157 158 stateDiff, err := state.NewDiff(m.lastAccepted, m) 159 if err != nil { 160 return err 161 } 162 163 err = tx.Unsigned.Visit(&executor.SemanticVerifier{ 164 Backend: m.backend, 165 State: stateDiff, 166 Tx: tx, 167 }) 168 if err != nil { 169 return err 170 } 171 172 executor := &executor.Executor{ 173 Codec: m.backend.Codec, 174 State: stateDiff, 175 Tx: tx, 176 } 177 return tx.Unsigned.Visit(executor) 178 } 179 180 func (m *manager) VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error { 181 if inputs.Len() == 0 { 182 return nil 183 } 184 185 // Check for conflicts in ancestors. 186 for { 187 state, ok := m.blkIDToState[blkID] 188 if !ok { 189 // The parent state isn't pinned in memory. 190 // This means the parent must be accepted already. 191 return nil 192 } 193 194 if state.importedInputs.Overlaps(inputs) { 195 return ErrConflictingParentTxs 196 } 197 198 blk := state.statelessBlock 199 blkID = blk.Parent() 200 } 201 } 202 203 func (m *manager) free(blkID ids.ID) { 204 delete(m.blkIDToState, blkID) 205 }