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