github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/executor/backend.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  	"time"
     9  
    10  	"github.com/MetalBlockchain/metalgo/ids"
    11  	"github.com/MetalBlockchain/metalgo/snow"
    12  	"github.com/MetalBlockchain/metalgo/utils/set"
    13  	"github.com/MetalBlockchain/metalgo/vms/platformvm/block"
    14  	"github.com/MetalBlockchain/metalgo/vms/platformvm/state"
    15  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool"
    16  )
    17  
    18  var errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")
    19  
    20  // Shared fields used by visitors.
    21  type backend struct {
    22  	mempool.Mempool
    23  	// lastAccepted is the ID of the last block that had Accept() called on it.
    24  	lastAccepted ids.ID
    25  
    26  	// blkIDToState is a map from a block's ID to the state of the block.
    27  	// Blocks are put into this map when they are verified.
    28  	// Proposal blocks are removed from this map when they are rejected
    29  	// or when a child is accepted.
    30  	// All other blocks are removed when they are accepted/rejected.
    31  	// Note that Genesis block is a commit block so no need to update
    32  	// blkIDToState with it upon backend creation (Genesis is already accepted)
    33  	blkIDToState map[ids.ID]*blockState
    34  	state        state.State
    35  
    36  	ctx *snow.Context
    37  }
    38  
    39  func (b *backend) GetState(blkID ids.ID) (state.Chain, bool) {
    40  	// If the block is in the map, it is either processing or a proposal block
    41  	// that was accepted without an accepted child.
    42  	if state, ok := b.blkIDToState[blkID]; ok {
    43  		if state.onAcceptState != nil {
    44  			return state.onAcceptState, true
    45  		}
    46  		return nil, false
    47  	}
    48  
    49  	// Note: If the last accepted block is a proposal block, we will have
    50  	//       returned in the above if statement.
    51  	return b.state, blkID == b.state.GetLastAccepted()
    52  }
    53  
    54  func (b *backend) getOnAbortState(blkID ids.ID) (state.Diff, bool) {
    55  	state, ok := b.blkIDToState[blkID]
    56  	if !ok || state.onAbortState == nil {
    57  		return nil, false
    58  	}
    59  	return state.onAbortState, true
    60  }
    61  
    62  func (b *backend) getOnCommitState(blkID ids.ID) (state.Diff, bool) {
    63  	state, ok := b.blkIDToState[blkID]
    64  	if !ok || state.onCommitState == nil {
    65  		return nil, false
    66  	}
    67  	return state.onCommitState, true
    68  }
    69  
    70  func (b *backend) GetBlock(blkID ids.ID) (block.Block, error) {
    71  	// See if the block is in memory.
    72  	if blk, ok := b.blkIDToState[blkID]; ok {
    73  		return blk.statelessBlock, nil
    74  	}
    75  
    76  	// The block isn't in memory. Check the database.
    77  	return b.state.GetStatelessBlock(blkID)
    78  }
    79  
    80  func (b *backend) LastAccepted() ids.ID {
    81  	return b.lastAccepted
    82  }
    83  
    84  func (b *backend) free(blkID ids.ID) {
    85  	delete(b.blkIDToState, blkID)
    86  }
    87  
    88  func (b *backend) getTimestamp(blkID ids.ID) time.Time {
    89  	// Check if the block is processing.
    90  	// If the block is processing, then we are guaranteed to have populated its
    91  	// timestamp in its state.
    92  	if blkState, ok := b.blkIDToState[blkID]; ok {
    93  		return blkState.timestamp
    94  	}
    95  
    96  	// The block isn't processing.
    97  	// According to the snowman.Block interface, the last accepted
    98  	// block is the only accepted block that must return a correct timestamp,
    99  	// so we just return the chain time.
   100  	return b.state.GetTimestamp()
   101  }
   102  
   103  // verifyUniqueInputs returns nil iff no blocks in the inclusive
   104  // ancestry of [blkID] consume an input in [inputs].
   105  func (b *backend) verifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error {
   106  	if inputs.Len() == 0 {
   107  		return nil
   108  	}
   109  
   110  	// Check for conflicts in ancestors.
   111  	for {
   112  		state, ok := b.blkIDToState[blkID]
   113  		if !ok {
   114  			// The parent state isn't pinned in memory.
   115  			// This means the parent must be accepted already.
   116  			return nil
   117  		}
   118  
   119  		if state.inputs.Overlaps(inputs) {
   120  			return errConflictingParentTxs
   121  		}
   122  
   123  		blk := state.statelessBlock
   124  		blkID = blk.Parent()
   125  	}
   126  }