github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/pre_fork_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 proposervm
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"time"
    11  
    12  	"go.uber.org/zap"
    13  
    14  	"github.com/ava-labs/avalanchego/ids"
    15  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    16  	"github.com/ava-labs/avalanchego/vms/proposervm/block"
    17  )
    18  
    19  var (
    20  	_ Block = (*preForkBlock)(nil)
    21  
    22  	errChildOfPreForkBlockHasProposer = errors.New("child of pre-fork block has proposer")
    23  )
    24  
    25  type preForkBlock struct {
    26  	snowman.Block
    27  	vm *VM
    28  }
    29  
    30  func (b *preForkBlock) Accept(ctx context.Context) error {
    31  	if err := b.acceptOuterBlk(); err != nil {
    32  		return err
    33  	}
    34  	return b.acceptInnerBlk(ctx)
    35  }
    36  
    37  func (*preForkBlock) acceptOuterBlk() error {
    38  	return nil
    39  }
    40  
    41  func (b *preForkBlock) acceptInnerBlk(ctx context.Context) error {
    42  	return b.Block.Accept(ctx)
    43  }
    44  
    45  func (b *preForkBlock) Verify(ctx context.Context) error {
    46  	parent, err := b.vm.getPreForkBlock(ctx, b.Block.Parent())
    47  	if err != nil {
    48  		return err
    49  	}
    50  	return parent.verifyPreForkChild(ctx, b)
    51  }
    52  
    53  func (b *preForkBlock) Options(ctx context.Context) ([2]snowman.Block, error) {
    54  	oracleBlk, ok := b.Block.(snowman.OracleBlock)
    55  	if !ok {
    56  		return [2]snowman.Block{}, snowman.ErrNotOracle
    57  	}
    58  
    59  	options, err := oracleBlk.Options(ctx)
    60  	if err != nil {
    61  		return [2]snowman.Block{}, err
    62  	}
    63  	// A pre-fork block's child options are always pre-fork blocks
    64  	return [2]snowman.Block{
    65  		&preForkBlock{
    66  			Block: options[0],
    67  			vm:    b.vm,
    68  		},
    69  		&preForkBlock{
    70  			Block: options[1],
    71  			vm:    b.vm,
    72  		},
    73  	}, nil
    74  }
    75  
    76  func (b *preForkBlock) getInnerBlk() snowman.Block {
    77  	return b.Block
    78  }
    79  
    80  func (b *preForkBlock) verifyPreForkChild(ctx context.Context, child *preForkBlock) error {
    81  	parentTimestamp := b.Timestamp()
    82  	if b.vm.Upgrades.IsApricotPhase4Activated(parentTimestamp) {
    83  		if err := verifyIsOracleBlock(ctx, b.Block); err != nil {
    84  			return err
    85  		}
    86  
    87  		b.vm.ctx.Log.Debug("allowing pre-fork block after the fork time",
    88  			zap.String("reason", "parent is an oracle block"),
    89  			zap.Stringer("blkID", b.ID()),
    90  		)
    91  	}
    92  
    93  	return child.Block.Verify(ctx)
    94  }
    95  
    96  // This method only returns nil once (during the transition)
    97  func (b *preForkBlock) verifyPostForkChild(ctx context.Context, child *postForkBlock) error {
    98  	if err := verifyIsNotOracleBlock(ctx, b.Block); err != nil {
    99  		return err
   100  	}
   101  
   102  	childID := child.ID()
   103  	childPChainHeight := child.PChainHeight()
   104  	currentPChainHeight, err := b.vm.ctx.ValidatorState.GetCurrentHeight(ctx)
   105  	if err != nil {
   106  		b.vm.ctx.Log.Error("block verification failed",
   107  			zap.String("reason", "failed to get current P-Chain height"),
   108  			zap.Stringer("blkID", childID),
   109  			zap.Error(err),
   110  		)
   111  		return err
   112  	}
   113  	if childPChainHeight > currentPChainHeight {
   114  		return fmt.Errorf("%w: %d > %d",
   115  			errPChainHeightNotReached,
   116  			childPChainHeight,
   117  			currentPChainHeight,
   118  		)
   119  	}
   120  	if childPChainHeight < b.vm.Upgrades.ApricotPhase4MinPChainHeight {
   121  		return errPChainHeightTooLow
   122  	}
   123  
   124  	// Make sure [b] is the parent of [child]'s inner block
   125  	expectedInnerParentID := b.ID()
   126  	innerParentID := child.innerBlk.Parent()
   127  	if innerParentID != expectedInnerParentID {
   128  		return errInnerParentMismatch
   129  	}
   130  
   131  	// A *preForkBlock can only have a *postForkBlock child
   132  	// if the *preForkBlock is the last *preForkBlock before activation takes effect
   133  	// (its timestamp is at or after the activation time)
   134  	parentTimestamp := b.Timestamp()
   135  	if !b.vm.Upgrades.IsApricotPhase4Activated(parentTimestamp) {
   136  		return errProposersNotActivated
   137  	}
   138  
   139  	// Child's timestamp must be at or after its parent's timestamp
   140  	childTimestamp := child.Timestamp()
   141  	if childTimestamp.Before(parentTimestamp) {
   142  		return errTimeNotMonotonic
   143  	}
   144  
   145  	// Child timestamp can't be too far in the future
   146  	maxTimestamp := b.vm.Time().Add(maxSkew)
   147  	if childTimestamp.After(maxTimestamp) {
   148  		return errTimeTooAdvanced
   149  	}
   150  
   151  	// Verify the lack of signature on the node
   152  	if child.SignedBlock.Proposer() != ids.EmptyNodeID {
   153  		return errChildOfPreForkBlockHasProposer
   154  	}
   155  
   156  	// Verify the inner block and track it as verified
   157  	return b.vm.verifyAndRecordInnerBlk(ctx, nil, child)
   158  }
   159  
   160  func (*preForkBlock) verifyPostForkOption(context.Context, *postForkOption) error {
   161  	return errUnexpectedBlockType
   162  }
   163  
   164  func (b *preForkBlock) buildChild(ctx context.Context) (Block, error) {
   165  	parentTimestamp := b.Timestamp()
   166  	if !b.vm.Upgrades.IsApricotPhase4Activated(parentTimestamp) {
   167  		// The chain hasn't forked yet
   168  		innerBlock, err := b.vm.ChainVM.BuildBlock(ctx)
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  
   173  		b.vm.ctx.Log.Info("built block",
   174  			zap.Stringer("blkID", innerBlock.ID()),
   175  			zap.Uint64("height", innerBlock.Height()),
   176  			zap.Time("parentTimestamp", parentTimestamp),
   177  		)
   178  
   179  		return &preForkBlock{
   180  			Block: innerBlock,
   181  			vm:    b.vm,
   182  		}, nil
   183  	}
   184  
   185  	// The chain is currently forking
   186  
   187  	parentID := b.ID()
   188  	newTimestamp := b.vm.Time().Truncate(time.Second)
   189  	if newTimestamp.Before(parentTimestamp) {
   190  		newTimestamp = parentTimestamp
   191  	}
   192  
   193  	// The child's P-Chain height is proposed as the optimal P-Chain height that
   194  	// is at least the minimum height
   195  	pChainHeight, err := b.vm.optimalPChainHeight(ctx, b.vm.Upgrades.ApricotPhase4MinPChainHeight)
   196  	if err != nil {
   197  		b.vm.ctx.Log.Error("unexpected build block failure",
   198  			zap.String("reason", "failed to calculate optimal P-chain height"),
   199  			zap.Stringer("parentID", parentID),
   200  			zap.Error(err),
   201  		)
   202  		return nil, err
   203  	}
   204  
   205  	innerBlock, err := b.vm.ChainVM.BuildBlock(ctx)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  
   210  	statelessBlock, err := block.BuildUnsigned(
   211  		parentID,
   212  		newTimestamp,
   213  		pChainHeight,
   214  		innerBlock.Bytes(),
   215  	)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	blk := &postForkBlock{
   221  		SignedBlock: statelessBlock,
   222  		postForkCommonComponents: postForkCommonComponents{
   223  			vm:       b.vm,
   224  			innerBlk: innerBlock,
   225  		},
   226  	}
   227  
   228  	b.vm.ctx.Log.Info("built block",
   229  		zap.Stringer("blkID", blk.ID()),
   230  		zap.Stringer("innerBlkID", innerBlock.ID()),
   231  		zap.Uint64("height", blk.Height()),
   232  		zap.Uint64("pChainHeight", pChainHeight),
   233  		zap.Time("parentTimestamp", parentTimestamp),
   234  		zap.Time("blockTimestamp", newTimestamp))
   235  	return blk, nil
   236  }
   237  
   238  func (*preForkBlock) pChainHeight(context.Context) (uint64, error) {
   239  	return 0, nil
   240  }