github.com/ava-labs/avalanchego@v1.11.11/vms/avm/block/executor/block.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 "context" 8 "errors" 9 "fmt" 10 "time" 11 12 "go.uber.org/zap" 13 14 "github.com/ava-labs/avalanchego/chains/atomic" 15 "github.com/ava-labs/avalanchego/ids" 16 "github.com/ava-labs/avalanchego/snow/consensus/snowman" 17 "github.com/ava-labs/avalanchego/vms/avm/block" 18 "github.com/ava-labs/avalanchego/vms/avm/state" 19 "github.com/ava-labs/avalanchego/vms/avm/txs/executor" 20 ) 21 22 const SyncBound = 10 * time.Second 23 24 var ( 25 _ snowman.Block = (*Block)(nil) 26 27 ErrUnexpectedMerkleRoot = errors.New("unexpected merkle root") 28 ErrTimestampBeyondSyncBound = errors.New("proposed timestamp is too far in the future relative to local time") 29 ErrEmptyBlock = errors.New("block contains no transactions") 30 ErrChildBlockEarlierThanParent = errors.New("proposed timestamp before current chain time") 31 ErrConflictingBlockTxs = errors.New("block contains conflicting transactions") 32 ErrIncorrectHeight = errors.New("block has incorrect height") 33 ErrBlockNotFound = errors.New("block not found") 34 ) 35 36 // Exported for testing in avm package. 37 type Block struct { 38 block.Block 39 manager *manager 40 } 41 42 func (b *Block) Verify(context.Context) error { 43 blkID := b.ID() 44 if _, ok := b.manager.blkIDToState[blkID]; ok { 45 // This block has already been verified. 46 return nil 47 } 48 49 // Currently we don't populate the blocks merkle root. 50 merkleRoot := b.Block.MerkleRoot() 51 if merkleRoot != ids.Empty { 52 return fmt.Errorf("%w: %s", ErrUnexpectedMerkleRoot, merkleRoot) 53 } 54 55 // Only allow timestamp to reasonably far forward 56 newChainTime := b.Timestamp() 57 now := b.manager.clk.Time() 58 maxNewChainTime := now.Add(SyncBound) 59 if newChainTime.After(maxNewChainTime) { 60 return fmt.Errorf( 61 "%w, proposed time (%s), local time (%s)", 62 ErrTimestampBeyondSyncBound, 63 newChainTime, 64 now, 65 ) 66 } 67 68 txs := b.Txs() 69 if len(txs) == 0 { 70 return ErrEmptyBlock 71 } 72 73 // Syntactic verification is generally pretty fast, so we verify this first 74 // before performing any possible DB reads. 75 for _, tx := range txs { 76 err := tx.Unsigned.Visit(&executor.SyntacticVerifier{ 77 Backend: b.manager.backend, 78 Tx: tx, 79 }) 80 if err != nil { 81 txID := tx.ID() 82 b.manager.mempool.MarkDropped(txID, err) 83 return err 84 } 85 } 86 87 // Verify that the parent exists. 88 parentID := b.Parent() 89 parent, err := b.manager.GetStatelessBlock(parentID) 90 if err != nil { 91 return err 92 } 93 94 // Verify that currentBlkHeight = parentBlkHeight + 1. 95 expectedHeight := parent.Height() + 1 96 height := b.Height() 97 if expectedHeight != height { 98 return fmt.Errorf( 99 "%w: expected height %d, got %d", 100 ErrIncorrectHeight, 101 expectedHeight, 102 height, 103 ) 104 } 105 106 stateDiff, err := state.NewDiff(parentID, b.manager) 107 if err != nil { 108 return err 109 } 110 111 parentChainTime := stateDiff.GetTimestamp() 112 // The proposed timestamp must not be before the parent's timestamp. 113 if newChainTime.Before(parentChainTime) { 114 return fmt.Errorf( 115 "%w: proposed timestamp (%s), chain time (%s)", 116 ErrChildBlockEarlierThanParent, 117 newChainTime, 118 parentChainTime, 119 ) 120 } 121 122 stateDiff.SetTimestamp(newChainTime) 123 124 blockState := &blockState{ 125 statelessBlock: b.Block, 126 onAcceptState: stateDiff, 127 atomicRequests: make(map[ids.ID]*atomic.Requests), 128 } 129 130 for _, tx := range txs { 131 // Verify that the tx is valid according to the current state of the 132 // chain. 133 err := tx.Unsigned.Visit(&executor.SemanticVerifier{ 134 Backend: b.manager.backend, 135 State: stateDiff, 136 Tx: tx, 137 }) 138 if err != nil { 139 txID := tx.ID() 140 b.manager.mempool.MarkDropped(txID, err) 141 return err 142 } 143 144 // Apply the txs state changes to the state. 145 // 146 // Note: This must be done inside the same loop as semantic verification 147 // to ensure that semantic verification correctly accounts for 148 // transactions that occurred earlier in the block. 149 executor := &executor.Executor{ 150 Codec: b.manager.backend.Codec, 151 State: stateDiff, 152 Tx: tx, 153 } 154 err = tx.Unsigned.Visit(executor) 155 if err != nil { 156 txID := tx.ID() 157 b.manager.mempool.MarkDropped(txID, err) 158 return err 159 } 160 161 // Verify that the transaction we just executed didn't consume inputs 162 // that were already imported in a previous transaction. 163 if blockState.importedInputs.Overlaps(executor.Inputs) { 164 txID := tx.ID() 165 b.manager.mempool.MarkDropped(txID, ErrConflictingBlockTxs) 166 return ErrConflictingBlockTxs 167 } 168 blockState.importedInputs.Union(executor.Inputs) 169 170 // Now that the tx would be marked as accepted, we should add it to the 171 // state for the next transaction in the block. 172 stateDiff.AddTx(tx) 173 174 for chainID, txRequests := range executor.AtomicRequests { 175 // Add/merge in the atomic requests represented by [tx] 176 chainRequests, exists := blockState.atomicRequests[chainID] 177 if !exists { 178 blockState.atomicRequests[chainID] = txRequests 179 continue 180 } 181 182 chainRequests.PutRequests = append(chainRequests.PutRequests, txRequests.PutRequests...) 183 chainRequests.RemoveRequests = append(chainRequests.RemoveRequests, txRequests.RemoveRequests...) 184 } 185 } 186 187 // Verify that none of the transactions consumed any inputs that were 188 // already imported in a currently processing block. 189 err = b.manager.VerifyUniqueInputs(parentID, blockState.importedInputs) 190 if err != nil { 191 return err 192 } 193 194 // Now that the block has been executed, we can add the block data to the 195 // state diff. 196 stateDiff.SetLastAccepted(blkID) 197 stateDiff.AddBlock(b.Block) 198 199 b.manager.blkIDToState[blkID] = blockState 200 b.manager.mempool.Remove(txs...) 201 return nil 202 } 203 204 func (b *Block) Accept(context.Context) error { 205 blkID := b.ID() 206 defer b.manager.free(blkID) 207 208 txs := b.Txs() 209 for _, tx := range txs { 210 if err := b.manager.onAccept(tx); err != nil { 211 return fmt.Errorf( 212 "failed to mark tx %q as accepted: %w", 213 blkID, 214 err, 215 ) 216 } 217 } 218 219 b.manager.lastAccepted = blkID 220 b.manager.mempool.Remove(txs...) 221 222 blkState, ok := b.manager.blkIDToState[blkID] 223 if !ok { 224 return fmt.Errorf("%w: %s", ErrBlockNotFound, blkID) 225 } 226 227 // Update the state to reflect the changes made in [onAcceptState]. 228 blkState.onAcceptState.Apply(b.manager.state) 229 230 defer b.manager.state.Abort() 231 batch, err := b.manager.state.CommitBatch() 232 if err != nil { 233 return fmt.Errorf( 234 "failed to stage state diff for block %s: %w", 235 blkID, 236 err, 237 ) 238 } 239 240 // Note that this method writes [batch] to the database. 241 if err := b.manager.backend.Ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { 242 return fmt.Errorf("failed to apply state diff to shared memory: %w", err) 243 } 244 245 if err := b.manager.metrics.MarkBlockAccepted(b); err != nil { 246 return err 247 } 248 249 txChecksum, utxoChecksum := b.manager.state.Checksums() 250 b.manager.backend.Ctx.Log.Trace( 251 "accepted block", 252 zap.Stringer("blkID", blkID), 253 zap.Uint64("height", b.Height()), 254 zap.Stringer("parentID", b.Parent()), 255 zap.Stringer("txChecksum", txChecksum), 256 zap.Stringer("utxoChecksum", utxoChecksum), 257 ) 258 return nil 259 } 260 261 func (b *Block) Reject(context.Context) error { 262 blkID := b.ID() 263 defer b.manager.free(blkID) 264 265 b.manager.backend.Ctx.Log.Verbo( 266 "rejecting block", 267 zap.Stringer("blkID", blkID), 268 zap.Uint64("height", b.Height()), 269 zap.Stringer("parentID", b.Parent()), 270 ) 271 272 for _, tx := range b.Txs() { 273 if err := b.manager.VerifyTx(tx); err != nil { 274 b.manager.backend.Ctx.Log.Debug("dropping invalidated tx", 275 zap.Stringer("txID", tx.ID()), 276 zap.Stringer("blkID", blkID), 277 zap.Error(err), 278 ) 279 continue 280 } 281 if err := b.manager.mempool.Add(tx); err != nil { 282 b.manager.backend.Ctx.Log.Debug("dropping valid tx", 283 zap.Stringer("txID", tx.ID()), 284 zap.Stringer("blkID", blkID), 285 zap.Error(err), 286 ) 287 } 288 } 289 290 // If we added transactions to the mempool, we should be willing to build a 291 // block. 292 b.manager.mempool.RequestBuildBlock() 293 return nil 294 }