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 }