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  }