github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/block/executor/manager.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  
     9  	"github.com/MetalBlockchain/metalgo/chains/atomic"
    10  	"github.com/MetalBlockchain/metalgo/ids"
    11  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    12  	"github.com/MetalBlockchain/metalgo/utils/set"
    13  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    14  	"github.com/MetalBlockchain/metalgo/vms/avm/block"
    15  	"github.com/MetalBlockchain/metalgo/vms/avm/metrics"
    16  	"github.com/MetalBlockchain/metalgo/vms/avm/state"
    17  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    18  	"github.com/MetalBlockchain/metalgo/vms/avm/txs/executor"
    19  	"github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool"
    20  )
    21  
    22  var (
    23  	_ Manager = (*manager)(nil)
    24  
    25  	ErrChainNotSynced       = errors.New("chain not synced")
    26  	ErrConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")
    27  )
    28  
    29  type Manager interface {
    30  	state.Versions
    31  
    32  	// Returns the ID of the most recently accepted block.
    33  	LastAccepted() ids.ID
    34  
    35  	SetPreference(blkID ids.ID)
    36  	Preferred() ids.ID
    37  
    38  	GetBlock(blkID ids.ID) (snowman.Block, error)
    39  	GetStatelessBlock(blkID ids.ID) (block.Block, error)
    40  	NewBlock(block.Block) snowman.Block
    41  
    42  	// VerifyTx verifies that the transaction can be issued based on the currently
    43  	// preferred state. This should *not* be used to verify transactions in a block.
    44  	VerifyTx(tx *txs.Tx) error
    45  
    46  	// VerifyUniqueInputs returns nil iff no blocks in the inclusive
    47  	// ancestry of [blkID] consume an input in [inputs].
    48  	VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error
    49  }
    50  
    51  func NewManager(
    52  	mempool mempool.Mempool,
    53  	metrics metrics.Metrics,
    54  	state state.State,
    55  	backend *executor.Backend,
    56  	clk *mockable.Clock,
    57  	onAccept func(*txs.Tx) error,
    58  ) Manager {
    59  	lastAccepted := state.GetLastAccepted()
    60  	return &manager{
    61  		backend:      backend,
    62  		state:        state,
    63  		metrics:      metrics,
    64  		mempool:      mempool,
    65  		clk:          clk,
    66  		onAccept:     onAccept,
    67  		blkIDToState: map[ids.ID]*blockState{},
    68  		lastAccepted: lastAccepted,
    69  		preferred:    lastAccepted,
    70  	}
    71  }
    72  
    73  type manager struct {
    74  	backend *executor.Backend
    75  	state   state.State
    76  	metrics metrics.Metrics
    77  	mempool mempool.Mempool
    78  	clk     *mockable.Clock
    79  	// Invariant: onAccept is called when [tx] is being marked as accepted, but
    80  	// before its state changes are applied.
    81  	// Invariant: any error returned by onAccept should be considered fatal.
    82  	onAccept func(*txs.Tx) error
    83  
    84  	// blkIDToState is a map from a block's ID to the state of the block.
    85  	// Blocks are put into this map when they are verified.
    86  	// Blocks are removed from this map when they are decided.
    87  	blkIDToState map[ids.ID]*blockState
    88  
    89  	// lastAccepted is the ID of the last block that had Accept() called on it.
    90  	lastAccepted ids.ID
    91  	preferred    ids.ID
    92  }
    93  
    94  type blockState struct {
    95  	statelessBlock block.Block
    96  	onAcceptState  state.Diff
    97  	importedInputs set.Set[ids.ID]
    98  	atomicRequests map[ids.ID]*atomic.Requests
    99  }
   100  
   101  func (m *manager) GetState(blkID ids.ID) (state.Chain, bool) {
   102  	// If the block is in the map, it is processing.
   103  	if state, ok := m.blkIDToState[blkID]; ok {
   104  		return state.onAcceptState, true
   105  	}
   106  	return m.state, blkID == m.lastAccepted
   107  }
   108  
   109  func (m *manager) LastAccepted() ids.ID {
   110  	return m.lastAccepted
   111  }
   112  
   113  func (m *manager) SetPreference(blockID ids.ID) {
   114  	m.preferred = blockID
   115  }
   116  
   117  func (m *manager) Preferred() ids.ID {
   118  	return m.preferred
   119  }
   120  
   121  func (m *manager) GetBlock(blkID ids.ID) (snowman.Block, error) {
   122  	blk, err := m.GetStatelessBlock(blkID)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	return m.NewBlock(blk), nil
   127  }
   128  
   129  func (m *manager) GetStatelessBlock(blkID ids.ID) (block.Block, error) {
   130  	// See if the block is in memory.
   131  	if blkState, ok := m.blkIDToState[blkID]; ok {
   132  		return blkState.statelessBlock, nil
   133  	}
   134  	// The block isn't in memory. Check the database.
   135  	return m.state.GetBlock(blkID)
   136  }
   137  
   138  func (m *manager) NewBlock(blk block.Block) snowman.Block {
   139  	return &Block{
   140  		Block:   blk,
   141  		manager: m,
   142  	}
   143  }
   144  
   145  func (m *manager) VerifyTx(tx *txs.Tx) error {
   146  	if !m.backend.Bootstrapped {
   147  		return ErrChainNotSynced
   148  	}
   149  
   150  	err := tx.Unsigned.Visit(&executor.SyntacticVerifier{
   151  		Backend: m.backend,
   152  		Tx:      tx,
   153  	})
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	stateDiff, err := state.NewDiff(m.lastAccepted, m)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	err = tx.Unsigned.Visit(&executor.SemanticVerifier{
   164  		Backend: m.backend,
   165  		State:   stateDiff,
   166  		Tx:      tx,
   167  	})
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	executor := &executor.Executor{
   173  		Codec: m.backend.Codec,
   174  		State: stateDiff,
   175  		Tx:    tx,
   176  	}
   177  	return tx.Unsigned.Visit(executor)
   178  }
   179  
   180  func (m *manager) VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error {
   181  	if inputs.Len() == 0 {
   182  		return nil
   183  	}
   184  
   185  	// Check for conflicts in ancestors.
   186  	for {
   187  		state, ok := m.blkIDToState[blkID]
   188  		if !ok {
   189  			// The parent state isn't pinned in memory.
   190  			// This means the parent must be accepted already.
   191  			return nil
   192  		}
   193  
   194  		if state.importedInputs.Overlaps(inputs) {
   195  			return ErrConflictingParentTxs
   196  		}
   197  
   198  		blk := state.statelessBlock
   199  		blkID = blk.Parent()
   200  	}
   201  }
   202  
   203  func (m *manager) free(blkID ids.ID) {
   204  	delete(m.blkIDToState, blkID)
   205  }