github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/block/executor/verifier.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  	"fmt"
     9  
    10  	"github.com/ava-labs/avalanchego/chains/atomic"
    11  	"github.com/ava-labs/avalanchego/ids"
    12  	"github.com/ava-labs/avalanchego/utils/set"
    13  	"github.com/ava-labs/avalanchego/vms/components/gas"
    14  	"github.com/ava-labs/avalanchego/vms/platformvm/block"
    15  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    16  	"github.com/ava-labs/avalanchego/vms/platformvm/status"
    17  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    18  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
    19  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/fee"
    20  )
    21  
    22  var (
    23  	_ block.Visitor = (*verifier)(nil)
    24  
    25  	ErrConflictingBlockTxs = errors.New("block contains conflicting transactions")
    26  
    27  	errApricotBlockIssuedAfterFork           = errors.New("apricot block issued after fork")
    28  	errBanffStandardBlockWithoutChanges      = errors.New("BanffStandardBlock performs no state changes")
    29  	errIncorrectBlockHeight                  = errors.New("incorrect block height")
    30  	errChildBlockEarlierThanParent           = errors.New("proposed timestamp before current chain time")
    31  	errOptionBlockTimestampNotMatchingParent = errors.New("option block proposed timestamp not matching parent block one")
    32  )
    33  
    34  // verifier handles the logic for verifying a block.
    35  type verifier struct {
    36  	*backend
    37  	txExecutorBackend *executor.Backend
    38  	pChainHeight      uint64
    39  }
    40  
    41  func (v *verifier) BanffAbortBlock(b *block.BanffAbortBlock) error {
    42  	if err := v.banffOptionBlock(b); err != nil {
    43  		return err
    44  	}
    45  	return v.abortBlock(b)
    46  }
    47  
    48  func (v *verifier) BanffCommitBlock(b *block.BanffCommitBlock) error {
    49  	if err := v.banffOptionBlock(b); err != nil {
    50  		return err
    51  	}
    52  	return v.commitBlock(b)
    53  }
    54  
    55  func (v *verifier) BanffProposalBlock(b *block.BanffProposalBlock) error {
    56  	if err := v.banffNonOptionBlock(b); err != nil {
    57  		return err
    58  	}
    59  
    60  	parentID := b.Parent()
    61  	onDecisionState, err := state.NewDiff(parentID, v.backend)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	// Advance the time to [nextChainTime].
    67  	nextChainTime := b.Timestamp()
    68  	if _, err := executor.AdvanceTimeTo(v.txExecutorBackend, onDecisionState, nextChainTime); err != nil {
    69  		return err
    70  	}
    71  
    72  	feeCalculator := state.PickFeeCalculator(v.txExecutorBackend.Config, onDecisionState)
    73  	inputs, atomicRequests, onAcceptFunc, err := v.processStandardTxs(
    74  		b.Transactions,
    75  		feeCalculator,
    76  		onDecisionState,
    77  		b.Parent(),
    78  	)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	onCommitState, err := state.NewDiffOn(onDecisionState)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	onAbortState, err := state.NewDiffOn(onDecisionState)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	return v.proposalBlock(
    94  		&b.ApricotProposalBlock,
    95  		onDecisionState,
    96  		onCommitState,
    97  		onAbortState,
    98  		feeCalculator,
    99  		inputs,
   100  		atomicRequests,
   101  		onAcceptFunc,
   102  	)
   103  }
   104  
   105  func (v *verifier) BanffStandardBlock(b *block.BanffStandardBlock) error {
   106  	if err := v.banffNonOptionBlock(b); err != nil {
   107  		return err
   108  	}
   109  
   110  	parentID := b.Parent()
   111  	onAcceptState, err := state.NewDiff(parentID, v.backend)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	// Advance the time to [b.Timestamp()].
   117  	changed, err := executor.AdvanceTimeTo(
   118  		v.txExecutorBackend,
   119  		onAcceptState,
   120  		b.Timestamp(),
   121  	)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	// If this block doesn't perform any changes, then it should never have been
   127  	// issued.
   128  	if !changed && len(b.Transactions) == 0 {
   129  		return errBanffStandardBlockWithoutChanges
   130  	}
   131  
   132  	feeCalculator := state.PickFeeCalculator(v.txExecutorBackend.Config, onAcceptState)
   133  	return v.standardBlock(&b.ApricotStandardBlock, feeCalculator, onAcceptState)
   134  }
   135  
   136  func (v *verifier) ApricotAbortBlock(b *block.ApricotAbortBlock) error {
   137  	if err := v.apricotCommonBlock(b); err != nil {
   138  		return err
   139  	}
   140  	return v.abortBlock(b)
   141  }
   142  
   143  func (v *verifier) ApricotCommitBlock(b *block.ApricotCommitBlock) error {
   144  	if err := v.apricotCommonBlock(b); err != nil {
   145  		return err
   146  	}
   147  	return v.commitBlock(b)
   148  }
   149  
   150  func (v *verifier) ApricotProposalBlock(b *block.ApricotProposalBlock) error {
   151  	if err := v.apricotCommonBlock(b); err != nil {
   152  		return err
   153  	}
   154  
   155  	parentID := b.Parent()
   156  	onCommitState, err := state.NewDiff(parentID, v.backend)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	onAbortState, err := state.NewDiff(parentID, v.backend)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	var (
   166  		timestamp     = onCommitState.GetTimestamp() // Equal to parent timestamp
   167  		feeCalculator = state.NewStaticFeeCalculator(v.txExecutorBackend.Config, timestamp)
   168  	)
   169  	return v.proposalBlock(b, nil, onCommitState, onAbortState, feeCalculator, nil, nil, nil)
   170  }
   171  
   172  func (v *verifier) ApricotStandardBlock(b *block.ApricotStandardBlock) error {
   173  	if err := v.apricotCommonBlock(b); err != nil {
   174  		return err
   175  	}
   176  
   177  	parentID := b.Parent()
   178  	onAcceptState, err := state.NewDiff(parentID, v)
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	var (
   184  		timestamp     = onAcceptState.GetTimestamp() // Equal to parent timestamp
   185  		feeCalculator = state.NewStaticFeeCalculator(v.txExecutorBackend.Config, timestamp)
   186  	)
   187  	return v.standardBlock(b, feeCalculator, onAcceptState)
   188  }
   189  
   190  func (v *verifier) ApricotAtomicBlock(b *block.ApricotAtomicBlock) error {
   191  	// We call [commonBlock] here rather than [apricotCommonBlock] because below
   192  	// this check we perform the more strict check that ApricotPhase5 isn't
   193  	// activated.
   194  	if err := v.commonBlock(b); err != nil {
   195  		return err
   196  	}
   197  
   198  	parentID := b.Parent()
   199  	currentTimestamp := v.getTimestamp(parentID)
   200  	cfg := v.txExecutorBackend.Config
   201  	if cfg.UpgradeConfig.IsApricotPhase5Activated(currentTimestamp) {
   202  		return fmt.Errorf(
   203  			"the chain timestamp (%d) is after the apricot phase 5 time (%d), hence atomic transactions should go through the standard block",
   204  			currentTimestamp.Unix(),
   205  			cfg.UpgradeConfig.ApricotPhase5Time.Unix(),
   206  		)
   207  	}
   208  
   209  	feeCalculator := state.NewStaticFeeCalculator(v.txExecutorBackend.Config, currentTimestamp)
   210  	atomicExecutor := executor.AtomicTxExecutor{
   211  		Backend:       v.txExecutorBackend,
   212  		FeeCalculator: feeCalculator,
   213  		ParentID:      parentID,
   214  		StateVersions: v,
   215  		Tx:            b.Tx,
   216  	}
   217  
   218  	if err := b.Tx.Unsigned.Visit(&atomicExecutor); err != nil {
   219  		txID := b.Tx.ID()
   220  		v.MarkDropped(txID, err) // cache tx as dropped
   221  		return fmt.Errorf("tx %s failed semantic verification: %w", txID, err)
   222  	}
   223  
   224  	atomicExecutor.OnAccept.AddTx(b.Tx, status.Committed)
   225  
   226  	if err := v.verifyUniqueInputs(parentID, atomicExecutor.Inputs); err != nil {
   227  		return err
   228  	}
   229  
   230  	v.Mempool.Remove(b.Tx)
   231  
   232  	blkID := b.ID()
   233  	v.blkIDToState[blkID] = &blockState{
   234  		statelessBlock: b,
   235  
   236  		onAcceptState: atomicExecutor.OnAccept,
   237  
   238  		inputs:          atomicExecutor.Inputs,
   239  		timestamp:       atomicExecutor.OnAccept.GetTimestamp(),
   240  		atomicRequests:  atomicExecutor.AtomicRequests,
   241  		verifiedHeights: set.Of(v.pChainHeight),
   242  	}
   243  	return nil
   244  }
   245  
   246  func (v *verifier) banffOptionBlock(b block.BanffBlock) error {
   247  	if err := v.commonBlock(b); err != nil {
   248  		return err
   249  	}
   250  
   251  	// Banff option blocks must be uniquely generated from the
   252  	// BanffProposalBlock. This means that the timestamp must be
   253  	// standardized to a specific value. Therefore, we require the timestamp to
   254  	// be equal to the parents timestamp.
   255  	parentID := b.Parent()
   256  	parentBlkTime := v.getTimestamp(parentID)
   257  	blkTime := b.Timestamp()
   258  	if !blkTime.Equal(parentBlkTime) {
   259  		return fmt.Errorf(
   260  			"%w parent block timestamp (%s) option block timestamp (%s)",
   261  			errOptionBlockTimestampNotMatchingParent,
   262  			parentBlkTime,
   263  			blkTime,
   264  		)
   265  	}
   266  	return nil
   267  }
   268  
   269  func (v *verifier) banffNonOptionBlock(b block.BanffBlock) error {
   270  	if err := v.commonBlock(b); err != nil {
   271  		return err
   272  	}
   273  
   274  	parentID := b.Parent()
   275  	parentState, ok := v.GetState(parentID)
   276  	if !ok {
   277  		return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID)
   278  	}
   279  
   280  	newChainTime := b.Timestamp()
   281  	parentChainTime := parentState.GetTimestamp()
   282  	if newChainTime.Before(parentChainTime) {
   283  		return fmt.Errorf(
   284  			"%w: proposed timestamp (%s), chain time (%s)",
   285  			errChildBlockEarlierThanParent,
   286  			newChainTime,
   287  			parentChainTime,
   288  		)
   289  	}
   290  
   291  	nextStakerChangeTime, err := state.GetNextStakerChangeTime(parentState)
   292  	if err != nil {
   293  		return fmt.Errorf("could not verify block timestamp: %w", err)
   294  	}
   295  
   296  	now := v.txExecutorBackend.Clk.Time()
   297  	return executor.VerifyNewChainTime(
   298  		newChainTime,
   299  		nextStakerChangeTime,
   300  		now,
   301  	)
   302  }
   303  
   304  func (v *verifier) apricotCommonBlock(b block.Block) error {
   305  	// We can use the parent timestamp here, because we are guaranteed that the
   306  	// parent was verified. Apricot blocks only update the timestamp with
   307  	// AdvanceTimeTxs. This means that this block's timestamp will be equal to
   308  	// the parent block's timestamp; unless this is a CommitBlock. In order for
   309  	// the timestamp of the CommitBlock to be after the Banff activation,
   310  	// the parent ApricotProposalBlock must include an AdvanceTimeTx with a
   311  	// timestamp after the Banff timestamp. This is verified not to occur
   312  	// during the verification of the ProposalBlock.
   313  	parentID := b.Parent()
   314  	timestamp := v.getTimestamp(parentID)
   315  	if v.txExecutorBackend.Config.UpgradeConfig.IsBanffActivated(timestamp) {
   316  		return fmt.Errorf("%w: timestamp = %s", errApricotBlockIssuedAfterFork, timestamp)
   317  	}
   318  	return v.commonBlock(b)
   319  }
   320  
   321  func (v *verifier) commonBlock(b block.Block) error {
   322  	parentID := b.Parent()
   323  	parent, err := v.GetBlock(parentID)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	expectedHeight := parent.Height() + 1
   329  	height := b.Height()
   330  	if expectedHeight != height {
   331  		return fmt.Errorf(
   332  			"%w expected %d, but found %d",
   333  			errIncorrectBlockHeight,
   334  			expectedHeight,
   335  			height,
   336  		)
   337  	}
   338  	return nil
   339  }
   340  
   341  // abortBlock populates the state of this block if [nil] is returned
   342  func (v *verifier) abortBlock(b block.Block) error {
   343  	parentID := b.Parent()
   344  	onAbortState, ok := v.getOnAbortState(parentID)
   345  	if !ok {
   346  		return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID)
   347  	}
   348  
   349  	blkID := b.ID()
   350  	v.blkIDToState[blkID] = &blockState{
   351  		statelessBlock:  b,
   352  		onAcceptState:   onAbortState,
   353  		timestamp:       onAbortState.GetTimestamp(),
   354  		verifiedHeights: set.Of(v.pChainHeight),
   355  	}
   356  	return nil
   357  }
   358  
   359  // commitBlock populates the state of this block if [nil] is returned
   360  func (v *verifier) commitBlock(b block.Block) error {
   361  	parentID := b.Parent()
   362  	onCommitState, ok := v.getOnCommitState(parentID)
   363  	if !ok {
   364  		return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID)
   365  	}
   366  
   367  	blkID := b.ID()
   368  	v.blkIDToState[blkID] = &blockState{
   369  		statelessBlock:  b,
   370  		onAcceptState:   onCommitState,
   371  		timestamp:       onCommitState.GetTimestamp(),
   372  		verifiedHeights: set.Of(v.pChainHeight),
   373  	}
   374  	return nil
   375  }
   376  
   377  // proposalBlock populates the state of this block if [nil] is returned
   378  func (v *verifier) proposalBlock(
   379  	b *block.ApricotProposalBlock,
   380  	onDecisionState state.Diff,
   381  	onCommitState state.Diff,
   382  	onAbortState state.Diff,
   383  	feeCalculator fee.Calculator,
   384  	inputs set.Set[ids.ID],
   385  	atomicRequests map[ids.ID]*atomic.Requests,
   386  	onAcceptFunc func(),
   387  ) error {
   388  	txExecutor := executor.ProposalTxExecutor{
   389  		OnCommitState: onCommitState,
   390  		OnAbortState:  onAbortState,
   391  		Backend:       v.txExecutorBackend,
   392  		FeeCalculator: feeCalculator,
   393  		Tx:            b.Tx,
   394  	}
   395  
   396  	if err := b.Tx.Unsigned.Visit(&txExecutor); err != nil {
   397  		txID := b.Tx.ID()
   398  		v.MarkDropped(txID, err) // cache tx as dropped
   399  		return err
   400  	}
   401  
   402  	onCommitState.AddTx(b.Tx, status.Committed)
   403  	onAbortState.AddTx(b.Tx, status.Aborted)
   404  
   405  	v.Mempool.Remove(b.Tx)
   406  
   407  	blkID := b.ID()
   408  	v.blkIDToState[blkID] = &blockState{
   409  		proposalBlockState: proposalBlockState{
   410  			onDecisionState: onDecisionState,
   411  			onCommitState:   onCommitState,
   412  			onAbortState:    onAbortState,
   413  		},
   414  
   415  		statelessBlock: b,
   416  
   417  		onAcceptFunc: onAcceptFunc,
   418  
   419  		inputs: inputs,
   420  		// It is safe to use [b.onAbortState] here because the timestamp will
   421  		// never be modified by an Apricot Abort block and the timestamp will
   422  		// always be the same as the Banff Proposal Block.
   423  		timestamp:       onAbortState.GetTimestamp(),
   424  		atomicRequests:  atomicRequests,
   425  		verifiedHeights: set.Of(v.pChainHeight),
   426  	}
   427  	return nil
   428  }
   429  
   430  // standardBlock populates the state of this block if [nil] is returned
   431  func (v *verifier) standardBlock(
   432  	b *block.ApricotStandardBlock,
   433  	feeCalculator fee.Calculator,
   434  	onAcceptState state.Diff,
   435  ) error {
   436  	inputs, atomicRequests, onAcceptFunc, err := v.processStandardTxs(b.Transactions, feeCalculator, onAcceptState, b.Parent())
   437  	if err != nil {
   438  		return err
   439  	}
   440  
   441  	v.Mempool.Remove(b.Transactions...)
   442  
   443  	blkID := b.ID()
   444  	v.blkIDToState[blkID] = &blockState{
   445  		statelessBlock: b,
   446  
   447  		onAcceptState: onAcceptState,
   448  		onAcceptFunc:  onAcceptFunc,
   449  
   450  		timestamp:       onAcceptState.GetTimestamp(),
   451  		inputs:          inputs,
   452  		atomicRequests:  atomicRequests,
   453  		verifiedHeights: set.Of(v.pChainHeight),
   454  	}
   455  	return nil
   456  }
   457  
   458  func (v *verifier) processStandardTxs(txs []*txs.Tx, feeCalculator fee.Calculator, state state.Diff, parentID ids.ID) (
   459  	set.Set[ids.ID],
   460  	map[ids.ID]*atomic.Requests,
   461  	func(),
   462  	error,
   463  ) {
   464  	// Complexity is limited first to avoid processing too large of a block.
   465  	if timestamp := state.GetTimestamp(); v.txExecutorBackend.Config.UpgradeConfig.IsEtnaActivated(timestamp) {
   466  		var blockComplexity gas.Dimensions
   467  		for _, tx := range txs {
   468  			txComplexity, err := fee.TxComplexity(tx.Unsigned)
   469  			if err != nil {
   470  				txID := tx.ID()
   471  				v.MarkDropped(txID, err)
   472  				return nil, nil, nil, err
   473  			}
   474  
   475  			blockComplexity, err = blockComplexity.Add(&txComplexity)
   476  			if err != nil {
   477  				return nil, nil, nil, err
   478  			}
   479  		}
   480  
   481  		blockGas, err := blockComplexity.ToGas(v.txExecutorBackend.Config.DynamicFeeConfig.Weights)
   482  		if err != nil {
   483  			return nil, nil, nil, err
   484  		}
   485  
   486  		// If this block exceeds the available capacity, ConsumeGas will return
   487  		// an error.
   488  		feeState := state.GetFeeState()
   489  		feeState, err = feeState.ConsumeGas(blockGas)
   490  		if err != nil {
   491  			return nil, nil, nil, err
   492  		}
   493  
   494  		// Updating the fee state prior to executing the transactions is fine
   495  		// because the fee calculator was already created.
   496  		state.SetFeeState(feeState)
   497  	}
   498  
   499  	var (
   500  		onAcceptFunc   func()
   501  		inputs         set.Set[ids.ID]
   502  		funcs          = make([]func(), 0, len(txs))
   503  		atomicRequests = make(map[ids.ID]*atomic.Requests)
   504  	)
   505  	for _, tx := range txs {
   506  		txExecutor := executor.StandardTxExecutor{
   507  			Backend:       v.txExecutorBackend,
   508  			State:         state,
   509  			FeeCalculator: feeCalculator,
   510  			Tx:            tx,
   511  		}
   512  		if err := tx.Unsigned.Visit(&txExecutor); err != nil {
   513  			txID := tx.ID()
   514  			v.MarkDropped(txID, err) // cache tx as dropped
   515  			return nil, nil, nil, err
   516  		}
   517  		// ensure it doesn't overlap with current input batch
   518  		if inputs.Overlaps(txExecutor.Inputs) {
   519  			return nil, nil, nil, ErrConflictingBlockTxs
   520  		}
   521  		// Add UTXOs to batch
   522  		inputs.Union(txExecutor.Inputs)
   523  
   524  		state.AddTx(tx, status.Committed)
   525  		if txExecutor.OnAccept != nil {
   526  			funcs = append(funcs, txExecutor.OnAccept)
   527  		}
   528  
   529  		for chainID, txRequests := range txExecutor.AtomicRequests {
   530  			// Add/merge in the atomic requests represented by [tx]
   531  			chainRequests, exists := atomicRequests[chainID]
   532  			if !exists {
   533  				atomicRequests[chainID] = txRequests
   534  				continue
   535  			}
   536  
   537  			chainRequests.PutRequests = append(chainRequests.PutRequests, txRequests.PutRequests...)
   538  			chainRequests.RemoveRequests = append(chainRequests.RemoveRequests, txRequests.RemoveRequests...)
   539  		}
   540  	}
   541  
   542  	if err := v.verifyUniqueInputs(parentID, inputs); err != nil {
   543  		return nil, nil, nil, err
   544  	}
   545  
   546  	if numFuncs := len(funcs); numFuncs == 1 {
   547  		onAcceptFunc = funcs[0]
   548  	} else if numFuncs > 1 {
   549  		onAcceptFunc = func() {
   550  			for _, f := range funcs {
   551  				f()
   552  			}
   553  		}
   554  	}
   555  
   556  	return inputs, atomicRequests, onAcceptFunc, nil
   557  }