github.com/MetalBlockchain/metalgo@v1.11.9/vms/components/chain/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 chain 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 11 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 12 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 13 ) 14 15 var ( 16 _ snowman.Block = (*BlockWrapper)(nil) 17 _ block.WithVerifyContext = (*BlockWrapper)(nil) 18 19 errExpectedBlockWithVerifyContext = errors.New("expected block.WithVerifyContext") 20 ) 21 22 // BlockWrapper wraps a snowman Block while adding a smart caching layer to improve 23 // VM performance. 24 type BlockWrapper struct { 25 snowman.Block 26 27 state *State 28 } 29 30 // Verify verifies the underlying block, evicts from the unverified block cache 31 // and if the block passes verification, adds it to [cache.verifiedBlocks]. 32 // Note: it is guaranteed that if a block passes verification it will be added to 33 // consensus and eventually be decided ie. either Accept/Reject will be called 34 // on [bw] removing it from [verifiedBlocks]. 35 func (bw *BlockWrapper) Verify(ctx context.Context) error { 36 if err := bw.Block.Verify(ctx); err != nil { 37 // Note: we cannot cache blocks failing verification in case 38 // the error is temporary and the block could become valid in 39 // the future. 40 return err 41 } 42 43 blkID := bw.ID() 44 bw.state.unverifiedBlocks.Evict(blkID) 45 bw.state.verifiedBlocks[blkID] = bw 46 return nil 47 } 48 49 // ShouldVerifyWithContext checks if the underlying block should be verified 50 // with a block context. If the underlying block does not implement the 51 // block.WithVerifyContext interface, returns false without an error. Does not 52 // touch any block cache. 53 func (bw *BlockWrapper) ShouldVerifyWithContext(ctx context.Context) (bool, error) { 54 blkWithCtx, ok := bw.Block.(block.WithVerifyContext) 55 if !ok { 56 return false, nil 57 } 58 return blkWithCtx.ShouldVerifyWithContext(ctx) 59 } 60 61 // VerifyWithContext verifies the underlying block with the given block context, 62 // evicts from the unverified block cache and if the block passes verification, 63 // adds it to [cache.verifiedBlocks]. 64 // Note: it is guaranteed that if a block passes verification it will be added 65 // to consensus and eventually be decided ie. either Accept/Reject will be 66 // called on [bw] removing it from [verifiedBlocks]. 67 // 68 // Note: If the underlying block does not implement the block.WithVerifyContext 69 // interface, an error is always returned because ShouldVerifyWithContext will 70 // always return false in this case and VerifyWithContext should never be 71 // called. 72 func (bw *BlockWrapper) VerifyWithContext(ctx context.Context, blockCtx *block.Context) error { 73 blkWithCtx, ok := bw.Block.(block.WithVerifyContext) 74 if !ok { 75 return fmt.Errorf("%w but got %T", errExpectedBlockWithVerifyContext, bw.Block) 76 } 77 78 if err := blkWithCtx.VerifyWithContext(ctx, blockCtx); err != nil { 79 // Note: we cannot cache blocks failing verification in case 80 // the error is temporary and the block could become valid in 81 // the future. 82 return err 83 } 84 85 blkID := bw.ID() 86 bw.state.unverifiedBlocks.Evict(blkID) 87 bw.state.verifiedBlocks[blkID] = bw 88 return nil 89 } 90 91 // Accept accepts the underlying block, removes it from verifiedBlocks, caches it as a decided 92 // block, and updates the last accepted block. 93 func (bw *BlockWrapper) Accept(ctx context.Context) error { 94 blkID := bw.ID() 95 delete(bw.state.verifiedBlocks, blkID) 96 bw.state.decidedBlocks.Put(blkID, bw) 97 bw.state.lastAcceptedBlock = bw 98 99 return bw.Block.Accept(ctx) 100 } 101 102 // Reject rejects the underlying block, removes it from processing blocks, and caches it as a 103 // decided block. 104 func (bw *BlockWrapper) Reject(ctx context.Context) error { 105 blkID := bw.ID() 106 delete(bw.state.verifiedBlocks, blkID) 107 bw.state.decidedBlocks.Put(blkID, bw) 108 return bw.Block.Reject(ctx) 109 }