github.com/ava-labs/avalanchego@v1.11.11/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/ava-labs/avalanchego/chains/atomic"
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    17  	"github.com/ava-labs/avalanchego/vms/avm/block"
    18  	"github.com/ava-labs/avalanchego/vms/avm/state"
    19  	"github.com/ava-labs/avalanchego/vms/avm/txs/executor"
    20  )
    21  
    22  const SyncBound = 10 * time.Second
    23  
    24  var (
    25  	_ snowman.Block = (*Block)(nil)
    26  
    27  	ErrUnexpectedMerkleRoot        = errors.New("unexpected merkle root")
    28  	ErrTimestampBeyondSyncBound    = errors.New("proposed timestamp is too far in the future relative to local time")
    29  	ErrEmptyBlock                  = errors.New("block contains no transactions")
    30  	ErrChildBlockEarlierThanParent = errors.New("proposed timestamp before current chain time")
    31  	ErrConflictingBlockTxs         = errors.New("block contains conflicting transactions")
    32  	ErrIncorrectHeight             = errors.New("block has incorrect height")
    33  	ErrBlockNotFound               = errors.New("block not found")
    34  )
    35  
    36  // Exported for testing in avm package.
    37  type Block struct {
    38  	block.Block
    39  	manager *manager
    40  }
    41  
    42  func (b *Block) Verify(context.Context) error {
    43  	blkID := b.ID()
    44  	if _, ok := b.manager.blkIDToState[blkID]; ok {
    45  		// This block has already been verified.
    46  		return nil
    47  	}
    48  
    49  	// Currently we don't populate the blocks merkle root.
    50  	merkleRoot := b.Block.MerkleRoot()
    51  	if merkleRoot != ids.Empty {
    52  		return fmt.Errorf("%w: %s", ErrUnexpectedMerkleRoot, merkleRoot)
    53  	}
    54  
    55  	// Only allow timestamp to reasonably far forward
    56  	newChainTime := b.Timestamp()
    57  	now := b.manager.clk.Time()
    58  	maxNewChainTime := now.Add(SyncBound)
    59  	if newChainTime.After(maxNewChainTime) {
    60  		return fmt.Errorf(
    61  			"%w, proposed time (%s), local time (%s)",
    62  			ErrTimestampBeyondSyncBound,
    63  			newChainTime,
    64  			now,
    65  		)
    66  	}
    67  
    68  	txs := b.Txs()
    69  	if len(txs) == 0 {
    70  		return ErrEmptyBlock
    71  	}
    72  
    73  	// Syntactic verification is generally pretty fast, so we verify this first
    74  	// before performing any possible DB reads.
    75  	for _, tx := range txs {
    76  		err := tx.Unsigned.Visit(&executor.SyntacticVerifier{
    77  			Backend: b.manager.backend,
    78  			Tx:      tx,
    79  		})
    80  		if err != nil {
    81  			txID := tx.ID()
    82  			b.manager.mempool.MarkDropped(txID, err)
    83  			return err
    84  		}
    85  	}
    86  
    87  	// Verify that the parent exists.
    88  	parentID := b.Parent()
    89  	parent, err := b.manager.GetStatelessBlock(parentID)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	// Verify that currentBlkHeight = parentBlkHeight + 1.
    95  	expectedHeight := parent.Height() + 1
    96  	height := b.Height()
    97  	if expectedHeight != height {
    98  		return fmt.Errorf(
    99  			"%w: expected height %d, got %d",
   100  			ErrIncorrectHeight,
   101  			expectedHeight,
   102  			height,
   103  		)
   104  	}
   105  
   106  	stateDiff, err := state.NewDiff(parentID, b.manager)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	parentChainTime := stateDiff.GetTimestamp()
   112  	// The proposed timestamp must not be before the parent's timestamp.
   113  	if newChainTime.Before(parentChainTime) {
   114  		return fmt.Errorf(
   115  			"%w: proposed timestamp (%s), chain time (%s)",
   116  			ErrChildBlockEarlierThanParent,
   117  			newChainTime,
   118  			parentChainTime,
   119  		)
   120  	}
   121  
   122  	stateDiff.SetTimestamp(newChainTime)
   123  
   124  	blockState := &blockState{
   125  		statelessBlock: b.Block,
   126  		onAcceptState:  stateDiff,
   127  		atomicRequests: make(map[ids.ID]*atomic.Requests),
   128  	}
   129  
   130  	for _, tx := range txs {
   131  		// Verify that the tx is valid according to the current state of the
   132  		// chain.
   133  		err := tx.Unsigned.Visit(&executor.SemanticVerifier{
   134  			Backend: b.manager.backend,
   135  			State:   stateDiff,
   136  			Tx:      tx,
   137  		})
   138  		if err != nil {
   139  			txID := tx.ID()
   140  			b.manager.mempool.MarkDropped(txID, err)
   141  			return err
   142  		}
   143  
   144  		// Apply the txs state changes to the state.
   145  		//
   146  		// Note: This must be done inside the same loop as semantic verification
   147  		// to ensure that semantic verification correctly accounts for
   148  		// transactions that occurred earlier in the block.
   149  		executor := &executor.Executor{
   150  			Codec: b.manager.backend.Codec,
   151  			State: stateDiff,
   152  			Tx:    tx,
   153  		}
   154  		err = tx.Unsigned.Visit(executor)
   155  		if err != nil {
   156  			txID := tx.ID()
   157  			b.manager.mempool.MarkDropped(txID, err)
   158  			return err
   159  		}
   160  
   161  		// Verify that the transaction we just executed didn't consume inputs
   162  		// that were already imported in a previous transaction.
   163  		if blockState.importedInputs.Overlaps(executor.Inputs) {
   164  			txID := tx.ID()
   165  			b.manager.mempool.MarkDropped(txID, ErrConflictingBlockTxs)
   166  			return ErrConflictingBlockTxs
   167  		}
   168  		blockState.importedInputs.Union(executor.Inputs)
   169  
   170  		// Now that the tx would be marked as accepted, we should add it to the
   171  		// state for the next transaction in the block.
   172  		stateDiff.AddTx(tx)
   173  
   174  		for chainID, txRequests := range executor.AtomicRequests {
   175  			// Add/merge in the atomic requests represented by [tx]
   176  			chainRequests, exists := blockState.atomicRequests[chainID]
   177  			if !exists {
   178  				blockState.atomicRequests[chainID] = txRequests
   179  				continue
   180  			}
   181  
   182  			chainRequests.PutRequests = append(chainRequests.PutRequests, txRequests.PutRequests...)
   183  			chainRequests.RemoveRequests = append(chainRequests.RemoveRequests, txRequests.RemoveRequests...)
   184  		}
   185  	}
   186  
   187  	// Verify that none of the transactions consumed any inputs that were
   188  	// already imported in a currently processing block.
   189  	err = b.manager.VerifyUniqueInputs(parentID, blockState.importedInputs)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	// Now that the block has been executed, we can add the block data to the
   195  	// state diff.
   196  	stateDiff.SetLastAccepted(blkID)
   197  	stateDiff.AddBlock(b.Block)
   198  
   199  	b.manager.blkIDToState[blkID] = blockState
   200  	b.manager.mempool.Remove(txs...)
   201  	return nil
   202  }
   203  
   204  func (b *Block) Accept(context.Context) error {
   205  	blkID := b.ID()
   206  	defer b.manager.free(blkID)
   207  
   208  	txs := b.Txs()
   209  	for _, tx := range txs {
   210  		if err := b.manager.onAccept(tx); err != nil {
   211  			return fmt.Errorf(
   212  				"failed to mark tx %q as accepted: %w",
   213  				blkID,
   214  				err,
   215  			)
   216  		}
   217  	}
   218  
   219  	b.manager.lastAccepted = blkID
   220  	b.manager.mempool.Remove(txs...)
   221  
   222  	blkState, ok := b.manager.blkIDToState[blkID]
   223  	if !ok {
   224  		return fmt.Errorf("%w: %s", ErrBlockNotFound, blkID)
   225  	}
   226  
   227  	// Update the state to reflect the changes made in [onAcceptState].
   228  	blkState.onAcceptState.Apply(b.manager.state)
   229  
   230  	defer b.manager.state.Abort()
   231  	batch, err := b.manager.state.CommitBatch()
   232  	if err != nil {
   233  		return fmt.Errorf(
   234  			"failed to stage state diff for block %s: %w",
   235  			blkID,
   236  			err,
   237  		)
   238  	}
   239  
   240  	// Note that this method writes [batch] to the database.
   241  	if err := b.manager.backend.Ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil {
   242  		return fmt.Errorf("failed to apply state diff to shared memory: %w", err)
   243  	}
   244  
   245  	if err := b.manager.metrics.MarkBlockAccepted(b); err != nil {
   246  		return err
   247  	}
   248  
   249  	txChecksum, utxoChecksum := b.manager.state.Checksums()
   250  	b.manager.backend.Ctx.Log.Trace(
   251  		"accepted block",
   252  		zap.Stringer("blkID", blkID),
   253  		zap.Uint64("height", b.Height()),
   254  		zap.Stringer("parentID", b.Parent()),
   255  		zap.Stringer("txChecksum", txChecksum),
   256  		zap.Stringer("utxoChecksum", utxoChecksum),
   257  	)
   258  	return nil
   259  }
   260  
   261  func (b *Block) Reject(context.Context) error {
   262  	blkID := b.ID()
   263  	defer b.manager.free(blkID)
   264  
   265  	b.manager.backend.Ctx.Log.Verbo(
   266  		"rejecting block",
   267  		zap.Stringer("blkID", blkID),
   268  		zap.Uint64("height", b.Height()),
   269  		zap.Stringer("parentID", b.Parent()),
   270  	)
   271  
   272  	for _, tx := range b.Txs() {
   273  		if err := b.manager.VerifyTx(tx); err != nil {
   274  			b.manager.backend.Ctx.Log.Debug("dropping invalidated tx",
   275  				zap.Stringer("txID", tx.ID()),
   276  				zap.Stringer("blkID", blkID),
   277  				zap.Error(err),
   278  			)
   279  			continue
   280  		}
   281  		if err := b.manager.mempool.Add(tx); err != nil {
   282  			b.manager.backend.Ctx.Log.Debug("dropping valid tx",
   283  				zap.Stringer("txID", tx.ID()),
   284  				zap.Stringer("blkID", blkID),
   285  				zap.Error(err),
   286  			)
   287  		}
   288  	}
   289  
   290  	// If we added transactions to the mempool, we should be willing to build a
   291  	// block.
   292  	b.manager.mempool.RequestBuildBlock()
   293  	return nil
   294  }