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  }